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      * Prepare a {@link Signature} with necessary additional information prior to signing.
409      * 
410      * <p>
411      * <strong>NOTE:</strong>Since this operation modifies the specified Signature object, it should be called
412      * <strong>prior</strong> to marshalling the Signature object.
413      * </p>
414      * 
415      * <p>
416      * The following Signature values will be added:
417      * <ul>
418      * <li>signature algorithm URI</li>
419      * <li>canonicalization algorithm URI</li>
420      * <li>HMAC output length (if applicable and a value is configured)</li>
421      * <li>a {@link KeyInfo} element representing the signing credential</li>
422      * </ul>
423      * </p>
424      * 
425      * <p>Existing (non-null) values of these parameters on the specified signature
426      * will <strong>NOT</strong> be overwritten, however.</p>
427      * 
428      * <p>
429      * All values are determined by the specified {@link SecurityConfiguration}. If a security configuration is not
430      * supplied, the global security configuration ({@link Configuration#getGlobalSecurityConfiguration()}) will be
431      * used.
432      * </p>
433      * 
434      * <p>
435      * The signature algorithm URI and optional HMAC output length are derived from the signing credential.
436      * </p>
437      * 
438      * <p>
439      * The KeyInfo to be generated is based on the {@link NamedKeyInfoGeneratorManager} defined in the security
440      * configuration, and is determined by the type of the signing credential and an optional KeyInfo generator manager
441      * name. If the latter is ommited, the default manager ({@link NamedKeyInfoGeneratorManager#getDefaultManager()})
442      * of the security configuration's named generator manager will be used.
443      * </p>
444      * 
445      * @param signature the Signature to be updated
446      * @param signingCredential the credential with which the Signature will be computed
447      * @param config the SecurityConfiguration to use (may be null)
448      * @param keyInfoGenName the named KeyInfoGeneratorManager configuration to use (may be null)
449      * @throws SecurityException thrown if there is an error generating the KeyInfo from the signing credential
450      */
451     public static void prepareSignatureParams(Signature signature, Credential signingCredential,
452             SecurityConfiguration config, String keyInfoGenName) throws SecurityException {
453 
454         SecurityConfiguration secConfig;
455         if (config != null) {
456             secConfig = config;
457         } else {
458             secConfig = Configuration.getGlobalSecurityConfiguration();
459         }
460 
461         // The algorithm URI is derived from the credential
462         String signAlgo = signature.getSignatureAlgorithm();
463         if (signAlgo == null) {
464             signAlgo = secConfig.getSignatureAlgorithmURI(signingCredential);
465             signature.setSignatureAlgorithm(signAlgo);
466         }        
467         
468         // If we're doing HMAC, set the output length
469         if (SecurityHelper.isHMAC(signAlgo)) {
470             if (signature.getHMACOutputLength() == null) {
471                 signature.setHMACOutputLength(secConfig.getSignatureHMACOutputLength());
472             }            
473         }
474         
475         if (signature.getCanonicalizationAlgorithm() == null) {
476             signature.setCanonicalizationAlgorithm(secConfig.getSignatureCanonicalizationAlgorithm());
477         }        
478         
479         if (signature.getKeyInfo() == null) {
480             KeyInfoGenerator kiGenerator = getKeyInfoGenerator(signingCredential, secConfig, keyInfoGenName);
481             if (kiGenerator != null) {
482                 try {
483                     KeyInfo keyInfo = kiGenerator.generate(signingCredential);
484                     signature.setKeyInfo(keyInfo);
485                 } catch (SecurityException e) {
486                     log.error("Error generating KeyInfo from credential", e);
487                     throw e;
488                 }
489             } else {
490                 log.info("No factory for named KeyInfoGenerator {} was found for credential type {}", keyInfoGenName,
491                         signingCredential.getCredentialType().getName());
492                 log.info("No KeyInfo will be generated for Signature");
493             }
494         }        
495     }
496 
497     /**
498      * Build an instance of {@link EncryptionParameters} suitable for passing to an {@link Encrypter}.
499      * 
500      * <p>
501      * The following parameter values will be added:
502      * <ul>
503      * <li>the encryption credential (optional)</li>
504      * <li>encryption algorithm URI</li>
505      * <li>an appropriate {@link KeyInfoGenerator} instance which will be used to generate a {@link KeyInfo} element
506      * from the encryption credential</li>
507      * </ul>
508      * </p>
509      * 
510      * <p>
511      * All values are determined by the specified {@link SecurityConfiguration}. If a security configuration is not
512      * supplied, the global security configuration ({@link Configuration#getGlobalSecurityConfiguration()}) will be
513      * used.
514      * </p>
515      * 
516      * <p>
517      * The encryption algorithm URI is derived from the optional supplied encryption credential. If omitted, the value
518      * of {@link SecurityConfiguration#getAutoGeneratedDataEncryptionKeyAlgorithmURI()} will be used.
519      * </p>
520      * 
521      * <p>
522      * The KeyInfoGenerator to be used is based on the {@link NamedKeyInfoGeneratorManager} defined in the security
523      * configuration, and is determined by the type of the signing credential and an optional KeyInfo generator manager
524      * name. If the latter is ommited, the default manager ({@link NamedKeyInfoGeneratorManager#getDefaultManager()})
525      * of the security configuration's named generator manager will be used.
526      * </p>
527      * 
528      * @param encryptionCredential the credential with which the data will be encrypted (may be null)
529      * @param config the SecurityConfiguration to use (may be null)
530      * @param keyInfoGenName the named KeyInfoGeneratorManager configuration to use (may be null)
531      * @return a new instance of EncryptionParameters
532      */
533     public static EncryptionParameters buildDataEncryptionParams(Credential encryptionCredential,
534             SecurityConfiguration config, String keyInfoGenName) {
535 
536         SecurityConfiguration secConfig;
537         if (config != null) {
538             secConfig = config;
539         } else {
540             secConfig = Configuration.getGlobalSecurityConfiguration();
541         }
542 
543         EncryptionParameters encParams = new EncryptionParameters();
544         encParams.setEncryptionCredential(encryptionCredential);
545 
546         if (encryptionCredential == null) {
547             encParams.setAlgorithm(secConfig.getAutoGeneratedDataEncryptionKeyAlgorithmURI());
548         } else {
549             encParams.setAlgorithm(secConfig.getDataEncryptionAlgorithmURI(encryptionCredential));
550 
551             KeyInfoGenerator kiGenerator = getKeyInfoGenerator(encryptionCredential, secConfig, keyInfoGenName);
552             if (kiGenerator != null) {
553                 encParams.setKeyInfoGenerator(kiGenerator);
554             } else {
555                 log.info("No factory for named KeyInfoGenerator {} was found for credential type{}", keyInfoGenName,
556                         encryptionCredential.getCredentialType().getName());
557                 log.info("No KeyInfo will be generated for EncryptedData");
558             }
559         }
560 
561         return encParams;
562     }
563 
564     /**
565      * Build an instance of {@link KeyEncryptionParameters} suitable for passing to an {@link Encrypter}.
566      * 
567      * <p>
568      * The following parameter values will be added:
569      * <ul>
570      * <li>the key encryption credential</li>
571      * <li>key transport encryption algorithm URI</li>
572      * <li>an appropriate {@link KeyInfoGenerator} instance which will be used to generate a {@link KeyInfo} element
573      * from the key encryption credential</li>
574      * <li>intended recipient of the resultant encrypted key (optional)</li>
575      * </ul>
576      * </p>
577      * 
578      * <p>
579      * All values are determined by the specified {@link SecurityConfiguration}. If a security configuration is not
580      * supplied, the global security configuration ({@link Configuration#getGlobalSecurityConfiguration()}) will be
581      * used.
582      * </p>
583      * 
584      * <p>
585      * The encryption algorithm URI is derived from the optional supplied encryption credential. If omitted, the value
586      * of {@link SecurityConfiguration#getAutoGeneratedDataEncryptionKeyAlgorithmURI()} will be used.
587      * </p>
588      * 
589      * <p>
590      * The KeyInfoGenerator to be used is based on the {@link NamedKeyInfoGeneratorManager} defined in the security
591      * configuration, and is determined by the type of the signing credential and an optional KeyInfo generator manager
592      * name. If the latter is ommited, the default manager ({@link NamedKeyInfoGeneratorManager#getDefaultManager()})
593      * of the security configuration's named generator manager will be used.
594      * </p>
595      * 
596      * @param encryptionCredential the credential with which the key will be encrypted
597      * @param wrappedKeyAlgorithm the JCA key algorithm name of the key to be encrypted (may be null)
598      * @param config the SecurityConfiguration to use (may be null)
599      * @param keyInfoGenName the named KeyInfoGeneratorManager configuration to use (may be null)
600      * @param recipient the intended recipient of the resultant encrypted key, typically the owner of the key encryption
601      *            key (may be null)
602      * @return a new instance of KeyEncryptionParameters
603      * @throws SecurityException if encryption credential is not supplied
604      * 
605      */
606     public static KeyEncryptionParameters buildKeyEncryptionParams(Credential encryptionCredential,
607             String wrappedKeyAlgorithm, SecurityConfiguration config, String keyInfoGenName, String recipient)
608             throws SecurityException {
609 
610         SecurityConfiguration secConfig;
611         if (config != null) {
612             secConfig = config;
613         } else {
614             secConfig = Configuration.getGlobalSecurityConfiguration();
615         }
616 
617         KeyEncryptionParameters kekParams = new KeyEncryptionParameters();
618         kekParams.setEncryptionCredential(encryptionCredential);
619 
620         if (encryptionCredential == null) {
621             throw new SecurityException("Key encryption credential may not be null");
622         }
623 
624         kekParams.setAlgorithm(secConfig.getKeyTransportEncryptionAlgorithmURI(encryptionCredential,
625                 wrappedKeyAlgorithm));
626 
627         KeyInfoGenerator kiGenerator = getKeyInfoGenerator(encryptionCredential, secConfig, keyInfoGenName);
628         if (kiGenerator != null) {
629             kekParams.setKeyInfoGenerator(kiGenerator);
630         } else {
631             log.info("No factory for named KeyInfoGenerator {} was found for credential type {}", keyInfoGenName,
632                     encryptionCredential.getCredentialType().getName());
633             log.info("No KeyInfo will be generated for EncryptedKey");
634         }
635 
636         kekParams.setRecipient(recipient);
637 
638         return kekParams;
639     }
640 
641     /**
642      * Obtains a {@link KeyInfoGenerator} for the specified {@link Credential}.
643      * 
644      * <p>
645      * The KeyInfoGenerator returned is based on the {@link NamedKeyInfoGeneratorManager} defined by the specified
646      * security configuration via {@link SecurityConfiguration#getKeyInfoGeneratorManager()}, and is determined by the
647      * type of the signing credential and an optional KeyInfo generator manager name. If the latter is ommited, the
648      * default manager ({@link NamedKeyInfoGeneratorManager#getDefaultManager()}) of the security configuration's
649      * named generator manager will be used.
650      * </p>
651      * 
652      * <p>
653      * The generator is 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      * @param credential the credential for which a generator is desired
659      * @param config the SecurityConfiguration to use (may be null)
660      * @param keyInfoGenName the named KeyInfoGeneratorManager configuration to use (may be null)
661      * @return a KeyInfoGenerator appropriate for the specified credential
662      */
663     public static KeyInfoGenerator getKeyInfoGenerator(Credential credential, SecurityConfiguration config,
664             String keyInfoGenName) {
665 
666         SecurityConfiguration secConfig;
667         if (config != null) {
668             secConfig = config;
669         } else {
670             secConfig = Configuration.getGlobalSecurityConfiguration();
671         }
672 
673         NamedKeyInfoGeneratorManager kiMgr = secConfig.getKeyInfoGeneratorManager();
674         if (kiMgr != null) {
675             KeyInfoGeneratorFactory kiFactory = null;
676             if (DatatypeHelper.isEmpty(keyInfoGenName)) {
677                 kiFactory = kiMgr.getDefaultManager().getFactory(credential);
678             } else {
679                 kiFactory = kiMgr.getFactory(keyInfoGenName, credential);
680             }
681             if (kiFactory != null) {
682                 return kiFactory.newInstance();
683             }
684         }
685         return null;
686     }
687 
688     static {
689         // We use some Apache XML Security utility functions, so need to make sure library
690         // is initialized.
691         if (!Init.isInitialized()) {
692             Init.init();
693         }
694         
695         // Additonal algorithm URI to JCA key algorithm mappins, beyond what is currently
696         // supplied in the Apache XML Security mapper config.
697         dsaAlgorithmURIs = new HashSet<String>();
698         dsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_DSA);
699         
700         ecdsaAlgorithmURIs = new HashSet<String>();
701         ecdsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_ECDSA_SHA1);
702         
703         rsaAlgorithmURIs = new HashSet<String>();
704         rsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1);
705         rsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256);
706         rsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA384);
707         rsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA512);
708         rsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA512);
709         rsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_RIPEMD160);
710         rsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_NOT_RECOMMENDED_RSA_MD5);
711     }
712 }