1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.ws.security.processor;
21
22 import java.security.PrivateKey;
23 import java.security.cert.X509Certificate;
24 import java.security.spec.MGF1ParameterSpec;
25 import java.util.ArrayList;
26 import java.util.LinkedList;
27 import java.util.List;
28
29 import javax.crypto.Cipher;
30 import javax.crypto.KeyGenerator;
31 import javax.crypto.SecretKey;
32 import javax.crypto.spec.OAEPParameterSpec;
33 import javax.crypto.spec.PSource;
34
35 import org.apache.ws.security.WSConstants;
36 import org.apache.ws.security.WSDataRef;
37 import org.apache.ws.security.WSDocInfo;
38 import org.apache.ws.security.WSSecurityEngineResult;
39 import org.apache.ws.security.WSSecurityException;
40 import org.apache.ws.security.components.crypto.AlgorithmSuite;
41 import org.apache.ws.security.components.crypto.AlgorithmSuiteValidator;
42 import org.apache.ws.security.components.crypto.Crypto;
43 import org.apache.ws.security.components.crypto.CryptoType;
44 import org.apache.ws.security.handler.RequestData;
45 import org.apache.ws.security.message.token.SecurityTokenReference;
46 import org.apache.ws.security.str.EncryptedKeySTRParser;
47 import org.apache.ws.security.str.STRParser;
48 import org.apache.ws.security.util.Base64;
49 import org.apache.ws.security.util.WSSecurityUtil;
50 import org.apache.xml.security.algorithms.JCEMapper;
51 import org.w3c.dom.Document;
52 import org.w3c.dom.Element;
53 import org.w3c.dom.Node;
54 import org.w3c.dom.Text;
55
56 public class EncryptedKeyProcessor implements Processor {
57 private static org.apache.commons.logging.Log log =
58 org.apache.commons.logging.LogFactory.getLog(EncryptedKeyProcessor.class);
59
60 public List<WSSecurityEngineResult> handleToken(
61 Element elem,
62 RequestData data,
63 WSDocInfo wsDocInfo
64 ) throws WSSecurityException {
65 return handleToken(elem, data, wsDocInfo, data.getAlgorithmSuite());
66 }
67
68 public List<WSSecurityEngineResult> handleToken(
69 Element elem,
70 RequestData data,
71 WSDocInfo wsDocInfo,
72 AlgorithmSuite algorithmSuite
73 ) throws WSSecurityException {
74 if (log.isDebugEnabled()) {
75 log.debug("Found encrypted key element");
76 }
77 if (data.getDecCrypto() == null) {
78 throw new WSSecurityException(WSSecurityException.FAILURE, "noDecCryptoFile");
79 }
80 if (data.getCallbackHandler() == null) {
81 throw new WSSecurityException(WSSecurityException.FAILURE, "noCallback");
82 }
83
84
85
86
87 String encryptedKeyTransportMethod = X509Util.getEncAlgo(elem);
88 if (encryptedKeyTransportMethod == null) {
89 throw new WSSecurityException(
90 WSSecurityException.UNSUPPORTED_ALGORITHM, "noEncAlgo"
91 );
92 }
93 if (data.getWssConfig().isWsiBSPCompliant()) {
94 checkBSPCompliance(elem, encryptedKeyTransportMethod);
95 }
96 Cipher cipher = WSSecurityUtil.getCipherInstance(encryptedKeyTransportMethod);
97
98
99
100 Element tmpE =
101 WSSecurityUtil.getDirectChildElement(
102 elem, "CipherData", WSConstants.ENC_NS
103 );
104 Element xencCipherValue = null;
105 if (tmpE != null) {
106 xencCipherValue =
107 WSSecurityUtil.getDirectChildElement(tmpE, "CipherValue", WSConstants.ENC_NS);
108 }
109 if (xencCipherValue == null) {
110 throw new WSSecurityException(WSSecurityException.INVALID_SECURITY, "noCipher");
111 }
112
113 STRParser strParser = new EncryptedKeySTRParser();
114 X509Certificate[] certs =
115 getCertificatesFromEncryptedKey(elem, data, data.getDecCrypto(), wsDocInfo, strParser);
116
117
118 if (algorithmSuite != null) {
119 AlgorithmSuiteValidator algorithmSuiteValidator = new
120 AlgorithmSuiteValidator(algorithmSuite);
121
122 algorithmSuiteValidator.checkAsymmetricKeyLength(certs[0]);
123 algorithmSuiteValidator.checkEncryptionKeyWrapAlgorithm(
124 encryptedKeyTransportMethod
125 );
126 }
127
128 try {
129 PrivateKey privateKey = data.getDecCrypto().getPrivateKey(certs[0], data.getCallbackHandler());
130 OAEPParameterSpec oaepParameterSpec = null;
131 if (WSConstants.KEYTRANSPORT_RSAOEP.equals(encryptedKeyTransportMethod)) {
132
133 String digestAlgorithm = getDigestAlgorithm(elem);
134 String jceDigestAlgorithm = "SHA-1";
135 if (digestAlgorithm != null && !"".equals(digestAlgorithm)) {
136 jceDigestAlgorithm = JCEMapper.translateURItoJCEID(digestAlgorithm);
137 }
138
139 oaepParameterSpec =
140 new OAEPParameterSpec(
141 jceDigestAlgorithm, "MGF1", new MGF1ParameterSpec("SHA-1"), PSource.PSpecified.DEFAULT
142 );
143 }
144 if (oaepParameterSpec == null) {
145 cipher.init(Cipher.DECRYPT_MODE, privateKey);
146 } else {
147 cipher.init(Cipher.DECRYPT_MODE, privateKey, oaepParameterSpec);
148 }
149 } catch (Exception ex) {
150 throw new WSSecurityException(WSSecurityException.FAILED_CHECK, null, null, ex);
151 }
152
153 List<String> dataRefURIs = getDataRefURIs(elem);
154
155 byte[] encryptedEphemeralKey = null;
156 byte[] decryptedBytes = null;
157 try {
158 encryptedEphemeralKey = getDecodedBase64EncodedData(xencCipherValue);
159 decryptedBytes = cipher.doFinal(encryptedEphemeralKey);
160 } catch (IllegalStateException ex) {
161 throw new WSSecurityException(WSSecurityException.FAILED_CHECK, null, null, ex);
162 } catch (Exception ex) {
163 decryptedBytes = getRandomKey(dataRefURIs, elem.getOwnerDocument(), wsDocInfo);
164 }
165
166 List<WSDataRef> dataRefs = decryptDataRefs(dataRefURIs, elem.getOwnerDocument(), wsDocInfo,
167 decryptedBytes, data);
168
169 WSSecurityEngineResult result = new WSSecurityEngineResult(
170 WSConstants.ENCR,
171 decryptedBytes,
172 encryptedEphemeralKey,
173 dataRefs,
174 certs
175 );
176 result.put(
177 WSSecurityEngineResult.TAG_ENCRYPTED_KEY_TRANSPORT_METHOD,
178 encryptedKeyTransportMethod
179 );
180 result.put(WSSecurityEngineResult.TAG_ID, elem.getAttributeNS(null, "Id"));
181 result.put(WSSecurityEngineResult.TAG_X509_REFERENCE_TYPE, strParser.getCertificatesReferenceType());
182 wsDocInfo.addResult(result);
183 wsDocInfo.addTokenElement(elem);
184 return java.util.Collections.singletonList(result);
185 }
186
187
188
189
190
191
192
193
194
195
196
197 private static byte[] getRandomKey(List<String> dataRefURIs, Document doc, WSDocInfo wsDocInfo) throws WSSecurityException {
198 try {
199 String alg = "AES";
200 int size = 128;
201 if (!dataRefURIs.isEmpty()) {
202 String uri = dataRefURIs.iterator().next();
203 Element ee = ReferenceListProcessor.findEncryptedDataElement(doc, wsDocInfo, uri);
204 String algorithmURI = X509Util.getEncAlgo(ee);
205 alg = JCEMapper.getJCEKeyAlgorithmFromURI(algorithmURI);
206 size = WSSecurityUtil.getKeyLength(algorithmURI);
207 }
208 KeyGenerator kgen = KeyGenerator.getInstance(alg);
209 kgen.init(size * 8);
210 SecretKey k = kgen.generateKey();
211 return k.getEncoded();
212 } catch (Exception ex) {
213 throw new WSSecurityException(WSSecurityException.FAILED_CHECK, null, null, ex);
214 }
215 }
216
217
218
219
220
221
222
223
224 private static byte[] getDecodedBase64EncodedData(Element element) throws WSSecurityException {
225 StringBuilder sb = new StringBuilder();
226 Node node = element.getFirstChild();
227 while (node != null) {
228 if (Node.TEXT_NODE == node.getNodeType()) {
229 sb.append(((Text) node).getData());
230 }
231 node = node.getNextSibling();
232 }
233 String encodedData = sb.toString();
234 return Base64.decode(encodedData);
235 }
236
237 private static String getDigestAlgorithm(Node encBodyData) throws WSSecurityException {
238 Element tmpE =
239 WSSecurityUtil.getDirectChildElement(
240 encBodyData, "EncryptionMethod", WSConstants.ENC_NS
241 );
242 if (tmpE != null) {
243 Element digestElement =
244 WSSecurityUtil.getDirectChildElement(tmpE, "DigestMethod", WSConstants.SIG_NS);
245 if (digestElement != null) {
246 return digestElement.getAttribute("Algorithm");
247 }
248 }
249 return null;
250 }
251
252
253
254
255
256 private X509Certificate[] getCertificatesFromEncryptedKey(
257 Element xencEncryptedKey,
258 RequestData data,
259 Crypto crypto,
260 WSDocInfo wsDocInfo,
261 STRParser strParser
262 ) throws WSSecurityException {
263 Element keyInfo =
264 WSSecurityUtil.getDirectChildElement(
265 xencEncryptedKey, "KeyInfo", WSConstants.SIG_NS
266 );
267 if (keyInfo != null) {
268 Element strElement = null;
269 if (data.getWssConfig().isWsiBSPCompliant()) {
270 int result = 0;
271 Node node = keyInfo.getFirstChild();
272 while (node != null) {
273 if (Node.ELEMENT_NODE == node.getNodeType()) {
274 result++;
275 strElement = (Element)node;
276 }
277 node = node.getNextSibling();
278 }
279 if (result != 1) {
280 throw new WSSecurityException(
281 WSSecurityException.INVALID_SECURITY, "invalidDataRef"
282 );
283 }
284 } else {
285 strElement =
286 WSSecurityUtil.getDirectChildElement(
287 keyInfo,
288 SecurityTokenReference.SECURITY_TOKEN_REFERENCE,
289 WSConstants.WSSE_NS
290 );
291 }
292 if (strElement == null || strParser == null) {
293 throw new WSSecurityException(
294 WSSecurityException.INVALID_SECURITY, "noSecTokRef"
295 );
296 }
297 strParser.parseSecurityTokenReference(strElement, data, wsDocInfo, null);
298
299 X509Certificate[] certs = strParser.getCertificates();
300 if (certs == null || certs.length < 1 || certs[0] == null) {
301 throw new WSSecurityException(
302 WSSecurityException.FAILURE,
303 "noCertsFound",
304 new Object[] {"decryption (KeyId)"}
305 );
306 }
307 return certs;
308 } else if (!data.getWssConfig().isWsiBSPCompliant()
309 && crypto.getDefaultX509Identifier() != null) {
310 String alias = crypto.getDefaultX509Identifier();
311 CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ALIAS);
312 cryptoType.setAlias(alias);
313 X509Certificate[] certs = crypto.getX509Certificates(cryptoType);
314 if (certs == null || certs.length < 1 || certs[0] == null) {
315 throw new WSSecurityException(
316 WSSecurityException.FAILURE,
317 "noCertsFound",
318 new Object[] {"decryption (KeyId)"}
319 );
320 }
321 return certs;
322 } else {
323 throw new WSSecurityException(WSSecurityException.INVALID_SECURITY, "noKeyinfo");
324 }
325 }
326
327
328
329
330 private List<String> getDataRefURIs(Element xencEncryptedKey) {
331
332 Element refList =
333 WSSecurityUtil.getDirectChildElement(
334 xencEncryptedKey, "ReferenceList", WSConstants.ENC_NS
335 );
336 List<String> dataRefURIs = new LinkedList<String>();
337 if (refList != null) {
338 for (Node node = refList.getFirstChild(); node != null; node = node.getNextSibling()) {
339 if (Node.ELEMENT_NODE == node.getNodeType()
340 && WSConstants.ENC_NS.equals(node.getNamespaceURI())
341 && "DataReference".equals(node.getLocalName())) {
342 String dataRefURI = ((Element) node).getAttribute("URI");
343 if (dataRefURI.charAt(0) == '#') {
344 dataRefURI = dataRefURI.substring(1);
345 }
346 dataRefURIs.add(dataRefURI);
347 }
348 }
349 }
350 return dataRefURIs;
351 }
352
353
354
355
356 private List<WSDataRef> decryptDataRefs(List<String> dataRefURIs, Document doc,
357 WSDocInfo docInfo, byte[] decryptedBytes, RequestData data
358 ) throws WSSecurityException {
359
360
361
362
363 if (dataRefURIs == null || dataRefURIs.isEmpty()) {
364 return null;
365 }
366 List<WSDataRef> dataRefs = new ArrayList<WSDataRef>();
367 for (String dataRefURI : dataRefURIs) {
368 WSDataRef dataRef =
369 decryptDataRef(doc, dataRefURI, docInfo, decryptedBytes, data);
370 dataRefs.add(dataRef);
371 }
372 return dataRefs;
373 }
374
375
376
377
378 private WSDataRef decryptDataRef(
379 Document doc,
380 String dataRefURI,
381 WSDocInfo docInfo,
382 byte[] decryptedData,
383 RequestData data
384 ) throws WSSecurityException {
385 if (log.isDebugEnabled()) {
386 log.debug("found data reference: " + dataRefURI);
387 }
388
389
390
391 Element encryptedDataElement =
392 ReferenceListProcessor.findEncryptedDataElement(doc, docInfo, dataRefURI);
393 if (encryptedDataElement != null && data.isRequireSignedEncryptedDataElements()) {
394 WSSecurityUtil.verifySignedElement(encryptedDataElement, doc, docInfo.getSecurityHeader());
395 }
396
397
398
399 String symEncAlgo = X509Util.getEncAlgo(encryptedDataElement);
400 SecretKey symmetricKey = null;
401 try {
402 symmetricKey = WSSecurityUtil.prepareSecretKey(symEncAlgo, decryptedData);
403 } catch (IllegalArgumentException ex) {
404 throw new WSSecurityException(
405 WSSecurityException.UNSUPPORTED_ALGORITHM, "badEncAlgo",
406 new Object[]{symEncAlgo}, ex
407 );
408 }
409
410
411 AlgorithmSuite algorithmSuite = data.getAlgorithmSuite();
412 if (algorithmSuite != null) {
413 AlgorithmSuiteValidator algorithmSuiteValidator = new
414 AlgorithmSuiteValidator(algorithmSuite);
415
416 algorithmSuiteValidator.checkSymmetricKeyLength(symmetricKey.getEncoded().length);
417 algorithmSuiteValidator.checkSymmetricEncryptionAlgorithm(symEncAlgo);
418 }
419
420 return ReferenceListProcessor.decryptEncryptedData(
421 doc, dataRefURI, encryptedDataElement, symmetricKey, symEncAlgo
422 );
423 }
424
425
426
427
428
429 private void checkBSPCompliance(Element elem, String encAlgo) throws WSSecurityException {
430 String attribute = elem.getAttribute("Type");
431 if (attribute != null && !"".equals(attribute)) {
432 throw new WSSecurityException(
433 WSSecurityException.FAILED_CHECK, "badAttribute", new Object[]{attribute}
434 );
435 }
436 attribute = elem.getAttribute("MimeType");
437 if (attribute != null && !"".equals(attribute)) {
438 throw new WSSecurityException(
439 WSSecurityException.FAILED_CHECK, "badAttribute", new Object[]{attribute}
440 );
441 }
442 attribute = elem.getAttribute("Encoding");
443 if (attribute != null && !"".equals(attribute)) {
444 throw new WSSecurityException(
445 WSSecurityException.FAILED_CHECK, "badAttribute", new Object[]{attribute}
446 );
447 }
448 attribute = elem.getAttribute("Recipient");
449 if (attribute != null && !"".equals(attribute)) {
450 throw new WSSecurityException(
451 WSSecurityException.FAILED_CHECK, "badAttribute", new Object[]{attribute}
452 );
453 }
454
455
456 if (!WSConstants.KEYTRANSPORT_RSA15.equals(encAlgo)
457 && !WSConstants.KEYTRANSPORT_RSAOEP.equals(encAlgo)) {
458 throw new WSSecurityException(
459 WSSecurityException.INVALID_SECURITY, "badEncAlgo", new Object[]{encAlgo}
460 );
461 }
462 }
463
464 }