Author: coheigea Date: Mon Nov 22 11:42:02 2010 New Revision: 1037678 URL: http://svn.apache.org/viewvc?rev=1037678&view=rev Log: [WSS-238] - A fix for this issue - EncryptedKeyProcessor and ReferenceListProcessor can now handle a key identifier pointing to a SAML assertion - Functionality added to point an EncryptedData and/or an EncryptedKey structure to a SAML assertion.
Added: webservices/wss4j/branches/1_5_x-fixes/test/wssec/TestWSSecuritySAMLKeyIdentifier.java Modified: webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/message/WSSecBase.java webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/message/WSSecEncrypt.java webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/message/WSSecEncryptedKey.java webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/processor/EncryptedKeyProcessor.java webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/processor/ReferenceListProcessor.java webservices/wss4j/branches/1_5_x-fixes/test/wssec/TestWSSecurityNew3.java Modified: webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/message/WSSecBase.java URL: http://svn.apache.org/viewvc/webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/message/WSSecBase.java?rev=1037678&r1=1037677&r2=1037678&view=diff ============================================================================== --- webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/message/WSSecBase.java (original) +++ webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/message/WSSecBase.java Mon Nov 22 11:42:02 2010 @@ -84,7 +84,7 @@ public class WSSecBase { } /** - * Gets the value of the <code>keyIdentifyerType</code>. + * Gets the value of the <code>keyIdentifierType</code>. * * @return The <code>keyIdentifyerType</code>. * @see WSConstants#ISSUER_SERIAL Modified: webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/message/WSSecEncrypt.java URL: http://svn.apache.org/viewvc/webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/message/WSSecEncrypt.java?rev=1037678&r1=1037677&r2=1037678&view=diff ============================================================================== --- webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/message/WSSecEncrypt.java (original) +++ webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/message/WSSecEncrypt.java Mon Nov 22 11:42:02 2010 @@ -89,7 +89,7 @@ public class WSSecEncrypt extends WSSecE * Custom reference value */ private String customReferenceValue; - + /** * ValueType for the encrypted key reference */ @@ -520,16 +520,23 @@ public class WSSecEncrypt extends WSSecE if (keyInfo == null) { keyInfo = new KeyInfo(document); SecurityTokenReference secToken = new SecurityTokenReference(document); - Reference ref = new Reference(document); - if (encKeyIdDirectId) { - ref.setURI(encKeyId); + + if (useKeyIdentifier && + SecurityTokenReference.SAML_ID_URI.equals(customReferenceValue)) { + secToken.setSAMLKeyIdentifier((encKeyIdDirectId ? "":"#") + encKeyId); } else { - ref.setURI("#" + encKeyId); - } - if (encKeyValueType != null) { - ref.setValueType(encKeyValueType); + Reference ref = new Reference(document); + if (encKeyIdDirectId) { + ref.setURI(encKeyId); + } else { + ref.setURI("#" + encKeyId); + } + if (encKeyValueType != null) { + ref.setValueType(encKeyValueType); + } + secToken.setReference(ref); } - secToken.setReference(ref); + keyInfo.addUnknownElement(secToken.getElement()); Element keyInfoElement = keyInfo.getElement(); keyInfoElement.setAttributeNS( @@ -774,11 +781,11 @@ public class WSSecEncrypt extends WSSecE public boolean isEncryptSymmKey() { return encryptSymmKey; } - + public void setEncryptSymmKey(boolean encryptSymmKey) { this.encryptSymmKey = encryptSymmKey; } - + private String getSHA1(byte[] input) throws WSSecurityException { try { MessageDigest sha = null; @@ -794,7 +801,7 @@ public class WSSecEncrypt extends WSSecE ); } } - + public void setCustomReferenceValue(String customReferenceValue) { this.customReferenceValue = customReferenceValue; } Modified: webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/message/WSSecEncryptedKey.java URL: http://svn.apache.org/viewvc/webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/message/WSSecEncryptedKey.java?rev=1037678&r1=1037677&r2=1037678&view=diff ============================================================================== --- webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/message/WSSecEncryptedKey.java (original) +++ webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/message/WSSecEncryptedKey.java Mon Nov 22 11:42:02 2010 @@ -96,6 +96,16 @@ public class WSSecEncryptedKey extends W protected String encKeyId = null; /** + * Custom token value + */ + protected String customEKTokenValueType; + + /** + * Custom token id + */ + protected String customEKTokenId; + + /** * BinarySecurityToken to be included in the case where BST_DIRECT_REFERENCE * is used to refer to the asymmetric encryption cert */ @@ -288,6 +298,10 @@ public class WSSecEncryptedKey extends W ref.setValueType(bstToken.getValueType()); secToken.setReference(ref); break; + + case WSConstants.CUSTOM_KEY_IDENTIFIER: + secToken.setKeyIdentifier(customEKTokenValueType, customEKTokenId); + break; default: throw new WSSecurityException(WSSecurityException.FAILURE, "unsupportedKeyId"); @@ -533,4 +547,11 @@ public class WSSecEncryptedKey extends W return encryptedEphemeralKey; } + public void setCustomEKTokenValueType(String customEKTokenValueType) { + this.customEKTokenValueType = customEKTokenValueType; + } + + public void setCustomEKTokenId(String customEKTokenId) { + this.customEKTokenId = customEKTokenId; + } } Modified: webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/processor/EncryptedKeyProcessor.java URL: http://svn.apache.org/viewvc/webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/processor/EncryptedKeyProcessor.java?rev=1037678&r1=1037677&r2=1037678&view=diff ============================================================================== --- webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/processor/EncryptedKeyProcessor.java (original) +++ webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/processor/EncryptedKeyProcessor.java Mon Nov 22 11:42:02 2010 @@ -31,6 +31,8 @@ import org.apache.ws.security.WSSecurity import org.apache.ws.security.components.crypto.Crypto; import org.apache.ws.security.message.token.SecurityTokenReference; import org.apache.ws.security.message.token.X509Security; +import org.apache.ws.security.saml.SAMLKeyInfo; +import org.apache.ws.security.saml.SAMLUtil; import org.apache.ws.security.util.Base64; import org.apache.ws.security.util.WSSecurityUtil; import org.w3c.dom.Document; @@ -66,6 +68,8 @@ public class EncryptedKeyProcessor imple private X509Certificate cert = null; private String encryptedKeyTransportMethod = null; + + private WSDocInfo docInfo = null; public void handleToken( Element elem, @@ -85,6 +89,7 @@ public class EncryptedKeyProcessor imple if (cb == null) { throw new WSSecurityException(WSSecurityException.FAILURE, "noCallback"); } + docInfo = wsDocInfo; ArrayList dataRefUris = handleEncryptedKey((Element) elem, cb, decCrypto); encryptedKeyId = elem.getAttributeNS(null, "Id"); @@ -215,7 +220,21 @@ public class EncryptedKeyProcessor imple // This method is _not_ recommended by OASIS WS-S specification, X509 profile // else if (secRef.containsKeyIdentifier()) { - X509Certificate[] certs = secRef.getKeyIdentifier(crypto); + X509Certificate[] certs = null; + if (WSConstants.WSS_SAML_KI_VALUE_TYPE.equals(secRef.getKeyIdentifierValueType())) { + Element token = + secRef.getKeyIdentifierTokenElement(doc, docInfo, cb); + + if (crypto == null) { + throw new WSSecurityException( + WSSecurityException.FAILURE, "noSigCryptoFile" + ); + } + SAMLKeyInfo samlKi = SAMLUtil.getSAMLKeyInfo(token, crypto, cb); + certs = samlKi.getCerts(); + } else { + certs = secRef.getKeyIdentifier(crypto); + } if (certs == null || certs.length < 1 || certs[0] == null) { throw new WSSecurityException( WSSecurityException.FAILURE, Modified: webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/processor/ReferenceListProcessor.java URL: http://svn.apache.org/viewvc/webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/processor/ReferenceListProcessor.java?rev=1037678&r1=1037677&r2=1037678&view=diff ============================================================================== --- webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/processor/ReferenceListProcessor.java (original) +++ webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/processor/ReferenceListProcessor.java Mon Nov 22 11:42:02 2010 @@ -312,10 +312,20 @@ public class ReferenceListProcessor impl id = id.substring(1); } Processor p = wsDocInfo.getProcessor(id); - if (!(p instanceof EncryptedKeyProcessor - || p instanceof DerivedKeyTokenProcessor - || p instanceof SAMLTokenProcessor) - ) { + if (p instanceof EncryptedKeyProcessor) { + EncryptedKeyProcessor ekp = (EncryptedKeyProcessor) p; + decryptedData = ekp.getDecryptedBytes(); + } else if (p instanceof DerivedKeyTokenProcessor) { + DerivedKeyTokenProcessor dkp = (DerivedKeyTokenProcessor) p; + decryptedData = dkp.getKeyBytes(WSSecurityUtil.getKeyLength(algorithm)); + } else if (p instanceof SAMLTokenProcessor) { + SAMLTokenProcessor samlp = (SAMLTokenProcessor) p; + SAMLKeyInfo keyInfo = + SAMLUtil.getSAMLKeyInfo(samlp.getSamlTokenElement(), crypto, cb); + // TODO Handle malformed SAML tokens where they don't have the + // secret in them + decryptedData = keyInfo.getSecret(); + } else { // Try custom token WSPasswordCallback pwcb = new WSPasswordCallback(id, WSPasswordCallback.CUSTOM_TOKEN); try { @@ -337,42 +347,44 @@ public class ReferenceListProcessor impl ); } } - if (p instanceof EncryptedKeyProcessor) { - EncryptedKeyProcessor ekp = (EncryptedKeyProcessor) p; - decryptedData = ekp.getDecryptedBytes(); - } else if (p instanceof DerivedKeyTokenProcessor) { - DerivedKeyTokenProcessor dkp = (DerivedKeyTokenProcessor) p; - decryptedData = dkp.getKeyBytes(WSSecurityUtil.getKeyLength(algorithm)); - } else if (p instanceof SAMLTokenProcessor) { - SAMLTokenProcessor samlp = (SAMLTokenProcessor) p; - SAMLKeyInfo keyInfo = - SAMLUtil.getSAMLKeyInfo(samlp.getSamlTokenElement(), crypto, cb); + } else if (secRef.containsKeyIdentifier()) { + if (WSConstants.WSS_SAML_KI_VALUE_TYPE.equals(secRef.getKeyIdentifierValueType())) { + Element token = + secRef.getKeyIdentifierTokenElement(secRefToken.getOwnerDocument(), wsDocInfo, cb); + + if (crypto == null) { + throw new WSSecurityException( + WSSecurityException.FAILURE, "noSigCryptoFile" + ); + } + SAMLKeyInfo keyInfo = SAMLUtil.getSAMLKeyInfo(token, crypto, cb); // TODO Handle malformed SAML tokens where they don't have the // secret in them decryptedData = keyInfo.getSecret(); + } else { + String sha = secRef.getKeyIdentifierValue(); + + WSPasswordCallback pwcb = + new WSPasswordCallback( + secRef.getKeyIdentifierValue(), + null, + secRef.getKeyIdentifierValueType(), + WSPasswordCallback.ENCRYPTED_KEY_TOKEN + ); + + try { + Callback[] callbacks = new Callback[]{pwcb}; + cb.handle(callbacks); + } catch (Exception e) { + throw new WSSecurityException( + WSSecurityException.FAILURE, + "noPassword", + new Object[] {sha}, + e + ); + } + decryptedData = pwcb.getKey(); } - } else if (secRef.containsKeyIdentifier()){ - String sha = secRef.getKeyIdentifierValue(); - WSPasswordCallback pwcb = - new WSPasswordCallback( - secRef.getKeyIdentifierValue(), - null, - secRef.getKeyIdentifierValueType(), - WSPasswordCallback.ENCRYPTED_KEY_TOKEN - ); - - try { - Callback[] callbacks = new Callback[]{pwcb}; - cb.handle(callbacks); - } catch (Exception e) { - throw new WSSecurityException( - WSSecurityException.FAILURE, - "noPassword", - new Object[] {sha}, - e - ); - } - decryptedData = pwcb.getKey(); } else { throw new WSSecurityException(WSSecurityException.FAILED_CHECK, "noReference"); } Modified: webservices/wss4j/branches/1_5_x-fixes/test/wssec/TestWSSecurityNew3.java URL: http://svn.apache.org/viewvc/webservices/wss4j/branches/1_5_x-fixes/test/wssec/TestWSSecurityNew3.java?rev=1037678&r1=1037677&r2=1037678&view=diff ============================================================================== --- webservices/wss4j/branches/1_5_x-fixes/test/wssec/TestWSSecurityNew3.java (original) +++ webservices/wss4j/branches/1_5_x-fixes/test/wssec/TestWSSecurityNew3.java Mon Nov 22 11:42:02 2010 @@ -250,7 +250,7 @@ public class TestWSSecurityNew3 extends * e.g.: pc.setPassword(passStore.getPassword(pc.getIdentfifier)) * for Testing we supply a fixed name here. */ - pc.setPassword("password"); + pc.setPassword("security"); } else { throw new UnsupportedCallbackException(callbacks[i], "Unrecognized Callback"); } Added: webservices/wss4j/branches/1_5_x-fixes/test/wssec/TestWSSecuritySAMLKeyIdentifier.java URL: http://svn.apache.org/viewvc/webservices/wss4j/branches/1_5_x-fixes/test/wssec/TestWSSecuritySAMLKeyIdentifier.java?rev=1037678&view=auto ============================================================================== --- webservices/wss4j/branches/1_5_x-fixes/test/wssec/TestWSSecuritySAMLKeyIdentifier.java (added) +++ webservices/wss4j/branches/1_5_x-fixes/test/wssec/TestWSSecuritySAMLKeyIdentifier.java Mon Nov 22 11:42:02 2010 @@ -0,0 +1,211 @@ +/* + * Copyright 2010 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package wssec; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +import org.apache.ws.security.saml.SAMLIssuerFactory; +import org.apache.ws.security.saml.SAMLIssuer; +import org.apache.ws.security.util.WSSecurityUtil; + +import org.apache.axis.Message; +import org.apache.axis.MessageContext; +import org.apache.axis.client.AxisClient; +import org.apache.axis.configuration.NullProvider; +import org.apache.axis.message.SOAPEnvelope; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.ws.security.WSConstants; +import org.apache.ws.security.WSEncryptionPart; +import org.apache.ws.security.WSPasswordCallback; +import org.apache.ws.security.WSSecurityEngine; +import org.apache.ws.security.WSSecurityEngineResult; +import org.apache.ws.security.components.crypto.Crypto; +import org.apache.ws.security.components.crypto.CryptoFactory; +import org.apache.ws.security.message.WSSecEncrypt; +import org.apache.ws.security.message.WSSecHeader; +import org.apache.ws.security.message.token.SecurityTokenReference; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import org.opensaml.SAMLAssertion; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Vector; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.UnsupportedCallbackException; + +/** + * Test-case for checking KeyIdentifier (and not Reference) elements + * are used to identify SAML tokens + * + */ +public class TestWSSecuritySAMLKeyIdentifier extends TestCase implements CallbackHandler { + private static final Log LOG = LogFactory.getLog(TestWSSecuritySAMLKeyIdentifier.class); + private static final String SOAPMSG = + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + + "<SOAP-ENV:Envelope " + + "xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" " + + "xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" " + + "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" + + "<SOAP-ENV:Body>" + + "<ns1:testMethod xmlns:ns1=\"uri:LogTestService2\"></ns1:testMethod>" + + "</SOAP-ENV:Body>" + + "</SOAP-ENV:Envelope>"; + + private WSSecurityEngine secEngine = new WSSecurityEngine(); + private MessageContext msgContext; + private Message message; + + /** + * TestWSSecurity constructor + * + * @param name name of the test + */ + public TestWSSecuritySAMLKeyIdentifier(String name) { + super(name); + } + + /** + * JUnit suite + * + * @return a junit test suite + */ + public static Test suite() { + return new TestSuite(TestWSSecuritySAMLKeyIdentifier.class); + } + + /** + * Setup method + * + * @throws Exception Thrown when there is a problem in setup + */ + protected void setUp() throws Exception { + AxisClient tmpEngine = new AxisClient(new NullProvider()); + msgContext = new MessageContext(tmpEngine); + message = getSOAPMessage(); + } + + /** + * Constructs a soap envelope + * + * @return soap envelope + * @throws Exception if there is any problem constructing the soap envelope + */ + protected Message getSOAPMessage() throws Exception { + InputStream in = new ByteArrayInputStream(SOAPMSG.getBytes()); + Message msg = new Message(in); + msg.setMessageContext(msgContext); + return msg; + } + + /** + * The body of the SOAP request is encrypted using a secret key, which is in turn encrypted + * using the certificate embedded in the SAML assertion and referenced using a Key Identifier. + */ + public void testSAMLEncryptedKey() throws Exception { + SOAPEnvelope unsignedEnvelope = message.getSOAPEnvelope(); + + // Create a SAML assertion + SAMLIssuer saml = SAMLIssuerFactory.getInstance("saml4.properties"); + Document doc = unsignedEnvelope.getAsDocument(); + saml.setInstanceDoc(doc); + Crypto hokCrypto = CryptoFactory.getInstance("crypto.properties"); + saml.setUserCrypto(hokCrypto); + saml.setUsername("16c73ab6-b892-458f-abf5-2f875f74882e"); + SAMLAssertion assertion = saml.newAssertion(); + Node assertionNode = assertion.toDOM(doc); + + WSSecHeader secHeader = new WSSecHeader(); + secHeader.insertSecurityHeader(doc); + secHeader.getSecurityHeader().appendChild(assertionNode); + + // Encrypt the SOAP body + WSSecEncrypt builder = new WSSecEncrypt(); + builder.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e"); + builder.setSymmetricEncAlgorithm(WSConstants.TRIPLE_DES); + builder.setKeyIdentifierType(WSConstants.CUSTOM_KEY_IDENTIFIER); + builder.setCustomEKTokenValueType(SecurityTokenReference.SAML_ID_URI); + builder.setCustomEKTokenId(assertion.getId()); + + builder.prepare(doc, hokCrypto); + Vector parts = new Vector(); + WSEncryptionPart encP = new WSEncryptionPart("testMethod", "uri:LogTestService2", "Element"); + parts.add(encP); + Element refElement = builder.encryptForInternalRef(null, parts); + builder.addInternalRefElement(refElement); + builder.appendToHeader(secHeader); + + if (LOG.isDebugEnabled()) { + LOG.debug("Signed SAML message (HOK):"); + String outputString = + org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(doc); + LOG.debug(outputString); + } + + Vector results = verify(doc, hokCrypto); + WSSecurityEngineResult actionResult = + WSSecurityUtil.fetchActionResult(results, WSConstants.ST_UNSIGNED); + SAMLAssertion receivedAssertion = + (SAMLAssertion) actionResult.get(WSSecurityEngineResult.TAG_SAML_ASSERTION); + assertTrue(receivedAssertion != null); + } + + + /** + * Verifies the soap envelope + * + * @param doc + * @throws Exception Thrown when there is a problem in verification + */ + private Vector verify(Document doc, Crypto verifyCrypto) throws Exception { + Vector results = secEngine.processSecurityHeader(doc, null, this, verifyCrypto); + String outputString = + org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(doc); + assertTrue(outputString.indexOf("LogTestService2") > 0 ? true : false); + return results; + } + + public void handle(Callback[] callbacks) + throws IOException, UnsupportedCallbackException { + for (int i = 0; i < callbacks.length; i++) { + if (callbacks[i] instanceof WSPasswordCallback) { + WSPasswordCallback pc = (WSPasswordCallback) callbacks[i]; + /* + * here call a function/method to lookup the password for + * the given identifier (e.g. a user name or keystore alias) + * e.g.: pc.setPassword(passStore.getPassword(pc.getIdentfifier)) + * for Testing we supply a fixed name here. + */ + pc.setPassword("security"); + } else { + throw new UnsupportedCallbackException(callbacks[i], "Unrecognized Callback"); + } + } + } + + +} --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@ws.apache.org For additional commands, e-mail: dev-h...@ws.apache.org