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