blob: ea071a549a9dc445bee286a44b74b7b4c0aa5340 [file] [log] [blame]
/*
* Copyright 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.util;
import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.om.dom.DOMMetaFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.rahas.RahasConstants;
import org.apache.rahas.TrustException;
import org.apache.ws.security.*;
import org.apache.ws.security.components.crypto.Crypto;
import org.apache.ws.security.util.Base64;
import org.apache.ws.security.util.UUIDGenerator;
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.joda.time.DateTime;
import org.opensaml.Configuration;
import org.opensaml.DefaultBootstrap;
import org.opensaml.common.SAMLVersion;
import org.opensaml.saml2.core.NameID;
import org.opensaml.saml2.core.*;
import org.opensaml.xml.ConfigurationException;
import org.opensaml.xml.XMLObject;
import org.opensaml.xml.io.*;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.SAXException;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.security.cert.X509Certificate;
import java.util.List;
public class SAML2Utils {
private static final Log log = LogFactory.getLog(SAML2Utils.class);
public static Element getElementFromAssertion(XMLObject xmlObj) throws TrustException {
try {
MarshallerFactory marshallerFactory = org.opensaml.xml.Configuration.getMarshallerFactory();
Marshaller marshaller = marshallerFactory.getMarshaller(xmlObj);
Element assertionElement = marshaller.marshall(xmlObj,
((DOMMetaFactory)OMAbstractFactory.getMetaFactory(OMAbstractFactory.FEATURE_DOM)).newDocumentBuilderFactory().newDocumentBuilder().newDocument());
log.debug("DOM element is created successfully from the OpenSAML2 XMLObject");
return assertionElement;
} catch (Exception e) {
throw new TrustException("Error creating DOM object from the assertion", e);
}
}
/**
* Extract certificates or the key available in the SAMLAssertion
*
* @param elem The element to process.
* @param crypto The crypto properties.
* @param cb Callback class to get the Key
* @return the SAML2 Key Info
* @throws org.apache.ws.security.WSSecurityException If an error occurred while extracting KeyInfo.
*
*/
public static SAML2KeyInfo getSAML2KeyInfo(Element elem, Crypto crypto,
CallbackHandler cb) throws WSSecurityException {
Assertion assertion;
//build the assertion by unmarhalling the DOM element.
try {
DefaultBootstrap.bootstrap();
String keyInfoElementString = elem.toString();
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
documentBuilderFactory.setNamespaceAware(true);
DocumentBuilder docBuilder = documentBuilderFactory.newDocumentBuilder();
Document document = docBuilder.parse(new ByteArrayInputStream(keyInfoElementString.trim().getBytes()));
Element element = document.getDocumentElement();
UnmarshallerFactory unmarshallerFactory = Configuration
.getUnmarshallerFactory();
Unmarshaller unmarshaller = unmarshallerFactory
.getUnmarshaller(element);
assertion = (Assertion) unmarshaller
.unmarshall(element);
}
catch (ConfigurationException e) {
throw new WSSecurityException(
WSSecurityException.FAILURE, "Failure in bootstrapping", null, e);
} catch (UnmarshallingException e) {
throw new WSSecurityException(
WSSecurityException.FAILURE, "Failure in unmarshelling the assertion", null, e);
} catch (IOException e) {
throw new WSSecurityException(
WSSecurityException.FAILURE, "Failure in unmarshelling the assertion", null, e);
} catch (SAXException e) {
throw new WSSecurityException(
WSSecurityException.FAILURE, "Failure in unmarshelling the assertion", null, e);
} catch (ParserConfigurationException e) {
throw new WSSecurityException(
WSSecurityException.FAILURE, "Failure in unmarshelling the assertion", null, e);
}
return getSAML2KeyInfo(assertion, crypto, cb);
}
public static SAML2KeyInfo getSAML2KeyInfo(Assertion assertion, Crypto crypto,
CallbackHandler cb) throws WSSecurityException {
//First ask the cb whether it can provide the secret
WSPasswordCallback pwcb = new WSPasswordCallback(assertion.getID(), WSPasswordCallback.CUSTOM_TOKEN);
if (cb != null) {
try {
cb.handle(new Callback[]{pwcb});
} catch (Exception e1) {
throw new WSSecurityException(WSSecurityException.FAILURE, "noKey",
new Object[]{assertion.getID()}, e1);
}
}
byte[] key = pwcb.getKey();
if (key != null) {
return new SAML2KeyInfo(assertion, key);
} else {
// if the cb fails to provide the secret.
try {
// extract the subject
Subject samlSubject = assertion.getSubject();
if (samlSubject == null) {
throw new WSSecurityException(WSSecurityException.FAILURE,
"invalidSAML2Token", new Object[]{"for Signature (no Subject)"});
}
// extract the subject confirmation element from the subject
SubjectConfirmation subjectConf = samlSubject.getSubjectConfirmations().get(0);
if (subjectConf == null) {
throw new WSSecurityException(WSSecurityException.FAILURE,
"invalidSAML2Token", new Object[]{"for Signature (no Subject Confirmation)"});
}
// Get the subject confirmation data, KeyInfoConfirmationDataType extends SubjectConfirmationData.
SubjectConfirmationData scData = subjectConf.getSubjectConfirmationData();
if (scData == null) {
throw new WSSecurityException(WSSecurityException.FAILURE,
"invalidSAML2Token", new Object[]{"for Signature (no Subject Confirmation Data)"});
}
// Get the SAML specific XML representation of the keyInfo object
XMLObject KIElem = null;
List<XMLObject> scDataElements = scData.getOrderedChildren();
for (XMLObject xmlObj : scDataElements) {
if (xmlObj instanceof org.opensaml.xml.signature.KeyInfo) {
KIElem = xmlObj;
break;
}
}
Element keyInfoElement;
// Generate a DOM element from the XMLObject.
if (KIElem != null) {
MarshallerFactory marshallerFactory = org.opensaml.xml.Configuration.getMarshallerFactory();
Marshaller marshaller = marshallerFactory.getMarshaller(KIElem);
try {
keyInfoElement = marshaller.marshall(KIElem,
((DOMMetaFactory)OMAbstractFactory.getMetaFactory(OMAbstractFactory.FEATURE_DOM)).newDocumentBuilderFactory().newDocumentBuilder().newDocument());
} catch (ParserConfigurationException ex) {
// We never get here
throw new Error(ex);
}
} else {
throw new WSSecurityException(WSSecurityException.FAILURE,
"invalidSAML2Token", new Object[]{"for Signature (no key info element)"});
}
AttributeStatement attrStmt = assertion.getAttributeStatements().size() != 0 ?
assertion.getAttributeStatements().get(0) : null;
AuthnStatement authnStmt = assertion.getAuthnStatements().size() != 0 ?
assertion.getAuthnStatements().get(0) : null;
// if an attr stmt is present, then it has a symmetric key.
if (attrStmt != null) {
NodeList children = keyInfoElement.getChildNodes();
int len = children.getLength();
for (int i = 0; i < len; i++) {
Node child = children.item(i);
if (child.getNodeType() != Node.ELEMENT_NODE) {
continue;
}
QName el = new QName(child.getNamespaceURI(), child.getLocalName());
if (el.equals(WSSecurityEngine.ENCRYPTED_KEY)) {
byte[] secret = CommonUtil.getDecryptedBytes(cb, crypto, child);
return new SAML2KeyInfo(assertion, secret);
} else if (el.equals(new QName(WSConstants.WST_NS, "BinarySecret"))) {
Text txt = (Text) child.getFirstChild();
return new SAML2KeyInfo(assertion, Base64.decode(txt.getData()));
}
}
}
// If an authn stmt is present then it has a public key.
if (authnStmt != null) {
X509Certificate[] certs;
try {
KeyInfo ki = new KeyInfo(keyInfoElement, 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;
return new SAML2KeyInfo(assertion, certs);
}
}
} catch (XMLSecurityException e3) {
throw new WSSecurityException(WSSecurityException.FAILURE,
"invalidSAMLsecurity",
new Object[]{"cannot get certificate (key holder)"}, e3);
}
}
throw new WSSecurityException(WSSecurityException.FAILURE,
"invalidSAMLsecurity",
new Object[]{"cannot get certificate or key "});
} catch (MarshallingException e) {
throw new WSSecurityException(WSSecurityException.FAILURE,
"Failed marshalling the SAML Assertion", null, e);
}
}
}
/**
* Get the subject confirmation method of a SAML 2.0 assertion
*
* @param assertion SAML 2.0 assertion
* @return Subject Confirmation method
*/
public static String getSAML2SubjectConfirmationMethod(Assertion assertion) {
String subjectConfirmationMethod = RahasConstants.SAML20_SUBJECT_CONFIRMATION_HOK;
List<SubjectConfirmation> subjectConfirmations = assertion.getSubject().getSubjectConfirmations();
if (subjectConfirmations.size() > 0) {
subjectConfirmationMethod = subjectConfirmations.get(0).getMethod();
}
return subjectConfirmationMethod;
}
public static Assertion createAssertion() throws TrustException {
try {
Assertion assertion = (Assertion)CommonUtil.buildXMLObject(Assertion.DEFAULT_ELEMENT_NAME);
assertion.setVersion(SAMLVersion.VERSION_20);
// Set an UUID as the ID of an assertion
assertion.setID(UUIDGenerator.getUUID());
return assertion;
} catch (TrustException e) {
throw new TrustException("Unable to create an Assertion object", e);
}
}
public static Issuer createIssuer(String issuerName) throws TrustException {
try {
Issuer issuer = (Issuer)CommonUtil.buildXMLObject(Issuer.DEFAULT_ELEMENT_NAME);
issuer.setValue(issuerName);
return issuer;
} catch (TrustException e) {
throw new TrustException("Unable to create an Issuer object", e);
}
}
public static Conditions createConditions(DateTime creationTime, DateTime expirationTime) throws TrustException {
try {
Conditions conditions = (Conditions)CommonUtil.buildXMLObject(Conditions.DEFAULT_ELEMENT_NAME);
conditions.setNotBefore(creationTime);
conditions.setNotOnOrAfter(expirationTime);
return conditions;
} catch (TrustException e) {
throw new TrustException("Unable to create an Conditions object");
}
}
/**
* Create named identifier.
* @param principalName Name of the subject.
* @param format Format of the subject, whether it is an email, uid etc ...
* @return The NamedIdentifier object.
* @throws org.apache.rahas.TrustException If unable to find the builder.
*/
public static NameID createNamedIdentifier(String principalName, String format) throws TrustException{
NameID nameId = (NameID)CommonUtil.buildXMLObject(NameID.DEFAULT_ELEMENT_NAME);
nameId.setValue(principalName);
nameId.setFormat(format);
return nameId;
}
}