org.apache.ws.security.message.WSSignEnvelope Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of wss4j Show documentation
Show all versions of wss4j Show documentation
The Apache WSS4J project provides a Java implementation of the primary security standards
for Web Services, namely the OASIS Web Services Security (WS-Security) specifications
from the OASIS Web Services Security TC.
/*
* Copyright 2003-2004 The Apache Software Foundation.
*
* Licensed 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.ws.security.message;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ws.security.SOAPConstants;
import org.apache.ws.security.WSConstants;
import org.apache.ws.security.WSDocInfo;
import org.apache.ws.security.WSDocInfoStore;
import org.apache.ws.security.WSEncryptionPart;
import org.apache.ws.security.WSSecurityException;
import org.apache.ws.security.components.crypto.Crypto;
import org.apache.ws.security.message.token.BinarySecurity;
import org.apache.ws.security.message.token.PKIPathSecurity;
import org.apache.ws.security.message.token.Reference;
import org.apache.ws.security.message.token.SecurityTokenReference;
import org.apache.ws.security.message.token.X509Security;
import org.apache.ws.security.saml.SAMLUtil;
import org.apache.ws.security.transform.STRTransform;
import org.apache.ws.security.util.WSSecurityUtil;
import org.apache.xml.security.algorithms.SignatureAlgorithm;
import org.apache.xml.security.c14n.Canonicalizer;
import org.apache.xml.security.exceptions.XMLSecurityException;
import org.apache.xml.security.keys.KeyInfo;
import org.apache.xml.security.keys.content.X509Data;
import org.apache.xml.security.keys.content.x509.XMLX509IssuerSerial;
import org.apache.xml.security.signature.XMLSignature;
import org.apache.xml.security.signature.XMLSignatureException;
import org.apache.xml.security.transforms.TransformationException;
import org.apache.xml.security.transforms.Transforms;
import org.apache.xml.security.transforms.params.InclusiveNamespaces;
import org.apache.xml.security.utils.Constants;
import org.apache.xml.security.utils.XMLUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import java.security.cert.X509Certificate;
import java.util.HashSet;
import java.util.Set;
import java.util.Vector;
/**
* Signs a SOAP envelope according to WS Specification, X509 profile, and adds
* the signature data.
*
* @author Davanum Srinivas ([email protected])
* @author Werner Dittmann ([email protected])
*/
public class WSSignEnvelope extends WSBaseMessage {
private static Log log = LogFactory.getLog(WSSignEnvelope.class.getName());
private static Log tlog = LogFactory.getLog("org.apache.ws.security.TIME");
protected boolean useSingleCert = true;
protected String sigAlgo = null;
protected String canonAlgo = Canonicalizer.ALGO_ID_C14N_EXCL_OMIT_COMMENTS;
protected WSSAddUsernameToken usernameToken = null;
protected byte[] signatureValue = null;
/**
* Constructor.
* @deprecated replaced by {@link WSSecSignature#WSSecSignature()}
*/
public WSSignEnvelope() {
}
/**
* Constructor.
*
* @param actor The actor name of the wsse:Security
header
*
* @deprecated replaced by {@link WSSecSignature#WSSecSignature()}
* and {@link WSSecHeader} for actor specification.
*/
public WSSignEnvelope(String actor) {
super(actor);
}
/**
* Constructor.
*
* @param actor The actor name of the wsse:Security
header
* @param mu Set mustUnderstand
to true or false
*
* @deprecated replaced by {@link WSSecSignature#WSSecSignature()}
* and {@link WSSecHeader} for actor and mustunderstand
* specification.
*/
public WSSignEnvelope(String actor, boolean mu) {
super(actor, mu);
}
/**
* set the single cert flag.
*
* @param useSingleCert
* @deprecated replaced by {@link WSSecSignature#setUseSingleCertificate(boolean)}
*/
public void setUseSingleCertificate(boolean useSingleCert) {
this.useSingleCert = useSingleCert;
}
/**
* Get the single cert flag.
*
* @return If to use a single cert
* @deprecated replaced by {@link WSSecSignature#isUseSingleCertificate()}
*/
public boolean isUseSingleCertificate() {
return this.useSingleCert;
}
/**
* Set the name of the signature encryption algorithm to use.
*
* If the algorithm is not set then Triple RSA is used. Refer to WSConstants
* which algorithms are supported.
*
* @param algo
* Is the name of the signature algorithm
* @see WSConstants#RSA
* @see WSConstants#DSA
* @deprecated replaced by {@link WSSecSignature#setSignatureAlgorithm(String)}
*/
public void setSignatureAlgorithm(String algo) {
sigAlgo = algo;
}
/**
* Get the name of the signature algorithm that is being used.
*
* If the algorithm is not set then RSA is default.
*
* @return the identifier URI of the signature algorithm
* @deprecated replaced by {@link WSSecSignature#getSignatureAlgorithm()}
*/
public String getSignatureAlgorithm() {
return sigAlgo;
}
/**
* Set the canonicalization method to use.
*
* If the canonicalization method is not set then the recommended Exclusive
* XML Canonicalization is used by default Refer to WSConstants which
* algorithms are supported.
*
* @param algo
* Is the name of the signature algorithm
* @see WSConstants#C14N_OMIT_COMMENTS
* @see WSConstants#C14N_WITH_COMMENTS
* @see WSConstants#C14N_EXCL_OMIT_COMMENTS
* @see WSConstants#C14N_EXCL_WITH_COMMENTS
* @deprecated replaced by {@link WSSecSignature#setSigCanonicalization(String)}
*/
public void setSigCanonicalization(String algo) {
canonAlgo = algo;
}
/**
* Get the canonicalization method.
*
* If the canonicalization method was not set then Exclusive XML
* Canonicalization is used by default.
*
* @return TODO
* @deprecated replaced by {@link WSSecSignature#getSigCanonicalization()}
*/
public String getSigCanonicalization() {
return canonAlgo;
}
/**
* @param usernameToken The usernameToken to set.
* @deprecated replaced by {@link WSSecSignature#setUsernameToken(WSSecUsernameToken)}
*/
public void setUsernameToken(WSSAddUsernameToken usernameToken) {
this.usernameToken = usernameToken;
}
/**
* @return Returns the signatureValue.
* @deprecated replaced by {@link WSSecSignature#getSignatureValue()}
*/
public byte[] getSignatureValue() {
return signatureValue;
}
/**
* Builds a signed soap envelope.
*
* The method first gets an appropriate
* security header. According to the defined parameters for certificate
* handling the signature elements are constructed and inserted into the
* wsse:Signature
*
* @param doc The unsigned SOAP envelope as Document
* @param crypto An instance of the Crypto API to handle keystore and
* certificates
* @return A signed SOAP envelope as Document
* @throws WSSecurityException
* @deprecated replaced by {@link WSSecSignature#build(Document, Crypto, WSSecHeader)}
*/
public Document build(Document doc, Crypto crypto)
throws WSSecurityException {
doDebug = log.isDebugEnabled();
long t0 = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0;
if (tlog.isDebugEnabled()) {
t0 = System.currentTimeMillis();
}
if (doDebug) {
log.debug("Beginning signing...");
}
/*
* Gather some info about the document to process and store it for
* retrieval
*/
WSDocInfo wsDocInfo = new WSDocInfo(doc.hashCode());
wsDocInfo.setCrypto(crypto);
Element envelope = doc.getDocumentElement();
SOAPConstants soapConstants = WSSecurityUtil.getSOAPConstants(envelope);
Element securityHeader = insertSecurityHeader(doc);
// Set the id of the elements to be used as digest source
// String id = setBodyID(doc);
String certUri = null;
X509Certificate[] certs = null;
if (keyIdentifierType != WSConstants.UT_SIGNING) {
certs = crypto.getCertificates(user);
if (certs == null || certs.length <= 0) {
throw new WSSecurityException(
WSSecurityException.FAILURE,
"noUserCertsFound",
new Object[] { user, "signature" }
);
}
certUri = "CertId-" + certs[0].hashCode();
if (sigAlgo == null) {
String pubKeyAlgo = certs[0].getPublicKey().getAlgorithm();
log.debug("automatic sig algo detection: " + pubKeyAlgo);
if (pubKeyAlgo.equalsIgnoreCase("DSA")) {
sigAlgo = XMLSignature.ALGO_ID_SIGNATURE_DSA;
} else if (pubKeyAlgo.equalsIgnoreCase("RSA")) {
sigAlgo = XMLSignature.ALGO_ID_SIGNATURE_RSA;
} else {
throw new WSSecurityException(
WSSecurityException.FAILURE,
"unknownSignatureAlgorithm",
new Object[] {
pubKeyAlgo
}
);
}
}
}
XMLSignature sig = null;
if (canonAlgo.equals(WSConstants.C14N_EXCL_OMIT_COMMENTS)) {
Element canonElem = XMLUtils.createElementInSignatureSpace(doc,
Constants._TAG_CANONICALIZATIONMETHOD);
canonElem.setAttributeNS(null, Constants._ATT_ALGORITHM, canonAlgo);
if (wssConfig.isWsiBSPCompliant()) {
Set prefixes = getInclusivePrefixes(securityHeader, false);
InclusiveNamespaces inclusiveNamespaces = new InclusiveNamespaces(
doc, prefixes);
canonElem.appendChild(inclusiveNamespaces.getElement());
}
try {
SignatureAlgorithm signatureAlgorithm = new SignatureAlgorithm(
doc, sigAlgo);
sig = new XMLSignature(doc, null, signatureAlgorithm
.getElement(), canonElem);
} catch (XMLSecurityException e) {
log.error("", e);
throw new WSSecurityException(
WSSecurityException.FAILED_SIGNATURE, "noXMLSig", null, e
);
}
} else {
try {
sig = new XMLSignature(doc, null, sigAlgo, canonAlgo);
} catch (XMLSecurityException e) {
log.error("", e);
throw new WSSecurityException(
WSSecurityException.FAILED_SIGNATURE, "noXMLSig", null, e
);
}
}
/*
* If we don't generate a new Transforms for each addDocument here, then
* only the last Transforms is put into the according ds:Reference
* element, i.e. the first ds:Reference does not contain a Transforms
* element. Thus the verification fails (somehow)
*/
KeyInfo info = sig.getKeyInfo();
String keyInfoUri = "KeyId-" + info.hashCode();
info.setId(keyInfoUri);
SecurityTokenReference secRef = new SecurityTokenReference(doc);
String strUri = "STRId-" + secRef.hashCode();
secRef.setID(strUri);
if (tlog.isDebugEnabled()) {
t1 = System.currentTimeMillis();
}
if (parts == null) {
parts = new Vector();
WSEncryptionPart encP = new WSEncryptionPart(soapConstants
.getBodyQName().getLocalPart(), soapConstants
.getEnvelopeURI(), "Content");
parts.add(encP);
}
Transforms transforms = null;
for (int part = 0; part < parts.size(); part++) {
WSEncryptionPart encPart = (WSEncryptionPart) parts.get(part);
String idToSign = encPart.getId();
String elemName = encPart.getName();
String nmSpace = encPart.getNamespace();
/*
* Set up the elements to sign. There are two reserved element
* names: "Token" and "STRTransform" "Token": Setup the Signature to
* either sign the information that points to the security token or
* the token itself. If its a direct reference sign the token,
* otherwise sign the KeyInfo Element. "STRTransform": Setup the
* ds:Reference to use STR Transform
*
*/
try {
if (idToSign != null) {
Element toSignById = WSSecurityUtil
.findElementById(doc.getDocumentElement(),
idToSign, WSConstants.WSU_NS);
if (toSignById == null) {
toSignById = WSSecurityUtil.findElementById(doc
.getDocumentElement(), idToSign, null);
}
transforms = new Transforms(doc);
transforms
.addTransform(Transforms.TRANSFORM_C14N_EXCL_OMIT_COMMENTS);
if (wssConfig.isWsiBSPCompliant()) {
transforms.item(0).getElement().appendChild(
new InclusiveNamespaces(doc,
getInclusivePrefixes(toSignById))
.getElement());
}
sig.addDocument("#" + idToSign, transforms);
}
else if (elemName.equals("Token")) {
transforms = new Transforms(doc);
transforms
.addTransform(Transforms.TRANSFORM_C14N_EXCL_OMIT_COMMENTS);
if (keyIdentifierType == WSConstants.BST_DIRECT_REFERENCE) {
if (wssConfig.isWsiBSPCompliant()) {
transforms
.item(0)
.getElement()
.appendChild(
new InclusiveNamespaces(
doc,
getInclusivePrefixes(securityHeader))
.getElement());
}
sig.addDocument("#" + certUri, transforms);
} else {
if (wssConfig.isWsiBSPCompliant()) {
transforms.item(0).getElement().appendChild(
new InclusiveNamespaces(doc,
getInclusivePrefixes(info
.getElement()))
.getElement());
}
sig.addDocument("#" + keyInfoUri, transforms);
}
} else if (elemName.equals("STRTransform")) { // STRTransform
Element ctx = createSTRParameter(doc);
transforms = new Transforms(doc);
transforms.addTransform(
STRTransform.implementedTransformURI, ctx);
sig.addDocument("#" + strUri, transforms);
} else if (elemName.equals("Assertion")) { // Assertion
String id = null;
id = SAMLUtil.getAssertionId(envelope, elemName, nmSpace);
Element body = (Element) WSSecurityUtil.findElement(
envelope, elemName, nmSpace);
if (body == null) {
throw new WSSecurityException(
WSSecurityException.FAILURE, "noEncElement",
new Object[] { nmSpace + ", " + elemName });
}
transforms = new Transforms(doc);
transforms
.addTransform(Transforms.TRANSFORM_C14N_EXCL_OMIT_COMMENTS);
if (wssConfig.isWsiBSPCompliant()) {
transforms.item(0).getElement().appendChild(
new InclusiveNamespaces(doc,
getInclusivePrefixes(body))
.getElement());
}
String prefix = WSSecurityUtil.setNamespace(body,
WSConstants.WSU_NS, WSConstants.WSU_PREFIX);
body.setAttributeNS(WSConstants.WSU_NS, prefix + ":Id",
id);
sig.addDocument("#" + id, transforms);
} else {
Element body = (Element) WSSecurityUtil.findElement(
envelope, elemName, nmSpace);
if (body == null) {
throw new WSSecurityException(
WSSecurityException.FAILURE, "noEncElement",
new Object[] { nmSpace + ", " + elemName });
}
transforms = new Transforms(doc);
transforms
.addTransform(Transforms.TRANSFORM_C14N_EXCL_OMIT_COMMENTS);
if (wssConfig.isWsiBSPCompliant()) {
transforms.item(0).getElement().appendChild(
new InclusiveNamespaces(doc,
getInclusivePrefixes(body))
.getElement());
}
sig.addDocument("#" + setWsuId(body), transforms);
}
} catch (TransformationException e1) {
throw new WSSecurityException(
WSSecurityException.FAILED_SIGNATURE, "noXMLSig", null,
e1);
} catch (XMLSignatureException e1) {
throw new WSSecurityException(
WSSecurityException.FAILED_SIGNATURE, "noXMLSig", null,
e1);
}
}
sig.addResourceResolver(EnvelopeIdResolver.getInstance());
WSSecurityUtil.prependChildElement(securityHeader, sig.getElement());
if (tlog.isDebugEnabled()) {
t2 = System.currentTimeMillis();
}
byte[] secretKey = null;
switch (keyIdentifierType) {
case WSConstants.BST_DIRECT_REFERENCE:
Reference ref = new Reference(doc);
ref.setURI("#" + certUri);
BinarySecurity bstToken = null;
if (!useSingleCert) {
bstToken = new PKIPathSecurity(doc);
((PKIPathSecurity) bstToken).setX509Certificates(certs, false,
crypto);
} else {
bstToken = new X509Security(doc);
((X509Security) bstToken).setX509Certificate(certs[0]);
}
ref.setValueType(bstToken.getValueType());
secRef.setReference(ref);
bstToken.setID(certUri);
WSSecurityUtil.prependChildElement(securityHeader, bstToken.getElement());
wsDocInfo.setBst(bstToken.getElement());
break;
case WSConstants.ISSUER_SERIAL:
XMLX509IssuerSerial data = new XMLX509IssuerSerial(doc, certs[0]);
X509Data x509Data = new X509Data(doc);
x509Data.add(data);
secRef.setX509IssuerSerial(x509Data);
break;
case WSConstants.X509_KEY_IDENTIFIER:
secRef.setKeyIdentifier(certs[0]);
break;
case WSConstants.SKI_KEY_IDENTIFIER:
secRef.setKeyIdentifierSKI(certs[0], crypto);
break;
case WSConstants.UT_SIGNING:
Reference refUt = new Reference(doc);
refUt.setValueType(WSConstants.USERNAMETOKEN_NS + "#UsernameToken");
String utId = usernameToken.getId();
if (utId == null) {
utId = "usernameTokenId-" + usernameToken.hashCode();
usernameToken.setId(utId);
}
refUt.setURI("#" + utId);
secRef.setReference(refUt);
secretKey = usernameToken.getSecretKey();
break;
case WSConstants.THUMBPRINT_IDENTIFIER:
secRef.setKeyIdentifierThumb(certs[0]);
break;
default:
throw new WSSecurityException(WSSecurityException.FAILURE,
"unsupportedKeyId");
}
if (tlog.isDebugEnabled()) {
t3 = System.currentTimeMillis();
}
info.addUnknownElement(secRef.getElement());
WSDocInfoStore.store(wsDocInfo);
try {
if (keyIdentifierType == WSConstants.UT_SIGNING) {
sig.sign(sig.createSecretKey(secretKey));
} else {
sig.sign(crypto.getPrivateKey(user, password));
}
signatureValue = sig.getSignatureValue();
} catch (XMLSignatureException e1) {
throw new WSSecurityException(WSSecurityException.FAILED_SIGNATURE,
null, null, e1);
} catch (Exception e1) {
throw new WSSecurityException(WSSecurityException.FAILED_SIGNATURE,
null, null, e1);
} finally {
WSDocInfoStore.delete(wsDocInfo);
}
if (tlog.isDebugEnabled()) {
t4 = System.currentTimeMillis();
tlog.debug("SignEnvelope: cre-Sig= " + (t1 - t0)
+ " set transform= " + (t2 - t1) + " sec-ref= " + (t3 - t2)
+ " signature= " + (t4 - t3));
}
if (doDebug) {
log.debug("Signing complete.");
}
return (doc);
}
protected Element createSTRParameter(Document doc) {
Element transformParam = doc.createElementNS(WSConstants.WSSE_NS,
WSConstants.WSSE_PREFIX + ":TransformationParameters");
WSSecurityUtil.setNamespace(transformParam, WSConstants.WSSE_NS,
WSConstants.WSSE_PREFIX);
Element canonElem = doc.createElementNS(WSConstants.SIG_NS,
WSConstants.SIG_PREFIX + ":CanonicalizationMethod");
WSSecurityUtil.setNamespace(canonElem, WSConstants.SIG_NS,
WSConstants.SIG_PREFIX);
canonElem.setAttributeNS(null, "Algorithm",
Canonicalizer.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);
transformParam.appendChild(canonElem);
return transformParam;
}
protected Set getInclusivePrefixes(Element target) {
return getInclusivePrefixes(target, true);
}
protected Set getInclusivePrefixes(Element target, boolean excludeVisible) {
Set result = new HashSet();
Node parent = target;
NamedNodeMap attributes;
Node attribute;
while (!(parent.getParentNode() instanceof Document)) {
parent = parent.getParentNode();
attributes = parent.getAttributes();
for (int i = 0; i < attributes.getLength(); i++) {
attribute = attributes.item(i);
if (attribute.getNamespaceURI() != null
&& attribute.getNamespaceURI().equals(
org.apache.ws.security.WSConstants.XMLNS_NS)) {
if (attribute.getNodeName().equals("xmlns")) {
result.add("#default");
} else {
result.add(attribute.getLocalName());
}
}
}
}
if (excludeVisible == true) {
attributes = target.getAttributes();
for (int i = 0; i < attributes.getLength(); i++) {
attribute = attributes.item(i);
if (attribute.getNamespaceURI() != null
&& attribute.getNamespaceURI().equals(
org.apache.ws.security.WSConstants.XMLNS_NS)) {
if (attribute.getNodeName().equals("xmlns")) {
result.remove("#default");
} else {
result.remove(attribute.getLocalName());
}
}
if (attribute.getPrefix() != null) {
result.remove(attribute.getPrefix());
}
}
if (target.getPrefix() == null) {
result.remove("#default");
} else {
result.remove(target.getPrefix());
}
}
return result;
}
}