Author: nandana Date: Thu Aug 27 22:06:56 2009 New Revision: 808659 URL: http://svn.apache.org/viewvc?rev=808659&view=rev Log: RAMPART-231 Applying Thilina's patch for SAML 2.0 support. Thanks Thilina
Added: webservices/rampart/branches/java/1_5/modules/rampart-trust/src/main/java/org/apache/rahas/impl/SAML2TokenIssuer.java webservices/rampart/branches/java/1_5/modules/rampart-trust/src/main/java/org/apache/rahas/impl/util/SAML2KeyInfo.java webservices/rampart/branches/java/1_5/modules/rampart-trust/src/main/java/org/apache/rahas/impl/util/SAML2Utils.java webservices/rampart/branches/java/1_5/modules/rampart-trust/src/main/java/org/apache/rahas/impl/util/SAMLUtils.java webservices/rampart/branches/java/1_5/modules/rampart-trust/src/main/java/org/apache/rahas/impl/util/SignKeyHolder.java Added: webservices/rampart/branches/java/1_5/modules/rampart-trust/src/main/java/org/apache/rahas/impl/SAML2TokenIssuer.java URL: http://svn.apache.org/viewvc/webservices/rampart/branches/java/1_5/modules/rampart-trust/src/main/java/org/apache/rahas/impl/SAML2TokenIssuer.java?rev=808659&view=auto ============================================================================== --- webservices/rampart/branches/java/1_5/modules/rampart-trust/src/main/java/org/apache/rahas/impl/SAML2TokenIssuer.java (added) +++ webservices/rampart/branches/java/1_5/modules/rampart-trust/src/main/java/org/apache/rahas/impl/SAML2TokenIssuer.java Thu Aug 27 22:06:56 2009 @@ -0,0 +1,745 @@ +/* + * 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.rahas.*; +import org.apache.rahas.TrustException; +import org.apache.rahas.impl.util.SignKeyHolder; +import org.apache.rahas.impl.util.SAMLAttributeCallback; +import org.apache.rahas.impl.util.SAMLCallbackHandler; +import org.apache.axiom.soap.SOAPEnvelope; +import org.apache.axiom.om.OMElement; +import org.apache.axiom.om.OMNode; +import org.apache.axiom.om.util.UUIDGenerator; +import org.apache.axiom.om.impl.dom.jaxp.DocumentBuilderFactoryImpl; +import org.apache.axis2.context.MessageContext; +import org.apache.axis2.description.Parameter; +import org.apache.ws.security.components.crypto.Crypto; +import org.apache.ws.security.components.crypto.CryptoFactory; +import org.apache.ws.security.message.WSSecEncryptedKey; +import org.apache.ws.security.WSConstants; +import org.apache.ws.security.WSSecurityException; +import org.apache.ws.security.util.Base64; +import org.apache.ws.security.util.XmlSchemaDateFormat; +import org.apache.xml.security.utils.EncryptionConstants; +import org.apache.xml.security.c14n.Canonicalizer; +import org.apache.xml.security.signature.XMLSignature; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.opensaml.*; +import org.opensaml.Configuration; +import org.opensaml.saml1.core.NameIdentifier; +import org.opensaml.xml.*; +import org.opensaml.xml.schema.impl.XSStringBuilder; +import org.opensaml.xml.schema.XSString; +import org.opensaml.xml.security.x509.X509Credential; +import org.opensaml.xml.signature.*; +import org.opensaml.xml.io.*; +import org.opensaml.common.SAMLVersion; +import org.opensaml.common.SAMLObjectBuilder; +import org.opensaml.common.xml.SAMLConstants; +import org.opensaml.saml2.core.impl.AssertionBuilder; +import org.opensaml.saml2.core.impl.IssuerBuilder; +import org.opensaml.saml2.core.impl.NameIDBuilder; +import org.opensaml.saml2.core.impl.SubjectBuilder; +import org.opensaml.saml2.core.*; +import org.opensaml.saml2.metadata.EntitiesDescriptor; +import org.joda.time.DateTime; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Text; +import org.w3c.dom.Node; +import org.w3c.dom.ls.DOMImplementationLS; +import org.w3c.dom.ls.LSOutput; +import org.w3c.dom.ls.LSSerializer; +import org.w3c.dom.bootstrap.DOMImplementationRegistry; + +import javax.xml.namespace.QName; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.DocumentBuilder; +import java.util.Date; +import java.util.List; +import java.util.ArrayList; +import java.util.Arrays; +import java.security.cert.X509Certificate; +import java.security.cert.CertificateEncodingException; +import java.security.PrivateKey; +import java.text.DateFormat; +import java.io.InputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; + +public class SAML2TokenIssuer implements TokenIssuer { + + private Assertion SAMLAssertion; + + private String configParamName; + + private OMElement configElement; + + private String configFile; + + protected List<Signature> signatureList = new ArrayList<Signature>(); + + private boolean isSymmetricKeyBasedHoK = false; + + private Log log = LogFactory.getLog(SAML2TokenIssuer.class); + + public SOAPEnvelope issue(RahasData data) throws TrustException { + MessageContext inMsgCtx = data.getInMessageContext(); + + try { + SAMLTokenIssuerConfig config = null; + if (this.configElement != null) { + config = new SAMLTokenIssuerConfig(configElement + .getFirstChildWithName(SAMLTokenIssuerConfig.SAML_ISSUER_CONFIG)); + } + + // Look for the file + if (config == null && this.configFile != null) { + config = new SAMLTokenIssuerConfig(this.configFile); + //config = new SAMLTokenIssuerConfig("/home/thilina/Desktop/saml-issuer-config.xml"); + } + + // Look for the param + if (config == null && this.configParamName != null) { + Parameter param = inMsgCtx.getParameter(this.configParamName); + if (param != null && param.getParameterElement() != null) { + config = new SAMLTokenIssuerConfig(param + .getParameterElement().getFirstChildWithName( + SAMLTokenIssuerConfig.SAML_ISSUER_CONFIG)); + } else { + throw new TrustException("expectedParameterMissing", + new String[]{this.configParamName}); + } + } + + if (config == null) { + throw new TrustException("configurationIsNull"); + } + + SOAPEnvelope env = TrustUtil.createSOAPEnvelope(inMsgCtx + .getEnvelope().getNamespace().getNamespaceURI()); + + Crypto crypto; + if (config.cryptoElement != null) { // crypto props + // defined as + // elements + crypto = CryptoFactory.getInstance(TrustUtil + .toProperties(config.cryptoElement), inMsgCtx + .getAxisService().getClassLoader()); + } else { // crypto props defined in a properties file + crypto = CryptoFactory.getInstance(config.cryptoPropertiesFile, + 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(); + String keyType = data.getKeyType(); + + keySize = (keySize == -1) ? config.keySize : keySize; + + // Set the "javax.xml.parsers.DocumentBuilderFactory" sys. property to the endorsed JAMP impl. + String property = System.getProperty("javax.xml.parsers.DocumentBuilderFactory"); + System.setProperty("javax.xml.parsers.DocumentBuilderFactory", "org.apache.xerces.jaxp.DocumentBuilderFactoryImpl"); + + + //start building SAML 2.0 token + DefaultBootstrap.bootstrap(); + + //Build the assertion + AssertionBuilder assertionBuilder = new AssertionBuilder(); + Assertion assertion = assertionBuilder.buildObject(); + assertion.setVersion(SAMLVersion.VERSION_20); + + // Set an UUID as the ID of an assertion + assertion.setID(UUIDGenerator.getUUID()); + + //Set the issuer + IssuerBuilder issuerBuilder = new IssuerBuilder(); + Issuer issuer = issuerBuilder.buildObject(); + issuer.setValue(config.issuerName); + assertion.setIssuer(issuer); + + // Set the issued time. + assertion.setIssueInstant(new DateTime()); + + // Validity period + DateTime creationDate = new DateTime(); + DateTime expirationDate = new DateTime(creationDate.getMillis() + config.ttl); + + // These variables are used to build the trust assertion + Date creationTime = creationDate.toDate(); + Date expirationTime = expirationDate.toDate(); + + // Create the subject + Subject subject = createSubject(config, doc, crypto, creationDate, expirationDate, 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, config); + assertion.getAttributeStatements().add(attrStmt); + } else { + AuthnStatement authStmt = createAuthnStatement(data); + assertion.getAuthnStatements().add(authStmt); + } + + // Create a SignKeyHolder to hold the crypto objects that are used to sign the assertion + SignKeyHolder signKeyHolder = createSignKeyHolder(config, crypto); + + // Sign the assertion + assertion = setSignature(assertion, signKeyHolder); + + + OMElement rstrElem; + int wstVersion = data.getVersion(); + if (RahasConstants.VERSION_05_02 == wstVersion) { + rstrElem = TrustUtil.createRequestSecurityTokenResponseElement( + wstVersion, env.getBody()); + } else { + OMElement rstrcElem = TrustUtil + .createRequestSecurityTokenResponseCollectionElement( + wstVersion, env.getBody()); + rstrElem = TrustUtil.createRequestSecurityTokenResponseElement( + wstVersion, rstrcElem); + } + + TrustUtil.createTokenTypeElement(wstVersion, rstrElem).setText( + RahasConstants.TOK_TYPE_SAML_20); + + if (keyType.endsWith(RahasConstants.KEY_TYPE_SYMM_KEY)) { + TrustUtil.createKeySizeElement(wstVersion, rstrElem, keySize); + } + + if (config.addRequestedAttachedRef) { + TrustUtil.createRequestedAttachedRef(wstVersion, rstrElem, "#" + + assertion.getID(), RahasConstants.TOK_TYPE_SAML_20); + } + + if (config.addRequestedUnattachedRef) { + TrustUtil.createRequestedUnattachedRef(wstVersion, rstrElem, + assertion.getID(), RahasConstants.TOK_TYPE_SAML_20); + } + + if (data.getAppliesToAddress() != null) { + TrustUtil.createAppliesToElement(rstrElem, data + .getAppliesToAddress(), data.getAddressingNs()); + } + + // Use GMT time in milliseconds + DateFormat zulu = new XmlSchemaDateFormat(); + + // Add the Lifetime element + TrustUtil.createLifetimeElement(wstVersion, rstrElem, zulu + .format(creationTime), zulu.format(expirationTime)); + + // Create the RequestedSecurityToken element and add the SAML token + // to it + OMElement reqSecTokenElem = TrustUtil + .createRequestedSecurityTokenElement(wstVersion, rstrElem); + Token assertionToken; + + Node tempNode = assertion.getDOM(); + + //Serializing and re-generating the AXIOM element using the DOM Element created using xerces + Element element = assertion.getDOM(); + + ByteArrayOutputStream byteArrayOutputStrm = new ByteArrayOutputStream(); + + DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance(); + + DOMImplementationLS impl = + (DOMImplementationLS) registry.getDOMImplementation("LS"); + + LSSerializer writer = impl.createLSSerializer(); + LSOutput output = impl.createLSOutput(); + output.setByteStream(byteArrayOutputStrm); + writer.write(element, output); + String elementString = byteArrayOutputStrm.toString(); + + DocumentBuilderFactoryImpl.setDOOMRequired(true); + + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + documentBuilderFactory.setNamespaceAware(true); + DocumentBuilder docBuilder = documentBuilderFactory.newDocumentBuilder(); + Document document = docBuilder.parse(new ByteArrayInputStream(elementString.trim().getBytes())); + Element assertionElement = document.getDocumentElement(); + + reqSecTokenElem.addChild((OMNode) ((Element) rstrElem) + .getOwnerDocument().importNode(tempNode, true)); + + // Store the token + assertionToken = new Token(assertion.getID(), + (OMElement) assertionElement, creationTime, + expirationTime); + + // At this point we definitely have the secret + // Otherwise it should fail with an exception earlier + assertionToken.setSecret(data.getEphmeralKey()); + TrustUtil.getTokenStore(inMsgCtx).add(assertionToken); + + if (keyType.endsWith(RahasConstants.KEY_TYPE_SYMM_KEY) + && config.keyComputation != SAMLTokenIssuerConfig.KeyComputation.KEY_COMP_USE_REQ_ENT) { + + // Add the RequestedProofToken + TokenIssuerUtil.handleRequestedProofToken(data, wstVersion, + config, rstrElem, assertionToken, doc); + } + + return env; + + } catch (Exception e) { + e.printStackTrace(); + } + finally { + // Unset the DOM impl to default + DocumentBuilderFactoryImpl.setDOOMRequired(false); + } + + + return null; + } + + /** + * This method is used to create the subject of an assertion + * @param config + * @param doc + * @param crypto + * @param creationTime + * @param expirationTime + * @param data + * @return Subject + * @throws Exception + */ + private Subject createSubject(SAMLTokenIssuerConfig config, + Document doc, Crypto crypto, DateTime creationTime, + DateTime expirationTime, RahasData data) throws Exception { + + + XMLObjectBuilderFactory builderFactory = Configuration.getBuilderFactory(); + SAMLObjectBuilder<Subject> subjectBuilder = + (SAMLObjectBuilder<Subject>) builderFactory.getBuilder(Subject.DEFAULT_ELEMENT_NAME); + Subject subject = subjectBuilder.buildObject(); + Element keyInfoElem = null; + + // If it is a Symmetric Key + if (data.getKeyType().endsWith(RahasConstants.KEY_TYPE_SYMM_KEY)) { + + isSymmetricKeyBasedHoK = true; + Element encryptedKeyElem; + X509Certificate serviceCert = null; + try { + + // Get ApliesTo to figure out which service to issue the token + // for + serviceCert = config.getServiceCert(crypto, data.getAppliesToAddress()); + + // Create the encrypted key + WSSecEncryptedKey encrKeyBuilder = new WSSecEncryptedKey(); + + // Use thumbprint id + encrKeyBuilder + .setKeyIdentifierType(WSConstants.THUMBPRINT_IDENTIFIER); + + // SEt the encryption cert + encrKeyBuilder.setUseThisCert(serviceCert); + + // set keysize + int keysize = data.getKeysize(); + keysize = (keysize != -1) ? keysize : config.keySize; + encrKeyBuilder.setKeySize(keysize); + + encrKeyBuilder.setEphemeralKey(TokenIssuerUtil.getSharedSecret( + data, config.keyComputation, keysize)); + + // Set key encryption algo + encrKeyBuilder + .setKeyEncAlgo(EncryptionConstants.ALGO_ID_KEYTRANSPORT_RSA15); + + // Build + encrKeyBuilder.prepare(doc, crypto); + + // Extract the base64 encoded secret value + byte[] tempKey = new byte[keysize / 8]; + System.arraycopy(encrKeyBuilder.getEphemeralKey(), 0, tempKey, + 0, keysize / 8); + + data.setEphmeralKey(tempKey); + + // Extract the Encryptedkey DOM element + encryptedKeyElem = encrKeyBuilder.getEncryptedKeyElement(); + } catch (WSSecurityException e) { + throw new TrustException( + "errorInBuildingTheEncryptedKeyForPrincipal", + new String[]{serviceCert.getSubjectDN().getName()}, + e); + } + + keyInfoElem = doc.createElementNS(WSConstants.SIG_NS, + "ds:KeyInfo"); + ((OMElement) encryptedKeyElem).declareNamespace(WSConstants.SIG_NS, + WSConstants.SIG_PREFIX); + ((OMElement) encryptedKeyElem).declareNamespace(WSConstants.ENC_NS, + WSConstants.ENC_PREFIX); + + keyInfoElem.appendChild(encryptedKeyElem); + + } + + // If it is a public Key + else { + try { + String subjectNameId = data.getPrincipal().getName(); + + //Create NameID and attach it to the subject + NameIDBuilder nb = new NameIDBuilder(); + NameID nameID = nb.buildObject(); + nameID.setValue(subjectNameId); + nameID.setFormat(NameIdentifier.EMAIL); + subject.setNameID(nameID); + + + // Create the ds:KeyValue element with the ds:X509Data + X509Certificate clientCert = data.getClientCert(); + + if (clientCert == null) { + X509Certificate[] certs = crypto.getCertificates( + data.getPrincipal().getName()); + clientCert = certs[0]; + } + + byte[] clientCertBytes = clientCert.getEncoded(); + + String base64Cert = Base64.encode(clientCertBytes); + + Text base64CertText = doc.createTextNode(base64Cert); + + //----------------------------------------- + + Element x509CertElem = doc.createElementNS(WSConstants.SIG_NS, + "ds:X509Certificate"); + x509CertElem.appendChild(base64CertText); + Element x509DataElem = doc.createElementNS(WSConstants.SIG_NS, + "ds:X509Data"); + x509DataElem.appendChild(x509CertElem); + + + if (x509DataElem != null) { + keyInfoElem = doc.createElementNS(WSConstants.SIG_NS, "ds:KeyInfo"); + ((OMElement) x509DataElem).declareNamespace( + WSConstants.SIG_NS, WSConstants.SIG_PREFIX); + ((OMElement) x509DataElem).declareNamespace( + WSConstants.ENC_NS, WSConstants.ENC_PREFIX); + + keyInfoElem.appendChild(x509DataElem); + } + + } catch (Exception e) { + throw new TrustException("samlAssertionCreationError", e); + } + } + + // Unmarshall the keyInfo DOM element into an XMLObject + String keyInfoElementString = keyInfoElem.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(); + + + // Get appropriate unmarshaller + UnmarshallerFactory unmarshallerFactory = Configuration.getUnmarshallerFactory(); + Unmarshaller unmarshaller = unmarshallerFactory.getUnmarshaller(element); + + // Unmarshall using the document root element, an keyInfo element in this case + XMLObject keyInfoElement = null; + try { + keyInfoElement = unmarshaller.unmarshall(element); + } catch (UnmarshallingException e) { + throw new TrustException("Error unmarshalling KeyInfo Element", e); + } + + + //Build the Subject Confirmation + SAMLObjectBuilder<SubjectConfirmation> subjectConfirmationBuilder = + (SAMLObjectBuilder<SubjectConfirmation>) builderFactory.getBuilder(SubjectConfirmation.DEFAULT_ELEMENT_NAME); + SubjectConfirmation subjectConfirmation = subjectConfirmationBuilder.buildObject(); + + //Set the subject Confirmation method + subjectConfirmation.setMethod("urn:oasis:names:tc:SAML:2.0:cm:holder-of-key"); + + SAMLObjectBuilder<KeyInfoConfirmationDataType> keyInfoSubjectConfirmationDataBuilder = + (SAMLObjectBuilder<KeyInfoConfirmationDataType>) builderFactory.getBuilder(KeyInfoConfirmationDataType.TYPE_NAME); + + //Build the subject confirmation data element + KeyInfoConfirmationDataType scData = keyInfoSubjectConfirmationDataBuilder. + buildObject(SubjectConfirmationData.DEFAULT_ELEMENT_NAME, KeyInfoConfirmationDataType.TYPE_NAME); + + //Set the keyInfo element + scData.getKeyInfos().add(keyInfoElement); + + // 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); + + if(log.isDebugEnabled()){ + log.debug("SAML2.0 subject is constructed successfully."); + } + return subject; + } + + + /** + * This method is used to sign the assertion + * @param assertion + * @param cred + * @return Assertion + * @throws Exception + */ + public Assertion setSignature(Assertion assertion, SignKeyHolder cred) throws Exception{ + + // Build the signature object and set the credentials. + Signature signature = (Signature) buildXMLObject(Signature.DEFAULT_ELEMENT_NAME); + signature.setSigningCredential(cred); + signature.setSignatureAlgorithm(cred.getSignatureAlgorithm()); + signature.setCanonicalizationAlgorithm(Canonicalizer.ALGO_ID_C14N_EXCL_OMIT_COMMENTS); + + //Build the KeyInfo element and set the certificate + try { + KeyInfo keyInfo = (KeyInfo) buildXMLObject(KeyInfo.DEFAULT_ELEMENT_NAME); + X509Data data = (X509Data) buildXMLObject(X509Data.DEFAULT_ELEMENT_NAME); + org.opensaml.xml.signature.X509Certificate cert = (org.opensaml.xml.signature.X509Certificate) buildXMLObject(org.opensaml.xml.signature.X509Certificate.DEFAULT_ELEMENT_NAME); + String value = org.apache.xml.security.utils.Base64.encode(cred.getEntityCertificate().getEncoded()); + cert.setValue(value); + data.getX509Certificates().add(cert); + keyInfo.getX509Datas().add(data); + 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); + org.apache.xml.security.Init.init(); + Signer.signObjects(signatureList); + } catch (CertificateEncodingException e) { + throw new TrustException("Error in setting the signature", e); + } catch (SignatureException e) { + throw new TrustException("errorMarshellingOrSigning", e); + } catch (MarshallingException e) { + throw new TrustException("errorMarshellingOrSigning", e); + } + + if(log.isDebugEnabled()){ + log.debug("SAML2.0 assertion is marshalled and signed.."); + } + + return assertion; + } + + + /** + * This method is used to build the assertion elements + * @param objectQName + * @return + * @throws Exception + */ + protected static XMLObject buildXMLObject(QName objectQName) throws Exception { + XMLObjectBuilder builder = org.opensaml.xml.Configuration.getBuilderFactory().getBuilder(objectQName); + if (builder == null) { + throw new TrustException("Unable to retrieve builder for object QName " + + objectQName); + } + return builder.buildObject(objectQName.getNamespaceURI(), objectQName.getLocalPart(), + objectQName.getPrefix()); + } + + /** + * This method is used to create SignKeyHolder instances that contains the credentials required for signing the + * assertion + * @param config + * @param crypto + * @return + * @throws TrustException + */ + public SignKeyHolder createSignKeyHolder(SAMLTokenIssuerConfig config, Crypto crypto) throws TrustException { + + SignKeyHolder signKeyHolder = new SignKeyHolder(); + + try { + X509Certificate[] issuerCerts = crypto + .getCertificates(config.issuerKeyAlias); + + 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( + config.issuerKeyAlias, config.issuerKeyPassword); + + signKeyHolder.setIssuerCerts(issuerCerts); + signKeyHolder.setIssuerPK((PrivateKey) issuerPK); + signKeyHolder.setSignatureAlgorithm(sigAlgo); + + } catch (Exception e) { + throw new TrustException("Error creating issuer signature"); + } + + if(log.isDebugEnabled()){ + log.debug("SignKeyHolder object is created with the credentials.."); + } + + return signKeyHolder; + } + + /** + * Creates the Attribute Statement + * @param data + * @param config + * @return + * @throws SAMLException + */ + public AttributeStatement createAttributeStatement(RahasData data, SAMLTokenIssuerConfig config) throws SAMLException { + + XMLObjectBuilderFactory builderFactory = Configuration.getBuilderFactory(); + SAMLObjectBuilder<AttributeStatement> attrStmtBuilder = + (SAMLObjectBuilder<AttributeStatement>) builderFactory.getBuilder(AttributeStatement.DEFAULT_ELEMENT_NAME); + + AttributeStatement attrstmt = attrStmtBuilder.buildObject(); + + Attribute[] attributes = null; + + //Call the attribute callback handlers to get any attributes if exists + if (config.getCallbackHander() != null) { + SAMLAttributeCallback cb = new SAMLAttributeCallback(data); + SAMLCallbackHandler handler = config.getCallbackHander(); + handler.handle(cb); + attributes = cb.getSAML2Attributes(); + } + + //else add the attribute with a default value + else { + SAMLObjectBuilder<Attribute> attrBuilder = + (SAMLObjectBuilder<Attribute>) builderFactory.getBuilder(Attribute.DEFAULT_ELEMENT_NAME); + Attribute attribute = attrBuilder.buildObject(); + attribute.setName("Name"); + attribute.setNameFormat("urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified"); + + 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 + attrstmt.getAttributes().addAll(Arrays.asList(attributes)); + + if(log.isDebugEnabled()){ + log.debug("SAML2.0 attribute statement is constructed successfully."); + } + + return attrstmt; + } + + /** + * build the authentication statement + * @param data + * @return + */ + public AuthnStatement createAuthnStatement(RahasData data) { + XMLObjectBuilderFactory builderFactory = Configuration.getBuilderFactory(); + MessageContext inMsgCtx = data.getInMessageContext(); + + SAMLObjectBuilder<AuthnStatement> authStmtBuilder = + (SAMLObjectBuilder<AuthnStatement>) builderFactory.getBuilder(AuthnStatement.DEFAULT_ELEMENT_NAME); + + //build the auth stmt + AuthnStatement authStmt = authStmtBuilder.buildObject(); + + // set the authn instance + authStmt.setAuthnInstant(new DateTime()); + + SAMLObjectBuilder<AuthnContext> authCtxBuilder = + (SAMLObjectBuilder<AuthnContext>) builderFactory.getBuilder(AuthnContext.DEFAULT_ELEMENT_NAME); + AuthnContext authContext = authCtxBuilder.buildObject(); + + SAMLObjectBuilder<AuthnContextClassRef> authCtxClassRefBuilder = + (SAMLObjectBuilder<AuthnContextClassRef>) builderFactory.getBuilder(AuthnContextClassRef.DEFAULT_ELEMENT_NAME); + AuthnContextClassRef authCtxClassRef = authCtxClassRefBuilder.buildObject(); + + //if username/password based authn + if (inMsgCtx.getProperty(RahasConstants.USERNAME) != null) { + authCtxClassRef.setAuthnContextClassRef(AuthnContext.PASSWORD_AUTHN_CTX); + } + //if X.509 cert based authn + else if (inMsgCtx.getProperty(RahasConstants.X509_CERT) != null) { + authCtxClassRef.setAuthnContextClassRef(AuthnContext.X509_AUTHN_CTX); + } + + authContext.setAuthnContextClassRef(authCtxClassRef); + authStmt.setAuthnContext(authContext); + + if(log.isDebugEnabled()){ + log.debug("SAML2.0 authentication statement is constructed successfully."); + } + + return authStmt; + } + + + public String getResponseAction(RahasData data) throws TrustException { + return null; + } + + public void setConfigurationFile(String configFile) { + this.configFile = configFile; + } + + public void setConfigurationElement(OMElement configElement) { + this.configElement = configElement; + } + + public void setConfigurationParamName(String configParamName) { + this.configParamName = configParamName; + } + +} Added: webservices/rampart/branches/java/1_5/modules/rampart-trust/src/main/java/org/apache/rahas/impl/util/SAML2KeyInfo.java URL: http://svn.apache.org/viewvc/webservices/rampart/branches/java/1_5/modules/rampart-trust/src/main/java/org/apache/rahas/impl/util/SAML2KeyInfo.java?rev=808659&view=auto ============================================================================== --- webservices/rampart/branches/java/1_5/modules/rampart-trust/src/main/java/org/apache/rahas/impl/util/SAML2KeyInfo.java (added) +++ webservices/rampart/branches/java/1_5/modules/rampart-trust/src/main/java/org/apache/rahas/impl/util/SAML2KeyInfo.java Thu Aug 27 22:06:56 2009 @@ -0,0 +1,62 @@ +package org.apache.rahas.impl.util; + +import org.opensaml.saml2.core.Assertion; + +import java.security.cert.X509Certificate;/* + * 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. + */ + +/** + * TODO : This class should be moved to WSS4J once a new version of it is avaliable + * This class holds the secrets contained in a SAML2 token. + */ +public class SAML2KeyInfo { + /** + * Certificates + */ + private X509Certificate[] certs; + + /** + * Key bytes (e.g.: held in an encrypted key) + */ + private byte[] secret; + + /** + * SAMLAssertion + */ + Assertion assertion; + + public SAML2KeyInfo(Assertion assertions, X509Certificate[] certs) { + this.certs = certs; + this.assertion = assertions; + } + + public SAML2KeyInfo(Assertion assertions, byte[] secret) { + this.secret = secret; + this.assertion = assertions; + } + + public X509Certificate[] getCerts() { + return certs; + } + + public byte[] getSecret() { + return secret; + } + + public Assertion getAssertion() { + return assertion; + } +} Added: webservices/rampart/branches/java/1_5/modules/rampart-trust/src/main/java/org/apache/rahas/impl/util/SAML2Utils.java URL: http://svn.apache.org/viewvc/webservices/rampart/branches/java/1_5/modules/rampart-trust/src/main/java/org/apache/rahas/impl/util/SAML2Utils.java?rev=808659&view=auto ============================================================================== --- webservices/rampart/branches/java/1_5/modules/rampart-trust/src/main/java/org/apache/rahas/impl/util/SAML2Utils.java (added) +++ webservices/rampart/branches/java/1_5/modules/rampart-trust/src/main/java/org/apache/rahas/impl/util/SAML2Utils.java Thu Aug 27 22:06:56 2009 @@ -0,0 +1,309 @@ +/* + * 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.util; + +import org.apache.axiom.om.impl.dom.jaxp.DocumentBuilderFactoryImpl; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.rahas.TrustException; +import org.apache.ws.security.WSConstants; +import org.apache.ws.security.WSPasswordCallback; +import org.apache.ws.security.WSSecurityEngine; +import org.apache.ws.security.WSSecurityException; +import org.apache.ws.security.components.crypto.Crypto; +import org.apache.ws.security.processor.EncryptedKeyProcessor; +import org.apache.ws.security.util.Base64; +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.opensaml.Configuration; +import org.opensaml.DefaultBootstrap; +import org.opensaml.saml2.core.*; +import org.opensaml.xml.ConfigurationException; +import org.opensaml.xml.XMLObject; +import org.opensaml.xml.io.*; +import org.w3c.dom.*; +import org.w3c.dom.bootstrap.DOMImplementationRegistry; +import org.w3c.dom.ls.DOMImplementationLS; +import org.w3c.dom.ls.LSOutput; +import org.w3c.dom.ls.LSSerializer; +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.ByteArrayOutputStream; +import java.io.IOException; +import java.security.cert.X509Certificate; + +public class SAML2Utils { + + private static final Log log = LogFactory.getLog(SAML2Utils.class); + + public static Element getElementFromAssertion(XMLObject xmlObj) throws TrustException { + try { + + String jaxpProperty = System.getProperty("javax.xml.parsers.DocumentBuilderFactory"); + System.setProperty("javax.xml.parsers.DocumentBuilderFactory", "org.apache.xerces.jaxp.DocumentBuilderFactoryImpl"); + + MarshallerFactory marshallerFactory = org.opensaml.xml.Configuration.getMarshallerFactory(); + Marshaller marshaller = marshallerFactory.getMarshaller(xmlObj); + Element element = marshaller.marshall(xmlObj); + + // Reset the sys. property to its previous value. + if (jaxpProperty == null) { + System.getProperties().remove("javax.xml.parsers.DocumentBuilderFactory"); + } else { + System.setProperty("javax.xml.parsers.DocumentBuilderFactory", jaxpProperty); + } + + ByteArrayOutputStream byteArrayOutputStrm = new ByteArrayOutputStream(); + + DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance(); + + DOMImplementationLS impl = + (DOMImplementationLS) registry.getDOMImplementation("LS"); + + LSSerializer writer = impl.createLSSerializer(); + LSOutput output = impl.createLSOutput(); + output.setByteStream(byteArrayOutputStrm); + writer.write(element, output); + String elementString = byteArrayOutputStrm.toString(); + + DocumentBuilderFactoryImpl.setDOOMRequired(true); + + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + documentBuilderFactory.setNamespaceAware(true); + DocumentBuilder docBuilder = documentBuilderFactory.newDocumentBuilder(); + Document document = docBuilder.parse(new ByteArrayInputStream(elementString.trim().getBytes())); + Element assertionElement = document.getDocumentElement(); + DocumentBuilderFactoryImpl.setDOOMRequired(false); + + if (log.isDebugEnabled()) { + 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 + * @return the SAML2 Key Info + * @throws org.apache.ws.security.WSSecurityException + * + */ + 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 = (SubjectConfirmation) 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. + KeyInfoConfirmationDataType scData = (KeyInfoConfirmationDataType) 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 = scData.getKeyInfos() != null ? (XMLObject) scData.getKeyInfos().get(0) : null; + + Element keyInfoElement; + + // Generate a DOM element from the XMLObject. + if (KIElem != null) { + + // Set the "javax.xml.parsers.DocumentBuilderFactory" system property to make sure the endorsed JAXP + // implementation is picked over the default jaxp impl shipped with the JDK. + String jaxpProperty = System.getProperty("javax.xml.parsers.DocumentBuilderFactory"); + System.setProperty("javax.xml.parsers.DocumentBuilderFactory", "org.apache.xerces.jaxp.DocumentBuilderFactoryImpl"); + + MarshallerFactory marshallerFactory = org.opensaml.xml.Configuration.getMarshallerFactory(); + Marshaller marshaller = marshallerFactory.getMarshaller(KIElem); + keyInfoElement = marshaller.marshall(KIElem); + + // Reset the sys. property to its previous value. + if (jaxpProperty == null) { + System.getProperties().remove("javax.xml.parsers.DocumentBuilderFactory"); + } else { + System.setProperty("javax.xml.parsers.DocumentBuilderFactory", jaxpProperty); + } + + } else { + throw new WSSecurityException(WSSecurityException.FAILURE, + "invalidSAML2Token", new Object[]{"for Signature (no key info element)"}); + } + + AttributeStatement attrStmt = assertion.getAttributeStatements().size() != 0 ? + (AttributeStatement) assertion.getAttributeStatements().get(0) : null; + AuthnStatement authnStmt = assertion.getAuthnStatements().size() != 0 ? + (AuthnStatement) 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)) { + + EncryptedKeyProcessor proc = new EncryptedKeyProcessor(); + proc.handleEncryptedKey((Element) child, cb, crypto, null); + + return new SAML2KeyInfo(assertion, proc.getDecryptedBytes()); + } 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 presentm then it has a public key. + else if (authnStmt != null) { + + X509Certificate[] certs = null; + 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); + } + + } else { + throw new WSSecurityException(WSSecurityException.FAILURE, + "invalidSAMLsecurity", + new Object[]{"cannot get certificate or key "}); + } + + + 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); + } + } + } + +} + + Added: webservices/rampart/branches/java/1_5/modules/rampart-trust/src/main/java/org/apache/rahas/impl/util/SAMLUtils.java URL: http://svn.apache.org/viewvc/webservices/rampart/branches/java/1_5/modules/rampart-trust/src/main/java/org/apache/rahas/impl/util/SAMLUtils.java?rev=808659&view=auto ============================================================================== --- webservices/rampart/branches/java/1_5/modules/rampart-trust/src/main/java/org/apache/rahas/impl/util/SAMLUtils.java (added) +++ webservices/rampart/branches/java/1_5/modules/rampart-trust/src/main/java/org/apache/rahas/impl/util/SAMLUtils.java Thu Aug 27 22:06:56 2009 @@ -0,0 +1,30 @@ +package org.apache.rahas.impl.util; + +import org.apache.rahas.impl.SAMLTokenIssuerConfig; +import org.apache.ws.security.components.crypto.Crypto; +import org.apache.ws.security.WSSecurityException; + +import java.security.cert.X509Certificate; +import java.util.Collection; +import java.util.ArrayList; + +public class SAMLUtils { + + + public static Collection<X509Certificate> getCertChainCollection(X509Certificate[] issuerCerts){ + + ArrayList<X509Certificate> certCollection = new ArrayList<X509Certificate>(); + + if (issuerCerts == null) { + return certCollection; + } else { + for (X509Certificate cert : issuerCerts) { + certCollection.add(cert); + } + } + + return certCollection; + + } +} + Added: webservices/rampart/branches/java/1_5/modules/rampart-trust/src/main/java/org/apache/rahas/impl/util/SignKeyHolder.java URL: http://svn.apache.org/viewvc/webservices/rampart/branches/java/1_5/modules/rampart-trust/src/main/java/org/apache/rahas/impl/util/SignKeyHolder.java?rev=808659&view=auto ============================================================================== --- webservices/rampart/branches/java/1_5/modules/rampart-trust/src/main/java/org/apache/rahas/impl/util/SignKeyHolder.java (added) +++ webservices/rampart/branches/java/1_5/modules/rampart-trust/src/main/java/org/apache/rahas/impl/util/SignKeyHolder.java Thu Aug 27 22:06:56 2009 @@ -0,0 +1,118 @@ +/* + * 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.util; + + +import org.opensaml.xml.security.credential.Credential; +import org.opensaml.xml.security.credential.CredentialContextSet; +import org.opensaml.xml.security.credential.UsageType; +import org.opensaml.xml.security.x509.X509Credential; + +import javax.crypto.SecretKey; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.cert.X509CRL; +import java.security.cert.X509Certificate; +import java.util.Arrays; +import java.util.Collection; + +/** + * This class is used to store the signing credentials. + */ +public class SignKeyHolder implements X509Credential { + + private String signatureAlgorithm = null; + + private X509Certificate[] issuerCerts = null; + + private PrivateKey issuerPK = null; + + + public String getSignatureAlgorithm() { + return signatureAlgorithm; + } + + public void setSignatureAlgorithm(String signatureAlgorithm) { + this.signatureAlgorithm = signatureAlgorithm; + } + + + public X509Certificate[] getIssuerCerts() { + return issuerCerts; + } + + public void setIssuerCerts(X509Certificate[] issuerCerts) { + this.issuerCerts = issuerCerts; + } + + public PrivateKey getIssuerPK() { + return issuerPK; + } + + public void setIssuerPK(PrivateKey issuerPK) { + this.issuerPK = issuerPK; + } + + public SignKeyHolder(){ + } + + + public X509Certificate getEntityCertificate() { + return issuerCerts[0]; + } + + + public Collection<X509Certificate> getEntityCertificateChain() { + return Arrays.asList(issuerCerts); + } + + public Collection<X509CRL> getCRLs() { + return null; + } + + public String getEntityId() { + return null; + } + + public UsageType getUsageType() { + return null; + } + + public Collection<String> getKeyNames() { + return null; + } + + public PublicKey getPublicKey() { + return null; + } + + public PrivateKey getPrivateKey() { + return issuerPK; + } + + public SecretKey getSecretKey() { + return null; + } + + public CredentialContextSet getCredentalContextSet() { + return null; + } + + public Class<? extends Credential> getCredentialType() { + return null; + } +}