You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by co...@apache.org on 2012/04/25 17:20:39 UTC
svn commit: r1330338 - in /cxf/trunk/rt/rs/security/xml/src:
main/java/org/apache/cxf/rs/security/saml/sso/
test/java/org/apache/cxf/rs/security/saml/sso/ test/resources/
Author: coheigea
Date: Wed Apr 25 15:20:38 2012
New Revision: 1330338
URL: http://svn.apache.org/viewvc?rev=1330338&view=rev
Log:
Missed some files from last commit
Added:
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/sso/SAMLProtocolResponseValidator.java
cxf/trunk/rt/rs/security/xml/src/test/java/org/apache/cxf/rs/security/saml/sso/AbstractSAMLCallbackHandler.java
cxf/trunk/rt/rs/security/xml/src/test/java/org/apache/cxf/rs/security/saml/sso/KeystorePasswordCallback.java
cxf/trunk/rt/rs/security/xml/src/test/java/org/apache/cxf/rs/security/saml/sso/SAML2CallbackHandler.java
cxf/trunk/rt/rs/security/xml/src/test/java/org/apache/cxf/rs/security/saml/sso/SAML2PResponseComponentBuilder.java
cxf/trunk/rt/rs/security/xml/src/test/java/org/apache/cxf/rs/security/saml/sso/SAMLResponseValidatorTest.java
cxf/trunk/rt/rs/security/xml/src/test/resources/
cxf/trunk/rt/rs/security/xml/src/test/resources/alice.jks
cxf/trunk/rt/rs/security/xml/src/test/resources/alice.properties
Added: cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/sso/SAMLProtocolResponseValidator.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/sso/SAMLProtocolResponseValidator.java?rev=1330338&view=auto
==============================================================================
--- cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/sso/SAMLProtocolResponseValidator.java (added)
+++ cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/sso/SAMLProtocolResponseValidator.java Wed Apr 25 15:20:38 2012
@@ -0,0 +1,339 @@
+/**
+ * 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.cxf.rs.security.saml.sso;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.security.auth.callback.CallbackHandler;
+
+import org.w3c.dom.Document;
+
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.ws.security.WSDocInfo;
+import org.apache.ws.security.WSSConfig;
+import org.apache.ws.security.WSSecurityException;
+import org.apache.ws.security.components.crypto.Crypto;
+import org.apache.ws.security.handler.RequestData;
+import org.apache.ws.security.saml.SAMLKeyInfo;
+import org.apache.ws.security.saml.SAMLUtil;
+import org.apache.ws.security.saml.ext.AssertionWrapper;
+import org.apache.ws.security.validate.Credential;
+import org.apache.ws.security.validate.SamlAssertionValidator;
+import org.apache.ws.security.validate.SignatureTrustValidator;
+import org.apache.ws.security.validate.Validator;
+import org.opensaml.security.SAMLSignatureProfileValidator;
+import org.opensaml.xml.security.x509.BasicX509Credential;
+import org.opensaml.xml.signature.KeyInfo;
+import org.opensaml.xml.signature.Signature;
+import org.opensaml.xml.signature.SignatureValidator;
+import org.opensaml.xml.validation.ValidationException;
+import org.opensaml.xml.validation.ValidatorSuite;
+
+/**
+ * Validate a SAML (1.1 or 2.0) Protocol Response. It validates the Response against the specs,
+ * the signature of the Response (if it exists), and any internal Assertion stored in the Response
+ * - including any signature. It validates the status code of the Response as well.
+ */
+public class SAMLProtocolResponseValidator {
+
+ public static final String SAML2_STATUSCODE_SUCCESS =
+ "urn:oasis:names:tc:SAML:2.0:status:Success";
+ public static final String SAML1_STATUSCODE_SUCCESS = "Success";
+
+ private static final Logger LOG = LogUtils.getL7dLogger(SAMLProtocolResponseValidator.class);
+
+ private Validator assertionValidator = new SamlAssertionValidator();
+ private Validator signatureValidator = new SignatureTrustValidator();
+
+ /**
+ * Validate a SAML 2 Protocol Response
+ * @param samlResponse
+ * @param sigCrypto
+ * @param callbackHandler
+ * @throws WSSecurityException
+ */
+ public void validateSamlResponse(
+ org.opensaml.saml2.core.Response samlResponse,
+ Crypto sigCrypto,
+ CallbackHandler callbackHandler
+ ) throws WSSecurityException {
+ // Check the Status Code
+ if (samlResponse.getStatus() == null
+ || samlResponse.getStatus().getStatusCode() == null) {
+ LOG.fine("Either the SAML Response Status or StatusCode is null");
+ throw new WSSecurityException(WSSecurityException.FAILURE, "invalidSAMLsecurity");
+ }
+ if (!SAML2_STATUSCODE_SUCCESS.equals(samlResponse.getStatus().getStatusCode().getValue())) {
+ LOG.fine(
+ "SAML Status code of " + samlResponse.getStatus().getStatusCode().getValue()
+ + "does not equal " + SAML2_STATUSCODE_SUCCESS
+ );
+ throw new WSSecurityException(WSSecurityException.FAILURE, "invalidSAMLsecurity");
+ }
+
+ validateResponseAgainstSchemas(samlResponse);
+ validateResponseSignature(samlResponse, sigCrypto, callbackHandler);
+
+ // Validate Assertions
+ for (org.opensaml.saml2.core.Assertion assertion : samlResponse.getAssertions()) {
+ AssertionWrapper wrapper = new AssertionWrapper(assertion);
+ validateAssertion(
+ wrapper, sigCrypto, callbackHandler, samlResponse.getDOM().getOwnerDocument()
+ );
+ }
+ }
+
+ /**
+ * Validate a SAML 1.1 Protocol Response
+ * @param samlResponse
+ * @param sigCrypto
+ * @param callbackHandler
+ * @throws WSSecurityException
+ */
+ public void validateSamlResponse(
+ org.opensaml.saml1.core.Response samlResponse,
+ Crypto sigCrypto,
+ CallbackHandler callbackHandler
+ ) throws WSSecurityException {
+ // Check the Status Code
+ if (samlResponse.getStatus() == null
+ || samlResponse.getStatus().getStatusCode() == null) {
+ LOG.fine("Either the SAML Response Status or StatusCode is null");
+ throw new WSSecurityException(WSSecurityException.FAILURE, "invalidSAMLsecurity");
+ }
+ if (!SAML1_STATUSCODE_SUCCESS.equals(samlResponse.getStatus().getStatusCode().getValue())) {
+ LOG.fine(
+ "SAML Status code of " + samlResponse.getStatus().getStatusCode().getValue()
+ + "does not equal " + SAML1_STATUSCODE_SUCCESS
+ );
+ throw new WSSecurityException(WSSecurityException.FAILURE, "invalidSAMLsecurity");
+ }
+
+ validateResponseAgainstSchemas(samlResponse);
+ validateResponseSignature(samlResponse, sigCrypto, callbackHandler);
+
+ // Validate Assertions
+ for (org.opensaml.saml1.core.Assertion assertion : samlResponse.getAssertions()) {
+ AssertionWrapper wrapper = new AssertionWrapper(assertion);
+ validateAssertion(
+ wrapper, sigCrypto, callbackHandler, samlResponse.getDOM().getOwnerDocument()
+ );
+ }
+ }
+
+ /**
+ * Validate the Response against the schemas
+ */
+ private void validateResponseAgainstSchemas(
+ org.opensaml.saml2.core.Response samlResponse
+ ) throws WSSecurityException {
+ // Validate SAML Response against schemas
+ ValidatorSuite schemaValidators =
+ org.opensaml.Configuration.getValidatorSuite("saml2-core-schema-validator");
+ try {
+ schemaValidators.validate(samlResponse);
+ } catch (ValidationException e) {
+ LOG.log(Level.FINE, "Saml Validation error: " + e.getMessage(), e);
+ throw new WSSecurityException(WSSecurityException.FAILURE, "invalidSAMLsecurity");
+ }
+ }
+
+ /**
+ * Validate the Response against the schemas
+ */
+ private void validateResponseAgainstSchemas(
+ org.opensaml.saml1.core.Response samlResponse
+ ) throws WSSecurityException {
+ // Validate SAML Response against schemas
+ ValidatorSuite schemaValidators =
+ org.opensaml.Configuration.getValidatorSuite("saml1-core-schema-validator");
+ try {
+ schemaValidators.validate(samlResponse);
+ } catch (ValidationException e) {
+ LOG.log(Level.FINE, "Saml Validation error: " + e.getMessage(), e);
+ throw new WSSecurityException(WSSecurityException.FAILURE, "invalidSAMLsecurity");
+ }
+ }
+
+ /**
+ * Validate the Response signature (if it exists)
+ */
+ private void validateResponseSignature(
+ org.opensaml.saml2.core.Response samlResponse,
+ Crypto sigCrypto,
+ CallbackHandler callbackHandler
+ ) throws WSSecurityException {
+ if (!samlResponse.isSigned()) {
+ return;
+ }
+
+ validateResponseSignature(
+ samlResponse.getSignature(), samlResponse.getDOM().getOwnerDocument(),
+ sigCrypto, callbackHandler
+ );
+ }
+
+ /**
+ * Validate the Response signature (if it exists)
+ */
+ private void validateResponseSignature(
+ org.opensaml.saml1.core.Response samlResponse,
+ Crypto sigCrypto,
+ CallbackHandler callbackHandler
+ ) throws WSSecurityException {
+ if (!samlResponse.isSigned()) {
+ return;
+ }
+
+ validateResponseSignature(
+ samlResponse.getSignature(), samlResponse.getDOM().getOwnerDocument(),
+ sigCrypto, callbackHandler
+ );
+ }
+
+ /**
+ * Validate the response signature
+ */
+ private void validateResponseSignature(
+ Signature signature,
+ Document doc,
+ Crypto sigCrypto,
+ CallbackHandler callbackHandler
+ ) throws WSSecurityException {
+ RequestData requestData = new RequestData();
+ requestData.setSigCrypto(sigCrypto);
+ WSSConfig wssConfig = WSSConfig.getNewInstance();
+ requestData.setWssConfig(wssConfig);
+ requestData.setCallbackHandler(callbackHandler);
+ WSDocInfo docInfo = new WSDocInfo(doc);
+
+ KeyInfo keyInfo = signature.getKeyInfo();
+ SAMLKeyInfo samlKeyInfo = null;
+ try {
+ samlKeyInfo =
+ SAMLUtil.getCredentialFromKeyInfo(
+ keyInfo.getDOM(), requestData, docInfo,
+ requestData.getWssConfig().isWsiBSPCompliant()
+ );
+ } catch (WSSecurityException ex) {
+ LOG.log(Level.FINE, "Error in getting KeyInfo from SAML Response: " + ex.getMessage(), ex);
+ throw ex;
+ }
+ if (samlKeyInfo == null) {
+ LOG.fine("No KeyInfo supplied in the SAMLResponse signature");
+ throw new WSSecurityException(WSSecurityException.FAILURE, "invalidSAMLsecurity");
+ }
+
+ // Validate Signature against profiles
+ validateSignatureAgainstProfiles(signature, samlKeyInfo);
+
+ // Now verify trust on the signature
+ Credential trustCredential = new Credential();
+ trustCredential.setPublicKey(samlKeyInfo.getPublicKey());
+ trustCredential.setCertificates(samlKeyInfo.getCerts());
+
+ try {
+ signatureValidator.validate(trustCredential, requestData);
+ } catch (WSSecurityException e) {
+ LOG.log(Level.FINE, "Error in validating signature on SAML Response: " + e.getMessage(), e);
+ throw new WSSecurityException(WSSecurityException.FAILURE, "invalidSAMLsecurity");
+ }
+ }
+
+ /**
+ * Validate a signature against the profiles
+ */
+ private void validateSignatureAgainstProfiles(
+ Signature signature,
+ SAMLKeyInfo samlKeyInfo
+ ) throws WSSecurityException {
+ // Validate Signature against profiles
+ SAMLSignatureProfileValidator validator = new SAMLSignatureProfileValidator();
+ try {
+ validator.validate(signature);
+ } catch (ValidationException ex) {
+ LOG.log(Level.FINE, "Error in validating the SAML Signature: " + ex.getMessage(), ex);
+ throw new WSSecurityException(WSSecurityException.FAILURE, "invalidSAMLsecurity");
+ }
+
+ BasicX509Credential credential = new BasicX509Credential();
+ if (samlKeyInfo.getCerts() != null) {
+ credential.setEntityCertificate(samlKeyInfo.getCerts()[0]);
+ } else if (samlKeyInfo.getPublicKey() != null) {
+ credential.setPublicKey(samlKeyInfo.getPublicKey());
+ } else {
+ LOG.fine("Can't get X509Certificate or PublicKey to verify signature");
+ throw new WSSecurityException(WSSecurityException.FAILURE, "invalidSAMLsecurity");
+ }
+ SignatureValidator sigValidator = new SignatureValidator(credential);
+ try {
+ sigValidator.validate(signature);
+ } catch (ValidationException ex) {
+ LOG.log(Level.FINE, "Error in validating the SAML Signature: " + ex.getMessage(), ex);
+ throw new WSSecurityException(WSSecurityException.FAILURE, "invalidSAMLsecurity");
+ }
+ }
+
+ /**
+ * Validate an internal Assertion
+ */
+ private void validateAssertion(
+ AssertionWrapper assertion,
+ Crypto sigCrypto,
+ CallbackHandler callbackHandler,
+ Document doc
+ ) throws WSSecurityException {
+ Credential credential = new Credential();
+ credential.setAssertion(assertion);
+
+ RequestData requestData = new RequestData();
+ requestData.setSigCrypto(sigCrypto);
+ WSSConfig wssConfig = WSSConfig.getNewInstance();
+ requestData.setWssConfig(wssConfig);
+ requestData.setCallbackHandler(callbackHandler);
+
+ if (assertion.isSigned()) {
+ if (assertion.getSaml1() != null) {
+ assertion.getSaml1().getDOM().setIdAttributeNS(null, "AssertionID", true);
+ } else {
+ assertion.getSaml2().getDOM().setIdAttributeNS(null, "ID", true);
+ }
+
+ // Verify the signature
+ try {
+ assertion.verifySignature(requestData, new WSDocInfo(doc));
+ } catch (WSSecurityException e) {
+ e.printStackTrace();
+ LOG.log(Level.FINE, "Assertion failed signature validation", e);
+ throw new WSSecurityException(WSSecurityException.FAILURE, "invalidSAMLsecurity");
+ }
+ }
+
+ // Validate the Assertion & verify trust in the signature
+ try {
+ assertionValidator.validate(credential, requestData);
+ } catch (WSSecurityException ex) {
+ LOG.log(Level.FINE, "Assertion validation failed: " + ex.getMessage(), ex);
+ throw new WSSecurityException(WSSecurityException.FAILURE, "invalidSAMLsecurity");
+ }
+ }
+
+
+}
Added: cxf/trunk/rt/rs/security/xml/src/test/java/org/apache/cxf/rs/security/saml/sso/AbstractSAMLCallbackHandler.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/rs/security/xml/src/test/java/org/apache/cxf/rs/security/saml/sso/AbstractSAMLCallbackHandler.java?rev=1330338&view=auto
==============================================================================
--- cxf/trunk/rt/rs/security/xml/src/test/java/org/apache/cxf/rs/security/saml/sso/AbstractSAMLCallbackHandler.java (added)
+++ cxf/trunk/rt/rs/security/xml/src/test/java/org/apache/cxf/rs/security/saml/sso/AbstractSAMLCallbackHandler.java Wed Apr 25 15:20:38 2012
@@ -0,0 +1,201 @@
+/**
+ * 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.cxf.rs.security.saml.sso;
+
+import java.security.cert.X509Certificate;
+import java.util.Collections;
+import java.util.List;
+
+import javax.security.auth.callback.CallbackHandler;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import org.apache.ws.security.WSConstants;
+import org.apache.ws.security.message.WSSecEncryptedKey;
+import org.apache.ws.security.saml.ext.SAMLCallback;
+import org.apache.ws.security.saml.ext.bean.ActionBean;
+import org.apache.ws.security.saml.ext.bean.AttributeBean;
+import org.apache.ws.security.saml.ext.bean.AttributeStatementBean;
+import org.apache.ws.security.saml.ext.bean.AuthDecisionStatementBean;
+import org.apache.ws.security.saml.ext.bean.AuthenticationStatementBean;
+import org.apache.ws.security.saml.ext.bean.ConditionsBean;
+import org.apache.ws.security.saml.ext.bean.KeyInfoBean;
+import org.apache.ws.security.saml.ext.bean.KeyInfoBean.CERT_IDENTIFIER;
+import org.apache.ws.security.saml.ext.bean.SubjectBean;
+import org.apache.ws.security.saml.ext.bean.SubjectLocalityBean;
+
+/**
+ * A base implementation of a Callback Handler for a SAML assertion. By default it creates an
+ * authentication assertion.
+ */
+public abstract class AbstractSAMLCallbackHandler implements CallbackHandler {
+
+ public enum Statement {
+ AUTHN, ATTR, AUTHZ
+ };
+
+ protected String subjectName;
+ protected String subjectQualifier;
+ protected String confirmationMethod;
+ protected X509Certificate[] certs;
+ protected Statement statement = Statement.AUTHN;
+ protected CERT_IDENTIFIER certIdentifier = CERT_IDENTIFIER.X509_CERT;
+ protected byte[] ephemeralKey;
+ protected String issuer;
+ protected String subjectNameIDFormat;
+ protected String subjectLocalityIpAddress;
+ protected String subjectLocalityDnsAddress;
+ protected String resource;
+ protected List<?> customAttributeValues;
+ protected ConditionsBean conditions;
+
+ public void setConditions(ConditionsBean conditionsBean) {
+ this.conditions = conditionsBean;
+ }
+
+ public void setConfirmationMethod(String confMethod) {
+ confirmationMethod = confMethod;
+ }
+
+ public void setStatement(Statement statement) {
+ this.statement = statement;
+ }
+
+ public void setCertIdentifier(CERT_IDENTIFIER certIdentifier) {
+ this.certIdentifier = certIdentifier;
+ }
+
+ public void setCerts(X509Certificate[] certs) {
+ this.certs = certs;
+ }
+
+ public byte[] getEphemeralKey() {
+ return ephemeralKey;
+ }
+
+ public void setIssuer(String issuer) {
+ this.issuer = issuer;
+ }
+
+ public void setSubjectNameIDFormat(String subjectNameIDFormat) {
+ this.subjectNameIDFormat = subjectNameIDFormat;
+ }
+
+ public void setSubjectLocality(String ipAddress, String dnsAddress) {
+ this.subjectLocalityIpAddress = ipAddress;
+ this.subjectLocalityDnsAddress = dnsAddress;
+ }
+
+ public void setResource(String resource) {
+ this.resource = resource;
+ }
+
+ public void setCustomAttributeValues(List<?> customAttributeValues) {
+ this.customAttributeValues = customAttributeValues;
+ }
+
+ /**
+ * Note that the SubjectBean parameter should be null for SAML2.0
+ */
+ protected void createAndSetStatement(SubjectBean subjectBean, SAMLCallback callback) {
+ if (statement == Statement.AUTHN) {
+ AuthenticationStatementBean authBean = new AuthenticationStatementBean();
+ if (subjectBean != null) {
+ authBean.setSubject(subjectBean);
+ }
+ if (subjectLocalityIpAddress != null || subjectLocalityDnsAddress != null) {
+ SubjectLocalityBean subjectLocality = new SubjectLocalityBean();
+ subjectLocality.setIpAddress(subjectLocalityIpAddress);
+ subjectLocality.setDnsAddress(subjectLocalityDnsAddress);
+ authBean.setSubjectLocality(subjectLocality);
+ }
+ authBean.setAuthenticationMethod("Password");
+ callback.setAuthenticationStatementData(Collections.singletonList(authBean));
+ } else if (statement == Statement.ATTR) {
+ AttributeStatementBean attrBean = new AttributeStatementBean();
+ AttributeBean attributeBean = new AttributeBean();
+ if (subjectBean != null) {
+ attrBean.setSubject(subjectBean);
+ attributeBean.setSimpleName("role");
+ attributeBean.setQualifiedName("http://custom-ns");
+ } else {
+ attributeBean.setQualifiedName("role");
+ }
+ if (customAttributeValues != null) {
+ attributeBean.setCustomAttributeValues(customAttributeValues);
+ } else {
+ attributeBean.setAttributeValues(Collections.singletonList("user"));
+ }
+ attrBean.setSamlAttributes(Collections.singletonList(attributeBean));
+ callback.setAttributeStatementData(Collections.singletonList(attrBean));
+ } else {
+ AuthDecisionStatementBean authzBean = new AuthDecisionStatementBean();
+ if (subjectBean != null) {
+ authzBean.setSubject(subjectBean);
+ }
+ ActionBean actionBean = new ActionBean();
+ actionBean.setContents("Read");
+ authzBean.setActions(Collections.singletonList(actionBean));
+ authzBean.setResource("endpoint");
+ authzBean.setDecision(AuthDecisionStatementBean.Decision.PERMIT);
+ authzBean.setResource(resource);
+ callback.setAuthDecisionStatementData(Collections.singletonList(authzBean));
+ }
+ }
+
+ protected KeyInfoBean createKeyInfo() throws Exception {
+ KeyInfoBean keyInfo = new KeyInfoBean();
+ if (statement == Statement.AUTHN) {
+ keyInfo.setCertificate(certs[0]);
+ keyInfo.setCertIdentifer(certIdentifier);
+ } else if (statement == Statement.ATTR) {
+ // Build a new Document
+ DocumentBuilderFactory docBuilderFactory =
+ DocumentBuilderFactory.newInstance();
+ docBuilderFactory.setNamespaceAware(true);
+ DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
+ Document doc = docBuilder.newDocument();
+
+ // Create an Encrypted Key
+ WSSecEncryptedKey encrKey = new WSSecEncryptedKey();
+ encrKey.setKeyIdentifierType(WSConstants.ISSUER_SERIAL);
+ encrKey.setUseThisCert(certs[0]);
+ encrKey.prepare(doc, null);
+ ephemeralKey = encrKey.getEphemeralKey();
+ Element encryptedKeyElement = encrKey.getEncryptedKeyElement();
+
+ // Append the EncryptedKey to a KeyInfo element
+ Element keyInfoElement =
+ doc.createElementNS(
+ WSConstants.SIG_NS, WSConstants.SIG_PREFIX + ":" + WSConstants.KEYINFO_LN
+ );
+ keyInfoElement.setAttributeNS(
+ WSConstants.XMLNS_NS, "xmlns:" + WSConstants.SIG_PREFIX, WSConstants.SIG_NS
+ );
+ keyInfoElement.appendChild(encryptedKeyElement);
+
+ keyInfo.setElement(keyInfoElement);
+ }
+ return keyInfo;
+ }
+}
Added: cxf/trunk/rt/rs/security/xml/src/test/java/org/apache/cxf/rs/security/saml/sso/KeystorePasswordCallback.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/rs/security/xml/src/test/java/org/apache/cxf/rs/security/saml/sso/KeystorePasswordCallback.java?rev=1330338&view=auto
==============================================================================
--- cxf/trunk/rt/rs/security/xml/src/test/java/org/apache/cxf/rs/security/saml/sso/KeystorePasswordCallback.java (added)
+++ cxf/trunk/rt/rs/security/xml/src/test/java/org/apache/cxf/rs/security/saml/sso/KeystorePasswordCallback.java Wed Apr 25 15:20:38 2012
@@ -0,0 +1,69 @@
+/**
+ * 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.cxf.rs.security.saml.sso;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.UnsupportedCallbackException;
+
+import org.apache.ws.security.WSPasswordCallback;
+
+/**
+ */
+
+public class KeystorePasswordCallback implements CallbackHandler {
+
+ private Map<String, String> passwords =
+ new HashMap<String, String>();
+
+ public KeystorePasswordCallback() {
+ passwords.put("Alice", "abcd!1234");
+ passwords.put("alice", "password");
+ passwords.put("Bob", "abcd!1234");
+ passwords.put("bob", "password");
+ passwords.put("abcd", "dcba");
+ }
+
+ /**
+ * It attempts to get the password from the private
+ * alias/passwords map.
+ */
+ public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
+ for (int i = 0; i < callbacks.length; i++) {
+ WSPasswordCallback pc = (WSPasswordCallback)callbacks[i];
+
+ String pass = passwords.get(pc.getIdentifier());
+ if (pass != null) {
+ pc.setPassword(pass);
+ return;
+ }
+ }
+ }
+
+ /**
+ * Add an alias/password pair to the callback mechanism.
+ */
+ public void setAliasPassword(String alias, String password) {
+ passwords.put(alias, password);
+ }
+}
Added: cxf/trunk/rt/rs/security/xml/src/test/java/org/apache/cxf/rs/security/saml/sso/SAML2CallbackHandler.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/rs/security/xml/src/test/java/org/apache/cxf/rs/security/saml/sso/SAML2CallbackHandler.java?rev=1330338&view=auto
==============================================================================
--- cxf/trunk/rt/rs/security/xml/src/test/java/org/apache/cxf/rs/security/saml/sso/SAML2CallbackHandler.java (added)
+++ cxf/trunk/rt/rs/security/xml/src/test/java/org/apache/cxf/rs/security/saml/sso/SAML2CallbackHandler.java Wed Apr 25 15:20:38 2012
@@ -0,0 +1,89 @@
+/**
+ * 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.cxf.rs.security.saml.sso;
+
+import java.io.IOException;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+
+import org.apache.ws.security.components.crypto.Crypto;
+import org.apache.ws.security.components.crypto.CryptoFactory;
+import org.apache.ws.security.components.crypto.CryptoType;
+import org.apache.ws.security.saml.ext.SAMLCallback;
+import org.apache.ws.security.saml.ext.bean.KeyInfoBean;
+import org.apache.ws.security.saml.ext.bean.SubjectBean;
+import org.apache.ws.security.saml.ext.builder.SAML2Constants;
+import org.opensaml.common.SAMLVersion;
+
+/**
+ * A Callback Handler implementation for a SAML 2 assertion. By default it creates an
+ * authentication assertion using Sender Vouches.
+ */
+public class SAML2CallbackHandler extends AbstractSAMLCallbackHandler {
+
+ public SAML2CallbackHandler() throws Exception {
+ if (certs == null) {
+ Crypto crypto = CryptoFactory.getInstance("alice.properties");
+ CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ALIAS);
+ cryptoType.setAlias("alice");
+ certs = crypto.getX509Certificates(cryptoType);
+ }
+
+ subjectName = "uid=joe,ou=people,ou=saml-demo,o=example.com";
+ subjectQualifier = "www.example.com";
+ confirmationMethod = SAML2Constants.CONF_SENDER_VOUCHES;
+ }
+
+ public void handle(Callback[] callbacks)
+ throws IOException, UnsupportedCallbackException {
+ for (int i = 0; i < callbacks.length; i++) {
+ if (callbacks[i] instanceof SAMLCallback) {
+ SAMLCallback callback = (SAMLCallback) callbacks[i];
+ callback.setSamlVersion(SAMLVersion.VERSION_20);
+ callback.setIssuer(issuer);
+ if (conditions != null) {
+ callback.setConditions(conditions);
+ }
+
+ SubjectBean subjectBean =
+ new SubjectBean(
+ subjectName, subjectQualifier, confirmationMethod
+ );
+ if (subjectNameIDFormat != null) {
+ subjectBean.setSubjectNameIDFormat(subjectNameIDFormat);
+ }
+ if (SAML2Constants.CONF_HOLDER_KEY.equals(confirmationMethod)) {
+ try {
+ KeyInfoBean keyInfo = createKeyInfo();
+ subjectBean.setKeyInfo(keyInfo);
+ } catch (Exception ex) {
+ throw new IOException("Problem creating KeyInfo: " + ex.getMessage());
+ }
+ }
+ callback.setSubject(subjectBean);
+ createAndSetStatement(null, callback);
+ } else {
+ throw new UnsupportedCallbackException(callbacks[i], "Unrecognized Callback");
+ }
+ }
+ }
+
+}
Added: cxf/trunk/rt/rs/security/xml/src/test/java/org/apache/cxf/rs/security/saml/sso/SAML2PResponseComponentBuilder.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/rs/security/xml/src/test/java/org/apache/cxf/rs/security/saml/sso/SAML2PResponseComponentBuilder.java?rev=1330338&view=auto
==============================================================================
--- cxf/trunk/rt/rs/security/xml/src/test/java/org/apache/cxf/rs/security/saml/sso/SAML2PResponseComponentBuilder.java (added)
+++ cxf/trunk/rt/rs/security/xml/src/test/java/org/apache/cxf/rs/security/saml/sso/SAML2PResponseComponentBuilder.java Wed Apr 25 15:20:38 2012
@@ -0,0 +1,126 @@
+/**
+ * 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.cxf.rs.security.saml.sso;
+
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.opensaml.Configuration;
+import org.opensaml.common.SAMLObjectBuilder;
+import org.opensaml.common.SAMLVersion;
+import org.opensaml.saml2.core.Issuer;
+import org.opensaml.saml2.core.Response;
+import org.opensaml.saml2.core.Status;
+import org.opensaml.saml2.core.StatusCode;
+import org.opensaml.saml2.core.StatusMessage;
+import org.opensaml.xml.XMLObjectBuilderFactory;
+
+/**
+* A (basic) set of utility methods to construct SAML 2.0 Protocol Response statements
+*/
+public final class SAML2PResponseComponentBuilder {
+
+ private static SAMLObjectBuilder<Response> responseBuilder;
+
+ private static SAMLObjectBuilder<Issuer> issuerBuilder;
+
+ private static SAMLObjectBuilder<Status> statusBuilder;
+
+ private static SAMLObjectBuilder<StatusCode> statusCodeBuilder;
+
+ private static SAMLObjectBuilder<StatusMessage> statusMessageBuilder;
+
+ private static XMLObjectBuilderFactory builderFactory = Configuration.getBuilderFactory();
+
+ private SAML2PResponseComponentBuilder() {
+
+ }
+
+ @SuppressWarnings("unchecked")
+ public static Response createSAMLResponse(
+ String inResponseTo,
+ String issuer,
+ Status status
+ ) {
+ if (responseBuilder == null) {
+ responseBuilder = (SAMLObjectBuilder<Response>)
+ builderFactory.getBuilder(Response.DEFAULT_ELEMENT_NAME);
+ }
+ Response response = responseBuilder.buildObject();
+
+ response.setID(UUID.randomUUID().toString());
+ response.setIssueInstant(new DateTime());
+ response.setInResponseTo(inResponseTo);
+ response.setIssuer(createIssuer(issuer));
+ response.setStatus(status);
+ response.setVersion(SAMLVersion.VERSION_20);
+
+ return response;
+ }
+
+ @SuppressWarnings("unchecked")
+ public static Issuer createIssuer(
+ String issuerValue
+ ) {
+ if (issuerBuilder == null) {
+ issuerBuilder = (SAMLObjectBuilder<Issuer>)
+ builderFactory.getBuilder(Issuer.DEFAULT_ELEMENT_NAME);
+ }
+ Issuer issuer = issuerBuilder.buildObject();
+ issuer.setValue(issuerValue);
+
+ return issuer;
+ }
+
+ @SuppressWarnings("unchecked")
+ public static Status createStatus(
+ String statusCodeValue,
+ String statusMessage
+ ) {
+ if (statusBuilder == null) {
+ statusBuilder = (SAMLObjectBuilder<Status>)
+ builderFactory.getBuilder(Status.DEFAULT_ELEMENT_NAME);
+ }
+ if (statusCodeBuilder == null) {
+ statusCodeBuilder = (SAMLObjectBuilder<StatusCode>)
+ builderFactory.getBuilder(StatusCode.DEFAULT_ELEMENT_NAME);
+ }
+ if (statusMessageBuilder == null) {
+ statusMessageBuilder = (SAMLObjectBuilder<StatusMessage>)
+ builderFactory.getBuilder(StatusMessage.DEFAULT_ELEMENT_NAME);
+ }
+
+ Status status = statusBuilder.buildObject();
+
+ StatusCode statusCode = statusCodeBuilder.buildObject();
+ statusCode.setValue(statusCodeValue);
+ status.setStatusCode(statusCode);
+
+ if (statusMessage != null) {
+ StatusMessage statusMessageObject = statusMessageBuilder.buildObject();
+ statusMessageObject.setMessage(statusMessage);
+ status.setStatusMessage(statusMessageObject);
+ }
+
+ return status;
+ }
+
+
+}
\ No newline at end of file
Added: cxf/trunk/rt/rs/security/xml/src/test/java/org/apache/cxf/rs/security/saml/sso/SAMLResponseValidatorTest.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/rs/security/xml/src/test/java/org/apache/cxf/rs/security/saml/sso/SAMLResponseValidatorTest.java?rev=1330338&view=auto
==============================================================================
--- cxf/trunk/rt/rs/security/xml/src/test/java/org/apache/cxf/rs/security/saml/sso/SAMLResponseValidatorTest.java (added)
+++ cxf/trunk/rt/rs/security/xml/src/test/java/org/apache/cxf/rs/security/saml/sso/SAMLResponseValidatorTest.java Wed Apr 25 15:20:38 2012
@@ -0,0 +1,324 @@
+/**
+ * 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.cxf.rs.security.saml.sso;
+
+import java.io.InputStream;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import org.apache.ws.security.WSSecurityException;
+import org.apache.ws.security.components.crypto.Crypto;
+import org.apache.ws.security.components.crypto.CryptoType;
+import org.apache.ws.security.components.crypto.Merlin;
+import org.apache.ws.security.saml.ext.AssertionWrapper;
+import org.apache.ws.security.saml.ext.OpenSAMLUtil;
+import org.apache.ws.security.saml.ext.SAMLParms;
+import org.apache.ws.security.saml.ext.builder.SAML2Constants;
+import org.apache.ws.security.util.Loader;
+import org.opensaml.common.SignableSAMLObject;
+import org.opensaml.saml2.core.Response;
+import org.opensaml.saml2.core.Status;
+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;
+
+/**
+ * Some unit tests for the SAMLProtocolResponseValidator.
+ */
+public class SAMLResponseValidatorTest extends org.junit.Assert {
+
+ static {
+ OpenSAMLUtil.initSamlEngine();
+ }
+
+ @org.junit.Test
+ public void testCreateAndValidateResponse() throws Exception {
+ DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
+ docBuilderFactory.setNamespaceAware(true);
+ DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
+ Document doc = docBuilder.newDocument();
+
+ Status status =
+ SAML2PResponseComponentBuilder.createStatus(
+ SAMLProtocolResponseValidator.SAML2_STATUSCODE_SUCCESS, null
+ );
+ Response response =
+ SAML2PResponseComponentBuilder.createSAMLResponse(
+ "http://cxf.apache.org/saml", "http://cxf.apache.org/issuer", status
+ );
+
+ // Create an AuthenticationAssertion
+ SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
+ callbackHandler.setStatement(SAML2CallbackHandler.Statement.AUTHN);
+ callbackHandler.setIssuer("http://cxf.apache.org/issuer");
+ callbackHandler.setConfirmationMethod(SAML2Constants.CONF_BEARER);
+
+ SAMLParms samlParms = new SAMLParms();
+ samlParms.setCallbackHandler(callbackHandler);
+ AssertionWrapper assertion = new AssertionWrapper(samlParms);
+
+ response.getAssertions().add(assertion.getSaml2());
+
+ Element policyElement = OpenSAMLUtil.toDom(response, doc);
+ doc.appendChild(policyElement);
+ assertNotNull(policyElement);
+
+ Response marshalledResponse = (Response)OpenSAMLUtil.fromDom(policyElement);
+
+ // Validate the Response
+ SAMLProtocolResponseValidator validator = new SAMLProtocolResponseValidator();
+ validator.validateSamlResponse(marshalledResponse, null, null);
+ }
+
+ @org.junit.Test
+ public void testInvalidStatusCode() throws Exception {
+ DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
+ docBuilderFactory.setNamespaceAware(true);
+ DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
+ Document doc = docBuilder.newDocument();
+
+ Status status =
+ SAML2PResponseComponentBuilder.createStatus(
+ SAMLProtocolResponseValidator.SAML1_STATUSCODE_SUCCESS, null
+ );
+ Response response =
+ SAML2PResponseComponentBuilder.createSAMLResponse(
+ "http://cxf.apache.org/saml", "http://cxf.apache.org/issuer", status
+ );
+
+ // Create an AuthenticationAssertion
+ SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
+ callbackHandler.setStatement(SAML2CallbackHandler.Statement.AUTHN);
+ callbackHandler.setIssuer("http://cxf.apache.org/issuer");
+ callbackHandler.setConfirmationMethod(SAML2Constants.CONF_BEARER);
+
+ SAMLParms samlParms = new SAMLParms();
+ samlParms.setCallbackHandler(callbackHandler);
+ AssertionWrapper assertion = new AssertionWrapper(samlParms);
+
+ response.getAssertions().add(assertion.getSaml2());
+
+ Element policyElement = OpenSAMLUtil.toDom(response, doc);
+ doc.appendChild(policyElement);
+ assertNotNull(policyElement);
+
+ Response marshalledResponse = (Response)OpenSAMLUtil.fromDom(policyElement);
+
+ // Validate the Response
+ SAMLProtocolResponseValidator validator = new SAMLProtocolResponseValidator();
+ try {
+ validator.validateSamlResponse(marshalledResponse, null, null);
+ fail("Expected failure on an invalid SAML code");
+ } catch (WSSecurityException ex) {
+ // expected
+ }
+ }
+
+ @org.junit.Test
+ public void testResponseSignedAssertion() throws Exception {
+ DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
+ docBuilderFactory.setNamespaceAware(true);
+ DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
+ Document doc = docBuilder.newDocument();
+
+ Status status =
+ SAML2PResponseComponentBuilder.createStatus(
+ SAMLProtocolResponseValidator.SAML2_STATUSCODE_SUCCESS, null
+ );
+ Response response =
+ SAML2PResponseComponentBuilder.createSAMLResponse(
+ "http://cxf.apache.org/saml", "http://cxf.apache.org/issuer", status
+ );
+
+ // Create an AuthenticationAssertion
+ SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
+ callbackHandler.setStatement(SAML2CallbackHandler.Statement.AUTHN);
+ callbackHandler.setIssuer("http://cxf.apache.org/issuer");
+ callbackHandler.setConfirmationMethod(SAML2Constants.CONF_BEARER);
+
+ SAMLParms samlParms = new SAMLParms();
+ samlParms.setCallbackHandler(callbackHandler);
+ AssertionWrapper assertion = new AssertionWrapper(samlParms);
+
+ Crypto issuerCrypto = new Merlin();
+ KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
+ ClassLoader loader = Loader.getClassLoader(SAMLResponseValidatorTest.class);
+ InputStream input = Merlin.loadInputStream(loader, "alice.jks");
+ keyStore.load(input, "password".toCharArray());
+ ((Merlin)issuerCrypto).setKeyStore(keyStore);
+
+ assertion.signAssertion("alice", "password", issuerCrypto, false);
+
+ response.getAssertions().add(assertion.getSaml2());
+
+ Element policyElement = OpenSAMLUtil.toDom(response, doc);
+ doc.appendChild(policyElement);
+ assertNotNull(policyElement);
+
+ Response marshalledResponse = (Response)OpenSAMLUtil.fromDom(policyElement);
+
+ // Validate the Response
+ SAMLProtocolResponseValidator validator = new SAMLProtocolResponseValidator();
+ try {
+ validator.validateSamlResponse(marshalledResponse, null, new KeystorePasswordCallback());
+ fail("Expected failure on no Signature Crypto");
+ } catch (WSSecurityException ex) {
+ // expected
+ }
+
+ // Validate the Response
+ validator.validateSamlResponse(
+ marshalledResponse, issuerCrypto, new KeystorePasswordCallback()
+ );
+ }
+
+ @org.junit.Test
+ public void testSignedResponse() throws Exception {
+ DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
+ docBuilderFactory.setNamespaceAware(true);
+ DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
+ Document doc = docBuilder.newDocument();
+
+ Status status =
+ SAML2PResponseComponentBuilder.createStatus(
+ SAMLProtocolResponseValidator.SAML2_STATUSCODE_SUCCESS, null
+ );
+ Response response =
+ SAML2PResponseComponentBuilder.createSAMLResponse(
+ "http://cxf.apache.org/saml", "http://cxf.apache.org/issuer", status
+ );
+
+ // Create an AuthenticationAssertion
+ SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
+ callbackHandler.setStatement(SAML2CallbackHandler.Statement.AUTHN);
+ callbackHandler.setIssuer("http://cxf.apache.org/issuer");
+ callbackHandler.setConfirmationMethod(SAML2Constants.CONF_BEARER);
+
+ SAMLParms samlParms = new SAMLParms();
+ samlParms.setCallbackHandler(callbackHandler);
+ AssertionWrapper assertion = new AssertionWrapper(samlParms);
+
+ Crypto issuerCrypto = new Merlin();
+ KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
+ ClassLoader loader = Loader.getClassLoader(SAMLResponseValidatorTest.class);
+ InputStream input = Merlin.loadInputStream(loader, "alice.jks");
+ keyStore.load(input, "password".toCharArray());
+ ((Merlin)issuerCrypto).setKeyStore(keyStore);
+
+ response.getAssertions().add(assertion.getSaml2());
+ signResponse(response, "alice", "password", issuerCrypto);
+
+ Element policyElement = OpenSAMLUtil.toDom(response, doc);
+ doc.appendChild(policyElement);
+ assertNotNull(policyElement);
+
+ Response marshalledResponse = (Response)OpenSAMLUtil.fromDom(policyElement);
+
+ // Validate the Response
+ SAMLProtocolResponseValidator validator = new SAMLProtocolResponseValidator();
+ try {
+ validator.validateSamlResponse(marshalledResponse, null, new KeystorePasswordCallback());
+ fail("Expected failure on no Signature Crypto");
+ } catch (WSSecurityException ex) {
+ // expected
+ }
+
+ // Validate the Response
+ validator.validateSamlResponse(
+ marshalledResponse, issuerCrypto, new KeystorePasswordCallback()
+ );
+ }
+
+
+ /**
+ * Sign a SAML Response
+ */
+ private void signResponse(
+ Response response,
+ String issuerKeyName,
+ String issuerKeyPassword,
+ Crypto issuerCrypto
+ ) throws WSSecurityException {
+ //
+ // Create the signature
+ //
+ Signature signature = OpenSAMLUtil.buildSignature();
+ signature.setCanonicalizationAlgorithm(SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);
+
+ // prepare to sign the SAML token
+ CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ALIAS);
+ cryptoType.setAlias(issuerKeyName);
+ X509Certificate[] issuerCerts = issuerCrypto.getX509Certificates(cryptoType);
+ if (issuerCerts == null) {
+ throw new WSSecurityException(
+ "No issuer certs were found to sign the SAML Assertion using issuer name: "
+ + issuerKeyName);
+ }
+
+ String sigAlgo = SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1;
+ String pubKeyAlgo = issuerCerts[0].getPublicKey().getAlgorithm();
+
+ if (pubKeyAlgo.equalsIgnoreCase("DSA")) {
+ sigAlgo = SignatureConstants.ALGO_ID_SIGNATURE_DSA;
+ }
+
+ PrivateKey privateKey = null;
+ try {
+ privateKey = issuerCrypto.getPrivateKey(issuerKeyName, issuerKeyPassword);
+ } catch (Exception ex) {
+ throw new WSSecurityException(ex.getMessage(), ex);
+ }
+
+ signature.setSignatureAlgorithm(sigAlgo);
+
+ BasicX509Credential signingCredential = new BasicX509Credential();
+ signingCredential.setEntityCertificate(issuerCerts[0]);
+ signingCredential.setPrivateKey(privateKey);
+
+ signature.setSigningCredential(signingCredential);
+
+ X509KeyInfoGeneratorFactory kiFactory = new X509KeyInfoGeneratorFactory();
+ kiFactory.setEmitEntityCertificate(true);
+
+ try {
+ KeyInfo keyInfo = kiFactory.newInstance().generate(signingCredential);
+ signature.setKeyInfo(keyInfo);
+ } catch (org.opensaml.xml.security.SecurityException ex) {
+ throw new WSSecurityException(
+ "Error generating KeyInfo from signing credential", ex);
+ }
+
+ // add the signature to the assertion
+ SignableSAMLObject signableObject = (SignableSAMLObject) response;
+ signableObject.setSignature(signature);
+ signableObject.releaseDOM();
+ signableObject.releaseChildrenDOM(true);
+ }
+
+}
Added: cxf/trunk/rt/rs/security/xml/src/test/resources/alice.jks
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/rs/security/xml/src/test/resources/alice.jks?rev=1330338&view=auto
==============================================================================
Files cxf/trunk/rt/rs/security/xml/src/test/resources/alice.jks (added) and cxf/trunk/rt/rs/security/xml/src/test/resources/alice.jks Wed Apr 25 15:20:38 2012 differ
Added: cxf/trunk/rt/rs/security/xml/src/test/resources/alice.properties
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/rs/security/xml/src/test/resources/alice.properties?rev=1330338&view=auto
==============================================================================
--- cxf/trunk/rt/rs/security/xml/src/test/resources/alice.properties (added)
+++ cxf/trunk/rt/rs/security/xml/src/test/resources/alice.properties Wed Apr 25 15:20:38 2012
@@ -0,0 +1,21 @@
+# 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.
+org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin
+org.apache.ws.security.crypto.merlin.keystore.type=jks
+org.apache.ws.security.crypto.merlin.keystore.password=password
+org.apache.ws.security.crypto.merlin.keystore.alias=alice
+org.apache.ws.security.crypto.merlin.keystore.file=alice.jks
\ No newline at end of file