Add support for reading PKCS8 RSA keys in PEM format.
authorwassa <wassa@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Thu, 4 Dec 2003 07:35:09 +0000 (07:35 +0000)
committerwassa <wassa@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Thu, 4 Dec 2003 07:35:09 +0000 (07:35 +0000)
git-svn-id: https://subversion.switch.ch/svn/shibboleth/java-idp/trunk@799 ab3bd59b-922f-494d-bb5f-6f0a3c29deca

data/credentials2.xml
data/credentials4.xml
data/credentials5.xml [new file with mode: 0644]
src/edu/internet2/middleware/shibboleth/common/Credentials.java
tests/conf/test.pkcs1.derkey [new file with mode: 0644]
tests/conf/test.pkcs1.enc.pemkey [new file with mode: 0644]
tests/conf/test.pkcs1.pemkey [new file with mode: 0644]
tests/conf/test.pkcs8.enc.derkey [new file with mode: 0644]
tests/conf/test.pkcs8.enc.pemkey [new file with mode: 0644]
tests/conf/test.pkcs8.pemkey [new file with mode: 0644]
tests/edu/internet2/middleware/shibboleth/common/CredentialsTests.java

index 548ba36..9e4c5cb 100644 (file)
@@ -7,7 +7,7 @@
                <Certificate format="PEM">
                        <Path>/conf/test.pemcrt</Path>
                </Certificate>
-               <Key format="DER-PKCS8">
+               <Key format="DER">
                        <Path>/conf/test.pkcs8.derkey</Path>
                </Key>
        </FileResolver>
index 61a8ed4..36b693f 100644 (file)
@@ -9,7 +9,7 @@
                        <CAPath>/conf/test.single.2.pemcrt</CAPath>
                        <CAPath>/conf/test.single.3.pemcrt</CAPath>
                </Certificate>
-               <Key format="DER-PKCS8">
+               <Key format="DER">
                        <Path>/conf/test.pkcs8.derkey</Path>
                </Key>
        </FileResolver>
diff --git a/data/credentials5.xml b/data/credentials5.xml
new file mode 100644 (file)
index 0000000..945dd8a
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Credentials xmlns="urn:mace:shibboleth:credentials:1.0" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" 
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
+       xsi:schemaLocation="urn:mace:shibboleth:credentials:1.0 credentials.xsd">
+       
+       <FileResolver Id="test">
+               <Certificate format="PEM">
+                       <Path>/conf/test.pemcrt</Path>
+               </Certificate>
+               <Key format="PEM">
+                       <Path>/conf/test.pkcs8.pemkey</Path>
+               </Key>
+       </FileResolver>
+</Credentials>
\ No newline at end of file
index 2a555db..5f57c3b 100644 (file)
@@ -64,6 +64,8 @@ import org.w3c.dom.Element;
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
 
+import sun.misc.BASE64Decoder;
+
 /**
  * @author Walter Hoehn
  *  
@@ -192,15 +194,29 @@ class FileCredentialResolver implements CredentialResolver {
 
                PrivateKey key = null;
 
-               if (keyAlgorithm.equals("RSA") && keyFormat.equals("DER-PKCS8")) {
-                       try {
-                               key = getRSADERKey(new ShibResource(keyPath, this.getClass()).getInputStream());
-                       } catch (IOException ioe) {
-                               log.error("Could not load resource from specified location (" + keyPath + "): " + e);
-                               throw new CredentialFactoryException("Unable to load private key.");
+               if (keyAlgorithm.equals("RSA")) {
+
+                       if (keyFormat.equals("DER")) {
+                               try {
+                                       key = getRSADERKey(new ShibResource(keyPath, this.getClass()).getInputStream());
+                               } catch (IOException ioe) {
+                                       log.error("Could not load resource from specified location (" + keyPath + "): " + e);
+                                       throw new CredentialFactoryException("Unable to load private key.");
+                               }
+                       } else if (keyFormat.equals("PEM")) {
+                               try {
+                                       key = getRSAPEMKey(new ShibResource(keyPath, this.getClass()).getInputStream());
+                               } catch (IOException ioe) {
+                                       log.error("Could not load resource from specified location (" + keyPath + "): " + e);
+                                       throw new CredentialFactoryException("Unable to load private key.");
+                               }
+                       } else {
+                               log.error("File credential resolver only supports (DER) and (PEM) formats.");
+                               throw new CredentialFactoryException("Failed to initialize Credential Resolver.");
                        }
+
                } else {
-                       log.error("File credential resolver only supports the RSA keys in DER-encoded PKCS8 format (DER-PKCS8).");
+                       log.error("File credential resolver only supports the RSA keys.");
                        throw new CredentialFactoryException("Failed to initialize Credential Resolver.");
                }
 
@@ -215,7 +231,7 @@ class FileCredentialResolver implements CredentialResolver {
                //TODO provider optional
                //TODO provide a way to specify a separate CA bundle
 
-               //The loading code should work for other types, but the chain construction code 
+               //The loading code should work for other types, but the chain construction code
                //would break
                if (!certType.equals("X.509")) {
                        log.error("File credential resolver only supports the X.509 certificates.");
@@ -334,6 +350,52 @@ class FileCredentialResolver implements CredentialResolver {
 
        }
 
+       private PrivateKey getRSAPEMKey(InputStream inStream) throws CredentialFactoryException {
+
+               try {
+
+                       BufferedReader in = new BufferedReader(new InputStreamReader(inStream));
+                       String str;
+                       boolean insideBase64 = false;
+                       StringBuffer base64Key = null;
+                       while ((str = in.readLine()) != null) {
+
+                               if (insideBase64) {
+                                       if (str.matches("^.*-----END PRIVATE KEY-----.*$")) {
+                                               break;
+                                       }
+                                       {
+                                               base64Key.append(str);
+                                       }
+                               } else if (str.matches("^.*-----BEGIN PRIVATE KEY-----.*$")) {
+                                       insideBase64 = true;
+                                       base64Key = new StringBuffer();
+                               }
+                       }
+                       in.close();
+                       if (base64Key == null || base64Key.length() == 0) {
+                               log.error("Did not find BASE 64 encoded private key in file.");
+                               throw new CredentialFactoryException("Unable to load private key.");
+                       }
+
+                       BASE64Decoder decoder = new BASE64Decoder();
+                       //Probably want to give a different error for this exception
+                       byte[] pkcs8Bytes = decoder.decodeBuffer(base64Key.toString());
+                       try {
+                               PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(pkcs8Bytes);
+                               KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+                               return keyFactory.generatePrivate(keySpec);
+
+                       } catch (Exception e) {
+                               log.error("Unable to load private key: " + e);
+                               throw new CredentialFactoryException("Unable to load private key.");
+                       }
+               } catch (IOException p) {
+                       log.error("Could not load resource from specified location: " + p);
+                       throw new CredentialFactoryException("Unable to load key.");
+               }
+       }
+
        private String getCertFormat(Element e) throws CredentialFactoryException {
 
                NodeList certificateElements = e.getElementsByTagNameNS(Credentials.credentialsNamespace, "Certificate");
@@ -375,12 +437,11 @@ class FileCredentialResolver implements CredentialResolver {
                        log.debug("No format specified for certificate, using default (PEM) format.");
                        format = "PEM";
                }
-
-               if (!format.equals("DER-PKCS8")) {
-                       log.error("File credential resolver currently only supports (DER-PKCS8) format.");
-                       throw new CredentialFactoryException("Failed to initialize Credential Resolver.");
-               }
-
+               //TODO smarter
+               /*
+                * if (!format.equals("DER-PKCS8")) { log.error("File credential resolver currently only supports (DER-PKCS8)
+                * format."); throw new CredentialFactoryException("Failed to initialize Credential Resolver."); }
+                */
                return format;
        }
 
@@ -484,10 +545,9 @@ class FileCredentialResolver implements CredentialResolver {
 
        /**
         * 
-        * Loads a specified bundle of certs individually and returns an array of 
-        * <code>Certificate</code> objects.  This is needed because the standard 
-        * <code>CertificateFactory.getCertificates(InputStream)</code> method bails 
-        * out when it has trouble loading any cert and cannot handle "comments".
+        * Loads a specified bundle of certs individually and returns an array of <code>Certificate</code> objects. This
+        * is needed because the standard <code>CertificateFactory.getCertificates(InputStream)</code> method bails out
+        * when it has trouble loading any cert and cannot handle "comments".
         */
        private Certificate[] loadCertificates(InputStream inStream, String certType) throws CredentialFactoryException {
 
@@ -543,13 +603,15 @@ class FileCredentialResolver implements CredentialResolver {
        }
 
        /**
-        * Given an ArrayList containing a base certificate and an array of unordered certificates, 
-        * populates the ArrayList with an ordered certificate chain, based on subject and issuer.
+        * Given an ArrayList containing a base certificate and an array of unordered certificates, populates the ArrayList
+        * with an ordered certificate chain, based on subject and issuer.
         * 
-        * @param       chainSource array of certificates to pull from
-        * @param       chainDest ArrayList containing base certificate
-        * @throws InvalidCertificateChainException thrown if a chain cannot be constructed from 
-        *                      the specified elements
+        * @param chainSource
+        *            array of certificates to pull from
+        * @param chainDest
+        *            ArrayList containing base certificate
+        * @throws InvalidCertificateChainException
+        *             thrown if a chain cannot be constructed from the specified elements
         */
 
        protected void walkChain(X509Certificate[] chainSource, ArrayList chainDest) throws CredentialFactoryException {
@@ -575,8 +637,10 @@ class FileCredentialResolver implements CredentialResolver {
        /**
         * Boolean indication of whether a given private key and public key form a valid keypair.
         * 
-        * @param pubKey the public key
-        * @param privKey the private key
+        * @param pubKey
+        *            the public key
+        * @param privKey
+        *            the private key
         */
 
        protected boolean isMatchingKey(PublicKey pubKey, PrivateKey privKey) {
@@ -640,8 +704,9 @@ class FileCredentialResolver implements CredentialResolver {
                        buffer = b;
                }
 
-               /** 
-                * Returns an array of the bytes in the container. <p>
+               /**
+                * Returns an array of the bytes in the container.
+                * <p>
                 */
 
                private byte[] toByteArray() {
@@ -652,7 +717,7 @@ class FileCredentialResolver implements CredentialResolver {
                        return b;
                }
 
-               /** 
+               /**
                 * Add one byte to the end of the container.
                 */
 
diff --git a/tests/conf/test.pkcs1.derkey b/tests/conf/test.pkcs1.derkey
new file mode 100644 (file)
index 0000000..f93603b
Binary files /dev/null and b/tests/conf/test.pkcs1.derkey differ
diff --git a/tests/conf/test.pkcs1.enc.pemkey b/tests/conf/test.pkcs1.enc.pemkey
new file mode 100644 (file)
index 0000000..83221b8
--- /dev/null
@@ -0,0 +1,18 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,24D0AC0A45FE49C6
+
++NPze4BLeIFQZiQtGM1c8vrkhJmA9HRknIk+YfCE2lEpcz9Ccg19WuDIrJrtAJ7q
+w1v7m4T2FfHv5Xbh+op+EWmuxz3FdI6VWNvu+lHIM43FuFe6qh2BAid0SVm5uD1m
+LTeOQk0pMIov55GkSEhdxs5KqFVHrsqSGs9ELRIvAj0Ib6BkMR032m3DvYPJgTmH
+65OLkgYw8R6c7OhYCaxlpUtwGvMklHirek05V31qn3zpW6TDQ2qBvylJqt5oCtt1
+Zx/AByDhLx2OiUmrRv12nwjdUtyLo7WvNfsHVg3Kk7rlZtK0LMlKC2GIhP2ibqgk
+Q3WYLZ+6JJfHAvV9xHlhPpK98Z0D6jGgFGKzgct0ttwwjTNFUwU8+J8YHKHD9Ts0
+QyQa7JASLApmgknDHH6T8RdVlMRhsC4R9es2IMcPqWW3zckEM5uwzMScmSRlCyIy
+ZkEu9NKWEQdcFnSNp/SGzOd2iX36LKXHDvQsGu1ewIO1d58vgYOYjZbjFFSCnIyC
+gsCFD/USLDFW0Qpq+rl9J1oLPfFCKiDlCwsBg8E9hw9ZwMzij9olq+qycoXtH6Eo
+kWpkndxeQ8GWkXw5D+hcOfbeOT4ABLQwVjXRF/RB+T/14R4BLxjzEGlBBg2ltmYL
+gi5Y1pYTy+6x7DGepLIvYiuaDhyD2XpCVj0b3vqfrWpsjmftJI9CQNHef3/ZSgfT
+07CJHtUeWWF0JTVrLrN8EZT/0t6t0n1nY1CUnIYDDRw+9a/6rK8t8IZMlI2Hf19b
+SOaTzMaAQt8VYH767PE/yuPyB4QLLL8Dg9dHDYvdf1OuF/5JypTrFg==
+-----END RSA PRIVATE KEY-----
diff --git a/tests/conf/test.pkcs1.pemkey b/tests/conf/test.pkcs1.pemkey
new file mode 100644 (file)
index 0000000..1dacda1
--- /dev/null
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQDMVK9L2pr9Swf2gkf67VAztznU/rujBFyoRRGFMOy1PdYEOw/n
+5hMgZSLAkyQziFAgjrIwjuKfoPNuEZDH1P+rrFGpcVeouTWetz+Ryz6FtHhPXXDB
+bq93WTe4FQcldX47O8wTDPZJLRHEVkPDkwaXAkaqTB7nA83pBRbMLG+myQIDAQAB
+AoGAEfCVe3l0Bi0iQ9hobXWMhx3dLAA5hTSK1hNKWIFVJwyIz9JHsTqYD04R4QqK
+MSQUtOcSagUBjLZa6R0Nsx3WXJoIuV3rkN4eTuE25hkpcpY2gxnk1rB4o2+oiRUR
+dBiG6nOIyJ7kLaCa1azlW1I8J+2B3D6SLY96Mg7VxiMedukCQQDyDuTtUCndXTEe
+vkTCmwqQGKtt0Adxqa8OMHZxF+qDmIuMCSAgl1IERy8g9vVlqbLWP0w5oL2x2IOt
+NeAus7YjAkEA2BmBwDpyaldzC3qoakWHFKn4c1LAsVRErRZZrtr6ykFh/FHYsC8R
+/zdlFMaciyw2DM5AmaMVOcQ1sTk8vydAIwJADVv3FIXlGZuIF3tGzjrCVXuXEoN1
+tbc4ux6/mrRINCUaJotmY4YWd7f6COa4WnkILrzllxShA4zAj7G2gr1WYwJBALxQ
+DeeLhyTwK8q3CogBKqNxMpacaFqfSnFi5qgfHyidLVwJnsB5ZD948FbYdJY64qbq
+HRVFLjv2patYa31QIO0CQFWN+bleiLXOSkI6bDLMeQk+SikeHgdNIgomQGDpYu5d
+iBsIHDdqpiteqW6qcZNqd7YDYSSgKTtVo/A2cPkN7zw=
+-----END RSA PRIVATE KEY-----
diff --git a/tests/conf/test.pkcs8.enc.derkey b/tests/conf/test.pkcs8.enc.derkey
new file mode 100644 (file)
index 0000000..0476c67
Binary files /dev/null and b/tests/conf/test.pkcs8.enc.derkey differ
diff --git a/tests/conf/test.pkcs8.enc.pemkey b/tests/conf/test.pkcs8.enc.pemkey
new file mode 100644 (file)
index 0000000..56a5518
--- /dev/null
@@ -0,0 +1,17 @@
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIICoTAbBgkqhkiG9w0BBQMwDgQIUFkGlrGEVNsCAggABIICgIcFP7A3nik//yA5
+jb8iiIqUZIHLPGpaxldbrr4LRJAT/o4+zwtthMsVe8K3jg05Sid3+LJUYVZVKfxF
+kDu/fQtYL9vbyze3GawaDhrlsv77CEQB677OlI0er12QlhLjXuf4bKIkYzSwf+cF
+Tb+ROarggvv/Z3xYZU4xCrMHjEZLxnJLXeQ3i9PV0MznY2grQRI7jcv9UTUDCGfC
+sVn+yotMht4+7ttvJw7cfMh4VdlkBRZJkBBCIlEjENBAJWyZblsQKOLY40OslzHO
+TB2j0y/rY3iMgVW2rU4nrbGoFgLpmQcA7c93gx0mfBnSGTciGP6HeefXGqchsEiN
+oYHS7msaH++AJYWEK6J9mLU9zjwzapSNr/QFbcQ+c6SoKtoyWdWAI+eN5vhvDPnd
+SeG8wwvL3aWMYPOvnZQ0OidcHKAwh8hXu8bk6SOoRtXaqCu5kIjdLtvxhxzcPjvF
+aNhk+B2BbnvFMYLVHCwW+apiSsgMG9WX95/S49g6MqKEc4+g5ecGe+pMNXieXcp6
+ngPyjFyzUOjWdsDZU3MCDsvqFfKJXCN2KFrdKg0mISOh8wpzbvfghsay7vWTiI1W
+NLPEDv3CPzmJaHxqVHlKKbCuGPPUGibaNw0LygK7fWbgV0oZoAgMNXNuvw5Xvu7n
+s5Xw20q8Jv5F1E8w+Bz5JeUzF3gIM9BbEpYDMQPtyPB/xZw0ZETr2Muaufv7Xm1i
+qdvwyFf8Mpx860QD5mzHC3ivCwjjqjO8xCCiKdNMy/t6mJQ7gjCiVUwEAvYAOViu
+UVIJisYqkGIlKu5cz2ykw7JOKQ6j7Npgf1ndUoOTGkejsVVQfe8+bTEq6DZalmUh
+PWGEfig=
+-----END ENCRYPTED PRIVATE KEY-----
diff --git a/tests/conf/test.pkcs8.pemkey b/tests/conf/test.pkcs8.pemkey
new file mode 100644 (file)
index 0000000..a7cd26e
--- /dev/null
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMxUr0vamv1LB/aC
+R/rtUDO3OdT+u6MEXKhFEYUw7LU91gQ7D+fmEyBlIsCTJDOIUCCOsjCO4p+g824R
+kMfU/6usUalxV6i5NZ63P5HLPoW0eE9dcMFur3dZN7gVByV1fjs7zBMM9kktEcRW
+Q8OTBpcCRqpMHucDzekFFswsb6bJAgMBAAECgYAR8JV7eXQGLSJD2GhtdYyHHd0s
+ADmFNIrWE0pYgVUnDIjP0kexOpgPThHhCooxJBS05xJqBQGMtlrpHQ2zHdZcmgi5
+XeuQ3h5O4TbmGSlyljaDGeTWsHijb6iJFRF0GIbqc4jInuQtoJrVrOVbUjwn7YHc
+PpItj3oyDtXGIx526QJBAPIO5O1QKd1dMR6+RMKbCpAYq23QB3Gprw4wdnEX6oOY
+i4wJICCXUgRHLyD29WWpstY/TDmgvbHYg6014C6ztiMCQQDYGYHAOnJqV3MLeqhq
+RYcUqfhzUsCxVEStFlmu2vrKQWH8UdiwLxH/N2UUxpyLLDYMzkCZoxU5xDWxOTy/
+J0AjAkANW/cUheUZm4gXe0bOOsJVe5cSg3W1tzi7Hr+atEg0JRomi2ZjhhZ3t/oI
+5rhaeQguvOWXFKEDjMCPsbaCvVZjAkEAvFAN54uHJPAryrcKiAEqo3EylpxoWp9K
+cWLmqB8fKJ0tXAmewHlkP3jwVth0ljripuodFUUuO/alq1hrfVAg7QJAVY35uV6I
+tc5KQjpsMsx5CT5KKR4eB00iCiZAYOli7l2IGwgcN2qmK16pbqpxk2p3tgNhJKAp
+O1Wj8DZw+Q3vPA==
+-----END PRIVATE KEY-----
index 728d100..716a0ce 100644 (file)
@@ -102,17 +102,17 @@ public class CredentialsTests extends TestCase {
                                                        throw new SAXException("Could not load entity: " + e);
                                                }
                                        } else if (systemId.endsWith("xmldsig-core-schema.xsd")) {
-                                       InputStream stream;
-                                       try {
-                                               stream = new FileInputStream("src/schemas/xmldsig-core-schema.xsd");
-                                               if (stream != null) {
-                                                       return new InputSource(stream);
+                                               InputStream stream;
+                                               try {
+                                                       stream = new FileInputStream("src/schemas/xmldsig-core-schema.xsd");
+                                                       if (stream != null) {
+                                                               return new InputSource(stream);
+                                                       }
+                                                       throw new SAXException("Could not load entity: Null input stream");
+                                               } catch (FileNotFoundException e) {
+                                                       throw new SAXException("Could not load entity: " + e);
                                                }
-                                               throw new SAXException("Could not load entity: Null input stream");
-                                       } catch (FileNotFoundException e) {
-                                               throw new SAXException("Could not load entity: " + e);
-                                       }
-                               }else {
+                                       } else {
                                                return null;
                                        }
                                }
@@ -165,7 +165,7 @@ public class CredentialsTests extends TestCase {
                        fail("Failed to load credentials: " + e);
                }
        }
-       
+
        public void testKeyStoreX509AliasDefaulting() {
 
                try {
@@ -196,7 +196,7 @@ public class CredentialsTests extends TestCase {
                        fail("Failed to load credentials: " + e);
                }
        }
-       
+
        public void testFileX509NoPassword() {
 
                try {
@@ -227,7 +227,7 @@ public class CredentialsTests extends TestCase {
                        fail("Failed to load credentials: " + e);
                }
        }
-       
+
        public void testFileX509withCABundles() {
 
                try {
@@ -259,4 +259,35 @@ public class CredentialsTests extends TestCase {
                }
        }
 
+       public void testKeyStoreX509_PEM_PKCS8Key() {
+
+               try {
+                       InputStream inStream = new FileInputStream("data/credentials5.xml");
+                       parser.parse(new InputSource(inStream));
+                       Credentials credentials = new Credentials(parser.getDocument().getDocumentElement());
+
+                       assertTrue("Credential could not be found.", credentials.containsCredential("test"));
+                       Credential credential = credentials.getCredential("test");
+
+                       assertTrue(
+                               "Credential was loaded with an incorrect type.",
+                               credential.getCredentialType() == Credential.X509);
+                       assertNotNull("Private key was not loaded correctly.", credential.getPrivateKey());
+                       assertEquals(
+                               "Unexpected X509 certificate found.",
+                               credential.getX509Certificate().getSubjectDN().getName(),
+                               "CN=shib2.internet2.edu, OU=Unknown, O=Unknown, ST=Unknown, C=Unknown");
+                       assertEquals(
+                               "Unexpected certificate chain length.",
+                               new Integer(credential.getX509CertificateChain().length),
+                               new Integer(3));
+                       assertEquals(
+                               "Unexpected X509 certificate found.",
+                               credential.getX509CertificateChain()[2].getSubjectDN().getName(),
+                               "CN=HEPKI Master CA -- 20020701A, OU=Division of Information Technology, O=University of Wisconsin, L=Madison, ST=Wisconsin, C=US");
+               } catch (Exception e) {
+                       fail("Failed to load credentials: " + e);
+               }
+       }
+
 }