Index: src/org/apache/xml/security/encryption/XMLCipher.java =================================================================== --- src/org/apache/xml/security/encryption/XMLCipher.java (revision 957208) +++ src/org/apache/xml/security/encryption/XMLCipher.java (working copy) @@ -1264,7 +1264,14 @@ KeyInfo ki = encryptedKey.getKeyInfo(); if (ki != null) { try { - _key = ki.getSecretKey(); + String keyWrapAlg = encryptedKey.getEncryptionMethod().getAlgorithm(); + String keyType = JCEMapper.getJCEKeyAlgorithmFromURI(keyWrapAlg); + if ("RSA".equals(keyType)) { + _key = ki.getPrivateKey(); + } + else { + _key = ki.getSecretKey(); + } } catch (Exception e) { } Index: src/org/apache/xml/security/keys/KeyInfo.java =================================================================== --- src/org/apache/xml/security/keys/KeyInfo.java (revision 957208) +++ src/org/apache/xml/security/keys/KeyInfo.java (working copy) @@ -18,6 +18,7 @@ +import java.security.PrivateKey; import java.security.PublicKey; import java.security.cert.X509Certificate; import java.util.ArrayList; @@ -93,9 +94,14 @@ /** {@link org.apache.commons.logging} logging facility */ static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(KeyInfo.class.getName()); + List x509Datas=null; List encryptedKeys=null; + // We need at least one StorageResolver otherwise + // the KeyResolvers would not be called. + // The default StorageResolver is null. + static final List nullList; static { List list = new ArrayList(); @@ -716,9 +722,9 @@ } /** - * Searches the library wide keyresolvers for public keys + * Searches the library wide KeyResolvers for public keys * - * @return The publick contained in this Node. + * @return The public key contained in this Node. * @throws KeyResolverException */ PublicKey getPublicKeyFromStaticResolvers() throws KeyResolverException { @@ -753,9 +759,9 @@ } /** - * Searches the per-KeyInfo keyresolvers for public keys + * Searches the per-KeyInfo KeyResolvers for public keys * - * @return The publick contained in this Node. + * @return The public key contained in this Node. * @throws KeyResolverException */ PublicKey getPublicKeyFromInternalResolvers() throws KeyResolverException { @@ -791,7 +797,7 @@ /** * Method getX509Certificate * - * @return The certificate contined in this KeyInfo + * @return The certificate contained in this KeyInfo * @throws KeyResolverException */ public X509Certificate getX509Certificate() throws KeyResolverException { @@ -931,16 +937,16 @@ } /** - * Searches the library wide keyresolvers for Secret keys + * Searches the library wide KeyResolvers for Secret keys * * @return the secret key contained in this KeyInfo * @throws KeyResolverException */ SecretKey getSecretKeyFromStaticResolvers() throws KeyResolverException { - final int length=KeyResolver.length(); - int storageLength=this._storageResolvers.size(); - Iterator it = KeyResolver.iterator(); + final int length=KeyResolver.length(); + int storageLength=this._storageResolvers.size(); + Iterator it = KeyResolver.iterator(); for (int i = 0; i < length; i++) { KeyResolverSpi keyResolver = (KeyResolverSpi) it.next(); @@ -969,18 +975,18 @@ } /** - * Searches the per-KeyInfo keyresolvers for secret keys + * Searches the per-KeyInfo KeyResolvers for secret keys * * @return the secret key contained in this KeyInfo * @throws KeyResolverException */ SecretKey getSecretKeyFromInternalResolvers() throws KeyResolverException { - int storageLength=this._storageResolvers.size(); + int storageLength=this._storageResolvers.size(); for (int i = 0; i < this.lengthInternalKeyResolver(); i++) { KeyResolverSpi keyResolver = this.itemInternalKeyResolver(i); if (log.isDebugEnabled()) - log.debug("Try " + keyResolver.getClass().getName()); + log.debug("Try " + keyResolver.getClass().getName()); Node currentChild=this._constructionElement.getFirstChild(); String uri=this.getBaseURI(); @@ -1006,6 +1012,104 @@ } /** + * This method returns a private key. This is for Key Transport in XML Encryption. + * @return the private key contained in this KeyInfo + * @throws KeyResolverException + */ + public PrivateKey getPrivateKey() throws KeyResolverException { + PrivateKey pk = this.getPrivateKeyFromInternalResolvers(); + + if (pk != null) { + log.debug("I could find a private key using the per-KeyInfo key resolvers"); + + return pk; + } + log.debug("I couldn't find a secret key using the per-KeyInfo key resolvers"); + + + pk = this.getPrivateKeyFromStaticResolvers(); + + if (pk != null) { + log.debug("I could find a private key using the system-wide key resolvers"); + + return pk; + } + log.debug("I couldn't find a private key using the system-wide key resolvers"); + + return null; + } + + /** + * Searches the library wide KeyResolvers for Private keys + * + * @return the private key contained in this KeyInfo + * @throws KeyResolverException + */ + + PrivateKey getPrivateKeyFromStaticResolvers() throws KeyResolverException { + final int length=KeyResolver.length(); + Iterator it = KeyResolver.iterator(); + for (int i = 0; i < length; i++) { + KeyResolverSpi keyResolver = (KeyResolverSpi) it.next(); + + Node currentChild=this._constructionElement.getFirstChild(); + String uri=this.getBaseURI(); + while (currentChild!=null) { + if (currentChild.getNodeType() == Node.ELEMENT_NODE) { + // not using StorageResolvers at the moment + // since they cannot return private keys + StorageResolver storage = null; + PrivateKey pk = keyResolver.engineLookupAndResolvePrivateKey( + (Element) currentChild, + uri, + storage); + + if (pk != null) { + return pk; + } + } + currentChild=currentChild.getNextSibling(); + } + } + return null; + } + + /** + * Searches the per-KeyInfo KeyResolvers for private keys + * + * @return the private key contained in this KeyInfo + * @throws KeyResolverException + */ + + PrivateKey getPrivateKeyFromInternalResolvers() throws KeyResolverException { + int storageLength=this._storageResolvers.size(); + for (int i = 0; i < this.lengthInternalKeyResolver(); i++) { + KeyResolverSpi keyResolver = this.itemInternalKeyResolver(i); + if (log.isDebugEnabled()) + log.debug("Try " + keyResolver.getClass().getName()); + + Node currentChild=this._constructionElement.getFirstChild(); + String uri=this.getBaseURI(); + while (currentChild!=null) { + if (currentChild.getNodeType() == Node.ELEMENT_NODE) { + // not using StorageResolvers at the moment + // since they cannot return private keys + StorageResolver storage = null; + PrivateKey pk = keyResolver + .engineLookupAndResolvePrivateKey((Element) currentChild, uri, storage); + + if (pk != null) { + return pk; + } + } + currentChild=currentChild.getNextSibling(); + } + } + + return null; + } + + /** * Stores the individual (per-KeyInfo) {@link KeyResolver}s */ List _internalKeyResolvers = null; @@ -1052,11 +1156,11 @@ * @param storageResolver */ public void addStorageResolver(StorageResolver storageResolver) { - if (_storageResolvers == nullList ){ - _storageResolvers=new ArrayList(); + if (_storageResolvers == nullList) { + // Replace the default null StorageResolver + _storageResolvers = new ArrayList(); } - this._storageResolvers.add(storageResolver); - + this._storageResolvers.add(storageResolver); } //J- Index: src/org/apache/xml/security/keys/keyresolver/KeyResolverSpi.java =================================================================== --- src/org/apache/xml/security/keys/keyresolver/KeyResolverSpi.java (revision 957208) +++ src/org/apache/xml/security/keys/keyresolver/KeyResolverSpi.java (working copy) @@ -16,6 +16,7 @@ */ package org.apache.xml.security.keys.keyresolver; +import java.security.PrivateKey; import java.security.PublicKey; import java.security.cert.X509Certificate; import java.util.HashMap; @@ -40,8 +41,7 @@ */ public abstract class KeyResolverSpi { /** - * This method helps the {@link org.apache.xml.security.utils.resolver.ResourceResolver} to decide whether a - * {@link org.apache.xml.security.utils.resolver.ResourceResolverSpi} is able to perform the requested action. + * This method returns whether the KeyResolverSpi is able to perform the requested action. * * @param element * @param BaseURI @@ -70,7 +70,7 @@ }; /** - * Method engineResolvePublicKey + * Method engineLookupAndResolvePublicKey * * @param element * @param BaseURI @@ -119,7 +119,7 @@ }; /** - * Method engineResolveCertificate + * Method engineLookupResolveX509Certificate * * @param element * @param BaseURI @@ -154,7 +154,7 @@ }; /** - * Method engineResolveSecretKey + * Method engineLookupAndResolveSecretKey * * @param element * @param BaseURI @@ -171,6 +171,27 @@ return null; return tmp.engineResolveSecretKey(element, BaseURI, storage); } + + /** + * Method engineLookupAndResolvePrivateKey + * + * @param element + * @param BaseURI + * @param storage + * @return resolved PrivateKey key from the registered from the elements + * + * @throws KeyResolverException + */ + public PrivateKey engineLookupAndResolvePrivateKey( + Element element, String BaseURI, StorageResolver storage) + throws KeyResolverException { + // This method was added later, it has no equivalent + // engineResolvePrivateKey() in the old API. + // We cannot throw UnsupportedOperationException because + // KeyResolverSpi implementations who don't know about + // this method would stop the search too early. + return null; + } /** Field _properties */ protected java.util.Map _properties = null; Index: src/org/apache/xml/security/keys/keyresolver/implementations/RetrievalMethodResolver.java =================================================================== --- src/org/apache/xml/security/keys/keyresolver/implementations/RetrievalMethodResolver.java (revision 957208) +++ src/org/apache/xml/security/keys/keyresolver/implementations/RetrievalMethodResolver.java (working copy) @@ -193,7 +193,7 @@ } /** - * Retrieves a x509Certificate from the given information + * Retrieves a PublicKey from the given information * @param e * @param BaseURI * @param storage Index: src_unitTests/org/apache/xml/security/test/ModuleTest.java =================================================================== --- src_unitTests/org/apache/xml/security/test/ModuleTest.java (revision 957208) +++ src_unitTests/org/apache/xml/security/test/ModuleTest.java (working copy) @@ -57,6 +57,7 @@ suite.addTest(org.apache.xml.security.test.transforms.implementations.AllTests.suite()); suite.addTest(org.apache.xml.security.test.transforms.RegisterTest.suite()); suite.addTest(org.apache.xml.security.test.algorithms.AllTests.suite()); + suite.addTest(org.apache.xml.security.test.keys.keyresolver.KeyResolverTest.suite()); suite.addTest(org.apache.xml.security.test.keys.content.x509.XMLX509SKITest.suite()); suite.addTest(org.apache.xml.security.test.keys.content.x509.XMLX509IssuerSerialTest.suite()); suite.addTest(org.apache.xml.security.test.keys.content.x509.XMLX509CertificateTest.suite()); Index: src_unitTests/org/apache/xml/security/test/keys/keyresolver/KeyResolverTest.java =================================================================== --- src_unitTests/org/apache/xml/security/test/keys/keyresolver/KeyResolverTest.java (revision 0) +++ src_unitTests/org/apache/xml/security/test/keys/keyresolver/KeyResolverTest.java (revision 0) @@ -0,0 +1,181 @@ +/* + * Copyright 2008-2009 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.xml.security.test.keys.keyresolver; + +import java.math.BigInteger; +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.RSAPrivateKeySpec; +import java.security.spec.RSAPublicKeySpec; + +import javax.crypto.spec.SecretKeySpec; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +import org.apache.xml.security.encryption.EncryptedData; +import org.apache.xml.security.encryption.EncryptedKey; +import org.apache.xml.security.encryption.XMLCipher; +import org.apache.xml.security.keys.KeyInfo; +import org.apache.xml.security.keys.keyresolver.KeyResolver; +import org.apache.xml.security.keys.keyresolver.KeyResolverException; +import org.apache.xml.security.keys.keyresolver.KeyResolverSpi; +import org.apache.xml.security.keys.storage.StorageResolver; +import org.apache.xml.security.utils.Constants; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Text; + +/** + * KeyResolver test. + */ +public class KeyResolverTest extends TestCase { + + public KeyResolverTest() { + super("KeyResolverTest"); + } + + public KeyResolverTest(String name) { + super(name); + } + + public static Test suite() { + return new TestSuite(KeyResolverTest.class); + } + + /** + * Encrypt some data, embedded the data encryption key + * in the message using the key transport algorithm rsa-1_5. + * Decrypt the data by resolving the Key Encryption Key. + * This test verifies if a KeyResolver can return a PrivateKey. + */ + public void testResolvePrivateKey() throws Exception { + // Create a sample XML document + DocumentBuilderFactory fac = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = fac.newDocumentBuilder(); + Document document = builder.newDocument(); + + Element rootElement = document.createElement("root"); + document.appendChild(rootElement); + Element elem = document.createElement("elem"); + Text text = document.createTextNode("text"); + elem.appendChild(text); + rootElement.appendChild(elem); + + // Create a data encryption key + byte[] keyBytes = { 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7 }; + SecretKeySpec dataEncryptKey = new SecretKeySpec(keyBytes, "AES"); + + // Create public and private keys + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec( + new BigInteger( + "8710a2bcb2f3fdac177f0ae0461c2dd0ebf72e0d88a5400583a7d8bdabd6" + + "ae009d30cfdf6acb5b6a64cdc730bc630a39d946d08babffe62ea20a87e37c93b3b0e8a8e576045b" + + "bddfbde83ca9bfa180fe6a5f5eee60661936d728314e809201ef52cd71d9fa3c8ce83f9d30ab5e08" + + "1539219e7e45dd6a60be65ac95d2049b8f21", 16), + new BigInteger("10001", 16)); + + RSAPrivateKeySpec privKeySpec = new RSAPrivateKeySpec( + new BigInteger( + "8710a2bcb2f3fdac177f0ae0461c2dd0ebf72e0d88a5400583a7d8bdabd" + + "6ae009d30cfdf6acb5b6a64cdc730bc630a39d946d08babffe62ea20a87e37c93b3b0e8a8e576045" + + "bbddfbde83ca9bfa180fe6a5f5eee60661936d728314e809201ef52cd71d9fa3c8ce83f9d30ab5e0" + + "81539219e7e45dd6a60be65ac95d2049b8f21", 16), + new BigInteger( + "20c39e569c2aa80cc91e5e6b0d56e49e5bbf78827bf56a546c1d996c597" + + "5187cb9a50fa828e5efe51d52f5d112c20bc700b836facadca6e0051afcdfe866841e37d207c0295" + + "36ff8674b301e2198b2c56abb0a0313f8ff84c1fcd6fa541aa6e5d9c018fab4784d2940def5dc709" + + "ddc714d73b6c23b5d178eaa5933577b8e8ae9", 16)); + + RSAPublicKey pubKey = (RSAPublicKey) keyFactory.generatePublic(pubKeySpec); + RSAPrivateKey privKey = (RSAPrivateKey) keyFactory.generatePrivate(privKeySpec); + + // Encrypt the data encryption key with the key encryption key + XMLCipher keyCipher = XMLCipher.getInstance(XMLCipher.RSA_v1dot5); + keyCipher.init(XMLCipher.WRAP_MODE, pubKey); + EncryptedKey encryptedKey = keyCipher.encryptKey(document, dataEncryptKey); + + String keyName = "testResolvePrivateKey"; + KeyInfo kekInfo = new KeyInfo(document); + kekInfo.addKeyName(keyName); + encryptedKey.setKeyInfo(kekInfo); + + // Encrypt the data + XMLCipher xmlCipher = XMLCipher.getInstance(XMLCipher.AES_128); + xmlCipher.init(XMLCipher.ENCRYPT_MODE, dataEncryptKey); + + EncryptedData encryptedData = xmlCipher.getEncryptedData(); + KeyInfo keyInfo = new KeyInfo(document); + keyInfo.add(encryptedKey); + encryptedData.setKeyInfo(keyInfo); + + xmlCipher.doFinal(document, rootElement, true); + + Element encryptedDataElement = (Element) rootElement.getFirstChild(); + assertEquals("EncryptedData", encryptedDataElement.getLocalName()); + + // Register a KeyResolver for the PrivateKey + MyPrivateKeyResolver.pk = privKey; + MyPrivateKeyResolver.pkName = keyName; + KeyResolver.registerAtStart(MyPrivateKeyResolver.class.getName()); + KeyResolverSpi resolver = (KeyResolverSpi)KeyResolver.iterator().next(); + assertEquals(MyPrivateKeyResolver.class.getName(), resolver.getClass().getName()); + + // Decrypt the data by resolving the private key used as the KEK + XMLCipher decryptCipher = XMLCipher.getInstance(); + decryptCipher.init(XMLCipher.DECRYPT_MODE, null); + decryptCipher.doFinal(document, encryptedDataElement); + + Element decryptedElement = (Element) rootElement.getFirstChild(); + assertEquals("elem", decryptedElement.getLocalName()); + } + + // A KeyResolver that returns a PrivateKey for a specific KeyName. + public static class MyPrivateKeyResolver extends KeyResolverSpi { + + // We use static variables because KeyResolver.register() demands + // the use of the default constructor. + private static PrivateKey pk; + private static String pkName; + + public boolean engineCanResolve(Element element, String BaseURI, + StorageResolver storage) { + return false; + } + + public PrivateKey engineLookupAndResolvePrivateKey(Element element, + String BaseURI, StorageResolver storage) + throws KeyResolverException { + + if (Constants.SignatureSpecNS.equals(element.getNamespaceURI()) && + Constants._TAG_KEYNAME.equals(element.getLocalName())) { + String keyName = element.getFirstChild().getNodeValue(); + if (pkName.equals(keyName)) { + return pk; + } + } + + return null; + } + } +}