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