View Javadoc

1   /*
2    * Copyright [2006] [University Corporation for Advanced Internet Development, Inc.]
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.opensaml.xml.security.keyinfo;
18  
19  import java.math.BigInteger;
20  import java.security.KeyException;
21  import java.security.KeyFactory;
22  import java.security.NoSuchAlgorithmException;
23  import java.security.PublicKey;
24  import java.security.cert.CRLException;
25  import java.security.cert.CertificateEncodingException;
26  import java.security.cert.CertificateException;
27  import java.security.cert.CertificateFactory;
28  import java.security.cert.X509CRL;
29  import java.security.cert.X509Certificate;
30  import java.security.interfaces.DSAParams;
31  import java.security.interfaces.DSAPublicKey;
32  import java.security.interfaces.RSAPublicKey;
33  import java.security.spec.DSAParameterSpec;
34  import java.security.spec.DSAPublicKeySpec;
35  import java.security.spec.InvalidKeySpecException;
36  import java.security.spec.KeySpec;
37  import java.security.spec.RSAPublicKeySpec;
38  import java.util.Collection;
39  import java.util.LinkedList;
40  import java.util.List;
41  
42  import org.opensaml.xml.Configuration;
43  import org.opensaml.xml.XMLObjectBuilderFactory;
44  import org.opensaml.xml.security.x509.X509Util;
45  import org.opensaml.xml.signature.DSAKeyValue;
46  import org.opensaml.xml.signature.Exponent;
47  import org.opensaml.xml.signature.G;
48  import org.opensaml.xml.signature.KeyInfo;
49  import org.opensaml.xml.signature.KeyName;
50  import org.opensaml.xml.signature.KeyValue;
51  import org.opensaml.xml.signature.Modulus;
52  import org.opensaml.xml.signature.P;
53  import org.opensaml.xml.signature.Q;
54  import org.opensaml.xml.signature.RSAKeyValue;
55  import org.opensaml.xml.signature.X509Data;
56  import org.opensaml.xml.signature.X509IssuerName;
57  import org.opensaml.xml.signature.X509IssuerSerial;
58  import org.opensaml.xml.signature.X509SKI;
59  import org.opensaml.xml.signature.X509SerialNumber;
60  import org.opensaml.xml.signature.X509SubjectName;
61  import org.opensaml.xml.signature.Y;
62  import org.opensaml.xml.util.Base64;
63  import org.opensaml.xml.util.DatatypeHelper;
64  import org.slf4j.Logger;
65  import org.slf4j.LoggerFactory;
66  
67  /**
68   * Utility class for working with data inside a KeyInfo object.
69   * 
70   * Methods are provided for converting the representation stored in the XMLTooling KeyInfo to Java
71   * java.security native types, and for storing these Java native types inside a KeyInfo.
72   */
73  public class KeyInfoHelper {
74  
75      /** Class logger. */
76      private static Logger log = LoggerFactory.getLogger(KeyInfoHelper.class);
77      
78      /** Factory for {@link java.security.cert.X509Certificate} and
79       * {@link java.security.cert.X509CRL} creation. */
80      private static CertificateFactory x509CertFactory;
81      
82      /** Constructor. */
83      protected KeyInfoHelper(){
84          
85      }    
86  
87      /**
88       * Get the set of key names inside the specified {@link KeyInfo} as a list of strings.
89       * 
90       * @param keyInfo {@link KeyInfo} to retrieve key names from
91       * 
92       * @return a list of key name strings
93       */
94      public static List<String> getKeyNames(KeyInfo keyInfo) {
95          List<String> keynameList = new LinkedList<String>();
96  
97          if (keyInfo == null) {
98              return keynameList;
99          }
100 
101         List<KeyName> keyNames = keyInfo.getKeyNames();
102         for (KeyName keyName : keyNames) {
103             if (keyName.getValue() != null) {
104                 keynameList.add(keyName.getValue());
105             }
106         }
107 
108         return keynameList;
109     }
110     
111     /**
112      * Add a new {@link KeyName} value to a KeyInfo.
113      * 
114      * @param keyInfo the KeyInfo to which to add the new value
115      * @param keyNameValue the new key name value to add
116      */
117     public static void addKeyName(KeyInfo keyInfo, String keyNameValue) {
118         KeyName keyName = (KeyName) Configuration.getBuilderFactory()
119             .getBuilder(KeyName.DEFAULT_ELEMENT_NAME)
120             .buildObject(KeyName.DEFAULT_ELEMENT_NAME);
121         keyName.setValue(keyNameValue);
122         keyInfo.getKeyNames().add(keyName);
123     }
124 
125     /**
126      * Get a list of the Java {@link java.security.cert.X509Certificate} within the given KeyInfo.
127      * 
128      * @param keyInfo key info to extract the certificates from
129      * 
130      * @return a list of Java {@link java.security.cert.X509Certificate}s
131      * 
132      * @throws CertificateException thrown if there is a problem converting the 
133      *          X509 data into {@link java.security.cert.X509Certificate}s.
134      */
135     public static List<X509Certificate> getCertificates(KeyInfo keyInfo) throws CertificateException {
136         List<X509Certificate> certList = new LinkedList<X509Certificate>();
137 
138         if (keyInfo == null) {
139             return certList;
140         }
141 
142         List<X509Data> x509Datas = keyInfo.getX509Datas();
143         for (X509Data x509Data : x509Datas) {
144             if (x509Data != null) {
145                 certList.addAll(getCertificates(x509Data));
146             }
147         }
148 
149         return certList;
150     }
151 
152     /**
153      * Get a list of the Java {@link java.security.cert.X509Certificate} within the given {@link X509Data}.
154      * 
155      * @param x509Data {@link X509Data} from which to extract the certificate
156      * 
157      * @return a list of Java {@link java.security.cert.X509Certificate}s
158      * 
159      * @throws CertificateException thrown if there is a problem converting the 
160      *          X509 data into {@link java.security.cert.X509Certificate}s.
161      */
162     public static List<X509Certificate> getCertificates(X509Data x509Data) throws CertificateException {
163         List<X509Certificate> certList = new LinkedList<X509Certificate>();
164 
165         if (x509Data == null) {
166             return certList;
167         }
168 
169         for (org.opensaml.xml.signature.X509Certificate xmlCert : x509Data.getX509Certificates()) {
170             if (xmlCert != null && xmlCert.getValue() != null) {
171                 X509Certificate newCert = getCertificate(xmlCert);
172                 certList.add(newCert);
173             }
174         }
175 
176         return certList;
177     }
178 
179     /**
180      * Convert an {@link org.opensaml.xml.signature.X509Certificate} into a native Java representation.
181      * 
182      * @param xmlCert an {@link org.opensaml.xml.signature.X509Certificate}
183      * 
184      * @return a {@link java.security.cert.X509Certificate}
185      * 
186      * @throws CertificateException thrown if there is a problem converting the 
187      *           X509 data into {@link java.security.cert.X509Certificate}s.
188      */
189     public static X509Certificate getCertificate(org.opensaml.xml.signature.X509Certificate xmlCert)
190             throws CertificateException {
191 
192         if (xmlCert == null || xmlCert.getValue() == null) {
193             return null;
194         }
195 
196         Collection<X509Certificate> certs = X509Util.decodeCertificate(Base64.decode(xmlCert.getValue()));
197         if (certs != null && certs.iterator().hasNext()) {
198             return certs.iterator().next();
199         } else {
200             return null;
201         }
202     }
203 
204     /**
205      * Get a list of the Java {@link java.security.cert.X509CRL}s within the given {@link KeyInfo}.
206      * 
207      * @param keyInfo the {@link KeyInfo} to extract the CRL's from
208      * 
209      * @return a list of Java {@link java.security.cert.X509CRL}s
210      * 
211      * @throws CRLException thrown if there is a problem converting the 
212      *          CRL data into {@link java.security.cert.X509CRL}s
213      */
214     public static List<X509CRL> getCRLs(KeyInfo keyInfo) throws CRLException {
215         List<X509CRL> crlList = new LinkedList<X509CRL>();
216 
217         if (keyInfo == null) {
218             return crlList;
219         }
220 
221         List<X509Data> x509Datas = keyInfo.getX509Datas();
222         for (X509Data x509Data : x509Datas) {
223             if (x509Data != null) {
224                 crlList.addAll(getCRLs(x509Data));
225             }
226         }
227 
228         return crlList;
229     }
230 
231     /**
232      * Get a list of the Java {@link java.security.cert.X509CRL}s within the given {@link X509Data}.
233      * 
234      * @param x509Data {@link X509Data} to extract the CRLs from
235      * 
236      * @return a list of Java {@link java.security.cert.X509CRL}s
237      * 
238      * @throws CRLException thrown if there is a problem converting the 
239      *          CRL data into {@link java.security.cert.X509CRL}s
240      */
241     public static List<X509CRL> getCRLs(X509Data x509Data) throws CRLException {
242         List<X509CRL> crlList = new LinkedList<X509CRL>();
243 
244         if (x509Data == null) {
245             return crlList;
246         }
247 
248         for (org.opensaml.xml.signature.X509CRL xmlCRL : x509Data.getX509CRLs()) {
249             if (xmlCRL != null && xmlCRL.getValue() != null) {
250                 X509CRL newCRL = getCRL(xmlCRL);
251                 crlList.add(newCRL);
252             }
253         }
254 
255         return crlList;
256     }
257 
258     /**
259      * Convert an {@link org.opensaml.xml.signature.X509CRL} into a native Java representation.
260      * 
261      * @param xmlCRL object to extract the CRL from
262      * 
263      * @return a native Java {@link java.security.cert.X509CRL} object
264      * 
265      * @throws CRLException thrown if there is a problem converting the 
266      *          CRL data into {@link java.security.cert.X509CRL}s
267      */
268     public static X509CRL getCRL(org.opensaml.xml.signature.X509CRL xmlCRL) throws CRLException {
269 
270         if (xmlCRL == null || xmlCRL.getValue() == null) {
271             return null;
272         }
273         
274         Collection<X509CRL> crls = X509Util.decodeCRLs(Base64.decode(xmlCRL.getValue()));
275         return crls.iterator().next();
276     }
277 
278     /**
279      * Converts a native Java {@link java.security.cert.X509Certificate} into the corresponding 
280      * XMLObject and stores it in a {@link KeyInfo} in the first {@link X509Data} element. 
281      * The X509Data element will be created if necessary.
282      * 
283      * @param keyInfo the {@link KeyInfo} object into which to add the certificate
284      * @param cert the Java {@link java.security.cert.X509Certificate} to add
285      * @throws CertificateEncodingException thrown when there is an error converting the Java 
286      *           certificate representation to the XMLObject representation
287      */
288     public static void addCertificate(KeyInfo keyInfo, X509Certificate cert) throws CertificateEncodingException {
289         X509Data x509Data;
290         if (keyInfo.getX509Datas().size() == 0) {
291             x509Data = (X509Data) Configuration.getBuilderFactory()
292                 .getBuilder(X509Data.DEFAULT_ELEMENT_NAME)
293                 .buildObject(X509Data.DEFAULT_ELEMENT_NAME);
294             keyInfo.getX509Datas().add(x509Data);
295         } else {
296             x509Data = keyInfo.getX509Datas().get(0);
297         }
298         x509Data.getX509Certificates().add(buildX509Certificate(cert));
299     }
300 
301     /**
302      * Converts a native Java {@link java.security.cert.X509CRL} into the corresponding XMLObject and stores it
303      * in a {@link KeyInfo} in the first {@link X509Data} element.  The X509Data element
304      * will be created if necessary.
305      * 
306      * @param keyInfo the {@link KeyInfo} object into which to add the CRL
307      * @param crl the Java {@link java.security.cert.X509CRL} to add
308      * @throws CRLException thrown when there is an error converting the Java 
309      *           CRL representation to the XMLObject representation
310      */
311     public static void addCRL(KeyInfo keyInfo, X509CRL crl) throws CRLException {
312         X509Data x509Data;
313         if (keyInfo.getX509Datas().size() == 0) {
314             x509Data = (X509Data) Configuration.getBuilderFactory()
315                 .getBuilder(X509Data.DEFAULT_ELEMENT_NAME)
316                 .buildObject(X509Data.DEFAULT_ELEMENT_NAME);
317             keyInfo.getX509Datas().add(x509Data);
318         } else {
319             x509Data = keyInfo.getX509Datas().get(0);
320         }
321         x509Data.getX509CRLs().add(buildX509CRL(crl));
322     }
323     
324     /**
325      * Builds an {@link org.opensaml.xml.signature.X509Certificate} XMLObject from a native 
326      * Java {@link java.security.cert.X509Certificate}.
327      * 
328      * @param cert the Java {@link java.security.cert.X509Certificate} to convert
329      * @return a {@link org.opensaml.xml.signature.X509Certificate} XMLObject
330      * @throws CertificateEncodingException thrown when there is an error converting the Java 
331      *           certificate representation to the XMLObject representation
332      */
333     public static org.opensaml.xml.signature.X509Certificate 
334     buildX509Certificate(X509Certificate cert) throws CertificateEncodingException {
335         org.opensaml.xml.signature.X509Certificate xmlCert =
336             (org.opensaml.xml.signature.X509Certificate) Configuration.getBuilderFactory()
337             .getBuilder(org.opensaml.xml.signature.X509Certificate.DEFAULT_ELEMENT_NAME)
338             .buildObject(org.opensaml.xml.signature.X509Certificate.DEFAULT_ELEMENT_NAME);
339         
340         xmlCert.setValue(Base64.encodeBytes(cert.getEncoded()));
341         
342         return xmlCert;
343     }
344     
345     /**
346      * Builds an {@link org.opensaml.xml.signature.X509CRL} XMLObject from
347      * a native Java {@link java.security.cert.X509CRL}.
348      * 
349      * @param crl the Java {@link java.security.cert.X509CRL} to convert
350      * @return a {@link org.opensaml.xml.signature.X509CRL} XMLObject
351      * @throws CRLException thrown when there is an error converting the Java 
352      *           CRL representation to the XMLObject representation
353      */
354     public static org.opensaml.xml.signature.X509CRL buildX509CRL(X509CRL crl) throws CRLException {
355         org.opensaml.xml.signature.X509CRL xmlCRL =
356             (org.opensaml.xml.signature.X509CRL) Configuration.getBuilderFactory()
357             .getBuilder(org.opensaml.xml.signature.X509CRL.DEFAULT_ELEMENT_NAME)
358             .buildObject(org.opensaml.xml.signature.X509CRL.DEFAULT_ELEMENT_NAME);
359         
360         xmlCRL.setValue(Base64.encodeBytes(crl.getEncoded()));
361         
362         return xmlCRL;
363     }
364     
365     /**
366      * Build an {@link X509SubjectName} containing a given subject name.
367      * 
368      * @param subjectName the name content
369      * @return the new X509SubjectName
370      */
371     public static X509SubjectName buildX509SubjectName(String subjectName) {
372         X509SubjectName xmlSubjectName = (X509SubjectName) Configuration.getBuilderFactory()
373             .getBuilder(X509SubjectName.DEFAULT_ELEMENT_NAME)
374             .buildObject(X509SubjectName.DEFAULT_ELEMENT_NAME);
375         xmlSubjectName.setValue(subjectName); 
376         return xmlSubjectName;
377     }
378     
379     /**
380      * Build an {@link X509IssuerSerial} containing a given issuer name and serial number.
381      * 
382      * @param issuerName the name content
383      * @param serialNumber the serial number content
384      * @return the new X509IssuerSerial
385      */
386     public static X509IssuerSerial buildX509IssuerSerial(String issuerName, BigInteger serialNumber) {
387         X509IssuerName xmlIssuerName = (X509IssuerName) Configuration.getBuilderFactory()
388             .getBuilder(X509IssuerName.DEFAULT_ELEMENT_NAME)
389         .buildObject(X509IssuerName.DEFAULT_ELEMENT_NAME);
390         xmlIssuerName.setValue(issuerName);
391         
392         X509SerialNumber xmlSerialNumber = (X509SerialNumber) Configuration.getBuilderFactory()
393             .getBuilder(X509SerialNumber.DEFAULT_ELEMENT_NAME)
394             .buildObject(X509SerialNumber.DEFAULT_ELEMENT_NAME);
395         xmlSerialNumber.setValue(serialNumber);
396         
397         X509IssuerSerial xmlIssuerSerial = (X509IssuerSerial) Configuration.getBuilderFactory()
398             .getBuilder(X509IssuerSerial.DEFAULT_ELEMENT_NAME)
399             .buildObject(X509IssuerSerial.DEFAULT_ELEMENT_NAME);
400         xmlIssuerSerial.setX509IssuerName(xmlIssuerName);
401         xmlIssuerSerial.setX509SerialNumber(xmlSerialNumber);
402         
403         return xmlIssuerSerial;
404     }
405     
406     /**
407      * Build an {@link X509SKI} containing the subject key identifier extension value contained within
408      * a certificate.
409      * 
410      * @param javaCert the Java X509Certificate from which to extract the subject key identifier value.
411      * @return a new X509SKI object, or null if the certificate did not contain the subject key identifier extension
412      */
413     public static X509SKI buildX509SKI(X509Certificate javaCert) {
414         byte[] skiPlainValue = X509Util.getSubjectKeyIdentifier(javaCert);
415         if (skiPlainValue == null || skiPlainValue.length == 0) {
416             return null;
417         }
418         
419         X509SKI xmlSKI = (X509SKI) Configuration.getBuilderFactory()
420             .getBuilder(X509SKI.DEFAULT_ELEMENT_NAME)
421             .buildObject(X509SKI.DEFAULT_ELEMENT_NAME);
422         xmlSKI.setValue(Base64.encodeBytes(skiPlainValue));
423         
424         return xmlSKI;
425     }
426 
427     /**
428      * Converts a Java DSA or RSA public key into the corresponding XMLObject and stores it
429      * in a {@link KeyInfo} in a new {@link KeyValue} element.
430      * 
431      * As input, only supports {@link PublicKey}s which are instances of either
432      * {@link java.security.interfaces.DSAPublicKey} or
433      * {@link java.security.interfaces.RSAPublicKey}
434      * 
435      * @param keyInfo the {@link KeyInfo} element to which to add the key
436      * @param pk the native Java {@link PublicKey} to add
437      * @throws IllegalArgumentException thrown if an unsupported public key
438      *          type is passed
439      */
440     public static void addPublicKey(KeyInfo keyInfo, PublicKey pk) throws IllegalArgumentException {
441         KeyValue keyValue = (KeyValue) Configuration.getBuilderFactory()
442             .getBuilder(KeyValue.DEFAULT_ELEMENT_NAME)
443             .buildObject(KeyValue.DEFAULT_ELEMENT_NAME);
444         
445         if (pk instanceof RSAPublicKey) {
446             keyValue.setRSAKeyValue(buildRSAKeyValue((RSAPublicKey) pk));
447         } else if (pk instanceof DSAPublicKey) {
448             keyValue.setDSAKeyValue(buildDSAKeyValue((DSAPublicKey) pk));
449         } else {
450            throw new IllegalArgumentException("Only RSAPublicKey and DSAPublicKey are supported");
451         }
452         
453         keyInfo.getKeyValues().add(keyValue);
454     }
455     
456     /**
457      * Builds an {@link RSAKeyValue} XMLObject from the Java security RSA public key type.
458      * 
459      * @param rsaPubKey a native Java {@link RSAPublicKey}
460      * @return an {@link RSAKeyValue} XMLObject
461      */
462     public static RSAKeyValue buildRSAKeyValue(RSAPublicKey rsaPubKey) {
463         XMLObjectBuilderFactory builderFactory = Configuration.getBuilderFactory();
464         RSAKeyValue rsaKeyValue = (RSAKeyValue) builderFactory
465             .getBuilder(RSAKeyValue.DEFAULT_ELEMENT_NAME)
466             .buildObject(RSAKeyValue.DEFAULT_ELEMENT_NAME);
467         Modulus modulus = (Modulus) builderFactory
468             .getBuilder(Modulus.DEFAULT_ELEMENT_NAME)
469             .buildObject(Modulus.DEFAULT_ELEMENT_NAME);
470         Exponent exponent = (Exponent) builderFactory
471             .getBuilder(Exponent.DEFAULT_ELEMENT_NAME)
472             .buildObject(Exponent.DEFAULT_ELEMENT_NAME);
473         
474         modulus.setValueBigInt(rsaPubKey.getModulus());
475         rsaKeyValue.setModulus(modulus);
476         
477         exponent.setValueBigInt(rsaPubKey.getPublicExponent());
478         rsaKeyValue.setExponent(exponent);
479         
480         return rsaKeyValue;
481     }
482     
483     /**
484      * Builds a {@link DSAKeyValue} XMLObject from the Java security DSA public key type.
485      * 
486      * @param dsaPubKey a native Java {@link DSAPublicKey}
487      * @return an {@link DSAKeyValue} XMLObject
488      */
489     public static DSAKeyValue buildDSAKeyValue(DSAPublicKey dsaPubKey) {
490         XMLObjectBuilderFactory builderFactory = Configuration.getBuilderFactory();
491         DSAKeyValue dsaKeyValue = (DSAKeyValue) builderFactory
492             .getBuilder(DSAKeyValue.DEFAULT_ELEMENT_NAME)
493             .buildObject(DSAKeyValue.DEFAULT_ELEMENT_NAME);
494         Y y = (Y) builderFactory.getBuilder(Y.DEFAULT_ELEMENT_NAME).buildObject(Y.DEFAULT_ELEMENT_NAME);
495         G g = (G) builderFactory.getBuilder(G.DEFAULT_ELEMENT_NAME).buildObject(G.DEFAULT_ELEMENT_NAME);
496         P p = (P) builderFactory.getBuilder(P.DEFAULT_ELEMENT_NAME).buildObject(P.DEFAULT_ELEMENT_NAME);
497         Q q = (Q) builderFactory.getBuilder(Q.DEFAULT_ELEMENT_NAME).buildObject(Q.DEFAULT_ELEMENT_NAME);
498         
499         y.setValueBigInt(dsaPubKey.getY());
500         dsaKeyValue.setY(y);
501         
502         g.setValueBigInt(dsaPubKey.getParams().getG());
503         dsaKeyValue.setG(g);
504         
505         p.setValueBigInt(dsaPubKey.getParams().getP());
506         dsaKeyValue.setP(p);
507         
508         q.setValueBigInt(dsaPubKey.getParams().getQ());
509         dsaKeyValue.setQ(q);
510         
511         return dsaKeyValue;
512     }
513 
514 
515     /**
516      * Extracts all the public keys within the given {@link KeyInfo}'s {@link KeyValue}s.  This method only
517      * supports DSA and RSA key types.
518      * 
519      * @param keyInfo {@link KeyInfo} to extract the keys out of
520      * 
521      * @return a list of native Java {@link PublicKey} objects
522      * 
523      * @throws KeyException thrown if the given key data can not be converted into {@link PublicKey}
524      */
525     public static List<PublicKey> getPublicKeys(KeyInfo keyInfo) throws KeyException{
526         List<PublicKey> keys = new LinkedList<PublicKey>();
527 
528         if (keyInfo == null || keyInfo.getKeyValues() == null) {
529             return keys;
530         }
531         
532         for(KeyValue keyDescriptor : keyInfo.getKeyValues()){
533             keys.add(getKey(keyDescriptor));
534         }
535 
536         return keys;
537     }
538 
539     /**
540      * Extracts the DSA or RSA public key within the {@link KeyValue}.
541      * 
542      * @param keyValue the {@link KeyValue} to extract the key from
543      * 
544      * @return a native Java security {@link java.security.Key} object
545      * 
546      * @throws KeyException thrown if the given key data can not be converted into {@link PublicKey}
547      */
548     public static PublicKey getKey(KeyValue keyValue) throws KeyException{
549         if(keyValue.getDSAKeyValue() != null){
550             return getDSAKey(keyValue.getDSAKeyValue());
551         }else if(keyValue.getRSAKeyValue() != null){
552             return getRSAKey(keyValue.getRSAKeyValue());
553         }else{
554             return null;
555         }
556     }
557     
558     /**
559      * Builds an DSA key from a {@link DSAKeyValue} element.  The element must contain values
560      * for all required DSA public key parameters, including values for shared key family
561      * values P, Q and G.
562      * 
563      * @param keyDescriptor the {@link DSAKeyValue} key descriptor
564      * 
565      * @return a new {@link DSAPublicKey} instance of {@link PublicKey}
566      * 
567      * @throws KeyException thrown if the key algorithm is not supported by the JCE or the key spec does not
568      *             contain valid information
569      */
570     public static PublicKey getDSAKey(DSAKeyValue keyDescriptor) throws KeyException {
571         if (! hasCompleteDSAParams(keyDescriptor)) {
572             throw new KeyException("DSAKeyValue element did not contain at least one of DSA parameters P, Q or G");
573         }
574         
575         BigInteger gComponent = keyDescriptor.getG().getValueBigInt();
576         BigInteger pComponent = keyDescriptor.getP().getValueBigInt();
577         BigInteger qComponent = keyDescriptor.getQ().getValueBigInt();
578 
579         DSAParams  dsaParams = new DSAParameterSpec(pComponent, qComponent, gComponent);
580         return getDSAKey(keyDescriptor, dsaParams);
581     }
582     
583     /**
584      * Builds a DSA key from an {@link DSAKeyValue} element and the supplied Java {@link DSAParams},
585      * which supplies key material from a shared key family.
586      * 
587      * @param keyDescriptor the {@link DSAKeyValue} key descriptor
588      * @param dsaParams the {@link DSAParams} DSA key family parameters
589      * 
590      * @return a new {@link DSAPublicKey} instance of {@link PublicKey}
591      * 
592      * @throws KeyException thrown if the key algorithm is not supported by the JCE or the key spec does not
593      *             contain valid information
594      */
595     public static PublicKey getDSAKey(DSAKeyValue keyDescriptor, DSAParams dsaParams) throws KeyException {
596         BigInteger yComponent = keyDescriptor.getY().getValueBigInt();
597 
598         DSAPublicKeySpec keySpec = 
599             new DSAPublicKeySpec(yComponent, dsaParams.getP(), dsaParams.getQ(), dsaParams.getG());
600         return buildKey(keySpec, "DSA");
601     }
602     
603     /**
604      * Check whether the specified {@link DSAKeyValue} element has the all optional DSA
605      * values which can be shared amongst many keys in a DSA "key family", and
606      * are presumed to be known from context.
607      * 
608      * @param keyDescriptor the {@link DSAKeyValue} element to check
609      * @return true if all parameters are present and non-empty, false otherwise
610      */
611     public static boolean hasCompleteDSAParams(DSAKeyValue keyDescriptor) {
612         if (       keyDescriptor.getG() == null || DatatypeHelper.isEmpty(keyDescriptor.getG().getValue())
613                 || keyDescriptor.getP() == null || DatatypeHelper.isEmpty(keyDescriptor.getP().getValue())
614                 || keyDescriptor.getQ() == null || DatatypeHelper.isEmpty(keyDescriptor.getQ().getValue())
615         ) {
616             return false;
617         }
618         return true;
619     }
620 
621     /**
622      * Builds an RSA key from an {@link RSAKeyValue} element.
623      * 
624      * @param keyDescriptor the {@link RSAKeyValue} key descriptor
625      * 
626      * @return a new {@link RSAPublicKey} instance of {@link PublicKey}
627      * 
628      * @throws KeyException thrown if the key algorithm is not supported by the JCE or the key spec does not
629      *             contain valid information
630      */
631     public static PublicKey getRSAKey(RSAKeyValue keyDescriptor) throws KeyException {
632         BigInteger modulus = keyDescriptor.getModulus().getValueBigInt();
633         BigInteger exponent = keyDescriptor.getExponent().getValueBigInt();
634 
635         RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, exponent);
636         return buildKey(keySpec, "RSA");
637     }
638     
639     /**
640      * Decode a base64-encoded ds:CryptoBinary value to a native Java BigInteger type.
641      *
642      * @param base64Value base64-encoded CryptoBinary value
643      * @return the decoded BigInteger
644      */
645     public static final BigInteger decodeBigIntegerFromCryptoBinary(String base64Value) {
646        return new BigInteger(1, Base64.decode(base64Value));
647     }
648     
649     /**
650      * Encode a native Java BigInteger type to a base64-encoded ds:CryptoBinary value.
651      *
652      * @param bigInt the BigInteger value
653      * @return the encoded CryptoBinary value
654      */
655     public static final String encodeCryptoBinaryFromBigInteger(BigInteger bigInt) {
656         // This code is really complicated, for now just use the Apache xmlsec lib code directly.
657         byte[] bigIntBytes = org.apache.xml.security.utils.Base64.encode(bigInt, bigInt.bitLength());
658         return Base64.encodeBytes(bigIntBytes);
659     }
660 
661     /**
662      * Generates a public key from the given key spec.
663      * 
664      * @param keySpec {@link KeySpec} specification for the key
665      * @param keyAlgorithm key generation algorithm, only DSA and RSA supported
666      * 
667      * @return the generated {@link PublicKey}
668      * 
669      * @throws KeyException thrown if the key algorithm is not supported by the JCE or the key spec does not
670      *             contain valid information
671      */
672     protected static PublicKey buildKey(KeySpec keySpec, String keyAlgorithm) throws KeyException {
673         try {
674             KeyFactory keyFactory = KeyFactory.getInstance(keyAlgorithm);
675             return keyFactory.generatePublic(keySpec);
676         } catch (NoSuchAlgorithmException e) {
677             log.error(keyAlgorithm + " algorithm is not supported by this VM", e);
678             throw new KeyException(keyAlgorithm + "algorithm is not supported by the JCE", e);
679         } catch (InvalidKeySpecException e) {
680             log.error("Invalid key information", e);
681             throw new KeyException("Invalid key information", e);
682         }
683     }
684     
685     /**
686      * Get the Java certificate factory singleton.
687      * 
688      * @return {@link CertificateFactory} the factory used to create X509 certificate objects
689      * 
690      * @throws CertificateException thrown if the factory can not be created
691      */
692     protected static CertificateFactory getX509CertFactory() throws CertificateException {
693 
694         if (x509CertFactory == null) {
695             x509CertFactory = CertificateFactory.getInstance("X.509");
696         }
697 
698         return x509CertFactory;
699     }
700 }