View Javadoc

1   /*
2    * Copyright [2007] [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;
18  
19  import java.io.ByteArrayInputStream;
20  import java.io.File;
21  import java.io.IOException;
22  import java.math.BigInteger;
23  import java.security.GeneralSecurityException;
24  import java.security.Key;
25  import java.security.KeyException;
26  import java.security.KeyFactory;
27  import java.security.KeyPair;
28  import java.security.KeyPairGenerator;
29  import java.security.NoSuchAlgorithmException;
30  import java.security.NoSuchProviderException;
31  import java.security.PrivateKey;
32  import java.security.PublicKey;
33  import java.security.cert.CRLException;
34  import java.security.cert.CertificateException;
35  import java.security.cert.CertificateFactory;
36  import java.security.cert.X509Certificate;
37  import java.security.interfaces.DSAParams;
38  import java.security.interfaces.DSAPrivateKey;
39  import java.security.interfaces.DSAPublicKey;
40  import java.security.interfaces.ECPublicKey;
41  import java.security.interfaces.RSAPrivateCrtKey;
42  import java.security.interfaces.RSAPrivateKey;
43  import java.security.interfaces.RSAPublicKey;
44  import java.security.spec.DSAPublicKeySpec;
45  import java.security.spec.InvalidKeySpecException;
46  import java.security.spec.KeySpec;
47  import java.security.spec.RSAPublicKeySpec;
48  import java.security.spec.X509EncodedKeySpec;
49  import java.util.ArrayList;
50  import java.util.HashSet;
51  import java.util.List;
52  import java.util.Set;
53  
54  import javax.crypto.KeyGenerator;
55  import javax.crypto.SecretKey;
56  import org.apache.commons.ssl.PKCS8Key;
57  import org.apache.xml.security.Init;
58  import org.apache.xml.security.algorithms.JCEMapper;
59  import org.opensaml.xml.Configuration;
60  import org.opensaml.xml.encryption.EncryptionParameters;
61  import org.opensaml.xml.encryption.KeyEncryptionParameters;
62  import org.opensaml.xml.security.credential.BasicCredential;
63  import org.opensaml.xml.security.credential.Credential;
64  import org.opensaml.xml.security.keyinfo.BasicProviderKeyInfoCredentialResolver;
65  import org.opensaml.xml.security.keyinfo.KeyInfoCredentialResolver;
66  import org.opensaml.xml.security.keyinfo.KeyInfoGenerator;
67  import org.opensaml.xml.security.keyinfo.KeyInfoGeneratorFactory;
68  import org.opensaml.xml.security.keyinfo.KeyInfoProvider;
69  import org.opensaml.xml.security.keyinfo.NamedKeyInfoGeneratorManager;
70  import org.opensaml.xml.security.keyinfo.provider.DSAKeyValueProvider;
71  import org.opensaml.xml.security.keyinfo.provider.InlineX509DataProvider;
72  import org.opensaml.xml.security.keyinfo.provider.RSAKeyValueProvider;
73  import org.opensaml.xml.security.x509.BasicX509Credential;
74  import org.opensaml.xml.signature.KeyInfo;
75  import org.opensaml.xml.signature.Signature;
76  import org.opensaml.xml.signature.SignatureConstants;
77  import org.opensaml.xml.util.Base64;
78  import org.opensaml.xml.util.DatatypeHelper;
79  import org.opensaml.xml.util.LazySet;
80  import org.slf4j.Logger;
81  import org.slf4j.LoggerFactory;
82  
83  /**
84   * Helper methods for security-related requirements.
85   */
86  public final class SecurityHelper {
87  
88      /** Class logger. */
89      private static Logger log = LoggerFactory.getLogger(SecurityHelper.class);
90  
91      /** Additional algorithm URI's which imply RSA keys. */
92      private static Set<String> rsaAlgorithmURIs;
93  
94      /** Additional algorithm URI's which imply DSA keys. */
95      private static Set<String> dsaAlgorithmURIs;
96  
97      /** Additional algorithm URI's which imply ECDSA keys. */
98      private static Set<String> ecdsaAlgorithmURIs;
99  
100     /** Constructor. */
101     private SecurityHelper() {
102     }
103 
104     /**
105      * Get the Java security JCA/JCE algorithm identifier associated with an algorithm URI.
106      * 
107      * @param algorithmURI the algorithm URI to evaluate
108      * @return the Java algorithm identifier, or null if the mapping is unavailable or indeterminable from the URI
109      */
110     public static String getAlgorithmIDFromURI(String algorithmURI) {
111         return DatatypeHelper.safeTrimOrNullString(JCEMapper.translateURItoJCEID(algorithmURI));
112     }
113 
114     /**
115      * Check whether the signature method algorithm URI indicates HMAC.
116      * 
117      * @param signatureAlgorithm the signature method algorithm URI
118      * @return true if URI indicates HMAC, false otherwise
119      */
120     public static boolean isHMAC(String signatureAlgorithm) {
121         String algoClass = DatatypeHelper.safeTrimOrNullString(JCEMapper.getAlgorithmClassFromURI(signatureAlgorithm));
122         return ApacheXMLSecurityConstants.ALGO_CLASS_MAC.equals(algoClass);
123     }
124 
125     /**
126      * Get the Java security JCA/JCE key algorithm specifier associated with an algorithm URI.
127      * 
128      * @param algorithmURI the algorithm URI to evaluate
129      * @return the Java key algorithm specifier, or null if the mapping is unavailable or indeterminable from the URI
130      */
131     public static String getKeyAlgorithmFromURI(String algorithmURI) {
132         // The default Apache config file currently only includes the key algorithm for
133         // the block ciphers and key wrap URI's. Note: could use a custom config file which contains others.
134         String apacheValue = DatatypeHelper.safeTrimOrNullString(JCEMapper.getJCEKeyAlgorithmFromURI(algorithmURI));
135         if (apacheValue != null) {
136             return apacheValue;
137         }
138 
139         // HMAC uses any symmetric key, so there is no implied specific key algorithm
140         if (isHMAC(algorithmURI)) {
141             return null;
142         }
143 
144         // As a last ditch fallback, check some known common and supported ones.
145         if (rsaAlgorithmURIs.contains(algorithmURI)) {
146             return "RSA";
147         }
148         if (dsaAlgorithmURIs.contains(algorithmURI)) {
149             return "DSA";
150         }
151         if (ecdsaAlgorithmURIs.contains(algorithmURI)) {
152             return "ECDSA";
153         }
154 
155         return null;
156     }
157 
158     /**
159      * Get the length of the key indicated by the algorithm URI, if applicable and available.
160      * 
161      * @param algorithmURI the algorithm URI to evaluate
162      * @return the length of the key indicated by the algorithm URI, or null if the length is either unavailable or
163      *         indeterminable from the URI
164      */
165     public static Integer getKeyLengthFromURI(String algorithmURI) {
166         String algoClass = DatatypeHelper.safeTrimOrNullString(JCEMapper.getAlgorithmClassFromURI(algorithmURI));
167 
168         if (ApacheXMLSecurityConstants.ALGO_CLASS_BLOCK_ENCRYPTION.equals(algoClass)
169                 || ApacheXMLSecurityConstants.ALGO_CLASS_SYMMETRIC_KEY_WRAP.equals(algoClass)) {
170 
171             try {
172                 int keyLength = JCEMapper.getKeyLengthFromURI(algorithmURI);
173                 return new Integer(keyLength);
174             } catch (NumberFormatException e) {
175                 log.warn("XML Security config contained invalid key length value for algorithm URI: " + algorithmURI);
176             }
177         }
178 
179         log.info("Mapping from algorithm URI {} to key length not available", algorithmURI);
180         return null;
181     }
182 
183     /**
184      * Generates a random Java JCE symmetric Key object from the specified XML Encryption algorithm URI.
185      * 
186      * @param algoURI The XML Encryption algorithm URI
187      * @return a randomly-generated symmetric Key
188      * @throws NoSuchAlgorithmException thrown if the specified algorithm is invalid
189      * @throws KeyException thrown if the length of the key to generate could not be determined
190      */
191     public static SecretKey generateSymmetricKey(String algoURI) throws NoSuchAlgorithmException, KeyException {
192         String jceAlgorithmName = getKeyAlgorithmFromURI(algoURI);
193         if (DatatypeHelper.isEmpty(jceAlgorithmName)) {
194             log.error("Mapping from algorithm URI '" + algoURI
195                     + "' to key algorithm not available, key generation failed");
196             throw new NoSuchAlgorithmException("Algorithm URI'" + algoURI + "' is invalid for key generation");
197         }
198         Integer keyLength = getKeyLengthFromURI(algoURI);
199         if (keyLength == null) {
200             log.error("Key length could not be determined from algorithm URI, can't generate key");
201             throw new KeyException("Key length not determinable from algorithm URI, could not generate new key");
202         }
203         KeyGenerator keyGenerator = KeyGenerator.getInstance(jceAlgorithmName);
204         keyGenerator.init(keyLength);
205         return keyGenerator.generateKey();
206     }
207 
208     /**
209      * Extract the encryption key from the credential.
210      * 
211      * @param credential the credential containing the encryption key
212      * @return the encryption key (either a public key or a secret (symmetric) key
213      */
214     public static Key extractEncryptionKey(Credential credential) {
215         if (credential == null) {
216             return null;
217         }
218         if (credential.getPublicKey() != null) {
219             return credential.getPublicKey();
220         } else {
221             return credential.getSecretKey();
222         }
223     }
224 
225     /**
226      * Extract the decryption key from the credential.
227      * 
228      * @param credential the credential containing the decryption key
229      * @return the decryption key (either a private key or a secret (symmetric) key
230      */
231     public static Key extractDecryptionKey(Credential credential) {
232         if (credential == null) {
233             return null;
234         }
235         if (credential.getPrivateKey() != null) {
236             return credential.getPrivateKey();
237         } else {
238             return credential.getSecretKey();
239         }
240     }
241 
242     /**
243      * Extract the signing key from the credential.
244      * 
245      * @param credential the credential containing the signing key
246      * @return the signing key (either a private key or a secret (symmetric) key
247      */
248     public static Key extractSigningKey(Credential credential) {
249         if (credential == null) {
250             return null;
251         }
252         if (credential.getPrivateKey() != null) {
253             return credential.getPrivateKey();
254         } else {
255             return credential.getSecretKey();
256         }
257     }
258 
259     /**
260      * Extract the verification key from the credential.
261      * 
262      * @param credential the credential containing the verification key
263      * @return the verification key (either a public key or a secret (symmetric) key
264      */
265     public static Key extractVerificationKey(Credential credential) {
266         if (credential == null) {
267             return null;
268         }
269         if (credential.getPublicKey() != null) {
270             return credential.getPublicKey();
271         } else {
272             return credential.getSecretKey();
273         }
274     }
275 
276     /**
277      * Get the key length in bits of the specified key.
278      * 
279      * @param key the key to evaluate
280      * @return length of the key in bits, or null if the length can not be determined
281      */
282     public static Integer getKeyLength(Key key) {
283         // TODO investigate techniques (and use cases) to determine length in other cases,
284         // e.g. RSA and DSA keys, and non-RAW format symmetric keys
285         if (key instanceof SecretKey && "RAW".equals(key.getFormat())) {
286             return key.getEncoded().length * 8;
287         }
288         log.debug("Unable to determine length in bits of specified Key instance");
289         return null;
290     }
291 
292     /**
293      * Get a simple, minimal credential containing a secret (symmetric) key.
294      * 
295      * @param secretKey the symmetric key to wrap
296      * @return a credential containing the secret key specified
297      */
298     public static BasicCredential getSimpleCredential(SecretKey secretKey) {
299         if (secretKey == null) {
300             throw new IllegalArgumentException("A secret key is required");
301         }
302         BasicCredential cred = new BasicCredential();
303         cred.setSecretKey(secretKey);
304         return cred;
305     }
306 
307     /**
308      * Get a simple, minimal credential containing a public key, and optionally a private key.
309      * 
310      * @param publicKey the public key to wrap
311      * @param privateKey the private key to wrap, which may be null
312      * @return a credential containing the key(s) specified
313      */
314     public static BasicCredential getSimpleCredential(PublicKey publicKey, PrivateKey privateKey) {
315         if (publicKey == null) {
316             throw new IllegalArgumentException("A public key is required");
317         }
318         BasicCredential cred = new BasicCredential();
319         cred.setPublicKey(publicKey);
320         cred.setPrivateKey(privateKey);
321         return cred;
322     }
323 
324     /**
325      * Get a simple, minimal credential containing an end-entity X.509 certificate, and optionally a private key.
326      * 
327      * @param cert the end-entity certificate to wrap
328      * @param privateKey the private key to wrap, which may be null
329      * @return a credential containing the certificate and key specified
330      */
331     public static BasicX509Credential getSimpleCredential(X509Certificate cert, PrivateKey privateKey) {
332         if (cert == null) {
333             throw new IllegalArgumentException("A certificate is required");
334         }
335         BasicX509Credential cred = new BasicX509Credential();
336         cred.setEntityCertificate(cert);
337         cred.setPrivateKey(privateKey);
338         return cred;
339     }
340 
341     /**
342      * Decodes secret keys in DER and PEM format.
343      * 
344      * This method is not yet implemented.
345      * 
346      * @param key secret key
347      * @param password password if the key is encrypted or null if not
348      * 
349      * @return the decoded key
350      * 
351      * @throws KeyException thrown if the key can not be decoded
352      */
353     public static SecretKey decodeSecretKey(byte[] key, char[] password) throws KeyException {
354         // TODO
355         throw new UnsupportedOperationException("This method is not yet supported");
356     }
357 
358     /**
359      * Decodes RSA/DSA public keys in DER-encoded "SubjectPublicKeyInfo" format.
360      * 
361      * @param key encoded key
362      * @param password password if the key is encrypted or null if not
363      * 
364      * @return decoded key
365      * 
366      * @throws KeyException thrown if the key can not be decoded
367      */
368     public static PublicKey decodePublicKey(byte[] key, char[] password) throws KeyException {
369         X509EncodedKeySpec keySpec = new X509EncodedKeySpec(key);
370         try {
371             return buildKey(keySpec, "RSA");
372         }
373         catch (KeyException ex) {
374         }
375         try {
376             return buildKey(keySpec, "DSA");
377         }
378         catch (KeyException ex) {
379         }
380         try {
381             return buildKey(keySpec, "EC");
382         }
383         catch (KeyException ex) {
384         }
385         throw new KeyException("Unsupported key type.");
386     }
387 
388     /**
389      * Derives the public key from either a DSA or RSA private key.
390      * 
391      * @param key the private key to derive the public key from
392      * 
393      * @return the derived public key
394      * 
395      * @throws KeyException thrown if the given private key is not a DSA or RSA key or there is a problem generating the
396      *             public key
397      */
398     public static PublicKey derivePublicKey(PrivateKey key) throws KeyException {
399         KeyFactory factory;
400         if (key instanceof DSAPrivateKey) {
401             DSAPrivateKey dsaKey = (DSAPrivateKey) key;
402             DSAParams keyParams = dsaKey.getParams();
403             BigInteger y = keyParams.getQ().modPow(dsaKey.getX(), keyParams.getP());
404             DSAPublicKeySpec pubKeySpec = new DSAPublicKeySpec(y, keyParams.getP(), keyParams.getQ(), keyParams.getG());
405 
406             try {
407                 factory = KeyFactory.getInstance("DSA");
408                 return factory.generatePublic(pubKeySpec);
409             } catch (GeneralSecurityException e) {
410                 throw new KeyException("Unable to derive public key from DSA private key", e);
411             }
412         } else if (key instanceof RSAPrivateCrtKey) {
413             RSAPrivateCrtKey rsaKey = (RSAPrivateCrtKey) key;
414             RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(rsaKey.getModulus(), rsaKey.getPublicExponent());
415 
416             try {
417                 factory = KeyFactory.getInstance("RSA");
418                 return factory.generatePublic(pubKeySpec);
419             } catch (GeneralSecurityException e) {
420                 throw new KeyException("Unable to derive public key from RSA private key", e);
421             }
422         } else {
423             throw new KeyException("Private key was not a DSA or RSA key");
424         }
425     }
426 
427     /**
428      * Decodes RSA/DSA private keys in DER, PEM, or PKCS#8 (encrypted or unencrypted) formats.
429      * 
430      * @param key encoded key
431      * @param password decryption password or null if the key is not encrypted
432      * 
433      * @return deocded private key
434      * 
435      * @throws KeyException thrown if the key can not be decoded
436      */
437     public static PrivateKey decodePrivateKey(File key, char[] password) throws KeyException {
438         if (!key.exists()) {
439             throw new KeyException("Key file " + key.getAbsolutePath() + " does not exist");
440         }
441 
442         if (!key.canRead()) {
443             throw new KeyException("Key file " + key.getAbsolutePath() + " is not readable");
444         }
445 
446         try {
447             return decodePrivateKey(DatatypeHelper.fileToByteArray(key), password);
448         } catch (IOException e) {
449             throw new KeyException("Error reading Key file " + key.getAbsolutePath(), e);
450         }
451     }
452 
453     /**
454      * Decodes RSA/DSA private keys in DER, PEM, or PKCS#8 (encrypted or unencrypted) formats.
455      * 
456      * @param key encoded key
457      * @param password decryption password or null if the key is not encrypted
458      * 
459      * @return deocded private key
460      * 
461      * @throws KeyException thrown if the key can not be decoded
462      */
463     public static PrivateKey decodePrivateKey(byte[] key, char[] password) throws KeyException {
464         try {
465             PKCS8Key deocodedKey = new PKCS8Key(key, password);
466             return deocodedKey.getPrivateKey();
467         } catch (GeneralSecurityException e) {
468             throw new KeyException("Unable to decode private key", e);
469         }
470     }
471 
472     /**
473      * Build Java certificate from base64 encoding.
474      * 
475      * @param base64Cert base64-encoded certificate
476      * @return a native Java X509 certificate
477      * @throws CertificateException thrown if there is an error constructing certificate
478      */
479     public static java.security.cert.X509Certificate buildJavaX509Cert(String base64Cert) throws CertificateException {
480         CertificateFactory  cf = CertificateFactory.getInstance("X.509");
481         ByteArrayInputStream input = new ByteArrayInputStream(Base64.decode(base64Cert));
482         return (java.security.cert.X509Certificate) cf.generateCertificate(input);
483     }
484     
485     /**
486      * Build Java CRL from base64 encoding.
487      * 
488      * @param base64CRL base64-encoded CRL
489      * @return a native Java X509 CRL
490      * @throws CertificateException thrown if there is an error constructing certificate
491      * @throws CRLException  thrown if there is an error constructing CRL
492      */
493     public static java.security.cert.X509CRL buildJavaX509CRL(String base64CRL)
494             throws CertificateException, CRLException {
495         CertificateFactory  cf = CertificateFactory.getInstance("X.509");
496         ByteArrayInputStream input = new ByteArrayInputStream(Base64.decode(base64CRL));
497         return (java.security.cert.X509CRL) cf.generateCRL(input);
498     }
499     
500     /**
501      * Build Java DSA public key from base64 encoding.
502      * 
503      * @param base64EncodedKey base64-encoded DSA public key
504      * @return a native Java DSAPublicKey
505      * @throws KeyException thrown if there is an error constructing key
506      */
507     public static DSAPublicKey buildJavaDSAPublicKey(String base64EncodedKey) throws KeyException {
508         X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.decode(base64EncodedKey));
509         return (DSAPublicKey) buildKey(keySpec, "DSA");
510     }
511     
512     /**
513      * Build Java RSA public key from base64 encoding.
514      * 
515      * @param base64EncodedKey base64-encoded RSA public key
516      * @return a native Java RSAPublicKey
517      * @throws KeyException thrown if there is an error constructing key
518      */
519     public static RSAPublicKey buildJavaRSAPublicKey(String base64EncodedKey) throws KeyException {
520         X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.decode(base64EncodedKey));
521         return (RSAPublicKey) buildKey(keySpec, "RSA");
522     }
523     
524     /**
525      * Build Java EC public key from base64 encoding.
526      * 
527      * @param base64EncodedKey base64-encoded EC public key
528      * @return a native Java ECPublicKey
529      * @throws KeyException thrown if there is an error constructing key
530      */
531     public static ECPublicKey buildJavaECPublicKey(String base64EncodedKey) throws KeyException {
532         X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.decode(base64EncodedKey));
533         return (ECPublicKey) buildKey(keySpec, "EC");
534     }
535     
536     /**
537      * Build Java RSA private key from base64 encoding.
538      * 
539      * @param base64EncodedKey base64-encoded RSA private key
540      * @return a native Java RSAPrivateKey
541      * @throws KeyException thrown if there is an error constructing key
542      */
543     public static RSAPrivateKey buildJavaRSAPrivateKey(String base64EncodedKey)  throws KeyException {
544         PrivateKey key =  buildJavaPrivateKey(base64EncodedKey);
545         if (! (key instanceof RSAPrivateKey)) {
546             throw new KeyException("Generated key was not an RSAPrivateKey instance");
547         }
548         return (RSAPrivateKey) key;
549     }
550     
551     /**
552      * Build Java DSA private key from base64 encoding.
553      * 
554      * @param base64EncodedKey base64-encoded DSA private key
555      * @return a native Java DSAPrivateKey
556      * @throws KeyException thrown if there is an error constructing key
557      */
558     public static DSAPrivateKey buildJavaDSAPrivateKey(String base64EncodedKey)  throws KeyException {
559         PrivateKey key =  buildJavaPrivateKey(base64EncodedKey);
560         if (! (key instanceof DSAPrivateKey)) {
561             throw new KeyException("Generated key was not a DSAPrivateKey instance");
562         }
563         return (DSAPrivateKey) key;
564     }
565     
566     /**
567      * Build Java private key from base64 encoding. The key should have no password.
568      * 
569      * @param base64EncodedKey base64-encoded private key
570      * @return a native Java PrivateKey
571      * @throws KeyException thrown if there is an error constructing key
572      */
573     public static PrivateKey buildJavaPrivateKey(String base64EncodedKey)  throws KeyException {
574         return SecurityHelper.decodePrivateKey(Base64.decode(base64EncodedKey), null);
575     }
576     
577     /**
578      * Generates a public key from the given key spec.
579      * 
580      * @param keySpec {@link KeySpec} specification for the key
581      * @param keyAlgorithm key generation algorithm, only DSA and RSA supported
582      * 
583      * @return the generated {@link PublicKey}
584      * 
585      * @throws KeyException thrown if the key algorithm is not supported by the JCE or the key spec does not
586      *             contain valid information
587      */
588     public static PublicKey buildKey(KeySpec keySpec, String keyAlgorithm) throws KeyException {
589         try {
590             KeyFactory keyFactory = KeyFactory.getInstance(keyAlgorithm);
591             return keyFactory.generatePublic(keySpec);
592         } catch (NoSuchAlgorithmException e) {
593             throw new KeyException(keyAlgorithm + "algorithm is not supported by the JCE", e);
594         } catch (InvalidKeySpecException e) {
595             throw new KeyException("Invalid key information", e);
596         }
597     }
598     
599     /**
600      * Randomly generates a Java JCE symmetric Key object from the specified XML Encryption algorithm URI.
601      * 
602      * @param algoURI  The XML Encryption algorithm URI
603      * @return a randomly-generated symmteric key
604      * @throws NoSuchProviderException  provider not found
605      * @throws NoSuchAlgorithmException algorithm not found
606      */
607     public static SecretKey generateKeyFromURI(String algoURI) 
608             throws NoSuchAlgorithmException, NoSuchProviderException {
609         String jceAlgorithmName = JCEMapper.getJCEKeyAlgorithmFromURI(algoURI);
610         int keyLength = JCEMapper.getKeyLengthFromURI(algoURI);
611         return generateKey(jceAlgorithmName, keyLength, null);
612     }
613     
614     /**
615      * Randomly generates a Java JCE KeyPair object from the specified XML Encryption algorithm URI.
616      * 
617      * @param algoURI  The XML Encryption algorithm URI
618      * @param keyLength  the length of key to generate
619      * @return a randomly-generated KeyPair
620      * @throws NoSuchProviderException  provider not found
621      * @throws NoSuchAlgorithmException  algorithm not found
622      */
623     public static KeyPair generateKeyPairFromURI(String algoURI, int keyLength) 
624             throws NoSuchAlgorithmException, NoSuchProviderException {
625         String jceAlgorithmName = JCEMapper.getJCEKeyAlgorithmFromURI(algoURI);
626         return generateKeyPair(jceAlgorithmName, keyLength, null);
627     }
628     
629     /**
630      * Generate a random symmetric key.
631      * 
632      * @param algo key algorithm
633      * @param keyLength key length
634      * @param provider JCA provider
635      * @return randomly generated symmetric key
636      * @throws NoSuchAlgorithmException algorithm not found
637      * @throws NoSuchProviderException provider not found
638      */
639     public static SecretKey generateKey(String algo, int keyLength, String provider) 
640             throws NoSuchAlgorithmException, NoSuchProviderException {
641         SecretKey key = null;
642         KeyGenerator keyGenerator = null;
643         if (provider != null) {
644             keyGenerator = KeyGenerator.getInstance(algo, provider);
645         } else {
646             keyGenerator = KeyGenerator.getInstance(algo);
647         }
648         keyGenerator.init(keyLength);
649         key = keyGenerator.generateKey();
650         return key;
651     }
652     
653     /**
654      * Generate a random asymmetric key pair.
655      * 
656      * @param algo key algorithm
657      * @param keyLength key length
658      * @param provider JCA provider
659      * @return randomly generated key
660      * @throws NoSuchAlgorithmException algorithm not found
661      * @throws NoSuchProviderException provider not found
662      */
663     public static KeyPair generateKeyPair(String algo, int keyLength, String provider) 
664             throws NoSuchAlgorithmException, NoSuchProviderException {
665         KeyPairGenerator keyGenerator = null;
666         if (provider != null) {
667             keyGenerator = KeyPairGenerator.getInstance(algo, provider);
668         } else {
669             keyGenerator = KeyPairGenerator.getInstance(algo);
670         }
671         keyGenerator.initialize(keyLength);
672         return keyGenerator.generateKeyPair();
673     }
674     
675     /**
676      * Generate a random symmetric key and return in a BasicCredential.
677      * 
678      * @param algorithmURI The XML Encryption algorithm URI
679      * @return a basic credential containing a randomly generated symmetric key
680      * @throws NoSuchAlgorithmException algorithm not found
681      * @throws NoSuchProviderException provider not found
682      */
683     public static Credential generateKeyAndCredential(String algorithmURI) 
684             throws NoSuchAlgorithmException, NoSuchProviderException {
685         SecretKey key = generateKeyFromURI(algorithmURI);
686         BasicCredential credential = new BasicCredential();
687         credential.setSecretKey(key);
688         return credential;
689     }
690     
691     /**
692      * Generate a random asymmetric key pair and return in a BasicCredential.
693      * 
694      * @param algorithmURI The XML Encryption algorithm URI
695      * @param keyLength key length
696      * @param includePrivate if true, the private key will be included as well
697      * @return a basic credential containing a randomly generated asymmetric key pair
698      * @throws NoSuchAlgorithmException algorithm not found
699      * @throws NoSuchProviderException provider not found
700      */
701     public static Credential generateKeyPairAndCredential(String algorithmURI, int keyLength, boolean includePrivate) 
702             throws NoSuchAlgorithmException, NoSuchProviderException {
703         KeyPair keyPair = generateKeyPairFromURI(algorithmURI, keyLength);
704         BasicCredential credential = new BasicCredential();
705         credential.setPublicKey(keyPair.getPublic());
706         if (includePrivate) {
707             credential.setPrivateKey(keyPair.getPrivate());
708         }
709         return credential;
710     }
711     
712     /**
713      * Get a basic KeyInfo credential resolver which can process standard inline
714      * data - RSAKeyValue, DSAKeyValue, X509Data.
715      * 
716      * @return a new KeyInfoCredentialResolver instance
717      */
718     public static KeyInfoCredentialResolver buildBasicInlineKeyInfoResolver() {
719         List<KeyInfoProvider> providers = new ArrayList<KeyInfoProvider>();
720         providers.add( new RSAKeyValueProvider() );
721         providers.add( new DSAKeyValueProvider() );
722         providers.add( new InlineX509DataProvider() );
723         return new BasicProviderKeyInfoCredentialResolver(providers);
724     }
725     
726     /**
727      * Compare the supplied public and private keys, and determine if they correspond to the same key pair.
728      * 
729      * @param pubKey the public key
730      * @param privKey the private key
731      * @return true if the public and private are from the same key pair, false if not
732      * @throws SecurityException if the keys can not be evaluated, or if the key algorithm is unsupported or unknown
733      */
734     public static boolean matchKeyPair(PublicKey pubKey, PrivateKey privKey) throws SecurityException {
735         // This approach attempts to match the keys by signing and then validating some known data.
736 
737         if (pubKey == null || privKey == null) {
738             throw new SecurityException("Either public or private key was null");
739         }
740 
741         // Need to dynamically determine the JCA signature algorithm ID to use from the key algorithm.
742         // Don't currently have a direct mapping, so have to map to XML Signature algorithm URI first,
743         // then map that to JCA algorithm ID.
744         SecurityConfiguration secConfig = Configuration.getGlobalSecurityConfiguration();
745         if (secConfig == null) {
746             throw new SecurityException("Global security configuration was null, could not resolve signing algorithm");
747         }
748         String algoURI = secConfig.getSignatureAlgorithmURI(privKey.getAlgorithm());
749         if (algoURI == null) {
750             throw new SecurityException("Can't determine algorithm URI from key algorithm: " + privKey.getAlgorithm());
751         }
752         String jcaAlgoID = getAlgorithmIDFromURI(algoURI);
753         if (jcaAlgoID == null) {
754             throw new SecurityException("Can't determine JCA algorithm ID from algorithm URI: " + algoURI);
755         }
756 
757         if (log.isDebugEnabled()) {
758             log.debug("Attempting to match key pair containing key algorithms public '{}' private '{}', "
759                     + "using JCA signature algorithm '{}'", new Object[] { pubKey.getAlgorithm(),
760                     privKey.getAlgorithm(), jcaAlgoID, });
761         }
762 
763         byte[] data = "This is the data to sign".getBytes();
764         byte[] signature = SigningUtil.sign(privKey, jcaAlgoID, data);
765         return SigningUtil.verify(pubKey, jcaAlgoID, signature, data);
766     }
767 
768     /**
769      * Prepare a {@link Signature} with necessary additional information prior to signing.
770      * 
771      * <p>
772      * <strong>NOTE:</strong>Since this operation modifies the specified Signature object, it should be called
773      * <strong>prior</strong> to marshalling the Signature object.
774      * </p>
775      * 
776      * <p>
777      * The following Signature values will be added:
778      * <ul>
779      * <li>signature algorithm URI</li>
780      * <li>canonicalization algorithm URI</li>
781      * <li>HMAC output length (if applicable and a value is configured)</li>
782      * <li>a {@link KeyInfo} element representing the signing credential</li>
783      * </ul>
784      * </p>
785      * 
786      * <p>
787      * Existing (non-null) values of these parameters on the specified signature will <strong>NOT</strong> be
788      * overwritten, however.
789      * </p>
790      * 
791      * <p>
792      * All values are determined by the specified {@link SecurityConfiguration}. If a security configuration is not
793      * supplied, the global security configuration ({@link Configuration#getGlobalSecurityConfiguration()}) will be
794      * used.
795      * </p>
796      * 
797      * <p>
798      * The signature algorithm URI and optional HMAC output length are derived from the signing credential.
799      * </p>
800      * 
801      * <p>
802      * The KeyInfo to be generated is based on the {@link NamedKeyInfoGeneratorManager} defined in the security
803      * configuration, and is determined by the type of the signing credential and an optional KeyInfo generator manager
804      * name. If the latter is ommited, the default manager ({@link NamedKeyInfoGeneratorManager#getDefaultManager()})
805      * of the security configuration's named generator manager will be used.
806      * </p>
807      * 
808      * @param signature the Signature to be updated
809      * @param signingCredential the credential with which the Signature will be computed
810      * @param config the SecurityConfiguration to use (may be null)
811      * @param keyInfoGenName the named KeyInfoGeneratorManager configuration to use (may be null)
812      * @throws SecurityException thrown if there is an error generating the KeyInfo from the signing credential
813      */
814     public static void prepareSignatureParams(Signature signature, Credential signingCredential,
815             SecurityConfiguration config, String keyInfoGenName) throws SecurityException {
816 
817         SecurityConfiguration secConfig;
818         if (config != null) {
819             secConfig = config;
820         } else {
821             secConfig = Configuration.getGlobalSecurityConfiguration();
822         }
823 
824         // The algorithm URI is derived from the credential
825         String signAlgo = signature.getSignatureAlgorithm();
826         if (signAlgo == null) {
827             signAlgo = secConfig.getSignatureAlgorithmURI(signingCredential);
828             signature.setSignatureAlgorithm(signAlgo);
829         }
830 
831         // If we're doing HMAC, set the output length
832         if (SecurityHelper.isHMAC(signAlgo)) {
833             if (signature.getHMACOutputLength() == null) {
834                 signature.setHMACOutputLength(secConfig.getSignatureHMACOutputLength());
835             }
836         }
837 
838         if (signature.getCanonicalizationAlgorithm() == null) {
839             signature.setCanonicalizationAlgorithm(secConfig.getSignatureCanonicalizationAlgorithm());
840         }
841 
842         if (signature.getKeyInfo() == null) {
843             KeyInfoGenerator kiGenerator = getKeyInfoGenerator(signingCredential, secConfig, keyInfoGenName);
844             if (kiGenerator != null) {
845                 try {
846                     KeyInfo keyInfo = kiGenerator.generate(signingCredential);
847                     signature.setKeyInfo(keyInfo);
848                 } catch (SecurityException e) {
849                     log.error("Error generating KeyInfo from credential", e);
850                     throw e;
851                 }
852             } else {
853                 log.info("No factory for named KeyInfoGenerator {} was found for credential type {}", keyInfoGenName,
854                         signingCredential.getCredentialType().getName());
855                 log.info("No KeyInfo will be generated for Signature");
856             }
857         }
858     }
859 
860     /**
861      * Build an instance of {@link EncryptionParameters} suitable for passing to an
862      * {@link org.opensaml.xml.encryption.Encrypter}.
863      * 
864      * <p>
865      * The following parameter values will be added:
866      * <ul>
867      * <li>the encryption credential (optional)</li>
868      * <li>encryption algorithm URI</li>
869      * <li>an appropriate {@link KeyInfoGenerator} instance which will be used to generate a {@link KeyInfo} element
870      * from the encryption credential</li>
871      * </ul>
872      * </p>
873      * 
874      * <p>
875      * All values are determined by the specified {@link SecurityConfiguration}. If a security configuration is not
876      * supplied, the global security configuration ({@link Configuration#getGlobalSecurityConfiguration()}) will be
877      * used.
878      * </p>
879      * 
880      * <p>
881      * The encryption algorithm URI is derived from the optional supplied encryption credential. If omitted, the value
882      * of {@link SecurityConfiguration#getAutoGeneratedDataEncryptionKeyAlgorithmURI()} will be used.
883      * </p>
884      * 
885      * <p>
886      * The KeyInfoGenerator to be used is based on the {@link NamedKeyInfoGeneratorManager} defined in the security
887      * configuration, and is determined by the type of the signing credential and an optional KeyInfo generator manager
888      * name. If the latter is ommited, the default manager ({@link NamedKeyInfoGeneratorManager#getDefaultManager()})
889      * of the security configuration's named generator manager will be used.
890      * </p>
891      * 
892      * @param encryptionCredential the credential with which the data will be encrypted (may be null)
893      * @param config the SecurityConfiguration to use (may be null)
894      * @param keyInfoGenName the named KeyInfoGeneratorManager configuration to use (may be null)
895      * @return a new instance of EncryptionParameters
896      */
897     public static EncryptionParameters buildDataEncryptionParams(Credential encryptionCredential,
898             SecurityConfiguration config, String keyInfoGenName) {
899 
900         SecurityConfiguration secConfig;
901         if (config != null) {
902             secConfig = config;
903         } else {
904             secConfig = Configuration.getGlobalSecurityConfiguration();
905         }
906 
907         EncryptionParameters encParams = new EncryptionParameters();
908         encParams.setEncryptionCredential(encryptionCredential);
909 
910         if (encryptionCredential == null) {
911             encParams.setAlgorithm(secConfig.getAutoGeneratedDataEncryptionKeyAlgorithmURI());
912         } else {
913             encParams.setAlgorithm(secConfig.getDataEncryptionAlgorithmURI(encryptionCredential));
914 
915             KeyInfoGenerator kiGenerator = getKeyInfoGenerator(encryptionCredential, secConfig, keyInfoGenName);
916             if (kiGenerator != null) {
917                 encParams.setKeyInfoGenerator(kiGenerator);
918             } else {
919                 log.info("No factory for named KeyInfoGenerator {} was found for credential type{}", keyInfoGenName,
920                         encryptionCredential.getCredentialType().getName());
921                 log.info("No KeyInfo will be generated for EncryptedData");
922             }
923         }
924 
925         return encParams;
926     }
927 
928     /**
929      * Build an instance of {@link KeyEncryptionParameters} suitable for passing to an
930      * {@link org.opensaml.xml.encryption.Encrypter}.
931      * 
932      * <p>
933      * The following parameter values will be added:
934      * <ul>
935      * <li>the key encryption credential</li>
936      * <li>key transport encryption algorithm URI</li>
937      * <li>an appropriate {@link KeyInfoGenerator} instance which will be used to generate a {@link KeyInfo} element
938      * from the key encryption credential</li>
939      * <li>intended recipient of the resultant encrypted key (optional)</li>
940      * </ul>
941      * </p>
942      * 
943      * <p>
944      * All values are determined by the specified {@link SecurityConfiguration}. If a security configuration is not
945      * supplied, the global security configuration ({@link Configuration#getGlobalSecurityConfiguration()}) will be
946      * used.
947      * </p>
948      * 
949      * <p>
950      * The encryption algorithm URI is derived from the optional supplied encryption credential. If omitted, the value
951      * of {@link SecurityConfiguration#getAutoGeneratedDataEncryptionKeyAlgorithmURI()} will be used.
952      * </p>
953      * 
954      * <p>
955      * The KeyInfoGenerator to be used is based on the {@link NamedKeyInfoGeneratorManager} defined in the security
956      * configuration, and is determined by the type of the signing credential and an optional KeyInfo generator manager
957      * name. If the latter is ommited, the default manager ({@link NamedKeyInfoGeneratorManager#getDefaultManager()})
958      * of the security configuration's named generator manager will be used.
959      * </p>
960      * 
961      * @param encryptionCredential the credential with which the key will be encrypted
962      * @param wrappedKeyAlgorithm the JCA key algorithm name of the key to be encrypted (may be null)
963      * @param config the SecurityConfiguration to use (may be null)
964      * @param keyInfoGenName the named KeyInfoGeneratorManager configuration to use (may be null)
965      * @param recipient the intended recipient of the resultant encrypted key, typically the owner of the key encryption
966      *            key (may be null)
967      * @return a new instance of KeyEncryptionParameters
968      * @throws SecurityException if encryption credential is not supplied
969      * 
970      */
971     public static KeyEncryptionParameters buildKeyEncryptionParams(Credential encryptionCredential,
972             String wrappedKeyAlgorithm, SecurityConfiguration config, String keyInfoGenName, String recipient)
973             throws SecurityException {
974 
975         SecurityConfiguration secConfig;
976         if (config != null) {
977             secConfig = config;
978         } else {
979             secConfig = Configuration.getGlobalSecurityConfiguration();
980         }
981 
982         KeyEncryptionParameters kekParams = new KeyEncryptionParameters();
983         kekParams.setEncryptionCredential(encryptionCredential);
984 
985         if (encryptionCredential == null) {
986             throw new SecurityException("Key encryption credential may not be null");
987         }
988 
989         kekParams.setAlgorithm(secConfig.getKeyTransportEncryptionAlgorithmURI(encryptionCredential,
990                 wrappedKeyAlgorithm));
991 
992         KeyInfoGenerator kiGenerator = getKeyInfoGenerator(encryptionCredential, secConfig, keyInfoGenName);
993         if (kiGenerator != null) {
994             kekParams.setKeyInfoGenerator(kiGenerator);
995         } else {
996             log.info("No factory for named KeyInfoGenerator {} was found for credential type {}", keyInfoGenName,
997                     encryptionCredential.getCredentialType().getName());
998             log.info("No KeyInfo will be generated for EncryptedKey");
999         }
1000 
1001         kekParams.setRecipient(recipient);
1002 
1003         return kekParams;
1004     }
1005 
1006     /**
1007      * Obtains a {@link KeyInfoGenerator} for the specified {@link Credential}.
1008      * 
1009      * <p>
1010      * The KeyInfoGenerator returned is based on the {@link NamedKeyInfoGeneratorManager} defined by the specified
1011      * security configuration via {@link SecurityConfiguration#getKeyInfoGeneratorManager()}, and is determined by the
1012      * type of the signing credential and an optional KeyInfo generator manager name. If the latter is ommited, the
1013      * default manager ({@link NamedKeyInfoGeneratorManager#getDefaultManager()}) of the security configuration's
1014      * named generator manager will be used.
1015      * </p>
1016      * 
1017      * <p>
1018      * The generator is determined by the specified {@link SecurityConfiguration}. If a security configuration is not
1019      * supplied, the global security configuration ({@link Configuration#getGlobalSecurityConfiguration()}) will be
1020      * used.
1021      * </p>
1022      * 
1023      * @param credential the credential for which a generator is desired
1024      * @param config the SecurityConfiguration to use (may be null)
1025      * @param keyInfoGenName the named KeyInfoGeneratorManager configuration to use (may be null)
1026      * @return a KeyInfoGenerator appropriate for the specified credential
1027      */
1028     public static KeyInfoGenerator getKeyInfoGenerator(Credential credential, SecurityConfiguration config,
1029             String keyInfoGenName) {
1030 
1031         SecurityConfiguration secConfig;
1032         if (config != null) {
1033             secConfig = config;
1034         } else {
1035             secConfig = Configuration.getGlobalSecurityConfiguration();
1036         }
1037 
1038         NamedKeyInfoGeneratorManager kiMgr = secConfig.getKeyInfoGeneratorManager();
1039         if (kiMgr != null) {
1040             KeyInfoGeneratorFactory kiFactory = null;
1041             if (DatatypeHelper.isEmpty(keyInfoGenName)) {
1042                 kiFactory = kiMgr.getDefaultManager().getFactory(credential);
1043             } else {
1044                 kiFactory = kiMgr.getFactory(keyInfoGenName, credential);
1045             }
1046             if (kiFactory != null) {
1047                 return kiFactory.newInstance();
1048             }
1049         }
1050         return null;
1051     }
1052 
1053     static {
1054         // We use some Apache XML Security utility functions, so need to make sure library
1055         // is initialized.
1056         if (!Init.isInitialized()) {
1057             Init.init();
1058         }
1059 
1060         // Additonal algorithm URI to JCA key algorithm mappins, beyond what is currently
1061         // supplied in the Apache XML Security mapper config.
1062         dsaAlgorithmURIs = new LazySet<String>();
1063         dsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_DSA);
1064 
1065         ecdsaAlgorithmURIs = new LazySet<String>();
1066         ecdsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_ECDSA_SHA1);
1067 
1068         rsaAlgorithmURIs = new HashSet<String>(10);
1069         rsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1);
1070         rsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256);
1071         rsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA384);
1072         rsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA512);
1073         rsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA512);
1074         rsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_RIPEMD160);
1075         rsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_NOT_RECOMMENDED_RSA_MD5);
1076     }
1077 }