org.apache.wss4j.common.saml.SamlAssertionWrapper Maven / Gradle / Ivy
/**
* 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.common.saml;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import javax.security.auth.callback.CallbackHandler;
import org.apache.wss4j.common.crypto.Crypto;
import org.apache.wss4j.common.crypto.CryptoType;
import org.apache.wss4j.common.ext.WSSecurityException;
import org.apache.wss4j.common.saml.builder.SAML1ComponentBuilder;
import org.apache.wss4j.common.saml.builder.SAML2ComponentBuilder;
import org.apache.wss4j.common.util.DOM2Writer;
import org.apache.wss4j.common.util.InetAddressUtils;
import org.apache.xml.security.exceptions.XMLSecurityException;
import org.apache.xml.security.signature.XMLSignature;
import org.apache.xml.security.signature.XMLSignatureException;
import org.apache.xml.security.stax.impl.util.IDGenerator;
import org.joda.time.DateTime;
import org.opensaml.common.SAMLVersion;
import org.opensaml.common.SignableSAMLObject;
import org.opensaml.common.impl.SAMLObjectContentReference;
import org.opensaml.saml1.core.AttributeStatement;
import org.opensaml.saml1.core.AuthenticationStatement;
import org.opensaml.saml1.core.AuthorizationDecisionStatement;
import org.opensaml.saml1.core.ConfirmationMethod;
import org.opensaml.saml1.core.Statement;
import org.opensaml.saml1.core.Subject;
import org.opensaml.saml1.core.SubjectConfirmation;
import org.opensaml.saml1.core.SubjectStatement;
import org.opensaml.saml2.core.AuthnStatement;
import org.opensaml.saml2.core.AuthzDecisionStatement;
import org.opensaml.saml2.core.Issuer;
import org.opensaml.security.SAMLSignatureProfileValidator;
import org.opensaml.xml.XMLObject;
import org.opensaml.xml.security.x509.BasicX509Credential;
import org.opensaml.xml.security.x509.X509KeyInfoGeneratorFactory;
import org.opensaml.xml.signature.KeyInfo;
import org.opensaml.xml.signature.Signature;
import org.opensaml.xml.signature.SignatureConstants;
import org.opensaml.xml.signature.SignatureValidator;
import org.opensaml.xml.validation.ValidationException;
import org.opensaml.xml.validation.ValidatorSuite;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/**
* Class SamlAssertionWrapper can generate, sign, and validate both SAML v1.1
* and SAML v2.0 assertions.
*/
public class SamlAssertionWrapper {
/**
* Field log
*/
private static final org.slf4j.Logger LOG =
org.slf4j.LoggerFactory.getLogger(SamlAssertionWrapper.class);
/**
* Raw SAML assertion data
*/
private XMLObject xmlObject = null;
/**
* Typed SAML v1.1 assertion
*/
private org.opensaml.saml1.core.Assertion saml1 = null;
/**
* Typed SAML v2.0 assertion
*/
private org.opensaml.saml2.core.Assertion saml2 = null;
/**
* Which SAML specification to use (currently, only v1.1 and v2.0 are supported)
*/
private SAMLVersion samlVersion;
/**
* The Assertion as a DOM element
*/
private Element assertionElement;
/**
* The SAMLKeyInfo object associated with the Subject KeyInfo
*/
private SAMLKeyInfo subjectKeyInfo;
/**
* The SAMLKeyInfo object associated with the Signature on the Assertion
*/
private SAMLKeyInfo signatureKeyInfo;
/**
* Default Canonicalization algorithm used for signing.
*/
private final String defaultCanonicalizationAlgorithm = SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS;
/**
* Default RSA Signature algorithm used for signing.
*/
private final String defaultRSASignatureAlgorithm = SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1;
/**
* Default DSA Signature algorithm used for signing.
*/
private final String defaultDSASignatureAlgorithm = SignatureConstants.ALGO_ID_SIGNATURE_DSA;
/**
* Default Signature Digest algorithm
*/
private final String defaultSignatureDigestAlgorithm = SignatureConstants.ALGO_ID_DIGEST_SHA1;
/**
* Whether this object was instantiated with a DOM Element or an XMLObject initially
*/
private final boolean fromDOM;
/**
* Constructor SamlAssertionWrapper creates a new SamlAssertionWrapper instance.
*
* @param element of type Element
* @throws WSSecurityException
*/
public SamlAssertionWrapper(Element element) throws WSSecurityException {
OpenSAMLUtil.initSamlEngine();
parseElement(element);
fromDOM = true;
}
/**
* Constructor SamlAssertionWrapper creates a new SamlAssertionWrapper instance.
*
* @param saml2 of type Assertion
*/
public SamlAssertionWrapper(org.opensaml.saml2.core.Assertion saml2) {
this((XMLObject)saml2);
}
/**
* Constructor SamlAssertionWrapper creates a new SamlAssertionWrapper instance.
*
* @param saml1 of type Assertion
*/
public SamlAssertionWrapper(org.opensaml.saml1.core.Assertion saml1) {
this((XMLObject)saml1);
}
/**
* Constructor SamlAssertionWrapper creates a new SamlAssertionWrapper instance.
* This is the primary constructor. All other constructor calls should
* be routed to this method to ensure that the wrapper is initialized
* correctly.
*
* @param xmlObject of type XMLObject
*/
public SamlAssertionWrapper(XMLObject xmlObject) {
OpenSAMLUtil.initSamlEngine();
this.xmlObject = xmlObject;
if (xmlObject instanceof org.opensaml.saml1.core.Assertion) {
this.saml1 = (org.opensaml.saml1.core.Assertion) xmlObject;
samlVersion = SAMLVersion.VERSION_11;
} else if (xmlObject instanceof org.opensaml.saml2.core.Assertion) {
this.saml2 = (org.opensaml.saml2.core.Assertion) xmlObject;
samlVersion = SAMLVersion.VERSION_20;
} else {
LOG.error(
"SamlAssertionWrapper: found unexpected type "
+ (xmlObject != null ? xmlObject.getClass().getName() : null)
);
}
fromDOM = false;
}
/**
* Constructor SamlAssertionWrapper creates a new SamlAssertionWrapper instance.
* This constructor is primarily called on the client side to initialize
* the wrapper from a configuration file.
*
* @param samlCallback of type SAMLCallback
*/
public SamlAssertionWrapper(SAMLCallback samlCallback) throws WSSecurityException {
OpenSAMLUtil.initSamlEngine();
if (samlCallback.getAssertionElement() != null) {
parseElement(samlCallback.getAssertionElement());
fromDOM = true;
} else {
// If not then parse the SAMLCallback object
parseCallback(samlCallback);
fromDOM = false;
}
}
/**
* Method getSaml1 returns the saml1 of this SamlAssertionWrapper object.
*
* @return the saml1 (type Assertion) of this SamlAssertionWrapper object.
*/
public org.opensaml.saml1.core.Assertion getSaml1() {
return saml1;
}
/**
* Method getSaml2 returns the saml2 of this SamlAssertionWrapper object.
*
* @return the saml2 (type Assertion) of this SamlAssertionWrapper object.
*/
public org.opensaml.saml2.core.Assertion getSaml2() {
return saml2;
}
/**
* Method getXmlObject returns the xmlObject of this SamlAssertionWrapper object.
*
* @return the xmlObject (type XMLObject) of this SamlAssertionWrapper object.
*/
public XMLObject getXmlObject() {
return xmlObject;
}
/**
* Method isCreated returns the created of this SamlAssertionWrapper object.
*
* @return the created (type boolean) of this SamlAssertionWrapper object.
*/
public boolean isCreated() {
return saml1 != null || saml2 != null;
}
/**
* Create a DOM from the current XMLObject content. If the user-supplied doc is not null,
* reparent the returned Element so that it is compatible with the user-supplied document.
*
* @param doc of type Document
* @return Element
*/
public Element toDOM(Document doc) throws WSSecurityException {
if (fromDOM && assertionElement != null) {
parseElement(assertionElement);
if (doc != null) {
return (Element)doc.importNode(assertionElement, true);
}
return assertionElement;
}
assertionElement = OpenSAMLUtil.toDom(xmlObject, doc);
return assertionElement;
}
/**
* Method assertionToString ...
*
* @return String
*/
public String assertionToString() throws WSSecurityException {
if (assertionElement == null) {
Element element = toDOM(null);
return DOM2Writer.nodeToString(element);
}
return DOM2Writer.nodeToString(assertionElement);
}
/**
* Method getId returns the id of this SamlAssertionWrapper object.
*
* @return the id (type String) of this SamlAssertionWrapper object.
*/
public String getId() {
String id = null;
if (saml2 != null) {
id = saml2.getID();
} else if (saml1 != null) {
id = saml1.getID();
} else {
LOG.error("SamlAssertionWrapper: unable to return ID - no saml assertion object");
}
if (id == null || id.length() == 0) {
LOG.error("SamlAssertionWrapper: ID was null, seeting a new ID value");
id = IDGenerator.generateID("_");
if (saml2 != null) {
saml2.setID(id);
} else if (saml1 != null) {
saml1.setID(id);
}
}
return id;
}
/**
* Method getIssuerString returns the issuerString of this SamlAssertionWrapper object.
*
* @return the issuerString (type String) of this SamlAssertionWrapper object.
*/
public String getIssuerString() {
if (saml2 != null && saml2.getIssuer() != null) {
return saml2.getIssuer().getValue();
} else if (saml1 != null) {
return saml1.getIssuer();
}
LOG.error(
"SamlAssertionWrapper: unable to return Issuer string - no saml assertion "
+ "object or issuer is null"
);
return null;
}
/**
* Method getSubjectName returns the Subject name value
* @return the subjectName of this SamlAssertionWrapper object
*/
public String getSubjectName() {
if (saml2 != null) {
org.opensaml.saml2.core.Subject subject = saml2.getSubject();
if (subject != null && subject.getNameID() != null) {
return subject.getNameID().getValue();
}
} else if (saml1 != null) {
Subject samlSubject = null;
for (Statement stmt : saml1.getStatements()) {
if (stmt instanceof AttributeStatement) {
AttributeStatement attrStmt = (AttributeStatement) stmt;
samlSubject = attrStmt.getSubject();
} else if (stmt instanceof AuthenticationStatement) {
AuthenticationStatement authStmt = (AuthenticationStatement) stmt;
samlSubject = authStmt.getSubject();
} else {
AuthorizationDecisionStatement authzStmt =
(AuthorizationDecisionStatement)stmt;
samlSubject = authzStmt.getSubject();
}
if (samlSubject != null) {
break;
}
}
if (samlSubject != null && samlSubject.getNameIdentifier() != null) {
return samlSubject.getNameIdentifier().getNameIdentifier();
}
}
LOG.error(
"SamlAssertionWrapper: unable to return SubjectName - no saml assertion "
+ "object or subject is null"
);
return null;
}
/**
* Method getConfirmationMethods returns the confirmationMethods of this
* SamlAssertionWrapper object.
*
* @return the confirmationMethods of this SamlAssertionWrapper object.
*/
public List getConfirmationMethods() {
List methods = new ArrayList();
if (saml2 != null) {
org.opensaml.saml2.core.Subject subject = saml2.getSubject();
List confirmations =
subject.getSubjectConfirmations();
for (org.opensaml.saml2.core.SubjectConfirmation confirmation : confirmations) {
methods.add(confirmation.getMethod());
}
} else if (saml1 != null) {
List subjectStatements = new ArrayList();
subjectStatements.addAll(saml1.getSubjectStatements());
subjectStatements.addAll(saml1.getAuthenticationStatements());
subjectStatements.addAll(saml1.getAttributeStatements());
subjectStatements.addAll(saml1.getAuthorizationDecisionStatements());
for (SubjectStatement subjectStatement : subjectStatements) {
Subject subject = subjectStatement.getSubject();
if (subject != null) {
SubjectConfirmation confirmation = subject.getSubjectConfirmation();
if (confirmation != null) {
XMLObject data = confirmation.getSubjectConfirmationData();
if (data instanceof ConfirmationMethod) {
ConfirmationMethod method = (ConfirmationMethod) data;
methods.add(method.getConfirmationMethod());
}
List confirmationMethods =
confirmation.getConfirmationMethods();
for (ConfirmationMethod confirmationMethod : confirmationMethods) {
methods.add(confirmationMethod.getConfirmationMethod());
}
}
}
}
}
return methods;
}
/**
* Method isSigned returns the signed of this SamlAssertionWrapper object.
*
* @return the signed (type boolean) of this SamlAssertionWrapper object.
*/
public boolean isSigned() {
if (saml2 != null) {
return saml2.isSigned() || saml2.getSignature() != null;
} else if (saml1 != null) {
return saml1.isSigned() || saml1.getSignature() != null;
}
return false;
}
/**
* Method setSignature sets the signature of this SamlAssertionWrapper object.
*
* @param signature the signature of this SamlAssertionWrapper object.
*/
public void setSignature(Signature signature) {
setSignature(signature, defaultSignatureDigestAlgorithm);
}
/**
* Method setSignature sets the signature of this SamlAssertionWrapper object.
*
* @param signature the signature of this SamlAssertionWrapper object.
* @param signatureDigestAlgorithm the signature digest algorithm to use
*/
public void setSignature(Signature signature, String signatureDigestAlgorithm) {
if (xmlObject instanceof SignableSAMLObject) {
SignableSAMLObject signableObject = (SignableSAMLObject) xmlObject;
signableObject.setSignature(signature);
String digestAlg = signatureDigestAlgorithm;
if (digestAlg == null) {
digestAlg = defaultSignatureDigestAlgorithm;
}
SAMLObjectContentReference contentRef =
(SAMLObjectContentReference)signature.getContentReferences().get(0);
contentRef.setDigestAlgorithm(digestAlg);
signableObject.releaseDOM();
signableObject.releaseChildrenDOM(true);
} else {
LOG.error("Attempt to sign an unsignable object " + xmlObject.getClass().getName());
}
}
/**
* Create an enveloped signature on the assertion that has been created.
*
* @param issuerKeyName the Issuer KeyName to use with the issuerCrypto argument
* @param issuerKeyPassword the Issuer Password to use with the issuerCrypto argument
* @param issuerCrypto the Issuer Crypto instance
* @param sendKeyValue whether to send the key value or not
* @throws WSSecurityException
*/
public void signAssertion(String issuerKeyName, String issuerKeyPassword,
Crypto issuerCrypto, boolean sendKeyValue)
throws WSSecurityException {
signAssertion(issuerKeyName, issuerKeyPassword, issuerCrypto,
sendKeyValue, defaultCanonicalizationAlgorithm,
defaultRSASignatureAlgorithm, defaultSignatureDigestAlgorithm);
}
/**
* Create an enveloped signature on the assertion that has been created.
*
* @param issuerKeyName the Issuer KeyName to use with the issuerCrypto argument
* @param issuerKeyPassword the Issuer Password to use with the issuerCrypto argument
* @param issuerCrypto the Issuer Crypto instance
* @param sendKeyValue whether to send the key value or not
* @param canonicalizationAlgorithm the canonicalization algorithm to be used for signing
* @param signatureAlgorithm the signature algorithm to be used for signing
* @throws WSSecurityException
*/
public void signAssertion(String issuerKeyName, String issuerKeyPassword,
Crypto issuerCrypto, boolean sendKeyValue,
String canonicalizationAlgorithm, String signatureAlgorithm)
throws WSSecurityException {
signAssertion(issuerKeyName, issuerKeyPassword, issuerCrypto, sendKeyValue,
canonicalizationAlgorithm, signatureAlgorithm, defaultSignatureDigestAlgorithm);
}
/**
* Create an enveloped signature on the assertion that has been created.
*
* @param issuerKeyName the Issuer KeyName to use with the issuerCrypto argument
* @param issuerKeyPassword the Issuer Password to use with the issuerCrypto argument
* @param issuerCrypto the Issuer Crypto instance
* @param sendKeyValue whether to send the key value or not
* @param canonicalizationAlgorithm the canonicalization algorithm to be used for signing
* @param signatureAlgorithm the signature algorithm to be used for signing
* @param signatureDigestAlgorithm the signature Digest algorithm to use
* @throws WSSecurityException
*/
public void signAssertion(String issuerKeyName, String issuerKeyPassword,
Crypto issuerCrypto, boolean sendKeyValue,
String canonicalizationAlgorithm, String signatureAlgorithm,
String signatureDigestAlgorithm)
throws WSSecurityException {
//
// Create the signature
//
Signature signature = OpenSAMLUtil.buildSignature();
String c14nAlgo = canonicalizationAlgorithm;
if (c14nAlgo == null) {
c14nAlgo = defaultCanonicalizationAlgorithm;
}
signature.setCanonicalizationAlgorithm(c14nAlgo);
LOG.debug("Using Canonicalization algorithm " + c14nAlgo);
// prepare to sign the SAML token
CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ALIAS);
cryptoType.setAlias(issuerKeyName);
X509Certificate[] issuerCerts = issuerCrypto.getX509Certificates(cryptoType);
if (issuerCerts == null || issuerCerts.length == 0) {
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "empty",
"No issuer certs were found to sign the SAML Assertion using issuer name: "
+ issuerKeyName);
}
String sigAlgo = signatureAlgorithm;
if (sigAlgo == null) {
sigAlgo = defaultRSASignatureAlgorithm;
}
String pubKeyAlgo = issuerCerts[0].getPublicKey().getAlgorithm();
if (LOG.isDebugEnabled()) {
LOG.debug("automatic sig algo detection: " + pubKeyAlgo);
}
if (pubKeyAlgo.equalsIgnoreCase("DSA")) {
sigAlgo = defaultDSASignatureAlgorithm;
}
LOG.debug("Using Signature algorithm " + sigAlgo);
PrivateKey privateKey = null;
try {
privateKey = issuerCrypto.getPrivateKey(issuerKeyName, issuerKeyPassword);
} catch (Exception ex) {
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, ex);
}
if (privateKey == null) {
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "empty",
"No private key was found using issuer name: " + issuerKeyName);
}
signature.setSignatureAlgorithm(sigAlgo);
BasicX509Credential signingCredential = new BasicX509Credential();
signingCredential.setEntityCertificate(issuerCerts[0]);
signingCredential.setPrivateKey(privateKey);
signature.setSigningCredential(signingCredential);
X509KeyInfoGeneratorFactory kiFactory = new X509KeyInfoGeneratorFactory();
if (sendKeyValue) {
kiFactory.setEmitPublicKeyValue(true);
} else {
kiFactory.setEmitEntityCertificate(true);
}
try {
KeyInfo keyInfo = kiFactory.newInstance().generate(
signingCredential);
signature.setKeyInfo(keyInfo);
} catch (org.opensaml.xml.security.SecurityException ex) {
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "empty", ex,
"Error generating KeyInfo from signing credential");
}
// add the signature to the assertion
setSignature(signature, signatureDigestAlgorithm);
}
/**
* Verify the signature of this assertion
*
* @throws ValidationException
*/
public void verifySignature(
SAMLKeyInfoProcessor keyInfoProcessor, Crypto sigCrypto
) throws WSSecurityException {
Signature sig = getSignature();
if (sig != null) {
KeyInfo keyInfo = sig.getKeyInfo();
if (keyInfo == null) {
throw new WSSecurityException(
WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity",
"cannot get certificate or key"
);
}
SAMLKeyInfo samlKeyInfo =
SAMLUtil.getCredentialFromKeyInfo(keyInfo.getDOM(), keyInfoProcessor, sigCrypto);
verifySignature(samlKeyInfo);
} else {
LOG.debug("SamlAssertionWrapper: no signature to validate");
}
}
/**
* Verify the signature of this assertion
*
* @throws ValidationException
*/
public void verifySignature(SAMLKeyInfo samlKeyInfo) throws WSSecurityException {
Signature sig = getSignature();
if (sig != null) {
if (samlKeyInfo == null) {
throw new WSSecurityException(
WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity",
"cannot get certificate or key"
);
}
BasicX509Credential credential = new BasicX509Credential();
if (samlKeyInfo.getCerts() != null) {
credential.setEntityCertificate(samlKeyInfo.getCerts()[0]);
} else if (samlKeyInfo.getPublicKey() != null) {
credential.setPublicKey(samlKeyInfo.getPublicKey());
} else {
throw new WSSecurityException(
WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity",
"cannot get certificate or key"
);
}
SignatureValidator sigValidator = new SignatureValidator(credential);
try {
sigValidator.validate(sig);
} catch (ValidationException ex) {
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE,
"empty", ex, "SAML signature validation failed");
}
signatureKeyInfo = samlKeyInfo;
} else {
LOG.debug("SamlAssertionWrapper: no signature to validate");
}
}
/**
* Validate the signature of the Assertion against the Profile. This does not actually
* verify the signature itself (see the verifySignature method for this)
* @throws WSSecurityException
*/
public void validateSignatureAgainstProfile() throws WSSecurityException {
Signature sig = getSignature();
if (sig != null) {
SAMLSignatureProfileValidator validator = new SAMLSignatureProfileValidator();
try {
validator.validate(sig);
} catch (ValidationException ex) {
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE,
"empty", ex, "SAML signature validation failed");
}
}
}
/**
* This method parses the KeyInfo of the Subject. It then stores the SAMLKeyInfo object that
* has been obtained for future processing.
* @throws WSSecurityException
*/
public void parseSubject(
SAMLKeyInfoProcessor keyInfoProcessor,
Crypto sigCrypto,
CallbackHandler callbackHandler
) throws WSSecurityException {
if (saml1 != null) {
subjectKeyInfo =
SAMLUtil.getCredentialFromSubject(
saml1, keyInfoProcessor, sigCrypto, callbackHandler
);
} else if (saml2 != null) {
subjectKeyInfo =
SAMLUtil.getCredentialFromSubject(
saml2, keyInfoProcessor, sigCrypto, callbackHandler
);
}
}
/**
* Method getSamlVersion returns the samlVersion of this SamlAssertionWrapper object.
*
* @return the samlVersion (type SAMLVersion) of this SamlAssertionWrapper object.
*/
public SAMLVersion getSamlVersion() {
if (samlVersion == null) {
// Try to set the version.
if (LOG.isDebugEnabled()) {
LOG.debug(
"The SAML version was null in getSamlVersion(). Recomputing SAML version..."
);
}
if (saml1 != null && saml2 == null) {
samlVersion = SAMLVersion.VERSION_11;
} else if (saml1 == null && saml2 != null) {
samlVersion = SAMLVersion.VERSION_20;
} else {
// We are only supporting SAML v1.1 or SAML v2.0 at this time.
throw new IllegalStateException(
"Could not determine the SAML version number. Check your "
+ "configuration and try again."
);
}
}
return samlVersion;
}
/**
* Get the Assertion as a DOM Element.
* @return the assertion as a DOM Element
*/
public Element getElement() {
return assertionElement;
}
/**
* Get the SAMLKeyInfo associated with the signature of the assertion
* @return the SAMLKeyInfo associated with the signature of the assertion
*/
public SAMLKeyInfo getSignatureKeyInfo() {
return signatureKeyInfo;
}
/**
* Get the SAMLKeyInfo associated with the Subject KeyInfo
* @return the SAMLKeyInfo associated with the Subject KeyInfo
*/
public SAMLKeyInfo getSubjectKeyInfo() {
return subjectKeyInfo;
}
/**
* Get the SignatureValue bytes of the signed SAML Assertion
* @return the SignatureValue bytes of the signed SAML Assertion
* @throws WSSecurityException
*/
public byte[] getSignatureValue() throws WSSecurityException {
Signature sig = null;
if (saml2 != null && saml2.getSignature() != null) {
sig = saml2.getSignature();
} else if (saml1 != null && saml1.getSignature() != null) {
sig = saml1.getSignature();
}
if (sig != null) {
Element signatureElement = sig.getDOM();
try {
// Use XML-Security class to obtain SignatureValue
XMLSignature xmlSignature = new XMLSignature(signatureElement, "");
return xmlSignature.getSignatureValue();
} catch (XMLSignatureException e) {
throw new WSSecurityException(
WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity", e
);
} catch (XMLSecurityException e) {
throw new WSSecurityException(
WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity", e
);
}
}
return null;
}
public Signature getSignature() throws WSSecurityException {
Signature sig = null;
if (saml2 != null && saml2.getSignature() != null) {
sig = saml2.getSignature();
} else if (saml1 != null && saml1.getSignature() != null) {
sig = saml1.getSignature();
}
return sig;
}
/**
* Check the Conditions of the Assertion.
*/
public void checkConditions(int futureTTL) throws WSSecurityException {
DateTime validFrom = null;
DateTime validTill = null;
if (getSamlVersion().equals(SAMLVersion.VERSION_20)
&& getSaml2().getConditions() != null) {
validFrom = getSaml2().getConditions().getNotBefore();
validTill = getSaml2().getConditions().getNotOnOrAfter();
} else if (getSamlVersion().equals(SAMLVersion.VERSION_11)
&& getSaml1().getConditions() != null) {
validFrom = getSaml1().getConditions().getNotBefore();
validTill = getSaml1().getConditions().getNotOnOrAfter();
}
if (validFrom != null) {
DateTime currentTime = new DateTime();
currentTime = currentTime.plusSeconds(futureTTL);
if (validFrom.isAfter(currentTime)) {
LOG.debug("SAML Token condition (Not Before) not met");
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
}
}
if (validTill != null && validTill.isBeforeNow()) {
LOG.debug("SAML Token condition (Not On Or After) not met");
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
}
}
/**
* Check the IssueInstant value of the Assertion.
*/
public void checkIssueInstant(int futureTTL, int ttl) throws WSSecurityException {
DateTime issueInstant = null;
DateTime validTill = null;
if (getSamlVersion().equals(SAMLVersion.VERSION_20)
&& getSaml2().getConditions() != null) {
validTill = getSaml2().getConditions().getNotOnOrAfter();
issueInstant = getSaml2().getIssueInstant();
} else if (getSamlVersion().equals(SAMLVersion.VERSION_11)
&& getSaml1().getConditions() != null) {
validTill = getSaml1().getConditions().getNotOnOrAfter();
issueInstant = getSaml1().getIssueInstant();
}
// Check the IssueInstant is not in the future, subject to the future TTL
if (issueInstant != null) {
DateTime currentTime = new DateTime();
currentTime = currentTime.plusSeconds(futureTTL);
if (issueInstant.isAfter(currentTime)) {
LOG.debug("SAML Token IssueInstant not met");
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
}
// If there is no NotOnOrAfter, then impose a TTL on the IssueInstant.
if (validTill == null) {
currentTime = new DateTime();
currentTime.minusSeconds(ttl);
if (issueInstant.isBefore(currentTime)) {
LOG.debug("SAML Token IssueInstant not met. The assertion was created too long ago.");
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
}
}
}
}
/**
* Check the AudienceRestrictions of the Assertion
*/
public void checkAudienceRestrictions(List audienceRestrictions) throws WSSecurityException {
// Now check the audience restriction conditions
if (audienceRestrictions == null || audienceRestrictions.isEmpty()) {
return;
}
if (getSamlVersion().equals(SAMLVersion.VERSION_20) && getSaml2().getConditions() != null) {
org.opensaml.saml2.core.Conditions conditions = getSaml2().getConditions();
if (conditions != null && conditions.getAudienceRestrictions() != null
&& !conditions.getAudienceRestrictions().isEmpty()) {
boolean foundAddress = false;
for (org.opensaml.saml2.core.AudienceRestriction audienceRestriction
: conditions.getAudienceRestrictions()) {
if (audienceRestriction.getAudiences() != null) {
List audiences =
audienceRestriction.getAudiences();
for (org.opensaml.saml2.core.Audience audience : audiences) {
String audienceURI = audience.getAudienceURI();
if (audienceRestrictions.contains(audienceURI)) {
foundAddress = true;
break;
}
}
}
}
if (!foundAddress) {
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
}
}
} else if (getSamlVersion().equals(SAMLVersion.VERSION_11) && getSaml1().getConditions() != null) {
org.opensaml.saml1.core.Conditions conditions = getSaml1().getConditions();
if (conditions != null && conditions.getAudienceRestrictionConditions() != null
&& !conditions.getAudienceRestrictionConditions().isEmpty()) {
boolean foundAddress = false;
for (org.opensaml.saml1.core.AudienceRestrictionCondition audienceRestriction
: conditions.getAudienceRestrictionConditions()) {
if (audienceRestriction.getAudiences() != null) {
List audiences =
audienceRestriction.getAudiences();
for (org.opensaml.saml1.core.Audience audience : audiences) {
String audienceURI = audience.getUri();
if (audienceRestrictions.contains(audienceURI)) {
foundAddress = true;
break;
}
}
}
}
if (!foundAddress) {
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
}
}
}
}
/**
* Check the various attributes of the AuthnStatements of the assertion (if any)
*/
public void checkAuthnStatements(int futureTTL) throws WSSecurityException {
if (getSamlVersion().equals(SAMLVersion.VERSION_20)
&& getSaml2().getAuthnStatements() != null) {
List authnStatements = getSaml2().getAuthnStatements();
for (AuthnStatement authnStatement : authnStatements) {
DateTime authnInstant = authnStatement.getAuthnInstant();
DateTime sessionNotOnOrAfter = authnStatement.getSessionNotOnOrAfter();
String subjectLocalityAddress = null;
if (authnStatement.getSubjectLocality() != null
&& authnStatement.getSubjectLocality().getAddress() != null) {
subjectLocalityAddress = authnStatement.getSubjectLocality().getAddress();
}
validateAuthnStatement(authnInstant, sessionNotOnOrAfter,
subjectLocalityAddress, futureTTL);
}
} else if (getSamlVersion().equals(SAMLVersion.VERSION_11)
&& getSaml1().getAuthenticationStatements() != null) {
List authnStatements =
getSaml1().getAuthenticationStatements();
for (AuthenticationStatement authnStatement : authnStatements) {
DateTime authnInstant = authnStatement.getAuthenticationInstant();
String subjectLocalityAddress = null;
if (authnStatement.getSubjectLocality() != null
&& authnStatement.getSubjectLocality().getIPAddress() != null) {
subjectLocalityAddress = authnStatement.getSubjectLocality().getIPAddress();
}
validateAuthnStatement(authnInstant, null,
subjectLocalityAddress, futureTTL);
}
}
}
private void validateAuthnStatement(
DateTime authnInstant, DateTime sessionNotOnOrAfter, String subjectLocalityAddress,
int futureTTL
) throws WSSecurityException {
// AuthnInstant in the future
DateTime currentTime = new DateTime();
currentTime = currentTime.plusSeconds(futureTTL);
if (authnInstant.isAfter(currentTime)) {
LOG.debug("SAML Token AuthnInstant not met");
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
}
// Stale SessionNotOnOrAfter
if (sessionNotOnOrAfter != null && sessionNotOnOrAfter.isBeforeNow()) {
LOG.debug("SAML Token SessionNotOnOrAfter not met");
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
}
// Check that the SubjectLocality address is an IP address
if (subjectLocalityAddress != null
&& !(InetAddressUtils.isIPv4Address(subjectLocalityAddress)
|| InetAddressUtils.isIPv6Address(subjectLocalityAddress))) {
LOG.debug("SAML Token SubjectLocality address is not valid: " + subjectLocalityAddress);
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
}
}
/**
* Validate the samlAssertion against schemas/profiles
*/
public void validateAssertion(boolean validateSignatureAgainstProfile) throws WSSecurityException {
if (validateSignatureAgainstProfile) {
validateSignatureAgainstProfile();
}
if (getSaml1() != null) {
ValidatorSuite schemaValidators =
org.opensaml.Configuration.getValidatorSuite("saml1-schema-validator");
ValidatorSuite specValidators =
org.opensaml.Configuration.getValidatorSuite("saml1-spec-validator");
try {
schemaValidators.validate(getSaml1());
specValidators.validate(getSaml1());
} catch (ValidationException e) {
LOG.debug("Saml Validation error: " + e.getMessage(), e);
throw new WSSecurityException(
WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity", e
);
}
} else if (getSaml2() != null) {
ValidatorSuite schemaValidators =
org.opensaml.Configuration.getValidatorSuite("saml2-core-schema-validator");
ValidatorSuite specValidators =
org.opensaml.Configuration.getValidatorSuite("saml2-core-spec-validator");
try {
schemaValidators.validate(getSaml2());
specValidators.validate(getSaml2());
} catch (ValidationException e) {
LOG.debug("Saml Validation error: " + e.getMessage(), e);
throw new WSSecurityException(
WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity", e
);
}
}
}
/**
* Parse the DOM Element into Opensaml objects.
*/
private void parseElement(Element element) throws WSSecurityException {
this.xmlObject = OpenSAMLUtil.fromDom(element);
if (xmlObject instanceof org.opensaml.saml1.core.Assertion) {
this.saml1 = (org.opensaml.saml1.core.Assertion) xmlObject;
samlVersion = SAMLVersion.VERSION_11;
} else if (xmlObject instanceof org.opensaml.saml2.core.Assertion) {
this.saml2 = (org.opensaml.saml2.core.Assertion) xmlObject;
samlVersion = SAMLVersion.VERSION_20;
} else {
LOG.error(
"SamlAssertionWrapper: found unexpected type "
+ (xmlObject != null ? xmlObject.getClass().getName() : xmlObject)
);
}
assertionElement = element;
}
/**
* Parse a SAMLCallback object to create a SAML Assertion
*/
private void parseCallback(
SAMLCallback samlCallback
) throws WSSecurityException {
samlVersion = samlCallback.getSamlVersion();
if (samlVersion == null) {
samlVersion = SAMLVersion.VERSION_20;
}
String issuer = samlCallback.getIssuer();
if (samlVersion.equals(SAMLVersion.VERSION_11)) {
// Build a SAML v1.1 assertion
saml1 = SAML1ComponentBuilder.createSamlv1Assertion(issuer);
try {
// Process the SAML authentication statement(s)
List authenticationStatements =
SAML1ComponentBuilder.createSamlv1AuthenticationStatement(
samlCallback.getAuthenticationStatementData()
);
saml1.getAuthenticationStatements().addAll(authenticationStatements);
// Process the SAML attribute statement(s)
List attributeStatements =
SAML1ComponentBuilder.createSamlv1AttributeStatement(
samlCallback.getAttributeStatementData()
);
saml1.getAttributeStatements().addAll(attributeStatements);
// Process the SAML authorization decision statement(s)
List authDecisionStatements =
SAML1ComponentBuilder.createSamlv1AuthorizationDecisionStatement(
samlCallback.getAuthDecisionStatementData()
);
saml1.getAuthorizationDecisionStatements().addAll(authDecisionStatements);
// Build the complete assertion
org.opensaml.saml1.core.Conditions conditions =
SAML1ComponentBuilder.createSamlv1Conditions(samlCallback.getConditions());
saml1.setConditions(conditions);
if (samlCallback.getAdvice() != null) {
org.opensaml.saml1.core.Advice advice =
SAML1ComponentBuilder.createAdvice(samlCallback.getAdvice());
saml1.setAdvice(advice);
}
} catch (org.opensaml.xml.security.SecurityException ex) {
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "empty", ex,
"Error generating KeyInfo from signing credential"
);
}
// Set the OpenSaml2 XMLObject instance
xmlObject = saml1;
} else if (samlVersion.equals(SAMLVersion.VERSION_20)) {
// Build a SAML v2.0 assertion
saml2 = SAML2ComponentBuilder.createAssertion();
Issuer samlIssuer = SAML2ComponentBuilder.createIssuer(issuer);
// Authn Statement(s)
List authnStatements =
SAML2ComponentBuilder.createAuthnStatement(
samlCallback.getAuthenticationStatementData()
);
saml2.getAuthnStatements().addAll(authnStatements);
// Attribute statement(s)
List attributeStatements =
SAML2ComponentBuilder.createAttributeStatement(
samlCallback.getAttributeStatementData()
);
saml2.getAttributeStatements().addAll(attributeStatements);
// AuthzDecisionStatement(s)
List authDecisionStatements =
SAML2ComponentBuilder.createAuthorizationDecisionStatement(
samlCallback.getAuthDecisionStatementData()
);
saml2.getAuthzDecisionStatements().addAll(authDecisionStatements);
// Build the SAML v2.0 assertion
saml2.setIssuer(samlIssuer);
try {
org.opensaml.saml2.core.Subject subject =
SAML2ComponentBuilder.createSaml2Subject(samlCallback.getSubject());
saml2.setSubject(subject);
} catch (org.opensaml.xml.security.SecurityException ex) {
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "empty", ex,
"Error generating KeyInfo from signing credential"
);
}
org.opensaml.saml2.core.Conditions conditions =
SAML2ComponentBuilder.createConditions(samlCallback.getConditions());
saml2.setConditions(conditions);
if (samlCallback.getAdvice() != null) {
org.opensaml.saml2.core.Advice advice =
SAML2ComponentBuilder.createAdvice(samlCallback.getAdvice());
saml2.setAdvice(advice);
}
// Set the OpenSaml2 XMLObject instance
xmlObject = saml2;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy