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.math.BigInteger;
20  import java.security.GeneralSecurityException;
21  import java.security.Key;
22  import java.security.KeyException;
23  import java.security.KeyFactory;
24  import java.security.NoSuchAlgorithmException;
25  import java.security.PrivateKey;
26  import java.security.PublicKey;
27  import java.security.cert.X509Certificate;
28  import java.security.interfaces.DSAParams;
29  import java.security.interfaces.DSAPrivateKey;
30  import java.security.interfaces.RSAPrivateCrtKey;
31  import java.security.spec.DSAPublicKeySpec;
32  import java.security.spec.RSAPublicKeySpec;
33  import java.util.HashSet;
34  import java.util.Set;
35  
36  import javax.crypto.KeyGenerator;
37  import javax.crypto.SecretKey;
38  
39  import org.apache.commons.ssl.PKCS8Key;
40  import org.apache.xml.security.Init;
41  import org.apache.xml.security.algorithms.JCEMapper;
42  import org.opensaml.xml.Configuration;
43  import org.opensaml.xml.encryption.Encrypter;
44  import org.opensaml.xml.encryption.EncryptionParameters;
45  import org.opensaml.xml.encryption.KeyEncryptionParameters;
46  import org.opensaml.xml.security.credential.BasicCredential;
47  import org.opensaml.xml.security.credential.Credential;
48  import org.opensaml.xml.security.keyinfo.KeyInfoGenerator;
49  import org.opensaml.xml.security.keyinfo.KeyInfoGeneratorFactory;
50  import org.opensaml.xml.security.keyinfo.NamedKeyInfoGeneratorManager;
51  import org.opensaml.xml.security.x509.BasicX509Credential;
52  import org.opensaml.xml.signature.KeyInfo;
53  import org.opensaml.xml.signature.Signature;
54  import org.opensaml.xml.signature.SignatureConstants;
55  import org.opensaml.xml.util.DatatypeHelper;
56  import org.slf4j.Logger;
57  import org.slf4j.LoggerFactory;
58  
59  /**
60   * Helper methods for security-related requirements.
61   */
62  public final class SecurityHelper {
63  
64      /** Class logger. */
65      private static Logger log = LoggerFactory.getLogger(SecurityHelper.class);
66      
67      /** Additional algorithm URI's which imply RSA keys. */
68      private static Set<String> rsaAlgorithmURIs;
69      
70      /** Additional algorithm URI's which imply DSA keys. */
71      private static Set<String> dsaAlgorithmURIs;
72      
73      /** Additional algorithm URI's which imply ECDSA keys. */
74      private static Set<String> ecdsaAlgorithmURIs;
75  
76      /** Constructor. */
77      private SecurityHelper() {
78      }
79  
80      /**
81       * Get the Java security JCA/JCE algorithm identifier associated with an algorithm URI.
82       * 
83       * @param algorithmURI the algorithm URI to evaluate
84       * @return the Java algorithm identifier, or null if the mapping is unavailable or indeterminable from the URI
85       */
86      public static String getAlgorithmIDFromURI(String algorithmURI) {
87          return DatatypeHelper.safeTrimOrNullString(JCEMapper.translateURItoJCEID(algorithmURI));
88      }
89  
90      /**
91       * Check whether the signature method algorithm URI indicates HMAC.
92       * 
93       * @param signatureAlgorithm the signature method algorithm URI
94       * @return true if URI indicates HMAC, false otherwise
95       */
96      public static boolean isHMAC(String signatureAlgorithm) {
97          String algoClass = DatatypeHelper.safeTrimOrNullString(JCEMapper.getAlgorithmClassFromURI(signatureAlgorithm));
98          return ApacheXMLSecurityConstants.ALGO_CLASS_MAC.equals(algoClass);
99      }
100 
101     /**
102      * Get the Java security JCA/JCE key algorithm specifier associated with an algorithm URI.
103      * 
104      * @param algorithmURI the algorithm URI to evaluate
105      * @return the Java key algorithm specifier, or null if the mapping is unavailable or indeterminable from the URI
106      */
107     public static String getKeyAlgorithmFromURI(String algorithmURI) {
108         // The default Apache config file currently only includes the key algorithm for 
109         // the block ciphers and key wrap URI's.  Note: could use a custom config file which contains others.
110         String apacheValue = DatatypeHelper.safeTrimOrNullString(JCEMapper.getJCEKeyAlgorithmFromURI(algorithmURI));
111         if (apacheValue != null) {
112             return apacheValue;
113         }
114         
115         // HMAC uses any symmetric key, so there is no implied specific key algorithm
116         if (isHMAC(algorithmURI)) {
117             return null;
118         }
119         
120         // As a last ditch fallback, check some known common and supported ones.
121         if (rsaAlgorithmURIs.contains(algorithmURI)) {
122             return "RSA";
123         }
124         if (dsaAlgorithmURIs.contains(algorithmURI)) {
125             return "DSA";
126         }
127         if (ecdsaAlgorithmURIs.contains(algorithmURI)) {
128             return "ECDSA";
129         }
130         
131         return null;
132     }
133 
134     /**
135      * Get the length of the key indicated by the algorithm URI, if applicable and available.
136      * 
137      * @param algorithmURI the algorithm URI to evaluate
138      * @return the length of the key indicated by the algorithm URI, or null if the length is either unavailable or
139      *         indeterminable from the URI
140      */
141     public static Integer getKeyLengthFromURI(String algorithmURI) {
142         String algoClass = DatatypeHelper.safeTrimOrNullString(JCEMapper.getAlgorithmClassFromURI(algorithmURI));
143 
144         if (ApacheXMLSecurityConstants.ALGO_CLASS_BLOCK_ENCRYPTION.equals(algoClass)
145                 || ApacheXMLSecurityConstants.ALGO_CLASS_SYMMETRIC_KEY_WRAP.equals(algoClass)) {
146 
147             try {
148                 int keyLength = JCEMapper.getKeyLengthFromURI(algorithmURI);
149                 return new Integer(keyLength);
150             } catch (NumberFormatException e) {
151                 log.warn("XML Security config contained invalid key length value for algorithm URI: " + algorithmURI);
152             }
153         }
154 
155         log.info("Mapping from algorithm URI {} to key length not available", algorithmURI);
156         return null;
157     }
158 
159     /**
160      * Generates a random Java JCE symmetric Key object from the specified XML Encryption algorithm URI.
161      * 
162      * @param algoURI The XML Encryption algorithm URI
163      * @return a randomly-generated symmetric Key
164      * @throws NoSuchAlgorithmException thrown if the specified algorithm is invalid
165      * @throws KeyException thrown if the length of the key to generate could not be determined
166      */
167     public static SecretKey generateSymmetricKey(String algoURI) throws NoSuchAlgorithmException, KeyException {
168         String jceAlgorithmName = getKeyAlgorithmFromURI(algoURI);
169         if (DatatypeHelper.isEmpty(jceAlgorithmName)) {
170             log.error("Mapping from algorithm URI '" + algoURI
171                     + "' to key algorithm not available, key generation failed");
172             throw new NoSuchAlgorithmException("Algorithm URI'" + algoURI + "' is invalid for key generation");
173         }
174         Integer keyLength = getKeyLengthFromURI(algoURI);
175         if (keyLength == null) {
176             log.error("Key length could not be determined from algorithm URI, can't generate key");
177             throw new KeyException("Key length not determinable from algorithm URI, could not generate new key");
178         }
179         KeyGenerator keyGenerator = KeyGenerator.getInstance(jceAlgorithmName);
180         keyGenerator.init(keyLength);
181         return keyGenerator.generateKey();
182     }
183 
184     /**
185      * Extract the encryption key from the credential.
186      * 
187      * @param credential the credential containing the encryption key
188      * @return the encryption key (either a public key or a secret (symmetric) key
189      */
190     public static Key extractEncryptionKey(Credential credential) {
191         if (credential == null) {
192             return null;
193         }
194         if (credential.getPublicKey() != null) {
195             return credential.getPublicKey();
196         } else {
197             return credential.getSecretKey();
198         }
199     }
200 
201     /**
202      * Extract the decryption key from the credential.
203      * 
204      * @param credential the credential containing the decryption key
205      * @return the decryption key (either a private key or a secret (symmetric) key
206      */
207     public static Key extractDecryptionKey(Credential credential) {
208         if (credential == null) {
209             return null;
210         }
211         if (credential.getPrivateKey() != null) {
212             return credential.getPrivateKey();
213         } else {
214             return credential.getSecretKey();
215         }
216     }
217 
218     /**
219      * Extract the signing key from the credential.
220      * 
221      * @param credential the credential containing the signing key
222      * @return the signing key (either a private key or a secret (symmetric) key
223      */
224     public static Key extractSigningKey(Credential credential) {
225         if (credential == null) {
226             return null;
227         }
228         if (credential.getPrivateKey() != null) {
229             return credential.getPrivateKey();
230         } else {
231             return credential.getSecretKey();
232         }
233     }
234 
235     /**
236      * Extract the verification key from the credential.
237      * 
238      * @param credential the credential containing the verification key
239      * @return the verification key (either a public key or a secret (symmetric) key
240      */
241     public static Key extractVerificationKey(Credential credential) {
242         if (credential == null) {
243             return null;
244         }
245         if (credential.getPublicKey() != null) {
246             return credential.getPublicKey();
247         } else {
248             return credential.getSecretKey();
249         }
250     }
251 
252     /**
253      * Get the key length in bits of the specified key.
254      * 
255      * @param key the key to evaluate
256      * @return length of the key in bits, or null if the length can not be determined
257      */
258     public static Integer getKeyLength(Key key) {
259         // TODO investigate techniques (and use cases) to determine length in other cases,
260         // e.g. RSA and DSA keys, and non-RAW format symmetric keys
261         if (key instanceof SecretKey && "RAW".equals(key.getFormat())) {
262             return key.getEncoded().length * 8;
263         }
264         log.debug("Unable to determine length in bits of specified Key instance");
265         return null;
266     }
267 
268     /**
269      * Get a simple, minimal credential containing a secret (symmetric) key.
270      * 
271      * @param secretKey the symmetric key to wrap
272      * @return a credential containing the secret key specified
273      */
274     public static BasicCredential getSimpleCredential(SecretKey secretKey) {
275         if (secretKey == null) {
276             throw new IllegalArgumentException("A secret key is required");
277         }
278         BasicCredential cred = new BasicCredential();
279         cred.setSecretKey(secretKey);
280         return cred;
281     }
282 
283     /**
284      * Get a simple, minimal credential containing a public key, and optionally a private key.
285      * 
286      * @param publicKey the public key to wrap
287      * @param privateKey the private key to wrap, which may be null
288      * @return a credential containing the key(s) specified
289      */
290     public static BasicCredential getSimpleCredential(PublicKey publicKey, PrivateKey privateKey) {
291         if (publicKey == null) {
292             throw new IllegalArgumentException("A public key is required");
293         }
294         BasicCredential cred = new BasicCredential();
295         cred.setPublicKey(publicKey);
296         cred.setPrivateKey(privateKey);
297         return cred;
298     }
299 
300     /**
301      * Get a simple, minimal credential containing an end-entity X.509 certificate, and optionally a private key.
302      * 
303      * @param cert the end-entity certificate to wrap
304      * @param privateKey the private key to wrap, which may be null
305      * @return a credential containing the certificate and key specified
306      */
307     public static BasicX509Credential getSimpleCredential(X509Certificate cert, PrivateKey privateKey) {
308         if (cert == null) {
309             throw new IllegalArgumentException("A certificate is required");
310         }
311         BasicX509Credential cred = new BasicX509Credential();
312         cred.setEntityCertificate(cert);
313         cred.setPrivateKey(privateKey);
314         return cred;
315     }
316 
317     /**
318      * Decodes secret keys in DER and PEM format.
319      * 
320      * This method is not yet implemented.
321      * 
322      * @param key secret key
323      * @param password password if the key is encrypted or null if not
324      * 
325      * @return the decoded key
326      * 
327      * @throws KeyException thrown if the key can not be decoded
328      */
329     public static SecretKey decodeSecretKey(byte[] key, char[] password) throws KeyException {
330         // TODO
331         throw new UnsupportedOperationException("This method is not yet supported");
332     }
333 
334     /**
335      * Decodes RSA/DSA public keys in DER or PEM formats.
336      * 
337      * @param key encoded key
338      * @param password password if the key is encrypted or null if not
339      * 
340      * @return deocded key
341      * 
342      * @throws KeyException thrown if the key can not be decoded
343      */
344     public static PublicKey decodePublicKey(byte[] key, char[] password) throws KeyException {
345         // TODO
346         throw new UnsupportedOperationException("This method is not yet supported");
347     }
348 
349     /**
350      * Derives the public key from either a DSA or RSA private key.
351      * 
352      * @param key the private key to derive the public key from
353      * 
354      * @return the derived public key
355      * 
356      * @throws KeyException thrown if the given private key is not a DSA or RSA key or there is a problem generating the
357      *             public key
358      */
359     public static PublicKey derivePublicKey(PrivateKey key) throws KeyException {
360         KeyFactory factory;
361         if (key instanceof DSAPrivateKey) {
362             DSAPrivateKey dsaKey = (DSAPrivateKey) key;
363             DSAParams keyParams = dsaKey.getParams();
364             BigInteger y = keyParams.getQ().modPow(dsaKey.getX(), keyParams.getP());
365             DSAPublicKeySpec pubKeySpec = new DSAPublicKeySpec(y, keyParams.getP(), keyParams.getQ(), keyParams.getG());
366 
367             try {
368                 factory = KeyFactory.getInstance("DSA");
369                 return factory.generatePublic(pubKeySpec);
370             } catch (GeneralSecurityException e) {
371                 throw new KeyException("Unable to derive public key from DSA private key", e);
372             }
373         } else if (key instanceof RSAPrivateCrtKey) {
374             RSAPrivateCrtKey rsaKey = (RSAPrivateCrtKey) key;
375             RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(rsaKey.getModulus(), rsaKey.getPublicExponent());
376 
377             try {
378                 factory = KeyFactory.getInstance("RSA");
379                 return factory.generatePublic(pubKeySpec);
380             } catch (GeneralSecurityException e) {
381                 throw new KeyException("Unable to derive public key from RSA private key", e);
382             }
383         } else {
384             throw new KeyException("Private key was not a DSA or RSA key");
385         }
386     }
387 
388     /**
389      * Decodes RSA/DSA private keys in DER, PEM, or PKCS#8 (encrypted or unencrypted) formats.
390      * 
391      * @param key encoded key
392      * @param password decryption password or null if the key is not encrypted
393      * 
394      * @return deocded private key
395      * 
396      * @throws KeyException thrown if the key can not be decoded
397      */
398     public static PrivateKey decodePrivateKey(byte[] key, char[] password) throws KeyException {
399         try {
400             PKCS8Key deocodedKey = new PKCS8Key(key, password);
401             return deocodedKey.getPrivateKey();
402         } catch (GeneralSecurityException e) {
403             throw new KeyException("Unable to decode private key", e);
404         }
405     }
406     
407     /**
408      * Compare the supplied public and private keys, and determine if they correspond to the same
409      * key pair.
410      * 
411      * @param pubKey the public key
412      * @param privKey the private key
413      * @return true if the public and private are from the same key pair, false if not
414      * @throws SecurityException if the keys can not be evaluated, or if the key algorithm is unsupported
415      *                           or unknown
416      */
417     public static boolean matchKeyPair(PublicKey pubKey, PrivateKey privKey) throws SecurityException {
418         //This approach attempts to match the keys by signing and then validating some known data. 
419         
420         if (pubKey == null || privKey == null) {
421             throw new SecurityException("Either public or private key was null");
422         }
423         
424         // Need to dynamically determine the JCA signature algorithm ID to use from the key algorithm.
425         // Don't currently have a direct mapping, so have to map to XML Signature algorithm URI first,
426         // then map that to JCA algorithm ID.
427         SecurityConfiguration secConfig = Configuration.getGlobalSecurityConfiguration();
428         if (secConfig == null) {
429             throw new SecurityException("Global security configuration was null, could not resolve signing algorithm");
430         }
431         String algoURI =  secConfig.getSignatureAlgorithmURI(privKey.getAlgorithm());
432         if (algoURI == null) {
433            throw new SecurityException("Can't determine algorithm URI from key algorithm: " + privKey.getAlgorithm());
434         }
435         String jcaAlgoID = getAlgorithmIDFromURI(algoURI);
436         if (jcaAlgoID == null) {
437            throw new SecurityException("Can't determine JCA algorithm ID from algorithm URI: " + algoURI);
438         }
439         
440         if (log.isDebugEnabled()) {
441             log.debug("Attempting to match key pair containing key algorithms public '{}' private '{}', " 
442                     + "using JCA signature algorithm '{}'",
443                     new Object[] {pubKey.getAlgorithm(), privKey.getAlgorithm(), jcaAlgoID});
444         }
445             
446         byte[] data = "This is the data to sign".getBytes();
447         byte[] signature = SigningUtil.sign(privKey, jcaAlgoID, data);
448         return SigningUtil.verify(pubKey, jcaAlgoID, signature, data);
449     }
450 
451     /**
452      * Prepare a {@link Signature} with necessary additional information prior to signing.
453      * 
454      * <p>
455      * <strong>NOTE:</strong>Since this operation modifies the specified Signature object, it should be called
456      * <strong>prior</strong> to marshalling the Signature object.
457      * </p>
458      * 
459      * <p>
460      * The following Signature values will be added:
461      * <ul>
462      * <li>signature algorithm URI</li>
463      * <li>canonicalization algorithm URI</li>
464      * <li>HMAC output length (if applicable and a value is configured)</li>
465      * <li>a {@link KeyInfo} element representing the signing credential</li>
466      * </ul>
467      * </p>
468      * 
469      * <p>Existing (non-null) values of these parameters on the specified signature
470      * will <strong>NOT</strong> be overwritten, however.</p>
471      * 
472      * <p>
473      * All values are determined by the specified {@link SecurityConfiguration}. If a security configuration is not
474      * supplied, the global security configuration ({@link Configuration#getGlobalSecurityConfiguration()}) will be
475      * used.
476      * </p>
477      * 
478      * <p>
479      * The signature algorithm URI and optional HMAC output length are derived from the signing credential.
480      * </p>
481      * 
482      * <p>
483      * The KeyInfo to be generated is based on the {@link NamedKeyInfoGeneratorManager} defined in the security
484      * configuration, and is determined by the type of the signing credential and an optional KeyInfo generator manager
485      * name. If the latter is ommited, the default manager ({@link NamedKeyInfoGeneratorManager#getDefaultManager()})
486      * of the security configuration's named generator manager will be used.
487      * </p>
488      * 
489      * @param signature the Signature to be updated
490      * @param signingCredential the credential with which the Signature will be computed
491      * @param config the SecurityConfiguration to use (may be null)
492      * @param keyInfoGenName the named KeyInfoGeneratorManager configuration to use (may be null)
493      * @throws SecurityException thrown if there is an error generating the KeyInfo from the signing credential
494      */
495     public static void prepareSignatureParams(Signature signature, Credential signingCredential,
496             SecurityConfiguration config, String keyInfoGenName) throws SecurityException {
497 
498         SecurityConfiguration secConfig;
499         if (config != null) {
500             secConfig = config;
501         } else {
502             secConfig = Configuration.getGlobalSecurityConfiguration();
503         }
504 
505         // The algorithm URI is derived from the credential
506         String signAlgo = signature.getSignatureAlgorithm();
507         if (signAlgo == null) {
508             signAlgo = secConfig.getSignatureAlgorithmURI(signingCredential);
509             signature.setSignatureAlgorithm(signAlgo);
510         }        
511         
512         // If we're doing HMAC, set the output length
513         if (SecurityHelper.isHMAC(signAlgo)) {
514             if (signature.getHMACOutputLength() == null) {
515                 signature.setHMACOutputLength(secConfig.getSignatureHMACOutputLength());
516             }            
517         }
518         
519         if (signature.getCanonicalizationAlgorithm() == null) {
520             signature.setCanonicalizationAlgorithm(secConfig.getSignatureCanonicalizationAlgorithm());
521         }        
522         
523         if (signature.getKeyInfo() == null) {
524             KeyInfoGenerator kiGenerator = getKeyInfoGenerator(signingCredential, secConfig, keyInfoGenName);
525             if (kiGenerator != null) {
526                 try {
527                     KeyInfo keyInfo = kiGenerator.generate(signingCredential);
528                     signature.setKeyInfo(keyInfo);
529                 } catch (SecurityException e) {
530                     log.error("Error generating KeyInfo from credential", e);
531                     throw e;
532                 }
533             } else {
534                 log.info("No factory for named KeyInfoGenerator {} was found for credential type {}", keyInfoGenName,
535                         signingCredential.getCredentialType().getName());
536                 log.info("No KeyInfo will be generated for Signature");
537             }
538         }        
539     }
540 
541     /**
542      * Build an instance of {@link EncryptionParameters} suitable for passing to an {@link Encrypter}.
543      * 
544      * <p>
545      * The following parameter values will be added:
546      * <ul>
547      * <li>the encryption credential (optional)</li>
548      * <li>encryption algorithm URI</li>
549      * <li>an appropriate {@link KeyInfoGenerator} instance which will be used to generate a {@link KeyInfo} element
550      * from the encryption credential</li>
551      * </ul>
552      * </p>
553      * 
554      * <p>
555      * All values are determined by the specified {@link SecurityConfiguration}. If a security configuration is not
556      * supplied, the global security configuration ({@link Configuration#getGlobalSecurityConfiguration()}) will be
557      * used.
558      * </p>
559      * 
560      * <p>
561      * The encryption algorithm URI is derived from the optional supplied encryption credential. If omitted, the value
562      * of {@link SecurityConfiguration#getAutoGeneratedDataEncryptionKeyAlgorithmURI()} will be used.
563      * </p>
564      * 
565      * <p>
566      * The KeyInfoGenerator to be used is based on the {@link NamedKeyInfoGeneratorManager} defined in the security
567      * configuration, and is determined by the type of the signing credential and an optional KeyInfo generator manager
568      * name. If the latter is ommited, the default manager ({@link NamedKeyInfoGeneratorManager#getDefaultManager()})
569      * of the security configuration's named generator manager will be used.
570      * </p>
571      * 
572      * @param encryptionCredential the credential with which the data will be encrypted (may be null)
573      * @param config the SecurityConfiguration to use (may be null)
574      * @param keyInfoGenName the named KeyInfoGeneratorManager configuration to use (may be null)
575      * @return a new instance of EncryptionParameters
576      */
577     public static EncryptionParameters buildDataEncryptionParams(Credential encryptionCredential,
578             SecurityConfiguration config, String keyInfoGenName) {
579 
580         SecurityConfiguration secConfig;
581         if (config != null) {
582             secConfig = config;
583         } else {
584             secConfig = Configuration.getGlobalSecurityConfiguration();
585         }
586 
587         EncryptionParameters encParams = new EncryptionParameters();
588         encParams.setEncryptionCredential(encryptionCredential);
589 
590         if (encryptionCredential == null) {
591             encParams.setAlgorithm(secConfig.getAutoGeneratedDataEncryptionKeyAlgorithmURI());
592         } else {
593             encParams.setAlgorithm(secConfig.getDataEncryptionAlgorithmURI(encryptionCredential));
594 
595             KeyInfoGenerator kiGenerator = getKeyInfoGenerator(encryptionCredential, secConfig, keyInfoGenName);
596             if (kiGenerator != null) {
597                 encParams.setKeyInfoGenerator(kiGenerator);
598             } else {
599                 log.info("No factory for named KeyInfoGenerator {} was found for credential type{}", keyInfoGenName,
600                         encryptionCredential.getCredentialType().getName());
601                 log.info("No KeyInfo will be generated for EncryptedData");
602             }
603         }
604 
605         return encParams;
606     }
607 
608     /**
609      * Build an instance of {@link KeyEncryptionParameters} suitable for passing to an {@link Encrypter}.
610      * 
611      * <p>
612      * The following parameter values will be added:
613      * <ul>
614      * <li>the key encryption credential</li>
615      * <li>key transport encryption algorithm URI</li>
616      * <li>an appropriate {@link KeyInfoGenerator} instance which will be used to generate a {@link KeyInfo} element
617      * from the key encryption credential</li>
618      * <li>intended recipient of the resultant encrypted key (optional)</li>
619      * </ul>
620      * </p>
621      * 
622      * <p>
623      * All values are determined by the specified {@link SecurityConfiguration}. If a security configuration is not
624      * supplied, the global security configuration ({@link Configuration#getGlobalSecurityConfiguration()}) will be
625      * used.
626      * </p>
627      * 
628      * <p>
629      * The encryption algorithm URI is derived from the optional supplied encryption credential. If omitted, the value
630      * of {@link SecurityConfiguration#getAutoGeneratedDataEncryptionKeyAlgorithmURI()} will be used.
631      * </p>
632      * 
633      * <p>
634      * The KeyInfoGenerator to be used is based on the {@link NamedKeyInfoGeneratorManager} defined in the security
635      * configuration, and is determined by the type of the signing credential and an optional KeyInfo generator manager
636      * name. If the latter is ommited, the default manager ({@link NamedKeyInfoGeneratorManager#getDefaultManager()})
637      * of the security configuration's named generator manager will be used.
638      * </p>
639      * 
640      * @param encryptionCredential the credential with which the key will be encrypted
641      * @param wrappedKeyAlgorithm the JCA key algorithm name of the key to be encrypted (may be null)
642      * @param config the SecurityConfiguration to use (may be null)
643      * @param keyInfoGenName the named KeyInfoGeneratorManager configuration to use (may be null)
644      * @param recipient the intended recipient of the resultant encrypted key, typically the owner of the key encryption
645      *            key (may be null)
646      * @return a new instance of KeyEncryptionParameters
647      * @throws SecurityException if encryption credential is not supplied
648      * 
649      */
650     public static KeyEncryptionParameters buildKeyEncryptionParams(Credential encryptionCredential,
651             String wrappedKeyAlgorithm, SecurityConfiguration config, String keyInfoGenName, String recipient)
652             throws SecurityException {
653 
654         SecurityConfiguration secConfig;
655         if (config != null) {
656             secConfig = config;
657         } else {
658             secConfig = Configuration.getGlobalSecurityConfiguration();
659         }
660 
661         KeyEncryptionParameters kekParams = new KeyEncryptionParameters();
662         kekParams.setEncryptionCredential(encryptionCredential);
663 
664         if (encryptionCredential == null) {
665             throw new SecurityException("Key encryption credential may not be null");
666         }
667 
668         kekParams.setAlgorithm(secConfig.getKeyTransportEncryptionAlgorithmURI(encryptionCredential,
669                 wrappedKeyAlgorithm));
670 
671         KeyInfoGenerator kiGenerator = getKeyInfoGenerator(encryptionCredential, secConfig, keyInfoGenName);
672         if (kiGenerator != null) {
673             kekParams.setKeyInfoGenerator(kiGenerator);
674         } else {
675             log.info("No factory for named KeyInfoGenerator {} was found for credential type {}", keyInfoGenName,
676                     encryptionCredential.getCredentialType().getName());
677             log.info("No KeyInfo will be generated for EncryptedKey");
678         }
679 
680         kekParams.setRecipient(recipient);
681 
682         return kekParams;
683     }
684 
685     /**
686      * Obtains a {@link KeyInfoGenerator} for the specified {@link Credential}.
687      * 
688      * <p>
689      * The KeyInfoGenerator returned is based on the {@link NamedKeyInfoGeneratorManager} defined by the specified
690      * security configuration via {@link SecurityConfiguration#getKeyInfoGeneratorManager()}, and is determined by the
691      * type of the signing credential and an optional KeyInfo generator manager name. If the latter is ommited, the
692      * default manager ({@link NamedKeyInfoGeneratorManager#getDefaultManager()}) of the security configuration's
693      * named generator manager will be used.
694      * </p>
695      * 
696      * <p>
697      * The generator is determined by the specified {@link SecurityConfiguration}. If a security configuration is not
698      * supplied, the global security configuration ({@link Configuration#getGlobalSecurityConfiguration()}) will be
699      * used.
700      * </p>
701      * 
702      * @param credential the credential for which a generator is desired
703      * @param config the SecurityConfiguration to use (may be null)
704      * @param keyInfoGenName the named KeyInfoGeneratorManager configuration to use (may be null)
705      * @return a KeyInfoGenerator appropriate for the specified credential
706      */
707     public static KeyInfoGenerator getKeyInfoGenerator(Credential credential, SecurityConfiguration config,
708             String keyInfoGenName) {
709 
710         SecurityConfiguration secConfig;
711         if (config != null) {
712             secConfig = config;
713         } else {
714             secConfig = Configuration.getGlobalSecurityConfiguration();
715         }
716 
717         NamedKeyInfoGeneratorManager kiMgr = secConfig.getKeyInfoGeneratorManager();
718         if (kiMgr != null) {
719             KeyInfoGeneratorFactory kiFactory = null;
720             if (DatatypeHelper.isEmpty(keyInfoGenName)) {
721                 kiFactory = kiMgr.getDefaultManager().getFactory(credential);
722             } else {
723                 kiFactory = kiMgr.getFactory(keyInfoGenName, credential);
724             }
725             if (kiFactory != null) {
726                 return kiFactory.newInstance();
727             }
728         }
729         return null;
730     }
731 
732     static {
733         // We use some Apache XML Security utility functions, so need to make sure library
734         // is initialized.
735         if (!Init.isInitialized()) {
736             Init.init();
737         }
738         
739         // Additonal algorithm URI to JCA key algorithm mappins, beyond what is currently
740         // supplied in the Apache XML Security mapper config.
741         dsaAlgorithmURIs = new HashSet<String>();
742         dsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_DSA);
743         
744         ecdsaAlgorithmURIs = new HashSet<String>();
745         ecdsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_ECDSA_SHA1);
746         
747         rsaAlgorithmURIs = new HashSet<String>();
748         rsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1);
749         rsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256);
750         rsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA384);
751         rsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA512);
752         rsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA512);
753         rsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_RIPEMD160);
754         rsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_NOT_RECOMMENDED_RSA_MD5);
755     }
756 }