blob: ce59edef46affa363967c9531b8a8230bfd150dd [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.wss4j.stax.impl.processor.output;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.MGF1ParameterSpec;
import java.util.ArrayList;
import java.util.List;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.PSource;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamException;
import org.apache.wss4j.common.ext.WSPasswordCallback;
import org.apache.wss4j.common.ext.WSSecurityException;
import org.apache.wss4j.stax.ext.WSSConstants;
import org.apache.wss4j.stax.ext.WSSSecurityProperties;
import org.apache.wss4j.stax.securityToken.WSSecurityTokenConstants;
import org.apache.wss4j.stax.utils.WSSUtils;
import org.apache.xml.security.exceptions.XMLSecurityException;
import org.apache.xml.security.stax.config.JCEAlgorithmMapper;
import org.apache.xml.security.stax.ext.AbstractOutputProcessor;
import org.apache.xml.security.stax.ext.OutputProcessorChain;
import org.apache.xml.security.stax.ext.XMLSecurityConstants;
import org.apache.xml.security.stax.ext.stax.XMLSecAttribute;
import org.apache.xml.security.stax.ext.stax.XMLSecEvent;
import org.apache.xml.security.stax.impl.securityToken.GenericOutboundSecurityToken;
import org.apache.xml.security.stax.impl.util.IDGenerator;
import org.apache.xml.security.stax.securityToken.OutboundSecurityToken;
import org.apache.xml.security.stax.securityToken.SecurityTokenProvider;
import org.apache.xml.security.utils.XMLUtils;
public class EncryptedKeyOutputProcessor extends AbstractOutputProcessor {
public EncryptedKeyOutputProcessor() throws XMLSecurityException {
super();
}
@Override
public void processEvent(XMLSecEvent xmlSecEvent, OutputProcessorChain outputProcessorChain)
throws XMLStreamException, XMLSecurityException {
try {
String tokenId = outputProcessorChain.getSecurityContext().get(WSSConstants.PROP_USE_THIS_TOKEN_ID_FOR_ENCRYPTED_KEY);
if (tokenId == null) {
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE);
}
SecurityTokenProvider<OutboundSecurityToken> wrappingSecurityTokenProvider =
outputProcessorChain.getSecurityContext().getSecurityTokenProvider(tokenId);
if (wrappingSecurityTokenProvider == null) {
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE);
}
final OutboundSecurityToken wrappingSecurityToken = wrappingSecurityTokenProvider.getSecurityToken();
if (wrappingSecurityToken == null) {
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE);
}
SecurityTokenProvider<OutboundSecurityToken> encryptedKeySecurityTokenProvider = null;
GenericOutboundSecurityToken encryptedKeySecurityToken = null;
String sigTokenId =
outputProcessorChain.getSecurityContext().get(WSSConstants.PROP_USE_THIS_TOKEN_ID_FOR_SIGNATURE);
// See if a Symmetric Key is already available
String encTokenId =
outputProcessorChain.getSecurityContext().get(WSSConstants.PROP_USE_THIS_TOKEN_ID_FOR_ENCRYPTION);
if (encTokenId == null) {
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE);
}
encryptedKeySecurityTokenProvider =
outputProcessorChain.getSecurityContext().getSecurityTokenProvider(encTokenId);
if (encryptedKeySecurityTokenProvider == null) {
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE);
}
encryptedKeySecurityToken =
(GenericOutboundSecurityToken)encryptedKeySecurityTokenProvider.getSecurityToken();
boolean sharedToken = encTokenId.equals(sigTokenId);
FinalEncryptedKeyOutputProcessor finalEncryptedKeyOutputProcessor =
new FinalEncryptedKeyOutputProcessor(encryptedKeySecurityToken);
finalEncryptedKeyOutputProcessor.setXMLSecurityProperties(getSecurityProperties());
finalEncryptedKeyOutputProcessor.setAction(getAction());
XMLSecurityConstants.Action action = getAction();
if (WSSConstants.ENCRYPTION.equals(action)) {
if (wrappingSecurityToken.getProcessor() != null) {
finalEncryptedKeyOutputProcessor.addBeforeProcessor(wrappingSecurityToken.getProcessor());
finalEncryptedKeyOutputProcessor.init(outputProcessorChain);
} else if (sharedToken) {
finalEncryptedKeyOutputProcessor.addAfterProcessor(EncryptEndingOutputProcessor.class.getName());
//hint for the headerReordering processor where to place the EncryptedKey
if (getSecurityProperties().getActions().indexOf(WSSConstants.ENCRYPTION)
< getSecurityProperties().getActions().indexOf(WSSConstants.SIGNATURE)) {
finalEncryptedKeyOutputProcessor.addBeforeProcessor(WSSSignatureOutputProcessor.class.getName());
finalEncryptedKeyOutputProcessor.setAction(WSSConstants.SIGNATURE);
}
finalEncryptedKeyOutputProcessor.setOutputReferenceList(false);
finalEncryptedKeyOutputProcessor.init(outputProcessorChain);
ReferenceListOutputProcessor referenceListOutputProcessor = new ReferenceListOutputProcessor();
referenceListOutputProcessor.addBeforeProcessor(finalEncryptedKeyOutputProcessor);
referenceListOutputProcessor.setXMLSecurityProperties(getSecurityProperties());
referenceListOutputProcessor.setAction(getAction());
referenceListOutputProcessor.init(outputProcessorChain);
} else {
finalEncryptedKeyOutputProcessor.addAfterProcessor(EncryptEndingOutputProcessor.class.getName());
finalEncryptedKeyOutputProcessor.init(outputProcessorChain);
}
} else if (WSSConstants.SIGNATURE_WITH_DERIVED_KEY.equals(action)) {
if (wrappingSecurityToken.getProcessor() != null) {
finalEncryptedKeyOutputProcessor.addBeforeProcessor(wrappingSecurityToken.getProcessor());
} else {
finalEncryptedKeyOutputProcessor.addBeforeProcessor(WSSSignatureOutputProcessor.class.getName());
}
finalEncryptedKeyOutputProcessor.init(outputProcessorChain);
} else if (WSSConstants.ENCRYPTION_WITH_DERIVED_KEY.equals(action)) {
if (wrappingSecurityToken.getProcessor() != null) {
finalEncryptedKeyOutputProcessor.addBeforeProcessor(wrappingSecurityToken.getProcessor());
finalEncryptedKeyOutputProcessor.init(outputProcessorChain);
} else if (sharedToken) {
finalEncryptedKeyOutputProcessor.addBeforeProcessor(WSSSignatureOutputProcessor.class.getName());
finalEncryptedKeyOutputProcessor.addAfterProcessor(EncryptEndingOutputProcessor.class.getName());
//hint for the headerReordering processor where to place the EncryptedKey
if (getSecurityProperties().getActions().indexOf(WSSConstants.ENCRYPTION_WITH_DERIVED_KEY)
< getSecurityProperties().getActions().indexOf(WSSConstants.SIGNATURE_WITH_DERIVED_KEY)) {
finalEncryptedKeyOutputProcessor.setAction(WSSConstants.SIGNATURE_WITH_DERIVED_KEY);
}
finalEncryptedKeyOutputProcessor.setOutputReferenceList(false);
finalEncryptedKeyOutputProcessor.init(outputProcessorChain);
} else {
finalEncryptedKeyOutputProcessor.addAfterProcessor(EncryptEndingOutputProcessor.class.getName());
finalEncryptedKeyOutputProcessor.init(outputProcessorChain);
}
ReferenceListOutputProcessor referenceListOutputProcessor = new ReferenceListOutputProcessor();
referenceListOutputProcessor.addBeforeProcessor(finalEncryptedKeyOutputProcessor);
referenceListOutputProcessor.setXMLSecurityProperties(getSecurityProperties());
referenceListOutputProcessor.setAction(getAction());
referenceListOutputProcessor.init(outputProcessorChain);
} else {
finalEncryptedKeyOutputProcessor.init(outputProcessorChain);
}
outputProcessorChain.getSecurityContext().registerSecurityTokenProvider(
encryptedKeySecurityToken.getId(), encryptedKeySecurityTokenProvider);
encryptedKeySecurityToken.setProcessor(finalEncryptedKeyOutputProcessor);
} finally {
outputProcessorChain.removeProcessor(this);
}
outputProcessorChain.processEvent(xmlSecEvent);
}
class FinalEncryptedKeyOutputProcessor extends AbstractOutputProcessor {
private final OutboundSecurityToken securityToken;
private boolean outputReferenceList = true;
FinalEncryptedKeyOutputProcessor(OutboundSecurityToken securityToken) throws XMLSecurityException {
super();
this.addAfterProcessor(FinalEncryptedKeyOutputProcessor.class.getName());
this.securityToken = securityToken;
}
protected void setOutputReferenceList(boolean outputReferenceList) {
this.outputReferenceList = outputReferenceList;
}
@Override
public void processEvent(XMLSecEvent xmlSecEvent, OutputProcessorChain outputProcessorChain)
throws XMLStreamException, XMLSecurityException {
outputProcessorChain.processEvent(xmlSecEvent);
if (WSSUtils.isSecurityHeaderElement(xmlSecEvent, ((WSSSecurityProperties) getSecurityProperties()).getActor())) {
final QName headerElementName = WSSConstants.TAG_xenc_EncryptedKey;
OutputProcessorUtils.updateSecurityHeaderOrder(outputProcessorChain, headerElementName, getAction(), false);
OutputProcessorChain subOutputProcessorChain = outputProcessorChain.createSubChain(this);
PublicKey publicKey = null;
if (securityToken.getKeyWrappingToken().getX509Certificates() != null
&& securityToken.getKeyWrappingToken().getX509Certificates().length > 0) {
publicKey = securityToken.getKeyWrappingToken().getX509Certificates()[0].getPublicKey();
} else {
publicKey = securityToken.getKeyWrappingToken().getPublicKey();
}
if (publicKey == null) {
throw new WSSecurityException(
WSSecurityException.ErrorCode.FAILURE,
"failedCredentialLoad"
);
}
final String encryptionKeyTransportAlgorithm = getSecurityProperties().getEncryptionKeyTransportAlgorithm();
List<XMLSecAttribute> attributes = new ArrayList<>(1);
attributes.add(createAttribute(WSSConstants.ATT_NULL_Id, securityToken.getId()));
createStartElementAndOutputAsEvent(subOutputProcessorChain, headerElementName, true, attributes);
attributes = new ArrayList<>(1);
attributes.add(createAttribute(WSSConstants.ATT_NULL_Algorithm, encryptionKeyTransportAlgorithm));
createStartElementAndOutputAsEvent(subOutputProcessorChain, WSSConstants.TAG_xenc_EncryptionMethod, false, attributes);
final String encryptionKeyTransportMGFAlgorithm = getSecurityProperties().getEncryptionKeyTransportMGFAlgorithm();
if (XMLSecurityConstants.NS_XENC11_RSAOAEP.equals(encryptionKeyTransportAlgorithm)
|| XMLSecurityConstants.NS_XENC_RSAOAEPMGF1P.equals(encryptionKeyTransportAlgorithm)) {
byte[] oaepParams = getSecurityProperties().getEncryptionKeyTransportOAEPParams();
if (oaepParams != null) {
createStartElementAndOutputAsEvent(subOutputProcessorChain, XMLSecurityConstants.TAG_xenc_OAEPparams,
false, null);
createCharactersAndOutputAsEvent(subOutputProcessorChain, XMLUtils.encodeToString(oaepParams));
createEndElementAndOutputAsEvent(subOutputProcessorChain, XMLSecurityConstants.TAG_xenc_OAEPparams);
}
String encryptionKeyTransportDigestAlgorithm = getSecurityProperties().getEncryptionKeyTransportDigestAlgorithm();
if (encryptionKeyTransportDigestAlgorithm != null) {
attributes = new ArrayList<>(1);
attributes.add(createAttribute(XMLSecurityConstants.ATT_NULL_Algorithm, encryptionKeyTransportDigestAlgorithm));
createStartElementAndOutputAsEvent(subOutputProcessorChain, XMLSecurityConstants.TAG_dsig_DigestMethod,
true, attributes);
createEndElementAndOutputAsEvent(subOutputProcessorChain, XMLSecurityConstants.TAG_dsig_DigestMethod);
}
if (encryptionKeyTransportMGFAlgorithm != null) {
attributes = new ArrayList<>(1);
attributes.add(createAttribute(XMLSecurityConstants.ATT_NULL_Algorithm, encryptionKeyTransportMGFAlgorithm));
createStartElementAndOutputAsEvent(subOutputProcessorChain, XMLSecurityConstants.TAG_xenc11_MGF,
true, attributes);
createEndElementAndOutputAsEvent(subOutputProcessorChain, XMLSecurityConstants.TAG_xenc11_MGF);
}
}
createEndElementAndOutputAsEvent(subOutputProcessorChain, WSSConstants.TAG_xenc_EncryptionMethod);
createStartElementAndOutputAsEvent(subOutputProcessorChain, WSSConstants.TAG_dsig_KeyInfo, true, null);
createSecurityTokenReferenceStructureForEncryptedKey(
subOutputProcessorChain, securityToken,
((WSSSecurityProperties) getSecurityProperties()).getEncryptionKeyIdentifier(),
getSecurityProperties().isUseSingleCert()
);
createEndElementAndOutputAsEvent(subOutputProcessorChain, WSSConstants.TAG_dsig_KeyInfo);
createStartElementAndOutputAsEvent(subOutputProcessorChain, WSSConstants.TAG_xenc_CipherData, false, null);
createStartElementAndOutputAsEvent(subOutputProcessorChain, WSSConstants.TAG_xenc_CipherValue, false, null);
try {
//encrypt the symmetric session key with the public key from the receiver:
String jceid = JCEAlgorithmMapper.translateURItoJCEID(encryptionKeyTransportAlgorithm);
Cipher cipher = Cipher.getInstance(jceid);
AlgorithmParameterSpec algorithmParameterSpec = null;
if (XMLSecurityConstants.NS_XENC11_RSAOAEP.equals(encryptionKeyTransportAlgorithm)
|| XMLSecurityConstants.NS_XENC_RSAOAEPMGF1P.equals(encryptionKeyTransportAlgorithm)) {
String jceDigestAlgorithm = "SHA-1";
String encryptionKeyTransportDigestAlgorithm =
getSecurityProperties().getEncryptionKeyTransportDigestAlgorithm();
if (encryptionKeyTransportDigestAlgorithm != null) {
jceDigestAlgorithm = JCEAlgorithmMapper.translateURItoJCEID(encryptionKeyTransportDigestAlgorithm);
}
PSource.PSpecified pSource = PSource.PSpecified.DEFAULT;
byte[] oaepParams = getSecurityProperties().getEncryptionKeyTransportOAEPParams();
if (oaepParams != null) {
pSource = new PSource.PSpecified(oaepParams);
}
MGF1ParameterSpec mgfParameterSpec = new MGF1ParameterSpec("SHA-1");
if (encryptionKeyTransportMGFAlgorithm != null) {
String jceMGFAlgorithm = JCEAlgorithmMapper.translateURItoJCEID(encryptionKeyTransportMGFAlgorithm);
mgfParameterSpec = new MGF1ParameterSpec(jceMGFAlgorithm);
}
algorithmParameterSpec = new OAEPParameterSpec(jceDigestAlgorithm, "MGF1", mgfParameterSpec, pSource);
}
cipher.init(Cipher.WRAP_MODE, publicKey, algorithmParameterSpec);
Key secretKey = securityToken.getSecretKey("");
int blockSize = cipher.getBlockSize();
if (blockSize > 0 && blockSize < secretKey.getEncoded().length) {
throw new WSSecurityException(
WSSecurityException.ErrorCode.FAILURE,
"unsupportedKeyTransp",
new Object[] {"public key algorithm too weak to encrypt symmetric key"}
);
}
byte[] encryptedEphemeralKey = cipher.wrap(secretKey);
if (((WSSSecurityProperties)getSecurityProperties()).getCallbackHandler() != null) {
// Store the Encrypted Key in the CallbackHandler for processing on the inbound side
WSPasswordCallback callback =
new WSPasswordCallback(securityToken.getId(), WSPasswordCallback.SECRET_KEY);
callback.setKey(encryptedEphemeralKey);
try {
((WSSSecurityProperties)getSecurityProperties()).getCallbackHandler().handle(new Callback[]{callback});
} catch (IOException | UnsupportedCallbackException e) { // NOPMD
// Do nothing
}
}
createCharactersAndOutputAsEvent(subOutputProcessorChain,
XMLUtils.encodeToString(encryptedEphemeralKey));
} catch (NoSuchPaddingException | NoSuchAlgorithmException
| InvalidKeyException | IllegalBlockSizeException
| InvalidAlgorithmParameterException e) {
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, e);
}
createEndElementAndOutputAsEvent(subOutputProcessorChain, WSSConstants.TAG_xenc_CipherValue);
createEndElementAndOutputAsEvent(subOutputProcessorChain, WSSConstants.TAG_xenc_CipherData);
if (outputReferenceList && WSSConstants.ENCRYPTION.equals(getAction())) {
WSSUtils.createReferenceListStructureForEncryption(this, subOutputProcessorChain);
}
createEndElementAndOutputAsEvent(subOutputProcessorChain, headerElementName);
outputProcessorChain.removeProcessor(this);
}
}
protected void createSecurityTokenReferenceStructureForEncryptedKey(
OutputProcessorChain outputProcessorChain,
OutboundSecurityToken securityToken,
WSSecurityTokenConstants.KeyIdentifier keyIdentifier,
boolean useSingleCertificate)
throws XMLStreamException, XMLSecurityException {
if (securityToken.getCustomTokenReference() != null) {
outputDOMElement(securityToken.getCustomTokenReference(), outputProcessorChain);
return;
}
X509Certificate[] x509Certificates = securityToken.getKeyWrappingToken().getX509Certificates();
if ((x509Certificates == null || x509Certificates.length == 0)
&& securityToken.getKeyWrappingToken().getPublicKey() != null) {
WSSUtils.createKeyValueTokenStructure(this, outputProcessorChain,
securityToken.getKeyWrappingToken().getPublicKey());
return;
}
List<XMLSecAttribute> attributes = new ArrayList<>(2);
attributes.add(createAttribute(WSSConstants.ATT_WSU_ID, IDGenerator.generateID(null)));
if (WSSecurityTokenConstants.KEYIDENTIFIER_SECURITY_TOKEN_DIRECT_REFERENCE.equals(keyIdentifier) && !useSingleCertificate) {
attributes.add(createAttribute(WSSConstants.ATT_WSSE11_TOKEN_TYPE, WSSConstants.NS_X509_PKIPATH_V1));
}
createStartElementAndOutputAsEvent(outputProcessorChain, WSSConstants.TAG_WSSE_SECURITY_TOKEN_REFERENCE, false, attributes);
String tokenId = securityToken.getKeyWrappingToken().getId();
if (WSSecurityTokenConstants.KeyIdentifier_IssuerSerial.equals(keyIdentifier)) {
WSSUtils.createX509IssuerSerialStructure(this, outputProcessorChain, x509Certificates);
} else if (WSSecurityTokenConstants.KeyIdentifier_SkiKeyIdentifier.equals(keyIdentifier)) {
WSSUtils.createX509SubjectKeyIdentifierStructure(this, outputProcessorChain, x509Certificates);
} else if (WSSecurityTokenConstants.KeyIdentifier_X509KeyIdentifier.equals(keyIdentifier)) {
WSSUtils.createX509KeyIdentifierStructure(this, outputProcessorChain, x509Certificates);
} else if (WSSecurityTokenConstants.KEYIDENTIFIER_THUMBPRINT_IDENTIFIER.equals(keyIdentifier)) {
WSSUtils.createThumbprintKeyIdentifierStructure(this, outputProcessorChain, x509Certificates);
} else if (WSSecurityTokenConstants.KEYIDENTIFIER_ENCRYPTED_KEY_SHA1_IDENTIFIER.equals(keyIdentifier)) {
//not applicable, fallback to thumbprint...
WSSUtils.createThumbprintKeyIdentifierStructure(this, outputProcessorChain, x509Certificates);
} else if (WSSecurityTokenConstants.KEYIDENTIFIER_SECURITY_TOKEN_DIRECT_REFERENCE.equals(keyIdentifier)) {
String valueType;
if (useSingleCertificate) {
valueType = WSSConstants.NS_X509_V3_TYPE;
} else {
valueType = WSSConstants.NS_X509_PKIPATH_V1;
}
WSSUtils.createBSTReferenceStructure(this, outputProcessorChain, tokenId, valueType, true);
} else {
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "unsupportedSecurityToken");
}
createEndElementAndOutputAsEvent(outputProcessorChain, WSSConstants.TAG_WSSE_SECURITY_TOKEN_REFERENCE);
}
}
}