org.apache.ws.security.saml.WSSignSAMLEnvelope 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.
package org.apache.ws.security.saml;
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.EnvelopeIdResolver;
import org.apache.ws.security.message.WSSecHeader;
import org.apache.ws.security.message.WSSignEnvelope;
import org.apache.ws.security.message.token.BinarySecurity;
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.transform.STRTransform;
import org.apache.ws.security.util.WSSecurityUtil;
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.XMLX509Certificate;
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.opensaml.SAMLAssertion;
import org.opensaml.SAMLException;
import org.opensaml.SAMLObject;
import org.opensaml.SAMLSubject;
import org.opensaml.SAMLSubjectStatement;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import java.security.cert.X509Certificate;
import java.util.Iterator;
import java.util.Vector;
public class WSSignSAMLEnvelope extends WSSignEnvelope {
private static Log log = LogFactory.getLog(WSSignSAMLEnvelope.class
.getName());
private static Log tlog = LogFactory.getLog("org.apache.ws.security.TIME");
/**
* Constructor.
*
* @deprecated replaced by {@link WSSecSignatureSAML#constructor()}
*/
public WSSignSAMLEnvelope() {
}
/**
* Constructor.
*
* @param actor
* The actor name of the wsse:Security
header
* @param mu
* Set mustUnderstand
to true or false
*
* @deprecated replaced by {@link WSSecSignatureSAML#constructor()} and
* {@link WSSecHeader} for actor and mustunderstand
* specification.
*/
public WSSignSAMLEnvelope(String actor, boolean mu) {
super(actor, mu);
}
/**
* Builds a signed soap envelope with SAML token. 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 assertion
* the complete SAML assertion
* @param issuerCrypto
* An instance of the Crypto API to handle keystore SAML token
* issuer and to generate certificates
* @param issuerKeyName
* Private key to use in case of "sender-Vouches"
* @param issuerKeyPW
* Password for issuer private key
* @return A signed SOAP envelope as Document
* @throws org.apache.ws.security.WSSecurityException
* @deprecated replaced by
* {@link WSSecSignatureSAML#build(Document, Crypto, SAMLAssertion, Crypto, String, String, WSSecHeader)}
*/
public Document build(Document doc, Crypto userCrypto,
SAMLAssertion assertion, Crypto issuerCrypto, String issuerKeyName,
String issuerKeyPW) 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 ST signing...");
}
/*
* Get some information about the SAML token content. This controls how
* to deal with the whole stuff. First get the Authentication statement
* (includes Subject), then get the _first_ confirmation method only.
*/
SAMLSubjectStatement samlSubjS = null;
Iterator it = assertion.getStatements();
while (it.hasNext()) {
SAMLObject so = (SAMLObject) it.next();
if (so instanceof SAMLSubjectStatement) {
samlSubjS = (SAMLSubjectStatement) so;
break;
}
}
SAMLSubject samlSubj = null;
if (samlSubjS != null) {
samlSubj = samlSubjS.getSubject();
}
if (samlSubj == null) {
throw new WSSecurityException(WSSecurityException.FAILURE,
"invalidSAMLToken", new Object[] { "for Signature" });
}
String confirmMethod = null;
it = samlSubj.getConfirmationMethods();
if (it.hasNext()) {
confirmMethod = (String) it.next();
}
boolean senderVouches = false;
if (SAMLSubject.CONF_SENDER_VOUCHES.equals(confirmMethod)) {
senderVouches = true;
}
/*
* Gather some info about the document to process and store it for
* retrival
*/
WSDocInfo wsDocInfo = new WSDocInfo(doc.hashCode());
Element envelope = doc.getDocumentElement();
SOAPConstants soapConstants = WSSecurityUtil.getSOAPConstants(envelope);
Element securityHeader = insertSecurityHeader(doc);
X509Certificate[] certs = null;
if (senderVouches) {
certs = issuerCrypto.getCertificates(issuerKeyName);
wsDocInfo.setCrypto(issuerCrypto);
}
/*
* in case of key holder: - get the user's certificate that _must_ be
* included in the SAML token. To ensure the cert integrity the SAML
* token must be signed (by the issuer). Just check if its signed, but
* don't verify this SAML token's signature here (maybe later).
*/
else {
if (userCrypto == null || assertion.isSigned() == false) {
throw new WSSecurityException(WSSecurityException.FAILURE,
"invalidSAMLsecurity",
new Object[] { "for SAML Signature (Key Holder)" });
}
Element e = samlSubj.getKeyInfo();
try {
KeyInfo ki = new KeyInfo(e, null);
if (ki.containsX509Data()) {
X509Data data = ki.itemX509Data(0);
XMLX509Certificate certElem = null;
if (data != null && data.containsCertificate()) {
certElem = data.itemCertificate(0);
}
if (certElem != null) {
X509Certificate cert = certElem.getX509Certificate();
certs = new X509Certificate[1];
certs[0] = cert;
}
}
// TODO: get alias name for cert, check against username set by
// caller
} catch (XMLSecurityException e3) {
throw new WSSecurityException(WSSecurityException.FAILURE,
"invalidSAMLsecurity",
new Object[] { "cannot get certificate (key holder)" },
e3);
}
wsDocInfo.setCrypto(userCrypto);
}
// Set the id of the elements to be used as digest source
// String id = setBodyID(doc);
if (certs == null || certs.length <= 0) {
throw new WSSecurityException(WSSecurityException.FAILURE,
"invalidX509Data", new Object[] { "for Signature" });
}
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,
"invalidX509Data",
new Object[] { "for Signature - unkown public key Algo" });
}
}
XMLSignature sig = null;
try {
sig = new XMLSignature(doc, null, sigAlgo, canonAlgo);
} catch (XMLSecurityException e) {
throw new WSSecurityException(WSSecurityException.FAILED_SIGNATURE,
"noXMLSig");
}
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);
String certUri = "CertId-" + certs[0].hashCode();
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);
}
/*
* If the sender vouches, then we must sign the SAML token _and_ at
* least one part of the message (usually the SOAP body). To do so we
* need to - put in a reference to the SAML token. Thus we create a STR
* and insert it into the wsse:Security header - set a reference of the
* created STR to the signature and use STR Transfrom during the
* signature
*/
Transforms transforms = null;
SecurityTokenReference secRefSaml = null;
try {
if (senderVouches) {
secRefSaml = new SecurityTokenReference(doc);
String strSamlUri = "STRSAMLId-" + secRefSaml.hashCode();
secRefSaml.setID(strSamlUri);
// Decouple Refernce/KeyInfo setup - quick shot here
Reference ref = new Reference(doc);
ref.setURI("#" + assertion.getId());
ref.setValueType(WSConstants.WSS_SAML_NS
+ WSConstants.WSS_SAML_ASSERTION);
secRefSaml.setReference(ref);
// up to here
Element ctx = createSTRParameter(doc);
transforms = new Transforms(doc);
transforms.addTransform(STRTransform.implementedTransformURI,
ctx);
sig.addDocument("#" + strSamlUri, transforms);
}
for (int part = 0; part < parts.size(); part++) {
WSEncryptionPart encPart = (WSEncryptionPart) parts.get(part);
String elemName = encPart.getName();
String nmSpace = encPart.getNamespace();
/*
* Set up the elements to sign. There are two resevered 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
*
*/
if (elemName.equals("Token")) {
transforms = new Transforms(doc);
transforms
.addTransform(Transforms.TRANSFORM_C14N_EXCL_OMIT_COMMENTS);
if (keyIdentifierType == WSConstants.BST_DIRECT_REFERENCE) {
sig.addDocument("#" + certUri, transforms);
} else {
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 {
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);
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());
/*
* The order to prepend is: - signature - BinarySecurityToken (depends
* on mode) - SecurityTokenRefrence (depends on mode) - SAML token
*/
WSSecurityUtil.prependChildElement(doc, securityHeader, sig
.getElement(), false);
if (tlog.isDebugEnabled()) {
t2 = System.currentTimeMillis();
}
switch (keyIdentifierType) {
case WSConstants.BST_DIRECT_REFERENCE:
Reference ref = new Reference(doc);
if (senderVouches) {
ref.setURI("#" + certUri);
BinarySecurity bstToken = null;
bstToken = new X509Security(doc);
((X509Security) bstToken).setX509Certificate(certs[0]);
bstToken.setID(certUri);
WSSecurityUtil.prependChildElement(doc, securityHeader,
bstToken.getElement(), false);
wsDocInfo.setBst(bstToken.getElement());
ref.setValueType(bstToken.getValueType());
} else {
ref.setURI("#" + assertion.getId());
ref.setValueType(WSConstants.WSS_SAML_NS
+ WSConstants.WSS_SAML_ASSERTION);
}
secRef.setReference(ref);
break;
//
// case WSConstants.ISSUER_SERIAL :
// XMLX509IssuerSerial data =
// new XMLX509IssuerSerial(doc, certs[0]);
// secRef.setX509IssuerSerial(data);
// break;
//
// case WSConstants.X509_KEY_IDENTIFIER :
// secRef.setKeyIdentifier(certs[0]);
// break;
//
// case WSConstants.SKI_KEY_IDENTIFIER :
// secRef.setKeyIdentifierSKI(certs[0], crypto);
// break;
//
default:
throw new WSSecurityException(WSSecurityException.FAILURE,
"unsupportedKeyId");
}
if (tlog.isDebugEnabled()) {
t3 = System.currentTimeMillis();
}
info.addUnknownElement(secRef.getElement());
Element samlToken = null;
try {
samlToken = (Element) assertion.toDOM(doc);
} catch (SAMLException e2) {
throw new WSSecurityException(WSSecurityException.FAILED_SIGNATURE,
"noSAMLdoc", null, e2);
}
if (senderVouches) {
WSSecurityUtil.prependChildElement(doc, securityHeader, secRefSaml
.getElement(), true);
}
wsDocInfo.setAssertion(samlToken);
WSSecurityUtil
.prependChildElement(doc, securityHeader, samlToken, true);
WSDocInfoStore.store(wsDocInfo);
try {
if (senderVouches) {
sig
.sign(issuerCrypto.getPrivateKey(issuerKeyName,
issuerKeyPW));
} else {
sig.sign(userCrypto.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);
}
}