View Javadoc

1   /*
2    * Copyright [2006] [University Corporation for Advanced Internet Development, Inc.]
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.opensaml.xml.encryption;
18  
19  import java.security.Key;
20  import java.security.KeyException;
21  import java.security.NoSuchAlgorithmException;
22  import java.security.interfaces.DSAPublicKey;
23  import java.util.ArrayList;
24  import java.util.Iterator;
25  import java.util.List;
26  
27  import javax.crypto.SecretKey;
28  
29  import org.apache.xml.security.Init;
30  import org.apache.xml.security.encryption.XMLCipher;
31  import org.apache.xml.security.encryption.XMLEncryptionException;
32  import org.opensaml.xml.Configuration;
33  import org.opensaml.xml.XMLObject;
34  import org.opensaml.xml.XMLObjectBuilderFactory;
35  import org.opensaml.xml.io.Marshaller;
36  import org.opensaml.xml.io.MarshallingException;
37  import org.opensaml.xml.io.Unmarshaller;
38  import org.opensaml.xml.io.UnmarshallerFactory;
39  import org.opensaml.xml.io.UnmarshallingException;
40  import org.opensaml.xml.security.SecurityException;
41  import org.opensaml.xml.security.SecurityHelper;
42  import org.opensaml.xml.security.keyinfo.KeyInfoGenerator;
43  import org.opensaml.xml.signature.DigestMethod;
44  import org.opensaml.xml.signature.KeyInfo;
45  import org.opensaml.xml.signature.SignatureConstants;
46  import org.opensaml.xml.signature.XMLSignatureBuilder;
47  import org.opensaml.xml.util.DatatypeHelper;
48  import org.opensaml.xml.util.XMLConstants;
49  import org.opensaml.xml.util.XMLHelper;
50  import org.slf4j.Logger;
51  import org.slf4j.LoggerFactory;
52  import org.w3c.dom.Document;
53  import org.w3c.dom.Element;
54  
55  /**
56   * Supports encryption of XMLObjects, their content and keys, according to the XML Encryption specification, version
57   * 20021210.
58   * 
59   * <p>
60   * Various overloaded method variants are supplied for encrypting XMLObjects and their contents (with or without
61   * encryption of the associated data encryption key), as well as for encrypting keys separately.
62   * </p>
63   * 
64   * <p>
65   * The parameters for data encryption are specified with an instance of {@link EncryptionParameters}. The parameters
66   * for key encryption are specified with one or more instances of {@link KeyEncryptionParameters}.
67   * </p>
68   * 
69   * <p>
70   * The data encryption credential supplied by {@link EncryptionParameters#getEncryptionCredential()} is mandatory unless
71   * key encryption is also being performed and all associated key encryption parameters contain a valid key encryption
72   * credential containing a valid key encryption key. In this case the data encryption key will be randomly generated
73   * based on the algorithm URI supplied by {@link EncryptionParameters#getAlgorithm()}.
74   * </p>
75   * 
76   * <p>
77   * If encryption of the data encryption key is being performed using the overloaded methods for elements or content, the
78   * resulting EncryptedKey(s) will be placed inline within the KeyInfo of the resulting EncryptedData. If this is not the
79   * desired behavior, the XMLObject and the data encryption key should be encrypted separately, and the placement of
80   * EncryptedKey(s) handled by the caller. Specialized subclasses of this class maybe also handle key placement in an
81   * application-specific manner.
82   * </p>
83   * 
84   */
85  public class Encrypter {
86  
87      /** Class logger. */
88      private final Logger log = LoggerFactory.getLogger(Encrypter.class);
89  
90      /** Unmarshaller used to create EncryptedData objects from DOM element. */
91      private Unmarshaller encryptedDataUnmarshaller;
92  
93      /** Unmarshaller used to create EncryptedData objects from DOM element. */
94      private Unmarshaller encryptedKeyUnmarshaller;
95  
96      /** Builder instance for building KeyInfo objects. */
97      private XMLSignatureBuilder<KeyInfo> keyInfoBuilder;
98  
99      /** The name of the JCA security provider to use. */
100     private String jcaProviderName;
101 
102     /**
103      * Constructor.
104      * 
105      */
106     public Encrypter() {
107         UnmarshallerFactory unmarshallerFactory = Configuration.getUnmarshallerFactory();
108         encryptedDataUnmarshaller = unmarshallerFactory.getUnmarshaller(EncryptedData.DEFAULT_ELEMENT_NAME);
109         encryptedKeyUnmarshaller = unmarshallerFactory.getUnmarshaller(EncryptedKey.DEFAULT_ELEMENT_NAME);
110 
111         XMLObjectBuilderFactory builderFactory = Configuration.getBuilderFactory();
112         keyInfoBuilder = (XMLSignatureBuilder<KeyInfo>) builderFactory.getBuilder(KeyInfo.DEFAULT_ELEMENT_NAME);
113 
114         jcaProviderName = null;
115     }
116 
117     /**
118      * Get the Java Cryptography Architecture (JCA) security provider name that should be used to provide the encryption
119      * support.
120      * 
121      * Defaults to <code>null</code>, which means that the first registered provider which supports the requested
122      * encryption algorithm URI will be used.
123      * 
124      * @return the JCA provider name to use
125      */
126     public String getJCAProviderName() {
127         return jcaProviderName;
128     }
129 
130     /**
131      * Set the Java Cryptography Architecture (JCA) security provider name that should be used to provide the encryption
132      * support.
133      * 
134      * Defaults to <code>null</code>, which means that the first registered provider which supports the requested
135      * encryption algorithm URI will be used.
136      * 
137      * @param providerName the JCA provider name to use
138      */
139     public void setJCAProviderName(String providerName) {
140         jcaProviderName = providerName;
141     }
142 
143     /**
144      * Encrypts the DOM representation of the XMLObject.
145      * 
146      * @param xmlObject the XMLObject to be encrypted
147      * @param encParams parameters for encrypting the data
148      * 
149      * @return the resulting EncryptedData element
150      * @throws EncryptionException exception thrown on encryption errors
151      */
152     public EncryptedData encryptElement(XMLObject xmlObject, EncryptionParameters encParams) 
153             throws EncryptionException {
154         List<KeyEncryptionParameters> emptyKEKParamsList = new ArrayList<KeyEncryptionParameters>();
155         return encryptElement(xmlObject, encParams, emptyKEKParamsList, false);
156     }
157 
158     /**
159      * Encrypts the DOM representation of the XMLObject, encrypts the encryption key using the specified key encryption
160      * parameters and places the resulting EncryptedKey within the EncryptedData's KeyInfo.
161      * 
162      * @param xmlObject the XMLObject to be encrypted
163      * @param encParams parameters for encrypting the data
164      * @param kekParams parameters for encrypting the encryption key
165      * 
166      * @return the resulting EncryptedData element
167      * @throws EncryptionException exception thrown on encryption errors
168      */
169     public EncryptedData encryptElement(XMLObject xmlObject, EncryptionParameters encParams,
170             KeyEncryptionParameters kekParams) throws EncryptionException {
171         List<KeyEncryptionParameters> kekParamsList = new ArrayList<KeyEncryptionParameters>();
172         kekParamsList.add(kekParams);
173         return encryptElement(xmlObject, encParams, kekParamsList, false);
174     }
175 
176     /**
177      * Encrypts the DOM representation of the XMLObject, encrypts the encryption key using the specified key encryption
178      * parameters and places the resulting EncryptedKey(s) within the EncryptedData's KeyInfo.
179      * 
180      * @param xmlObject the XMLObject to be encrypted
181      * @param encParams parameters for encrypting the data
182      * @param kekParamsList parameters for encrypting the encryption key
183      * 
184      * @return the resulting EncryptedData element
185      * @throws EncryptionException exception thrown on encryption errors
186      */
187     public EncryptedData encryptElement(XMLObject xmlObject, EncryptionParameters encParams,
188             List<KeyEncryptionParameters> kekParamsList) throws EncryptionException {
189         return encryptElement(xmlObject, encParams, kekParamsList, false);
190     }
191 
192     /**
193      * Encrypts the DOM representation of the content of an XMLObject.
194      * 
195      * @param xmlObject the XMLObject to be encrypted
196      * @param encParams parameters for encrypting the data
197      * 
198      * @return the resulting EncryptedData element
199      * @throws EncryptionException exception thrown on encryption errors
200      */
201     public EncryptedData encryptElementContent(XMLObject xmlObject, EncryptionParameters encParams)
202             throws EncryptionException {
203         List<KeyEncryptionParameters> emptyKEKParamsList = new ArrayList<KeyEncryptionParameters>();
204         return encryptElement(xmlObject, encParams, emptyKEKParamsList, true);
205     }
206 
207     /**
208      * Encrypts the DOM representation of the content of an XMLObject, encrypts the encryption key using the specified
209      * key encryption parameters and places the resulting EncryptedKey within the EncryptedData's KeyInfo..
210      * 
211      * @param xmlObject the XMLObject to be encrypted
212      * @param encParams parameters for encrypting the data
213      * @param kekParams parameters for encrypting the encryption key
214      * 
215      * @return the resulting EncryptedData element
216      * @throws EncryptionException exception thrown on encryption errors
217      */
218     public EncryptedData encryptElementContent(XMLObject xmlObject, EncryptionParameters encParams,
219             KeyEncryptionParameters kekParams) throws EncryptionException {
220         List<KeyEncryptionParameters> kekParamsList = new ArrayList<KeyEncryptionParameters>();
221         kekParamsList.add(kekParams);
222         return encryptElement(xmlObject, encParams, kekParamsList, true);
223     }
224 
225     /**
226      * Encrypts the DOM representation of the content of an XMLObject, encrypts the encryption key using the specified
227      * key encryption parameters and places the resulting EncryptedKey(s) within the EncryptedData's KeyInfo..
228      * 
229      * @param xmlObject the XMLObject to be encrypted
230      * @param encParams parameters for encrypting the data
231      * @param kekParamsList parameters for encrypting the encryption key
232      * 
233      * @return the resulting EncryptedData element
234      * @throws EncryptionException exception thrown on encryption errors
235      */
236     public EncryptedData encryptElementContent(XMLObject xmlObject, EncryptionParameters encParams,
237             List<KeyEncryptionParameters> kekParamsList) throws EncryptionException {
238         return encryptElement(xmlObject, encParams, kekParamsList, true);
239     }
240 
241     /**
242      * Encrypts a key once for each key encryption parameters set that is supplied.
243      * 
244      * @param key the key to encrypt
245      * @param kekParamsList a list parameters for encrypting the key
246      * @param containingDocument the document that will own the DOM element underlying the resulting EncryptedKey
247      *            objects
248      * 
249      * @return the resulting list of EncryptedKey objects
250      * 
251      * @throws EncryptionException exception thrown on encryption errors
252      */
253     public List<EncryptedKey> encryptKey(Key key, List<KeyEncryptionParameters> kekParamsList,
254             Document containingDocument) throws EncryptionException {
255 
256         checkParams(kekParamsList, false);
257 
258         List<EncryptedKey> encKeys = new ArrayList<EncryptedKey>();
259 
260         for (KeyEncryptionParameters kekParam : kekParamsList) {
261             encKeys.add(encryptKey(key, kekParam, containingDocument));
262         }
263         return encKeys;
264     }
265 
266     /**
267      * Encrypts a key.
268      * 
269      * @param key the key to encrypt
270      * @param kekParams parameters for encrypting the key
271      * @param containingDocument the document that will own the DOM element underlying the resulting EncryptedKey object
272      * 
273      * @return the resulting EncryptedKey object
274      * 
275      * @throws EncryptionException exception thrown on encryption errors
276      */
277     public EncryptedKey encryptKey(Key key, KeyEncryptionParameters kekParams, Document containingDocument)
278             throws EncryptionException {
279 
280         checkParams(kekParams, false);
281 
282         Key encryptionKey = SecurityHelper.extractEncryptionKey(kekParams.getEncryptionCredential());
283         String encryptionAlgorithmURI = kekParams.getAlgorithm();
284 
285         EncryptedKey encryptedKey = encryptKey(key, encryptionKey, encryptionAlgorithmURI, containingDocument);
286 
287         if (kekParams.getKeyInfoGenerator() != null) {
288             KeyInfoGenerator generator = kekParams.getKeyInfoGenerator();
289             log.debug("Dynamically generating KeyInfo from Credential for EncryptedKey using generator: {}",
290                     generator.getClass().getName());
291             try {
292                 encryptedKey.setKeyInfo(generator.generate(kekParams.getEncryptionCredential()));
293             } catch (SecurityException e) {
294                 log.error("Error during EncryptedKey KeyInfo generation", e);
295                 throw new EncryptionException("Error during EncryptedKey KeyInfo generation", e);
296             }
297         }
298 
299         if (kekParams.getRecipient() != null) {
300             encryptedKey.setRecipient(kekParams.getRecipient());
301         }
302 
303         return encryptedKey;
304     }
305 
306     /**
307      * Encrypts a key using the specified encryption key and algorithm URI.
308      * 
309      * @param targetKey the key to encrypt
310      * @param encryptionKey the key with which to encrypt the target key
311      * @param encryptionAlgorithmURI the XML Encryption algorithm URI corresponding to the encryption key
312      * @param containingDocument the document that will own the resulting element
313      * @return the new EncryptedKey object
314      * @throws EncryptionException exception thrown on encryption errors
315      */
316     protected EncryptedKey encryptKey(Key targetKey, Key encryptionKey, String encryptionAlgorithmURI,
317             Document containingDocument) throws EncryptionException {
318 
319         if (targetKey == null) {
320             log.error("Target key for key encryption was null");
321             throw new EncryptionException("Target key was null");
322         }
323         if (encryptionKey == null) {
324             log.error("Encryption key for key encryption was null");
325             throw new EncryptionException("Encryption key was null");
326         }
327 
328         log.debug("Encrypting encryption key with algorithm: {}", encryptionAlgorithmURI);
329         XMLCipher xmlCipher;
330         try {
331             if (getJCAProviderName() != null) {
332                 xmlCipher = XMLCipher.getProviderInstance(encryptionAlgorithmURI, getJCAProviderName());
333             } else {
334                 xmlCipher = XMLCipher.getInstance(encryptionAlgorithmURI);
335             }
336             xmlCipher.init(XMLCipher.WRAP_MODE, encryptionKey);
337         } catch (XMLEncryptionException e) {
338             log.error("Error initializing cipher instance on key encryption", e);
339             throw new EncryptionException("Error initializing cipher instance on key encryption", e);
340         }
341 
342         org.apache.xml.security.encryption.EncryptedKey apacheEncryptedKey;
343         try {
344             apacheEncryptedKey = xmlCipher.encryptKey(containingDocument, targetKey);
345             postProcessApacheEncryptedKey(apacheEncryptedKey, targetKey, encryptionKey,
346                     encryptionAlgorithmURI, containingDocument);
347         } catch (XMLEncryptionException e) {
348             log.error("Error encrypting element on key encryption", e);
349             throw new EncryptionException("Error encrypting element on key encryption", e);
350         }
351 
352         EncryptedKey encryptedKey;
353         try {
354             Element encKeyElement = xmlCipher.martial(containingDocument, apacheEncryptedKey);
355             encryptedKey = (EncryptedKey) encryptedKeyUnmarshaller.unmarshall(encKeyElement);
356         } catch (UnmarshallingException e) {
357             log.error("Error unmarshalling EncryptedKey element", e);
358             throw new EncryptionException("Error unmarshalling EncryptedKey element");
359         }
360 
361         return encryptedKey;
362     }
363 
364     /**
365      *  
366      * Post-process the Apache EncryptedKey, prior to marshalling to DOM and unmarshalling into an XMLObject.
367      *  
368      * @param apacheEncryptedKey the Apache EncryptedKeyObject to post-process
369      * @param targetKey the key to encrypt
370      * @param encryptionKey the key with which to encrypt the target key
371      * @param encryptionAlgorithmURI the XML Encryption algorithm URI corresponding to the encryption key
372      * @param containingDocument the document that will own the resulting element
373      * 
374      * @throws EncryptionException exception thrown on encryption errors
375      */
376     protected void postProcessApacheEncryptedKey(org.apache.xml.security.encryption.EncryptedKey apacheEncryptedKey,
377             Key targetKey, Key encryptionKey, String encryptionAlgorithmURI, Document containingDocument)
378             throws EncryptionException {
379         
380         // Workaround for XML-Security library issue.  To maximize interop, explicitly express the library
381         // default of SHA-1 digest method input parameter to RSA-OAEP key transport algorithm.
382         // Check and only add if the library hasn't already done so, which it currently doesn't.
383         if (EncryptionConstants.ALGO_ID_KEYTRANSPORT_RSAOAEP.equals(encryptionAlgorithmURI)) {
384             boolean sawDigestMethod = false;
385             Iterator childIter = apacheEncryptedKey.getEncryptionMethod().getEncryptionMethodInformation();
386             while (childIter.hasNext()) {
387                 Element child = (Element) childIter.next();
388                 if (DigestMethod.DEFAULT_ELEMENT_NAME.equals(XMLHelper.getNodeQName(child))) {
389                     sawDigestMethod = true;
390                     break;
391                 }
392             }
393             if (! sawDigestMethod) {
394                 Element digestMethodElem = XMLHelper.constructElement(containingDocument,
395                         DigestMethod.DEFAULT_ELEMENT_NAME);
396                 XMLHelper.appendNamespaceDeclaration(digestMethodElem, 
397                         XMLConstants.XMLSIG_NS, XMLConstants.XMLSIG_PREFIX);
398                 digestMethodElem.setAttributeNS(null, DigestMethod.ALGORITHM_ATTRIB_NAME, 
399                         SignatureConstants.ALGO_ID_DIGEST_SHA1);
400                 apacheEncryptedKey.getEncryptionMethod().addEncryptionMethodInformation(digestMethodElem);
401             }
402         }
403         
404     }
405 
406     /**
407      * Encrypts the given XMLObject using the specified encryption key, algorithm URI and content mode flag.
408      * 
409      * @param xmlObject the XMLObject to be encrypted
410      * @param encryptionKey the key with which to encrypt the XMLObject
411      * @param encryptionAlgorithmURI the XML Encryption algorithm URI corresponding to the encryption key
412      * @param encryptContentMode whether just the content of the XMLObject should be encrypted
413      * @return the resulting EncryptedData object
414      * @throws EncryptionException exception thrown on encryption errors
415      */
416     protected EncryptedData encryptElement(XMLObject xmlObject, Key encryptionKey, String encryptionAlgorithmURI,
417             boolean encryptContentMode) throws EncryptionException {
418 
419         if (xmlObject == null) {
420             log.error("XMLObject for encryption was null");
421             throw new EncryptionException("XMLObject was null");
422         }
423         if (encryptionKey == null) {
424             log.error("Encryption key for key encryption was null");
425             throw new EncryptionException("Encryption key was null");
426         }
427         log.debug("Encrypting XMLObject using algorithm URI {} with content mode {}", encryptionAlgorithmURI,
428                 encryptContentMode);
429 
430         checkAndMarshall(xmlObject);
431 
432         Element targetElement = xmlObject.getDOM();
433         Document ownerDocument = targetElement.getOwnerDocument();
434 
435         XMLCipher xmlCipher;
436         try {
437             if (getJCAProviderName() != null) {
438                 xmlCipher = XMLCipher.getProviderInstance(encryptionAlgorithmURI, getJCAProviderName());
439             } else {
440                 xmlCipher = XMLCipher.getInstance(encryptionAlgorithmURI);
441             }
442             xmlCipher.init(XMLCipher.ENCRYPT_MODE, encryptionKey);
443         } catch (XMLEncryptionException e) {
444             log.error("Error initializing cipher instance on XMLObject encryption", e);
445             throw new EncryptionException("Error initializing cipher instance", e);
446         }
447 
448         org.apache.xml.security.encryption.EncryptedData apacheEncryptedData;
449         try {
450             apacheEncryptedData = xmlCipher.encryptData(ownerDocument, targetElement, encryptContentMode);
451         } catch (Exception e) {
452             log.error("Error encrypting XMLObject", e);
453             throw new EncryptionException("Error encrypting XMLObject", e);
454         }
455 
456         EncryptedData encryptedData;
457         try {
458             Element encDataElement = xmlCipher.martial(ownerDocument, apacheEncryptedData);
459             encryptedData = (EncryptedData) encryptedDataUnmarshaller.unmarshall(encDataElement);
460         } catch (UnmarshallingException e) {
461             log.error("Error unmarshalling EncryptedData element", e);
462             throw new EncryptionException("Error unmarshalling EncryptedData element", e);
463         }
464 
465         return encryptedData;
466     }
467 
468     /**
469      * Encrypts the given XMLObject using the specified encryption key, algorithm URI and content mode flag.
470      * EncryptedKeys, if any, are placed inline within the KeyInfo of the resulting EncryptedData.
471      * 
472      * @param xmlObject the XMLObject to be encrypted
473      * @param encParams the encryption parameters to use
474      * @param kekParamsList the key encryption parameters to use
475      * @param encryptContentMode whether just the content of the XMLObject should be encrypted
476      * 
477      * @return the resulting EncryptedData object
478      * @throws EncryptionException exception thrown on encryption errors
479      */
480     private EncryptedData encryptElement(XMLObject xmlObject, EncryptionParameters encParams,
481             List<KeyEncryptionParameters> kekParamsList, boolean encryptContentMode) throws EncryptionException {
482 
483         checkParams(encParams, kekParamsList);
484 
485         String encryptionAlgorithmURI = encParams.getAlgorithm();
486         Key encryptionKey = SecurityHelper.extractEncryptionKey(encParams.getEncryptionCredential());
487         if (encryptionKey == null) {
488             encryptionKey = generateEncryptionKey(encryptionAlgorithmURI);
489         }
490 
491         EncryptedData encryptedData = encryptElement(xmlObject, encryptionKey, encryptionAlgorithmURI,
492                 encryptContentMode);
493         Document ownerDocument = encryptedData.getDOM().getOwnerDocument();
494 
495         if (encParams.getKeyInfoGenerator() != null) {
496             KeyInfoGenerator generator = encParams.getKeyInfoGenerator();
497             log.debug("Dynamically generating KeyInfo from Credential for EncryptedData using generator: {}",
498                     generator.getClass().getName());
499             try {
500                 encryptedData.setKeyInfo(generator.generate(encParams.getEncryptionCredential()));
501             } catch (SecurityException e) {
502                 log.error("Error during EncryptedData KeyInfo generation", e);
503                 throw new EncryptionException("Error during EncryptedData KeyInfo generation", e);
504             }
505         }
506 
507         for (KeyEncryptionParameters kekParams : kekParamsList) {
508             EncryptedKey encryptedKey = encryptKey(encryptionKey, kekParams, ownerDocument);
509             if (encryptedData.getKeyInfo() == null) {
510                 KeyInfo keyInfo = keyInfoBuilder.buildObject();
511                 encryptedData.setKeyInfo(keyInfo);
512             }
513             encryptedData.getKeyInfo().getEncryptedKeys().add(encryptedKey);
514         }
515 
516         return encryptedData;
517     }
518 
519     /**
520      * Ensure that the XMLObject is marshalled.
521      * 
522      * @param xmlObject the object to check and marshall
523      * @throws EncryptionException thrown if there is an error when marshalling the XMLObject
524      */
525     protected void checkAndMarshall(XMLObject xmlObject) throws EncryptionException {
526         Element targetElement = xmlObject.getDOM();
527         if (targetElement == null) {
528             Marshaller marshaller = Configuration.getMarshallerFactory().getMarshaller(xmlObject);
529             try {
530                 targetElement = marshaller.marshall(xmlObject);
531             } catch (MarshallingException e) {
532                 log.error("Error marshalling target XMLObject", e);
533                 throw new EncryptionException("Error marshalling target XMLObject", e);
534             }
535         }
536     }
537 
538     /**
539      * Check data encryption parameters for consistency and required values.
540      * 
541      * @param encParams the data encryption parameters to check
542      * 
543      * @throws EncryptionException thrown if any parameters are missing or have invalid values
544      */
545     protected void checkParams(EncryptionParameters encParams) throws EncryptionException {
546         if (encParams == null) {
547             log.error("Data encryption parameters are required");
548             throw new EncryptionException("Data encryption parameters are required");
549         }
550         if (DatatypeHelper.isEmpty(encParams.getAlgorithm())) {
551             log.error("Data encryption algorithm URI is required");
552             throw new EncryptionException("Data encryption algorithm URI is required");
553         }
554     }
555 
556     /**
557      * Check key encryption parameters for consistency and required values.
558      * 
559      * @param kekParams the key encryption parameters to check
560      * @param allowEmpty if false, a null parameter is treated as an error
561      * 
562      * @throws EncryptionException thrown if any parameters are missing or have invalid values
563      */
564     protected void checkParams(KeyEncryptionParameters kekParams, boolean allowEmpty) throws EncryptionException {
565         if (kekParams == null) {
566             if (allowEmpty) {
567                 return;
568             } else {
569                 log.error("Key encryption parameters are required");
570                 throw new EncryptionException("Key encryption parameters are required");
571             }
572         }
573         Key key = SecurityHelper.extractEncryptionKey(kekParams.getEncryptionCredential());
574         if (key == null) {
575             log.error("Key encryption credential and contained key are required");
576             throw new EncryptionException("Key encryption credential and contained key are required");
577         }
578         if (key instanceof DSAPublicKey) {
579             log.error("Attempt made to use DSA key for encrypted key transport");
580             throw new EncryptionException("DSA keys may not be used for encrypted key transport");
581         }
582         if (DatatypeHelper.isEmpty(kekParams.getAlgorithm())) {
583             log.error("Key encryption algorithm URI is required");
584             throw new EncryptionException("Key encryption algorithm URI is required");
585         }
586     }
587 
588     /**
589      * Check a list of key encryption parameters for consistency and required values.
590      * 
591      * @param kekParamsList the key encryption parameters list to check
592      * @param allowEmpty if false, a null or empty list is treated as an error
593      * 
594      * @throws EncryptionException thrown if any parameters are missing or have invalid values
595      */
596     protected void checkParams(List<KeyEncryptionParameters> kekParamsList, boolean allowEmpty)
597             throws EncryptionException {
598         if (kekParamsList == null || kekParamsList.isEmpty()) {
599             if (allowEmpty) {
600                 return;
601             } else {
602                 log.error("Key encryption parameters list may not be empty");
603                 throw new EncryptionException("Key encryption parameters list may not be empty");
604             }
605         }
606         for (KeyEncryptionParameters kekParams : kekParamsList) {
607             checkParams(kekParams, false);
608         }
609     }
610 
611     /**
612      * Check the encryption parameters and key encryption parameters for valid combinations of options.
613      * 
614      * @param encParams the encryption parameters to use
615      * @param kekParamsList the key encryption parameters to use
616      * @throws EncryptionException exception thrown on encryption errors
617      */
618     protected void checkParams(EncryptionParameters encParams, List<KeyEncryptionParameters> kekParamsList)
619             throws EncryptionException {
620 
621         checkParams(encParams);
622         checkParams(kekParamsList, true);
623 
624         if (SecurityHelper.extractEncryptionKey(encParams.getEncryptionCredential()) == null
625                 && (kekParamsList == null || kekParamsList.isEmpty())) {
626             log.error("Using a generated encryption key requires a KeyEncryptionParameters "
627                     + "object and key encryption key");
628             throw new EncryptionException("Using a generated encryption key requires a KeyEncryptionParameters "
629                     + "object and key encryption key");
630         }
631     }
632 
633     /**
634      * Generate a random symmetric encryption key.
635      * 
636      * @param encryptionAlgorithmURI the encryption algorithm URI
637      * @return a randomly generated symmetric key
638      * @throws EncryptionException thrown if the key can not be generated based on the specified algorithm URI
639      */
640     protected SecretKey generateEncryptionKey(String encryptionAlgorithmURI) throws EncryptionException {
641         try {
642             log.debug("Generating random symmetric data encryption key from algorithm URI: {}", 
643                     encryptionAlgorithmURI);
644             return SecurityHelper.generateSymmetricKey(encryptionAlgorithmURI);
645         } catch (NoSuchAlgorithmException e) {
646             log.error("Could not generate encryption key, algorithm URI was invalid: " + encryptionAlgorithmURI);
647             throw new EncryptionException("Could not generate encryption key, algorithm URI was invalid: "
648                     + encryptionAlgorithmURI);
649         } catch (KeyException e) {
650             log.error("Could not generate encryption key from algorithm URI: " + encryptionAlgorithmURI);
651             throw new EncryptionException("Could not generate encryption key from algorithm URI: "
652                     + encryptionAlgorithmURI);
653         }
654     }
655 
656     /*
657      * Initialize the Apache XML security library if it hasn't been already
658      */
659     static {
660         if (!Init.isInitialized()) {
661             Init.init();
662         }
663     }
664 
665 }