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