blob: 2e32a9244516aea4fc51ba22ecbf8feb13c4f23f [file] [log] [blame]
/*
* Copyright 2004,2005 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.rahas.impl;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMNode;
import org.apache.axiom.soap.SOAPEnvelope;
import org.apache.axis2.context.MessageContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.rahas.*;
import org.apache.rahas.impl.util.*;
import org.apache.ws.security.components.crypto.Crypto;
import org.apache.ws.security.util.XmlSchemaDateFormat;
import org.apache.xml.security.c14n.Canonicalizer;
import org.apache.xml.security.signature.XMLSignature;
import org.joda.time.DateTime;
import org.opensaml.Configuration;
import org.opensaml.common.SAMLException;
import org.opensaml.common.SAMLObjectBuilder;
import org.opensaml.saml2.core.*;
import org.opensaml.xml.XMLObjectBuilderFactory;
import org.opensaml.xml.io.*;
import org.opensaml.xml.schema.XSString;
import org.opensaml.xml.schema.impl.XSStringBuilder;
import org.opensaml.xml.signature.*;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import java.security.PrivateKey;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* WS-Trust based SAML2 token issuer. This issuer will generate request security token responses with SAML2
* assertions.
*/
public class SAML2TokenIssuer implements TokenIssuer {
private String configParamName;
private OMElement configElement;
private String configFile;
protected List<Signature> signatureList = new ArrayList<Signature>();
private boolean isSymmetricKeyBasedHoK = false;
private SAMLTokenIssuerConfig tokenIssuerConfiguration;
private static Log log = LogFactory.getLog(SAML2TokenIssuer.class);
/**
* This is the main method which issues SAML2 assertions as security token responses. This method will
* read issuer configuration and in message context properties (Basically request security token properties)
* and will create a security token response with SAML2 assertion. The attributes are retrieved from a callback
* class.
* @param data A populated <code>RahasData</code> instance
* @return A SOAP message with security token response (as per ws-trust spec) with a SAML2 assertion.
* @throws TrustException If an error occurred while creating the response.
*/
public SOAPEnvelope issue(RahasData data) throws TrustException {
MessageContext inMsgCtx = data.getInMessageContext();
this.tokenIssuerConfiguration = CommonUtil.getTokenIssuerConfiguration(this.configElement,
this.configFile, inMsgCtx.getParameter(this.configParamName));
if (tokenIssuerConfiguration == null) {
if (log.isDebugEnabled()) {
String parameterName;
if (this.configElement != null) {
parameterName = "OMElement - " + this.configElement.toString();
} else if (this.configFile != null) {
parameterName = "File - " + this.configFile;
} else if (this.configParamName != null) {
parameterName = "With message context parameter name - " + this.configParamName;
} else {
parameterName = "No method to build configurations";
}
log.debug("Unable to build token configurations, " + parameterName);
}
throw new TrustException("configurationIsNull");
}
SOAPEnvelope env = TrustUtil.createSOAPEnvelope(inMsgCtx
.getEnvelope().getNamespace().getNamespaceURI());
Crypto crypto = tokenIssuerConfiguration.getIssuerCrypto(inMsgCtx
.getAxisService().getClassLoader());
// Get the document
Document doc = ((Element) env).getOwnerDocument();
// Get the key size and create a new byte array of that size
int keySize = data.getKeySize();
keySize = (keySize == -1) ? tokenIssuerConfiguration.getKeySize() : keySize;
data.setKeySize(keySize);
// Build the assertion
Assertion assertion = buildAssertion(doc, crypto, data);
// Sign the assertion
Assertion signedAssertion = signAssertion(doc, assertion, crypto);
return createRequestSecurityTokenResponse(data, signedAssertion, env);
}
/**
* This method prepares the final response. This method will create a request security token response as
* specified in WS-Trust specification. The equivalent XML would take following format,
* <wst:RequestSecurityTokenResponse xmlns:wst="...">
* <wst:TokenType>...</wst:TokenType>
* <wst:RequestedSecurityToken>...</wst:RequestedSecurityToken>
* ...
* <wsp:AppliesTo xmlns:wsp="...">...</wsp:AppliesTo>
* <wst:RequestedAttachedReference>
* ...
* </wst:RequestedAttachedReference>
* <wst:RequestedUnattachedReference>
* ...
* </wst:RequestedUnattachedReference>
* <wst:RequestedProofToken>...</wst:RequestedProofToken>
* <wst:Entropy>
* <wst:BinarySecret>...</wst:BinarySecret>
* </wst:Entropy>
* <wst:Lifetime>...</wst:Lifetime>
* </wst:RequestSecurityTokenResponse>
*
* Thus the RequestedSecurityToken will have SAML2 assertion passed.
* @param rahasData The configuration data which comes with RST
* @param assertion OpenSAM representation of SAML2 assertion.
* @param soapEnvelope SOAP message envelope
* @return SOAPEnvelope which includes RequestSecurityTokenResponse
* @throws TrustException If an error occurred while creating RequestSecurityTokenResponse.
*/
protected SOAPEnvelope createRequestSecurityTokenResponse(RahasData rahasData,
Assertion assertion,
SOAPEnvelope soapEnvelope) throws TrustException {
OMElement requestSecurityTokenResponse;
int wstVersion = rahasData.getVersion();
if (RahasConstants.VERSION_05_02 == wstVersion) {
requestSecurityTokenResponse = TrustUtil.createRequestSecurityTokenResponseElement(
wstVersion, soapEnvelope.getBody());
} else {
OMElement requestSecurityTokenResponseCollectionElement = TrustUtil
.createRequestSecurityTokenResponseCollectionElement(
wstVersion, soapEnvelope.getBody());
requestSecurityTokenResponse = TrustUtil.createRequestSecurityTokenResponseElement(
wstVersion, requestSecurityTokenResponseCollectionElement);
}
TrustUtil.createTokenTypeElement(wstVersion, requestSecurityTokenResponse).setText(
RahasConstants.TOK_TYPE_SAML_20);
if (rahasData.getKeyType().endsWith(RahasConstants.KEY_TYPE_SYMM_KEY)) {
TrustUtil.createKeySizeElement(wstVersion, requestSecurityTokenResponse, rahasData.getKeySize());
}
if (tokenIssuerConfiguration.isAddRequestedAttachedRef()) {
TrustUtil.createRequestedAttachedRef(wstVersion, requestSecurityTokenResponse, "#"
+ assertion.getID(), RahasConstants.TOK_TYPE_SAML_20);
}
if (tokenIssuerConfiguration.isAddRequestedUnattachedRef()) {
TrustUtil.createRequestedUnattachedRef(wstVersion, requestSecurityTokenResponse,
assertion.getID(), RahasConstants.TOK_TYPE_SAML_20);
}
if (rahasData.getAppliesToAddress() != null) {
TrustUtil.createAppliesToElement(requestSecurityTokenResponse, rahasData
.getAppliesToAddress(), rahasData.getAddressingNs());
}
// Use GMT time in milliseconds
DateFormat xmlSchemaDateFormat = new XmlSchemaDateFormat();
// Add the Lifetime element
TrustUtil.createLifetimeElement(wstVersion, requestSecurityTokenResponse, xmlSchemaDateFormat
.format(rahasData.getAssertionCreatedDate()),
xmlSchemaDateFormat.format(rahasData.getAssertionExpiringDate()));
// Create the RequestedSecurityToken element and add the SAML token
// to it
OMElement requestedSecurityTokenElement = TrustUtil
.createRequestedSecurityTokenElement(wstVersion, requestSecurityTokenResponse);
Element assertionElement = assertion.getDOM();
requestedSecurityTokenElement.addChild((OMNode)assertionElement);
// Store the token
Token assertionToken = new Token(assertion.getID(),
(OMElement) assertionElement, rahasData.getAssertionCreatedDate(),
rahasData.getAssertionExpiringDate());
// At this point we definitely have the secret
// Otherwise it should fail with an exception earlier
assertionToken.setSecret(rahasData.getEphmeralKey());
TrustUtil.getTokenStore(rahasData.getInMessageContext()).add(assertionToken);
if (rahasData.getKeyType().endsWith(RahasConstants.KEY_TYPE_SYMM_KEY)
&& tokenIssuerConfiguration.getKeyComputation()
!= SAMLTokenIssuerConfig.KeyComputation.KEY_COMP_USE_REQ_ENT) {
Document doc = ((Element) soapEnvelope).getOwnerDocument();
// Add the RequestedProofToken
TokenIssuerUtil.handleRequestedProofToken(rahasData, wstVersion,
tokenIssuerConfiguration,
requestSecurityTokenResponse, assertionToken, doc);
}
return soapEnvelope;
}
/**
* This methods builds the SAML2 assertion. The equivalent XML would look as follows,
* <saml:Assertion
* xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
* xmlns:xs="http://www.w3.org/2001/XMLSchema"
* xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
* ID="b07b804c-7c29-ea16-7300-4f3d6f7928ac"
* Version="2.0"
* IssueInstant="2004-12-05T09:22:05Z">
* <saml:Issuer>https://idp.example.org/SAML2</saml:Issuer>
* <ds:Signature
* xmlns:ds="http://www.w3.org/2000/09/xmldsig#">...</ds:Signature>
* <saml:Subject>
* <saml:NameID
* Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient">
* 3f7b3dcf-1674-4ecd-92c8-1544f346baf8
* </saml:NameID>
* <saml:SubjectConfirmation
* Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
* <saml:SubjectConfirmationData
* InResponseTo="aaf23196-1773-2113-474a-fe114412ab72"
* Recipient="https://sp.example.com/SAML2/SSO/POST"
* NotOnOrAfter="2004-12-05T09:27:05Z"/>
* </saml:SubjectConfirmation>
* </saml:Subject>
* <saml:Conditions
* NotBefore="2004-12-05T09:17:05Z"
* NotOnOrAfter="2004-12-05T09:27:05Z">
* <saml:AudienceRestriction>
* <saml:Audience>https://sp.example.com/SAML2</saml:Audience>
* </saml:AudienceRestriction>
* </saml:Conditions>
* <saml:AuthnStatement
* AuthnInstant="2004-12-05T09:22:00Z"
* SessionIndex="b07b804c-7c29-ea16-7300-4f3d6f7928ac">
* <saml:AuthnContext>
* <saml:AuthnContextClassRef>
* urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport
* </saml:AuthnContextClassRef>
* </saml:AuthnContext>
* </saml:AuthnStatement>
* <saml:AttributeStatement>
* <saml:Attribute
* xmlns:x500="urn:oasis:names:tc:SAML:2.0:profiles:attribute:X500"
* x500:Encoding="LDAP"
* NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
* Name="urn:oid:1.3.6.1.4.1.5923.1.1.1.1"
* FriendlyName="eduPersonAffiliation">
* <saml:AttributeValue
* xsi:type="xs:string">member</saml:AttributeValue>
* <saml:AttributeValue
* xsi:type="xs:string">staff</saml:AttributeValue>
* </saml:Attribute>
* </saml:AttributeStatement>
* </saml:Assertion>
*
* Reference - en.wikipedia.org/wiki/SAML_2.0#SAML_2.0_Assertions
* @param doc The Document which comprises SAML 2 assertion.
* @param crypto Crypto properties.
* @param data The RST data and other configuration information.
* @return OpenSAML representation of an Assertion.
* @throws TrustException If an error occurred while creating the Assertion.
*/
protected Assertion buildAssertion(Document doc, Crypto crypto, RahasData data) throws TrustException {
//Build the assertion
Assertion assertion = SAML2Utils.createAssertion();
Issuer issuer = SAML2Utils.createIssuer(this.tokenIssuerConfiguration.getIssuerName());
assertion.setIssuer(issuer);
// Validity period
DateTime creationDate = new DateTime();
DateTime expirationDate = new DateTime(creationDate.getMillis() + tokenIssuerConfiguration.getTtl());
data.setAssertionCreatedDate(creationDate.toDate());
data.setAssertionExpiringDate(expirationDate.toDate());
// Set the issued time.
assertion.setIssueInstant(creationDate);
// These variables are used to build the trust assertion
Conditions conditions = SAML2Utils.createConditions(creationDate, expirationDate);
assertion.setConditions(conditions);
// Create the subject
Subject subject;
if (!data.getKeyType().endsWith(RahasConstants.KEY_TYPE_BEARER)) {
subject = createSubjectWithHolderOfKeySubjectConfirmation(doc, crypto,
creationDate, expirationDate, data);
} else {
subject = createSubjectWithBearerSubjectConfirmation(data);
}
// Set the subject
assertion.setSubject(subject);
// If a SymmetricKey is used build an attr stmt, if a public key is build an authn stmt.
if (isSymmetricKeyBasedHoK) {
AttributeStatement attrStmt = createAttributeStatement(data);
assertion.getAttributeStatements().add(attrStmt);
} else {
AuthnStatement authStmt = createAuthenticationStatement(data);
assertion.getAuthnStatements().add(authStmt);
if (data.getClaimDialect() != null && data.getClaimElem() != null) {
assertion.getAttributeStatements().add(createAttributeStatement(data));
}
}
return assertion;
}
/**
* This method will create a SAML 2 subject based on Holder of Key confirmation method.
* The relevant XML would look as follows,
* <saml2:Subject>
* <saml2:NameID>
* ...
* </saml2:NameID>
* <saml2:SubjectConfirmation
* Method="urn:oasis:names:tc:SAML:2.0:cm:holder-of-key">
* <saml2:SubjectConfirmationData
* xsi:type="saml2:KeyInfoConfirmationDataType">
* <ds:KeyInfo>
* <ds:KeyValue>...</ds:KeyValue>
* </ds:KeyInfo>
* </saml2:SubjectConfirmationData>
* </saml2:SubjectConfirmation>
* </saml2:Subject>
*
* KeyInfo can be created based on public key or symmetric key. That is decided by looking at
* the RahasData.getKeyType. TODO make sure this implementation is correct.
* Theoretically we should be able to have many subject confirmation methods in a SAML2 subject.
* TODO - Do we need to support that ?
* @param doc The original XML document which we need to include the assertion.
* @param crypto The relevant crypto properties
* @param creationTime The time that assertion was created.
* @param expirationTime The expiring time
* @param data The configuration data relevant request.
* @return OpenSAML representation of the SAML2 object.
* @throws TrustException If an error occurred while creating the subject.
*/
protected Subject createSubjectWithHolderOfKeySubjectConfirmation(Document doc, Crypto crypto,
DateTime creationTime,
DateTime expirationTime, RahasData data)
throws TrustException {
// Create the subject
Subject subject = (Subject)CommonUtil.buildXMLObject(Subject.DEFAULT_ELEMENT_NAME);
// Set the subject name identifier
if (data.getPrincipal() != null) {
setSubjectNamedIdentifierData(subject, data.getPrincipal().getName(), NameID.EMAIL);
}
// Create KeyInfo
KeyInfo keyInfo = createKeyInfo(doc, crypto, data);
//Build the Subject Confirmation
SubjectConfirmation subjectConfirmation
= (SubjectConfirmation)CommonUtil.buildXMLObject(SubjectConfirmation.DEFAULT_ELEMENT_NAME);
//Set the subject Confirmation method
subjectConfirmation.setMethod("urn:oasis:names:tc:SAML:2.0:cm:holder-of-key");
//Build the subject confirmation data element
KeyInfoConfirmationDataType scData = createKeyInfoConfirmationDataType();
//Set the keyInfo element
scData.getKeyInfos().add(keyInfo);
// Set the validity period
scData.setNotBefore(creationTime);
scData.setNotOnOrAfter(expirationTime);
//Set the subject confirmation data
subjectConfirmation.setSubjectConfirmationData(scData);
//set the subject confirmation
subject.getSubjectConfirmations().add(subjectConfirmation);
log.debug("SAML2.0 subject is constructed successfully.");
return subject;
}
private KeyInfoConfirmationDataType createKeyInfoConfirmationDataType() {
XMLObjectBuilderFactory builderFactory = Configuration.getBuilderFactory();
@SuppressWarnings({"unchecked"}) SAMLObjectBuilder<KeyInfoConfirmationDataType> keyInfoSubjectConfirmationDataBuilder =
(SAMLObjectBuilder<KeyInfoConfirmationDataType>) builderFactory.getBuilder
(KeyInfoConfirmationDataType.TYPE_NAME);
//Build the subject confirmation data element
return keyInfoSubjectConfirmationDataBuilder.
buildObject(SubjectConfirmationData.DEFAULT_ELEMENT_NAME, KeyInfoConfirmationDataType.TYPE_NAME);
}
/**
* This method creates a subject element with the bearer subject confirmation method.
* <saml:Subject>
* <saml:NameIdentifier
* NameQualifier="www.example.com"
* Format="urn:oasis:names:tc:SAML:1.1:nameid-
* format:X509SubjectName">
* uid=joe,ou=people,ou=saml-demo,o=baltimore.com
* </saml:NameIdentifier>
* <saml:SubjectConfirmation>
* <saml:ConfirmationMethod>
* urn:oasis:names:tc:SAML:1.0:cm:bearer
* </saml:ConfirmationMethod>
* </saml:SubjectConfirmation>
* </saml:Subject>
* @param data RahasData element
* @return SAML 2.0 Subject element with Bearer subject confirmation
* @throws org.apache.rahas.TrustException if an error occurred while creating the subject.
*/
protected Subject createSubjectWithBearerSubjectConfirmation(RahasData data) throws TrustException {
// Create the subject
Subject subject = (Subject)CommonUtil.buildXMLObject(Subject.DEFAULT_ELEMENT_NAME);
//Create NameID and attach it to the subject
setSubjectNamedIdentifierData(subject, data.getPrincipal().getName(), NameID.EMAIL);
//Build the Subject Confirmation
SubjectConfirmation subjectConfirmation
= (SubjectConfirmation)CommonUtil.buildXMLObject(SubjectConfirmation.DEFAULT_ELEMENT_NAME);
//Set the subject Confirmation method
subjectConfirmation.setMethod("urn:oasis:names:tc:SAML:2.0:cm:bearer");
subject.getSubjectConfirmations().add(subjectConfirmation);
return subject;
}
/**
* This method signs the given assertion with issuer's private key.
* @param document The original RST document.
* @param assertion Assertion to be signed.
* @param crypto The cryptographic properties.
* @return The signed assertion.
* @throws TrustException If an error occurred while signing the assertion.
*/
protected Assertion signAssertion(Document document, Assertion assertion, Crypto crypto) throws TrustException {
// Create a SignKeyHolder to hold the crypto objects that are used to sign the assertion
SignKeyHolder signKeyHolder = createSignKeyHolder(crypto);
// Build the signature object and set the credentials.
Signature signature = (Signature) CommonUtil.buildXMLObject(Signature.DEFAULT_ELEMENT_NAME);
signature.setSigningCredential(signKeyHolder);
signature.setSignatureAlgorithm(signKeyHolder.getSignatureAlgorithm());
signature.setCanonicalizationAlgorithm(Canonicalizer.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);
//Build the KeyInfo element and set the certificate
try {
KeyInfo keyInfo = (KeyInfo) CommonUtil.buildXMLObject(KeyInfo.DEFAULT_ELEMENT_NAME);
X509Data x509Data = (X509Data) CommonUtil.buildXMLObject(X509Data.DEFAULT_ELEMENT_NAME);
org.opensaml.xml.signature.X509Certificate cert
= (org.opensaml.xml.signature.X509Certificate) CommonUtil.buildXMLObject
(org.opensaml.xml.signature.X509Certificate.DEFAULT_ELEMENT_NAME);
String value
= org.apache.xml.security.utils.Base64.encode(signKeyHolder.getEntityCertificate().getEncoded());
cert.setValue(value);
x509Data.getX509Certificates().add(cert);
keyInfo.getX509Datas().add(x509Data);
signature.setKeyInfo(keyInfo);
assertion.setSignature(signature);
signatureList.add(signature);
//Marshall and Sign
MarshallerFactory marshallerFactory = org.opensaml.xml.Configuration.getMarshallerFactory();
Marshaller marshaller = marshallerFactory.getMarshaller(assertion);
marshaller.marshall(assertion, document);
Signer.signObjects(signatureList);
} catch (CertificateEncodingException e) {
throw new TrustException("Error in setting the signature", e);
} catch (SignatureException e) {
throw new TrustException("errorSigningAssertion", e);
} catch (MarshallingException e) {
throw new TrustException("errorMarshallingAssertion", e);
}
log.debug("SAML2.0 assertion is marshalled and signed..");
return assertion;
}
/**
* This method is used to create SignKeyHolder instances that contains the credentials required for signing the
* assertion
* @param crypto The crypto properties associated with the issuer.
* @return SignKeyHolder object.
* @throws TrustException If an error occurred while creating SignKeyHolder object.
*/
private SignKeyHolder createSignKeyHolder(Crypto crypto) throws TrustException {
SignKeyHolder signKeyHolder = new SignKeyHolder();
try {
X509Certificate[] issuerCerts = CommonUtil.getCertificatesByAlias(crypto,
this.tokenIssuerConfiguration.getIssuerKeyAlias());
String sigAlgo = XMLSignature.ALGO_ID_SIGNATURE_RSA;
String pubKeyAlgo = issuerCerts[0].getPublicKey().getAlgorithm();
if (pubKeyAlgo.equalsIgnoreCase("DSA")) {
sigAlgo = XMLSignature.ALGO_ID_SIGNATURE_DSA;
}
java.security.Key issuerPK = crypto.getPrivateKey(
this.tokenIssuerConfiguration.getIssuerKeyAlias(),
this.tokenIssuerConfiguration.getIssuerKeyPassword());
signKeyHolder.setIssuerCerts(issuerCerts);
signKeyHolder.setIssuerPK((PrivateKey) issuerPK);
signKeyHolder.setSignatureAlgorithm(sigAlgo);
} catch (Exception e) {
throw new TrustException("Error creating issuer signature");
}
log.debug("SignKeyHolder object is created with the credentials..");
return signKeyHolder;
}
/**
* This method creates an AttributeStatement. The relevant XML would look like as follows,
* <saml:AttributeStatement>
* <saml:Attribute
* xmlns:x500="urn:oasis:names:tc:SAML:2.0:profiles:attribute:X500"
* x500:Encoding="LDAP"
* NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
* Name="urn:oid:1.3.6.1.4.1.5923.1.1.1.1"
* FriendlyName="eduPersonAffiliation">
* <saml:AttributeValue
* xsi:type="xs:string">member</saml:AttributeValue>
* <saml:AttributeValue
* xsi:type="xs:string">staff</saml:AttributeValue>
* </saml:Attribute>
* </saml:AttributeStatement>
* Reference - http://en.wikipedia.org/wiki/SAML_2.0#SAML_2.0_Assertions
* @param data The RahasData which carry information about RST.
* @return An AttributeStatement with filled attributes retrieved by calling callback class.
* @throws TrustException If an error occurred while creating the AttributeStatement.
*/
protected AttributeStatement createAttributeStatement(RahasData data) throws TrustException {
AttributeStatement attributeStatement
= (AttributeStatement) CommonUtil.buildXMLObject(AttributeStatement.DEFAULT_ELEMENT_NAME);
Attribute[] attributes;
SAMLCallbackHandler handler = CommonUtil.getSAMLCallbackHandler(this.tokenIssuerConfiguration, data);
SAMLAttributeCallback cb = new SAMLAttributeCallback(data);
if (handler != null) {
try {
handler.handle(cb);
} catch (SAMLException e) {
throw new TrustException(
"errorCallingSAMLCallback",
e);
}
attributes = cb.getSAML2Attributes();
} else { //else add the attribute with a default value
log.debug("No callback registered to get attributes ... Using default attributes");
// TODO do we need to remove this ?
Attribute attribute = (Attribute) CommonUtil.buildXMLObject(Attribute.DEFAULT_ELEMENT_NAME);
attribute.setName("Name");
attribute.setNameFormat("urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified");
XMLObjectBuilderFactory builderFactory = Configuration.getBuilderFactory();
XSStringBuilder attributeValueBuilder = (XSStringBuilder) builderFactory
.getBuilder(XSString.TYPE_NAME);
XSString stringValue = attributeValueBuilder.buildObject(
AttributeValue.DEFAULT_ELEMENT_NAME, XSString.TYPE_NAME);
stringValue.setValue("Colombo/Rahas");
attribute.getAttributeValues().add(stringValue);
attributes = new Attribute[1];
attributes[0] = attribute;
}
//add attributes to the attribute statement
attributeStatement.getAttributes().addAll(Arrays.asList(attributes));
log.debug("SAML2.0 attribute statement is constructed successfully.");
return attributeStatement;
}
/**
* This method creates an authentication statement. The equivalent XML would look as follows,
* <saml:AuthnStatement
* AuthnInstant="2004-12-05T09:22:00Z"
* SessionIndex="b07b804c-7c29-ea16-7300-4f3d6f7928ac">
* <saml:AuthnContext>
* <saml:AuthnContextClassRef>
* urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport
* </saml:AuthnContextClassRef>
* </saml:AuthnContext>
* </saml:AuthnStatement>
* @param data The RahasData which carry information about RST.
* @return OpenSAML representation of an AuthnStatement class.
* @throws TrustException If an error occurred while creating the authentication statement.
*/
protected AuthnStatement createAuthenticationStatement(RahasData data) throws TrustException {
MessageContext inMsgCtx = data.getInMessageContext();
//build the auth stmt
AuthnStatement authenticationStatement
= (AuthnStatement)CommonUtil.buildXMLObject(AuthnStatement.DEFAULT_ELEMENT_NAME);
// set the authn instance
// TODO do we need to use the same time as specified in the conditions ?
authenticationStatement.setAuthnInstant(new DateTime());
// Create authentication context
AuthnContext authContext = (AuthnContext)CommonUtil.buildXMLObject(AuthnContext.DEFAULT_ELEMENT_NAME);
// Create authentication context class reference
AuthnContextClassRef authCtxClassRef
= (AuthnContextClassRef)CommonUtil.buildXMLObject(AuthnContextClassRef.DEFAULT_ELEMENT_NAME);
//if username/password based authn
if (inMsgCtx.getProperty(RahasConstants.USERNAME) != null) {
authCtxClassRef.setAuthnContextClassRef(AuthnContext.PASSWORD_AUTHN_CTX);
} else if (inMsgCtx.getProperty(RahasConstants.X509_CERT) != null) { //if X.509 cert based authn
authCtxClassRef.setAuthnContextClassRef(AuthnContext.X509_AUTHN_CTX);
}
authContext.setAuthnContextClassRef(authCtxClassRef);
authenticationStatement.setAuthnContext(authContext);
log.debug("SAML2.0 authentication statement is constructed successfully.");
return authenticationStatement;
}
/**
* This method will set the subject principal details to the given subject.
* @param subject The subject.
* @param subjectNameId Subject name id, to identify the principal
* @param format Format of the subjectNameId, i.e. email, x509subject etc ...
* @throws TrustException If an error occurred while building NameID.
*/
protected static void setSubjectNamedIdentifierData(Subject subject, String subjectNameId, String format)
throws TrustException {
//Create NameID and attach it to the subject
NameID nameID = SAML2Utils.createNamedIdentifier(subjectNameId, format);
subject.setNameID(nameID);
}
/**
* This method creates the KeyInfo relevant for the assertion. The KeyInfo could be created in 2 ways.
* 1. Using symmetric key - KeyInfo is created using a symmetric key
* 2. Using a public key - KeyInfo created using a public key
* The methodology is decided by looking at RahasData.getKeyType() method.
* @param doc The document which we are processing.
* @param crypto Includes crypto properties relevant to issuer.
* @param data Includes metadata about the RST.
* @return OpenSAML representation of KeyInfo.
* @throws TrustException If an error occurred while creating the KeyInfo object.
*/
protected KeyInfo createKeyInfo(Document doc, Crypto crypto, RahasData data)
throws TrustException {
KeyInfo keyInfo;
// If it is a Symmetric Key
if (data.getKeyType().endsWith(RahasConstants.KEY_TYPE_SYMM_KEY)) {
isSymmetricKeyBasedHoK = true;
X509Certificate serviceCert = null;
try {
// Get AppliesTo to figure out which service to issue the token
// for
serviceCert = this.tokenIssuerConfiguration.getServiceCert(crypto, data.getAppliesToAddress());
keyInfo = CommonUtil.getSymmetricKeyBasedKeyInfo(doc, data, serviceCert, data.getKeySize(), crypto,
tokenIssuerConfiguration.getKeyComputation());
} catch (Exception e) {
if (serviceCert != null) {
throw new TrustException(
"errorInBuildingTheEncryptedKeyForPrincipal",
new String[]{serviceCert.getSubjectDN().getName()},
e);
} else {
throw new TrustException(
"errorInBuildingTheEncryptedKeyForPrincipal",
new String[]{"UnknownSubjectDN"},
e);
}
}
} else if (data.getKeyType().endsWith(RahasConstants.KEY_TYPE_PUBLIC_KEY)) { // If it is a public Key
try {
// Create the ds:KeyValue element with the ds:X509Data
X509Certificate clientCert = data.getClientCert();
if (clientCert == null) {
// TODO are we always looking up by alias ? Dont we need to lookup by any other attribute ?
clientCert = CommonUtil.getCertificateByAlias(crypto, data.getPrincipal().getName());
}
keyInfo = CommonUtil.getCertificateBasedKeyInfo(clientCert);
} catch (Exception e) {
throw new TrustException("samlAssertionCreationError", e);
}
} else {
log.error("Unidentified key type " + data.getKeyType());
throw new TrustException(
"unidentifiedKeyType",
new String[]{data.getKeyType()});
}
return keyInfo;
}
/**
* @inheritDoc
*/
public String getResponseAction(RahasData data) throws TrustException {
return null;
}
/**
* @inheritDoc
*/
public void setConfigurationFile(String configFile) {
this.configFile = configFile;
}
/**
* @inheritDoc
*/
public void setConfigurationElement(OMElement configElement) {
this.configElement = configElement;
}
/**
* @inheritDoc
*/
public void setConfigurationParamName(String configParamName) {
this.configParamName = configParamName;
}
}