1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.wss4j.stax.impl.processor.output;
20
21 import java.io.IOException;
22 import java.security.InvalidAlgorithmParameterException;
23 import java.security.InvalidKeyException;
24 import java.security.Key;
25 import java.security.NoSuchAlgorithmException;
26 import java.security.PublicKey;
27 import java.security.cert.X509Certificate;
28 import java.security.spec.AlgorithmParameterSpec;
29 import java.security.spec.MGF1ParameterSpec;
30 import java.util.ArrayList;
31 import java.util.List;
32
33 import javax.crypto.Cipher;
34 import javax.crypto.IllegalBlockSizeException;
35 import javax.crypto.NoSuchPaddingException;
36 import javax.crypto.spec.OAEPParameterSpec;
37 import javax.crypto.spec.PSource;
38 import javax.security.auth.callback.Callback;
39 import javax.security.auth.callback.UnsupportedCallbackException;
40 import javax.xml.namespace.QName;
41 import javax.xml.stream.XMLStreamException;
42
43 import org.apache.wss4j.common.ext.WSPasswordCallback;
44 import org.apache.wss4j.common.ext.WSSecurityException;
45 import org.apache.wss4j.stax.ext.WSSConstants;
46 import org.apache.wss4j.stax.ext.WSSSecurityProperties;
47 import org.apache.wss4j.stax.securityToken.WSSecurityTokenConstants;
48 import org.apache.wss4j.stax.utils.WSSUtils;
49 import org.apache.xml.security.exceptions.XMLSecurityException;
50 import org.apache.xml.security.stax.config.JCEAlgorithmMapper;
51 import org.apache.xml.security.stax.ext.AbstractOutputProcessor;
52 import org.apache.xml.security.stax.ext.OutputProcessorChain;
53 import org.apache.xml.security.stax.ext.XMLSecurityConstants;
54 import org.apache.xml.security.stax.ext.stax.XMLSecAttribute;
55 import org.apache.xml.security.stax.ext.stax.XMLSecEvent;
56 import org.apache.xml.security.stax.impl.securityToken.GenericOutboundSecurityToken;
57 import org.apache.xml.security.stax.impl.util.IDGenerator;
58 import org.apache.xml.security.stax.securityToken.OutboundSecurityToken;
59 import org.apache.xml.security.stax.securityToken.SecurityTokenProvider;
60 import org.apache.xml.security.utils.XMLUtils;
61
62 public class EncryptedKeyOutputProcessor extends AbstractOutputProcessor {
63
64 public EncryptedKeyOutputProcessor() throws XMLSecurityException {
65 super();
66 }
67
68 @Override
69 public void processEvent(XMLSecEvent xmlSecEvent, OutputProcessorChain outputProcessorChain)
70 throws XMLStreamException, XMLSecurityException {
71 try {
72
73 String tokenId = outputProcessorChain.getSecurityContext().get(WSSConstants.PROP_USE_THIS_TOKEN_ID_FOR_ENCRYPTED_KEY);
74 if (tokenId == null) {
75 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE);
76 }
77 SecurityTokenProvider<OutboundSecurityToken> wrappingSecurityTokenProvider =
78 outputProcessorChain.getSecurityContext().getSecurityTokenProvider(tokenId);
79 if (wrappingSecurityTokenProvider == null) {
80 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE);
81 }
82 final OutboundSecurityToken wrappingSecurityToken = wrappingSecurityTokenProvider.getSecurityToken();
83 if (wrappingSecurityToken == null) {
84 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE);
85 }
86
87 SecurityTokenProvider<OutboundSecurityToken> encryptedKeySecurityTokenProvider = null;
88 GenericOutboundSecurityToken encryptedKeySecurityToken = null;
89
90 String sigTokenId =
91 outputProcessorChain.getSecurityContext().get(WSSConstants.PROP_USE_THIS_TOKEN_ID_FOR_SIGNATURE);
92
93 String encTokenId =
94 outputProcessorChain.getSecurityContext().get(WSSConstants.PROP_USE_THIS_TOKEN_ID_FOR_ENCRYPTION);
95 if (encTokenId == null) {
96 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE);
97 }
98
99 encryptedKeySecurityTokenProvider =
100 outputProcessorChain.getSecurityContext().getSecurityTokenProvider(encTokenId);
101 if (encryptedKeySecurityTokenProvider == null) {
102 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE);
103 }
104 encryptedKeySecurityToken =
105 (GenericOutboundSecurityToken)encryptedKeySecurityTokenProvider.getSecurityToken();
106
107 boolean sharedToken = encTokenId.equals(sigTokenId);
108
109 FinalEncryptedKeyOutputProcessor finalEncryptedKeyOutputProcessor =
110 new FinalEncryptedKeyOutputProcessor(encryptedKeySecurityToken);
111 finalEncryptedKeyOutputProcessor.setXMLSecurityProperties(getSecurityProperties());
112 finalEncryptedKeyOutputProcessor.setAction(getAction(), getActionOrder());
113 XMLSecurityConstants.Action action = getAction();
114 if (WSSConstants.ENCRYPTION.equals(action)) {
115 if (wrappingSecurityToken.getProcessor() != null) {
116 finalEncryptedKeyOutputProcessor.addBeforeProcessor(wrappingSecurityToken.getProcessor().getClass());
117 finalEncryptedKeyOutputProcessor.init(outputProcessorChain);
118 } else if (sharedToken) {
119 finalEncryptedKeyOutputProcessor.addAfterProcessor(EncryptEndingOutputProcessor.class);
120
121
122 if (getSecurityProperties().getActions().indexOf(WSSConstants.ENCRYPTION)
123 < getSecurityProperties().getActions().indexOf(WSSConstants.SIGNATURE)) {
124 finalEncryptedKeyOutputProcessor.addBeforeProcessor(WSSSignatureOutputProcessor.class);
125 finalEncryptedKeyOutputProcessor.setAction(WSSConstants.SIGNATURE, getActionOrder());
126 }
127 finalEncryptedKeyOutputProcessor.setOutputReferenceList(false);
128 finalEncryptedKeyOutputProcessor.init(outputProcessorChain);
129
130 ReferenceListOutputProcessor referenceListOutputProcessor = new ReferenceListOutputProcessor();
131 referenceListOutputProcessor.addBeforeProcessor(finalEncryptedKeyOutputProcessor.getClass());
132 referenceListOutputProcessor.setXMLSecurityProperties(getSecurityProperties());
133 referenceListOutputProcessor.setAction(getAction(), getActionOrder());
134 referenceListOutputProcessor.init(outputProcessorChain);
135 } else {
136 finalEncryptedKeyOutputProcessor.addAfterProcessor(EncryptEndingOutputProcessor.class);
137 finalEncryptedKeyOutputProcessor.init(outputProcessorChain);
138 }
139 } else if (WSSConstants.SIGNATURE_WITH_DERIVED_KEY.equals(action)) {
140 if (wrappingSecurityToken.getProcessor() != null) {
141 finalEncryptedKeyOutputProcessor.addBeforeProcessor(wrappingSecurityToken.getProcessor().getClass());
142 } else {
143 finalEncryptedKeyOutputProcessor.addBeforeProcessor(WSSSignatureOutputProcessor.class);
144 }
145 finalEncryptedKeyOutputProcessor.init(outputProcessorChain);
146 } else if (WSSConstants.ENCRYPTION_WITH_DERIVED_KEY.equals(action)) {
147 if (wrappingSecurityToken.getProcessor() != null) {
148 finalEncryptedKeyOutputProcessor.addBeforeProcessor(wrappingSecurityToken.getProcessor().getClass());
149 finalEncryptedKeyOutputProcessor.init(outputProcessorChain);
150 } else if (sharedToken) {
151 finalEncryptedKeyOutputProcessor.addBeforeProcessor(WSSSignatureOutputProcessor.class);
152 finalEncryptedKeyOutputProcessor.addAfterProcessor(EncryptEndingOutputProcessor.class);
153
154
155 if (getSecurityProperties().getActions().indexOf(WSSConstants.ENCRYPTION_WITH_DERIVED_KEY)
156 < getSecurityProperties().getActions().indexOf(WSSConstants.SIGNATURE_WITH_DERIVED_KEY)) {
157 finalEncryptedKeyOutputProcessor.setAction(WSSConstants.SIGNATURE_WITH_DERIVED_KEY, getActionOrder());
158 }
159 finalEncryptedKeyOutputProcessor.setOutputReferenceList(false);
160 finalEncryptedKeyOutputProcessor.init(outputProcessorChain);
161 } else {
162 finalEncryptedKeyOutputProcessor.addAfterProcessor(EncryptEndingOutputProcessor.class);
163 finalEncryptedKeyOutputProcessor.init(outputProcessorChain);
164 }
165 ReferenceListOutputProcessor referenceListOutputProcessor = new ReferenceListOutputProcessor();
166 referenceListOutputProcessor.addBeforeProcessor(finalEncryptedKeyOutputProcessor.getClass());
167 referenceListOutputProcessor.setXMLSecurityProperties(getSecurityProperties());
168 referenceListOutputProcessor.setAction(getAction(), getActionOrder());
169 referenceListOutputProcessor.init(outputProcessorChain);
170 } else {
171 finalEncryptedKeyOutputProcessor.init(outputProcessorChain);
172 }
173
174 outputProcessorChain.getSecurityContext().registerSecurityTokenProvider(
175 encryptedKeySecurityToken.getId(), encryptedKeySecurityTokenProvider);
176 encryptedKeySecurityToken.setProcessor(finalEncryptedKeyOutputProcessor);
177 } finally {
178 outputProcessorChain.removeProcessor(this);
179 }
180 outputProcessorChain.processEvent(xmlSecEvent);
181 }
182
183 class FinalEncryptedKeyOutputProcessor extends AbstractOutputProcessor {
184
185 private final OutboundSecurityToken securityToken;
186 private boolean outputReferenceList = true;
187
188 FinalEncryptedKeyOutputProcessor(OutboundSecurityToken securityToken) throws XMLSecurityException {
189 super();
190 this.addAfterProcessor(EncryptedKeyOutputProcessor.class);
191 this.securityToken = securityToken;
192 }
193
194 protected void setOutputReferenceList(boolean outputReferenceList) {
195 this.outputReferenceList = outputReferenceList;
196 }
197
198 @Override
199 public void processEvent(XMLSecEvent xmlSecEvent, OutputProcessorChain outputProcessorChain)
200 throws XMLStreamException, XMLSecurityException {
201
202 outputProcessorChain.processEvent(xmlSecEvent);
203
204 if (WSSUtils.isSecurityHeaderElement(xmlSecEvent, ((WSSSecurityProperties) getSecurityProperties()).getActor())) {
205
206 final QName headerElementName = WSSConstants.TAG_xenc_EncryptedKey;
207 OutputProcessorUtils.updateSecurityHeaderOrder(outputProcessorChain, headerElementName, getAction(), false);
208
209 OutputProcessorChain subOutputProcessorChain = outputProcessorChain.createSubChain(this);
210
211 PublicKey publicKey = null;
212 if (securityToken.getKeyWrappingToken().getX509Certificates() != null
213 && securityToken.getKeyWrappingToken().getX509Certificates().length > 0) {
214 publicKey = securityToken.getKeyWrappingToken().getX509Certificates()[0].getPublicKey();
215 } else {
216 publicKey = securityToken.getKeyWrappingToken().getPublicKey();
217 }
218 if (publicKey == null) {
219 throw new WSSecurityException(
220 WSSecurityException.ErrorCode.FAILURE,
221 "failedCredentialLoad"
222 );
223 }
224
225 final String encryptionKeyTransportAlgorithm = getSecurityProperties().getEncryptionKeyTransportAlgorithm();
226
227 List<XMLSecAttribute> attributes = new ArrayList<>(1);
228 attributes.add(createAttribute(WSSConstants.ATT_NULL_Id, securityToken.getId()));
229 createStartElementAndOutputAsEvent(subOutputProcessorChain, headerElementName, true, attributes);
230
231 attributes = new ArrayList<>(1);
232 attributes.add(createAttribute(WSSConstants.ATT_NULL_Algorithm, encryptionKeyTransportAlgorithm));
233 createStartElementAndOutputAsEvent(subOutputProcessorChain, WSSConstants.TAG_xenc_EncryptionMethod, false, attributes);
234
235 final String encryptionKeyTransportMGFAlgorithm = getSecurityProperties().getEncryptionKeyTransportMGFAlgorithm();
236
237 if (XMLSecurityConstants.NS_XENC11_RSAOAEP.equals(encryptionKeyTransportAlgorithm)
238 || XMLSecurityConstants.NS_XENC_RSAOAEPMGF1P.equals(encryptionKeyTransportAlgorithm)) {
239
240 byte[] oaepParams = getSecurityProperties().getEncryptionKeyTransportOAEPParams();
241 if (oaepParams != null) {
242 createStartElementAndOutputAsEvent(subOutputProcessorChain, XMLSecurityConstants.TAG_xenc_OAEPparams,
243 false, null);
244 createCharactersAndOutputAsEvent(subOutputProcessorChain, XMLUtils.encodeToString(oaepParams));
245 createEndElementAndOutputAsEvent(subOutputProcessorChain, XMLSecurityConstants.TAG_xenc_OAEPparams);
246 }
247
248 String encryptionKeyTransportDigestAlgorithm = getSecurityProperties().getEncryptionKeyTransportDigestAlgorithm();
249 if (encryptionKeyTransportDigestAlgorithm != null) {
250 attributes = new ArrayList<>(1);
251 attributes.add(createAttribute(XMLSecurityConstants.ATT_NULL_Algorithm, encryptionKeyTransportDigestAlgorithm));
252 createStartElementAndOutputAsEvent(subOutputProcessorChain, XMLSecurityConstants.TAG_dsig_DigestMethod,
253 true, attributes);
254 createEndElementAndOutputAsEvent(subOutputProcessorChain, XMLSecurityConstants.TAG_dsig_DigestMethod);
255 }
256
257 if (encryptionKeyTransportMGFAlgorithm != null) {
258 attributes = new ArrayList<>(1);
259 attributes.add(createAttribute(XMLSecurityConstants.ATT_NULL_Algorithm, encryptionKeyTransportMGFAlgorithm));
260 createStartElementAndOutputAsEvent(subOutputProcessorChain, XMLSecurityConstants.TAG_xenc11_MGF,
261 true, attributes);
262 createEndElementAndOutputAsEvent(subOutputProcessorChain, XMLSecurityConstants.TAG_xenc11_MGF);
263 }
264 }
265
266 createEndElementAndOutputAsEvent(subOutputProcessorChain, WSSConstants.TAG_xenc_EncryptionMethod);
267 createStartElementAndOutputAsEvent(subOutputProcessorChain, WSSConstants.TAG_dsig_KeyInfo, true, null);
268 createSecurityTokenReferenceStructureForEncryptedKey(
269 subOutputProcessorChain, securityToken,
270 ((WSSSecurityProperties) getSecurityProperties()).getEncryptionKeyIdentifier(),
271 getSecurityProperties().isUseSingleCert()
272 );
273 createEndElementAndOutputAsEvent(subOutputProcessorChain, WSSConstants.TAG_dsig_KeyInfo);
274 createStartElementAndOutputAsEvent(subOutputProcessorChain, WSSConstants.TAG_xenc_CipherData, false, null);
275 createStartElementAndOutputAsEvent(subOutputProcessorChain, WSSConstants.TAG_xenc_CipherValue, false, null);
276
277 try {
278
279 String jceid = JCEAlgorithmMapper.translateURItoJCEID(encryptionKeyTransportAlgorithm);
280 Cipher cipher = Cipher.getInstance(jceid);
281
282 AlgorithmParameterSpec algorithmParameterSpec = null;
283 if (XMLSecurityConstants.NS_XENC11_RSAOAEP.equals(encryptionKeyTransportAlgorithm)
284 || XMLSecurityConstants.NS_XENC_RSAOAEPMGF1P.equals(encryptionKeyTransportAlgorithm)) {
285
286 String jceDigestAlgorithm = "SHA-1";
287 String encryptionKeyTransportDigestAlgorithm =
288 getSecurityProperties().getEncryptionKeyTransportDigestAlgorithm();
289 if (encryptionKeyTransportDigestAlgorithm != null) {
290 jceDigestAlgorithm = JCEAlgorithmMapper.translateURItoJCEID(encryptionKeyTransportDigestAlgorithm);
291 }
292
293 PSource.PSpecified pSource = PSource.PSpecified.DEFAULT;
294 byte[] oaepParams = getSecurityProperties().getEncryptionKeyTransportOAEPParams();
295 if (oaepParams != null) {
296 pSource = new PSource.PSpecified(oaepParams);
297 }
298
299 MGF1ParameterSpec mgfParameterSpec = new MGF1ParameterSpec("SHA-1");
300 if (encryptionKeyTransportMGFAlgorithm != null) {
301 String jceMGFAlgorithm = JCEAlgorithmMapper.translateURItoJCEID(encryptionKeyTransportMGFAlgorithm);
302 mgfParameterSpec = new MGF1ParameterSpec(jceMGFAlgorithm);
303 }
304 algorithmParameterSpec = new OAEPParameterSpec(jceDigestAlgorithm, "MGF1", mgfParameterSpec, pSource);
305 }
306
307 cipher.init(Cipher.WRAP_MODE, publicKey, algorithmParameterSpec);
308
309 Key secretKey = securityToken.getSecretKey("");
310
311 int blockSize = cipher.getBlockSize();
312 if (blockSize > 0 && blockSize < secretKey.getEncoded().length) {
313 throw new WSSecurityException(
314 WSSecurityException.ErrorCode.FAILURE,
315 "unsupportedKeyTransp",
316 new Object[] {"public key algorithm too weak to encrypt symmetric key"}
317 );
318 }
319 byte[] encryptedEphemeralKey = cipher.wrap(secretKey);
320
321 if (((WSSSecurityProperties)getSecurityProperties()).getCallbackHandler() != null) {
322
323 WSPasswordCallback callback =
324 new WSPasswordCallback(securityToken.getId(), WSPasswordCallback.SECRET_KEY);
325 callback.setKey(encryptedEphemeralKey);
326 try {
327 ((WSSSecurityProperties)getSecurityProperties()).getCallbackHandler().handle(new Callback[]{callback});
328 } catch (IOException | UnsupportedCallbackException e) {
329
330 }
331 }
332
333 createCharactersAndOutputAsEvent(subOutputProcessorChain,
334 XMLUtils.encodeToString(encryptedEphemeralKey));
335
336 } catch (NoSuchPaddingException | NoSuchAlgorithmException
337 | InvalidKeyException | IllegalBlockSizeException
338 | InvalidAlgorithmParameterException e) {
339 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, e);
340 }
341
342 createEndElementAndOutputAsEvent(subOutputProcessorChain, WSSConstants.TAG_xenc_CipherValue);
343 createEndElementAndOutputAsEvent(subOutputProcessorChain, WSSConstants.TAG_xenc_CipherData);
344
345 if (outputReferenceList && WSSConstants.ENCRYPTION.equals(getAction())) {
346 WSSUtils.createReferenceListStructureForEncryption(this, subOutputProcessorChain);
347 }
348 createEndElementAndOutputAsEvent(subOutputProcessorChain, headerElementName);
349 outputProcessorChain.removeProcessor(this);
350 }
351 }
352
353 protected void createSecurityTokenReferenceStructureForEncryptedKey(
354 OutputProcessorChain outputProcessorChain,
355 OutboundSecurityToken securityToken,
356 WSSecurityTokenConstants.KeyIdentifier keyIdentifier,
357 boolean useSingleCertificate)
358 throws XMLStreamException, XMLSecurityException {
359
360 if (securityToken.getCustomTokenReference() != null) {
361 outputDOMElement(securityToken.getCustomTokenReference(), outputProcessorChain);
362 return;
363 }
364
365 X509Certificate[] x509Certificates = securityToken.getKeyWrappingToken().getX509Certificates();
366 if ((x509Certificates == null || x509Certificates.length == 0)
367 && securityToken.getKeyWrappingToken().getPublicKey() != null) {
368 WSSUtils.createKeyValueTokenStructure(this, outputProcessorChain,
369 securityToken.getKeyWrappingToken().getPublicKey());
370 return;
371 }
372
373 List<XMLSecAttribute> attributes = new ArrayList<>(2);
374 attributes.add(createAttribute(WSSConstants.ATT_WSU_ID, IDGenerator.generateID(null)));
375 if (WSSecurityTokenConstants.KEYIDENTIFIER_SECURITY_TOKEN_DIRECT_REFERENCE.equals(keyIdentifier) && !useSingleCertificate) {
376 attributes.add(createAttribute(WSSConstants.ATT_WSSE11_TOKEN_TYPE, WSSConstants.NS_X509_PKIPATH_V1));
377 }
378 createStartElementAndOutputAsEvent(outputProcessorChain, WSSConstants.TAG_WSSE_SECURITY_TOKEN_REFERENCE, false, attributes);
379
380 String tokenId = securityToken.getKeyWrappingToken().getId();
381
382 if (WSSecurityTokenConstants.KeyIdentifier_IssuerSerial.equals(keyIdentifier)) {
383 WSSUtils.createX509IssuerSerialStructure(this, outputProcessorChain, x509Certificates);
384 } else if (WSSecurityTokenConstants.KeyIdentifier_SkiKeyIdentifier.equals(keyIdentifier)) {
385 WSSUtils.createX509SubjectKeyIdentifierStructure(this, outputProcessorChain, x509Certificates);
386 } else if (WSSecurityTokenConstants.KeyIdentifier_X509KeyIdentifier.equals(keyIdentifier)) {
387 WSSUtils.createX509KeyIdentifierStructure(this, outputProcessorChain, x509Certificates);
388 } else if (WSSecurityTokenConstants.KEYIDENTIFIER_THUMBPRINT_IDENTIFIER.equals(keyIdentifier)) {
389 WSSUtils.createThumbprintKeyIdentifierStructure(this, outputProcessorChain, x509Certificates);
390 } else if (WSSecurityTokenConstants.KEYIDENTIFIER_ENCRYPTED_KEY_SHA1_IDENTIFIER.equals(keyIdentifier)) {
391
392 WSSUtils.createThumbprintKeyIdentifierStructure(this, outputProcessorChain, x509Certificates);
393 } else if (WSSecurityTokenConstants.KEYIDENTIFIER_SECURITY_TOKEN_DIRECT_REFERENCE.equals(keyIdentifier)) {
394 String valueType;
395 if (useSingleCertificate) {
396 valueType = WSSConstants.NS_X509_V3_TYPE;
397 } else {
398 valueType = WSSConstants.NS_X509_PKIPATH_V1;
399 }
400 WSSUtils.createBSTReferenceStructure(this, outputProcessorChain, tokenId, valueType, true);
401 } else {
402 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "unsupportedSecurityToken");
403 }
404 createEndElementAndOutputAsEvent(outputProcessorChain, WSSConstants.TAG_WSSE_SECURITY_TOKEN_REFERENCE);
405 }
406 }
407 }