Removed a bunch of dependencies on sun-proprietary classes.
authorwassa <wassa@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Fri, 29 Apr 2005 19:00:43 +0000 (19:00 +0000)
committerwassa <wassa@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Fri, 29 Apr 2005 19:00:43 +0000 (19:00 +0000)
Thanks to Chad La Joie for the patches.

git-svn-id: https://subversion.switch.ch/svn/shibboleth/java-idp/trunk@1432 ab3bd59b-922f-494d-bb5f-6f0a3c29deca

src/edu/internet2/middleware/shibboleth/aa/attrresolv/provider/Base64ValueHandler.java
src/edu/internet2/middleware/shibboleth/aa/attrresolv/provider/PersistentIDAttributeDefinition.java
src/edu/internet2/middleware/shibboleth/common/Credentials.java
src/edu/internet2/middleware/shibboleth/idp/provider/SAMLv1_1ArtifactQueryHandler.java
src/edu/internet2/middleware/shibboleth/idp/provider/ShibbolethV1SSOHandler.java
src/edu/internet2/middleware/shibboleth/utils/ExtKeyTool.java

index 659eedd..724d89e 100644 (file)
@@ -28,11 +28,10 @@ package edu.internet2.middleware.shibboleth.aa.attrresolv.provider;
 import java.util.Collection;
 import java.util.Iterator;
 
+import org.bouncycastle.util.encoders.Base64;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 
-import sun.misc.BASE64Encoder;
-
 /**
  * <code>ValueHandler</code> implementation for encoding byte array values in Base64..
  * 
@@ -44,7 +43,7 @@ public class Base64ValueHandler implements ValueHandler {
 
                if (!(value instanceof byte[])) { throw new ValueHandlerException(
                                "Base64ValueHandler could not encode a value of type: " + value.getClass().getName()); }
-               valueElement.appendChild(document.createTextNode(new BASE64Encoder().encode((byte[]) value)));
+               valueElement.appendChild(document.createTextNode(new String(Base64.encode((byte[]) value))));
        }
 
        public Iterator getValues(Collection internalValues) {
index 43ab372..36ec9b9 100644 (file)
@@ -1,50 +1,26 @@
-/* 
- * The Shibboleth License, Version 1. 
- * Copyright (c) 2002 
- * University Corporation for Advanced Internet Development, Inc. 
- * All rights reserved
- * 
- * 
- * Redistribution and use in source and binary forms, with or without 
- * modification, are permitted provided that the following conditions are met:
- * 
- * Redistributions of source code must retain the above copyright notice, this 
- * list of conditions and the following disclaimer.
- * 
- * Redistributions in binary form must reproduce the above copyright notice, 
- * this list of conditions and the following disclaimer in the documentation 
- * and/or other materials provided with the distribution, if any, must include 
- * the following acknowledgment: "This product includes software developed by 
- * the University Corporation for Advanced Internet Development 
- * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement 
- * may appear in the software itself, if and wherever such third-party 
- * acknowledgments normally appear.
- * 
- * Neither the name of Shibboleth nor the names of its contributors, nor 
- * Internet2, nor the University Corporation for Advanced Internet Development, 
- * Inc., nor UCAID may be used to endorse or promote products derived from this 
- * software without specific prior written permission. For written permission, 
- * please contact shibboleth@shibboleth.org
- * 
- * Products derived from this software may not be called Shibboleth, Internet2, 
- * UCAID, or the University Corporation for Advanced Internet Development, nor 
- * may Shibboleth appear in their name, without prior written permission of the 
- * University Corporation for Advanced Internet Development.
- * 
- * 
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
- * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
- * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK 
- * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. 
- * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY 
- * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT, 
- * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+/*
+ * The Shibboleth License, Version 1. Copyright (c) 2002 University Corporation for Advanced Internet Development, Inc.
+ * All rights reserved Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met: Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution, if any, must include the following acknowledgment: "This product includes software
+ * developed by the University Corporation for Advanced Internet Development <http://www.ucaid.edu>Internet2 Project.
+ * Alternately, this acknowledegement may appear in the software itself, if and wherever such third-party
+ * acknowledgments normally appear. Neither the name of Shibboleth nor the names of its contributors, nor Internet2, nor
+ * the University Corporation for Advanced Internet Development, Inc., nor UCAID may be used to endorse or promote
+ * products derived from this software without specific prior written permission. For written permission, please contact
+ * shibboleth@shibboleth.org Products derived from this software may not be called Shibboleth, Internet2, UCAID, or the
+ * University Corporation for Advanced Internet Development, nor may Shibboleth appear in their name, without prior
+ * written permission of the University Corporation for Advanced Internet Development. THIS SOFTWARE IS PROVIDED BY THE
+ * COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE
+ * DISCLAIMED AND THE ENTIRE RISK OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. IN NO
+ * EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC.
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 package edu.internet2.middleware.shibboleth.aa.attrresolv.provider;
@@ -66,11 +42,11 @@ import javax.naming.directory.Attribute;
 import javax.naming.directory.Attributes;
 
 import org.apache.log4j.Logger;
+import org.bouncycastle.util.encoders.Base64;
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
 
-import sun.misc.BASE64Encoder;
 import edu.internet2.middleware.shibboleth.aa.attrresolv.AttributeDefinitionPlugIn;
 import edu.internet2.middleware.shibboleth.aa.attrresolv.AttributeResolver;
 import edu.internet2.middleware.shibboleth.aa.attrresolv.Dependencies;
@@ -79,8 +55,8 @@ import edu.internet2.middleware.shibboleth.aa.attrresolv.ResolverAttribute;
 import edu.internet2.middleware.shibboleth.common.ShibResource;
 
 /**
- * <code>PersistentIDAttributeDefinition</code> implementation. Provides a persistent, but pseudonymous,
- * identifier for principals by hashing the principal name, requester, and a fixed secret salt.
+ * <code>PersistentIDAttributeDefinition</code> implementation. Provides a persistent, but pseudonymous, identifier
+ * for principals by hashing the principal name, requester, and a fixed secret salt.
  * 
  * @author Scott Cantor (cantor.2@osu.edu)
  */
@@ -92,15 +68,15 @@ public class PersistentIDAttributeDefinition extends BaseAttributeDefinition imp
        protected String scope;
 
        /**
-        * Constructor for PersistentIDAttributeDefinition.  Creates a PlugIn based on configuration
-        * information presented in a DOM Element.
+        * Constructor for PersistentIDAttributeDefinition. Creates a PlugIn based on configuration information presented in
+        * a DOM Element.
         */
        public PersistentIDAttributeDefinition(Element e) throws ResolutionPlugInException {
 
                super(e);
                localPersistentId = e.getAttributeNS(null, "sourceName");
 
-               //Make sure we understand how to resolve the local persistent ID for the principal.
+               // Make sure we understand how to resolve the local persistent ID for the principal.
                if (localPersistentId != null && localPersistentId.length() > 0) {
                        if (connectorDependencyIds.size() != 1 || !attributeDependencyIds.isEmpty()) {
                                log.error("Can't specify the sourceName attribute without a single connector dependency.");
@@ -114,14 +90,14 @@ public class PersistentIDAttributeDefinition extends BaseAttributeDefinition imp
                        throw new ResolutionPlugInException("Failed to initialize Attribute Definition PlugIn.");
                }
 
-               //Grab user specified scope
+               // Grab user specified scope
                scope = e.getAttribute("scope");
                if (scope == null || scope.equals("")) {
                        log.error("Attribute \"scope\" required to configure plugin.");
                        throw new ResolutionPlugInException("Failed to initialize Attribute Definition PlugIn.");
                }
 
-               //Salt can be either embedded in the element or pulled out of a keystore.
+               // Salt can be either embedded in the element or pulled out of a keystore.
                NodeList salts = e.getElementsByTagNameNS(AttributeResolver.resolverNamespace, "Salt");
                if (salts == null || salts.getLength() != 1) {
                        log.error("Missing <Salt> from attribute definition configuration.");
@@ -130,25 +106,16 @@ public class PersistentIDAttributeDefinition extends BaseAttributeDefinition imp
 
                Element salt = (Element) salts.item(0);
                Node child = salt.getFirstChild();
-               if (child != null
-                       && child.getNodeType() == Node.TEXT_NODE
-                       && child.getNodeValue() != null
-                       && child.getNodeValue().length() >= 16)
-                       this.salt = child.getNodeValue().getBytes();
+               if (child != null && child.getNodeType() == Node.TEXT_NODE && child.getNodeValue() != null
+                               && child.getNodeValue().length() >= 16) this.salt = child.getNodeValue().getBytes();
                else {
                        String ksPath = salt.getAttributeNS(null, "keyStorePath");
                        String keyAlias = salt.getAttributeNS(null, "keyStoreKeyAlias");
                        String ksPass = salt.getAttributeNS(null, "keyStorePassword");
                        String keyPass = salt.getAttributeNS(null, "keyStoreKeyPassword");
 
-                       if (ksPath == null
-                               || ksPath.length() == 0
-                               || keyAlias == null
-                               || keyAlias.length() == 0
-                               || ksPass == null
-                               || ksPass.length() == 0
-                               || keyPass == null
-                               || keyPass.length() == 0) {
+                       if (ksPath == null || ksPath.length() == 0 || keyAlias == null || keyAlias.length() == 0 || ksPass == null
+                                       || ksPass.length() == 0 || keyPass == null || keyPass.length() == 0) {
 
                                log.error("Missing <Salt> keyStore attributes from attribute definition configuration.");
                                throw new ResolutionPlugInException("Failed to initialize Attribute Definition PlugIn.");
@@ -161,57 +128,64 @@ public class PersistentIDAttributeDefinition extends BaseAttributeDefinition imp
                                SecretKey secret = (SecretKey) keyStore.getKey(keyAlias, keyPass.toCharArray());
 
                                if (usingDefaultSecret()) {
-                                       log.warn(
-                                               "You are running the PersistentIDAttributeDefinition PlugIn with the default secret key as a salt.  This is UNSAFE!  Please change "
-                                                       + "this configuration and restart the origin.");
+                                       log
+                                                       .warn("You are running the PersistentIDAttributeDefinition PlugIn with the default secret key as a salt.  This is UNSAFE!  Please change "
+                                                                       + "this configuration and restart the origin.");
                                }
                                this.salt = secret.getEncoded();
 
                        } catch (KeyStoreException ex) {
-                               log.error(
-                                       "An error occurred while loading the java keystore.  Unable to initialize Attribute Definition PlugIn: "
-                                               + ex);
-                               throw new ResolutionPlugInException("An error occurred while loading the java keystore.  Unable to initialize Attribute Definition PlugIn.");
+                               log
+                                               .error("An error occurred while loading the java keystore.  Unable to initialize Attribute Definition PlugIn: "
+                                                               + ex);
+                               throw new ResolutionPlugInException(
+                                               "An error occurred while loading the java keystore.  Unable to initialize Attribute Definition PlugIn.");
                        } catch (CertificateException ex) {
-                               log.error(
-                                       "The java keystore contained corrupted data.  Unable to initialize Attribute Definition PlugIn: "
-                                               + ex);
-                               throw new ResolutionPlugInException("The java keystore contained corrupted data.  Unable to initialize Attribute Definition PlugIn.");
+                               log
+                                               .error("The java keystore contained corrupted data.  Unable to initialize Attribute Definition PlugIn: "
+                                                               + ex);
+                               throw new ResolutionPlugInException(
+                                               "The java keystore contained corrupted data.  Unable to initialize Attribute Definition PlugIn.");
                        } catch (NoSuchAlgorithmException ex) {
-                               log.error(
-                                       "Appropriate JCE provider not found in the java environment. Unable to initialize Attribute Definition PlugIn: "
-                                               + ex);
-                               throw new ResolutionPlugInException("Appropriate JCE provider not found in the java environment. Unable to initialize Attribute Definition PlugIn.");
+                               log
+                                               .error("Appropriate JCE provider not found in the java environment. Unable to initialize Attribute Definition PlugIn: "
+                                                               + ex);
+                               throw new ResolutionPlugInException(
+                                               "Appropriate JCE provider not found in the java environment. Unable to initialize Attribute Definition PlugIn.");
                        } catch (IOException ex) {
-                               log.error(
-                                       "An error accessing while loading the java keystore.  Unable to initialize Attribute Definition PlugIn: "
-                                               + ex);
-                               throw new ResolutionPlugInException("An error occurred while accessing the java keystore.  Unable to initialize Attribute Definition PlugIn.");
+                               log
+                                               .error("An error accessing while loading the java keystore.  Unable to initialize Attribute Definition PlugIn: "
+                                                               + ex);
+                               throw new ResolutionPlugInException(
+                                               "An error occurred while accessing the java keystore.  Unable to initialize Attribute Definition PlugIn.");
                        } catch (UnrecoverableKeyException ex) {
-                               log.error(
-                                       "Secret could not be loaded from the java keystore.  Verify that the alias and password are correct: "
-                                               + ex);
-                               throw new ResolutionPlugInException("Secret could not be loaded from the java keystore.  Verify that the alias and password are correct. ");
+                               log
+                                               .error("Secret could not be loaded from the java keystore.  Verify that the alias and password are correct: "
+                                                               + ex);
+                               throw new ResolutionPlugInException(
+                                               "Secret could not be loaded from the java keystore.  Verify that the alias and password are correct. ");
                        }
                }
        }
 
        /**
-        * @see edu.internet2.middleware.shibboleth.aa.attrresolv.AttributeDefinitionPlugIn#resolve(edu.internet2.middleware.shibboleth.aa.attrresolv.ArpAttribute, java.security.Principal, java.lang.String, edu.internet2.middleware.shibboleth.aa.attrresolv.Dependencies)
+        * @see edu.internet2.middleware.shibboleth.aa.attrresolv.AttributeDefinitionPlugIn#resolve(edu.internet2.middleware.shibboleth.aa.attrresolv.ArpAttribute,
+        *      java.security.Principal, java.lang.String, edu.internet2.middleware.shibboleth.aa.attrresolv.Dependencies)
         */
        public void resolve(ResolverAttribute attribute, Principal principal, String requester, Dependencies depends)
-               throws ResolutionPlugInException {
+                       throws ResolutionPlugInException {
+
                log.debug("Resolving attribute: (" + getId() + ")");
-               
+
                if (requester == null || requester.equals("")) {
                        log.debug("Could not create ID for unauthenticated requester.");
                        attribute.setResolved();
                        return;
                }
-               
+
                String localId = null;
 
-               //Resolve the correct local persistent identifier.
+               // Resolve the correct local persistent identifier.
                if (!attributeDependencyIds.isEmpty()) {
                        ResolverAttribute dep = depends.getAttributeResolution((String) attributeDependencyIds.iterator().next());
                        if (dep != null) {
@@ -220,20 +194,18 @@ public class PersistentIDAttributeDefinition extends BaseAttributeDefinition imp
                                        log.debug("Found persistent ID value for attribute (" + getId() + ").");
                                        localId = (String) vals.next();
                                        if (vals.hasNext()) {
-                                               log.error(
-                                                       "An attribute dependency of attribute ("
-                                                               + getId()
+                                               log.error("An attribute dependency of attribute (" + getId()
                                                                + ") returned multiple values, expecting only one.");
                                                return;
                                        }
                                } else {
-                                       log.error(
-                                               "An attribute dependency of attribute (" + getId() + ") returned no values, expecting one.");
+                                       log.error("An attribute dependency of attribute (" + getId()
+                                                       + ") returned no values, expecting one.");
                                        return;
                                }
                        } else {
-                               log.error(
-                                       "An attribute dependency of attribute (" + getId() + ") was not included in the dependency chain.");
+                               log.error("An attribute dependency of attribute (" + getId()
+                                               + ") was not included in the dependency chain.");
                                return;
                        }
                } else if (!connectorDependencyIds.isEmpty()) {
@@ -242,11 +214,7 @@ public class PersistentIDAttributeDefinition extends BaseAttributeDefinition imp
                                Attribute attr = attrs.get(localPersistentId);
                                if (attr != null) {
                                        if (attr.size() != 1) {
-                                               log.error(
-                                                       "An attribute dependency of attribute ("
-                                                               + getId()
-                                                               + ") returned "
-                                                               + attr.size()
+                                               log.error("An attribute dependency of attribute (" + getId() + ") returned " + attr.size()
                                                                + " values, expecting only one.");
                                        } else {
                                                try {
@@ -276,14 +244,14 @@ public class PersistentIDAttributeDefinition extends BaseAttributeDefinition imp
                        attribute.setLifetime(lifeTime);
                }
 
-               //Hash the data together to produce the persistent ID.
+               // Hash the data together to produce the persistent ID.
                try {
                        MessageDigest md = MessageDigest.getInstance("SHA");
                        md.update(requester.getBytes());
                        md.update((byte) '!');
                        md.update(localId.getBytes());
                        md.update((byte) '!');
-                       String result = new BASE64Encoder().encode(md.digest(salt));
+                       String result = new String(Base64.encode(md.digest(salt)));
 
                        attribute.registerValueHandler(new ScopedStringValueHandler(scope));
                        attribute.addValue(result.replaceAll(System.getProperty("line.separator"), ""));
@@ -294,32 +262,11 @@ public class PersistentIDAttributeDefinition extends BaseAttributeDefinition imp
        }
 
        private boolean usingDefaultSecret() {
-               byte[] defaultKey =
-                       new byte[] {
-                               (byte) 0xC7,
-                               (byte) 0x49,
-                               (byte) 0x80,
-                               (byte) 0xD3,
-                               (byte) 0x02,
-                               (byte) 0x4A,
-                               (byte) 0x61,
-                               (byte) 0xEF,
-                               (byte) 0x25,
-                               (byte) 0x5D,
-                               (byte) 0xE3,
-                               (byte) 0x2F,
-                               (byte) 0x57,
-                               (byte) 0x51,
-                               (byte) 0x20,
-                               (byte) 0x15,
-                               (byte) 0xC7,
-                               (byte) 0x49,
-                               (byte) 0x80,
-                               (byte) 0xD3,
-                               (byte) 0x02,
-                               (byte) 0x4A,
-                               (byte) 0x61,
-                               (byte) 0xEF };
+
+               byte[] defaultKey = new byte[]{(byte) 0xC7, (byte) 0x49, (byte) 0x80, (byte) 0xD3, (byte) 0x02, (byte) 0x4A,
+                               (byte) 0x61, (byte) 0xEF, (byte) 0x25, (byte) 0x5D, (byte) 0xE3, (byte) 0x2F, (byte) 0x57, (byte) 0x51,
+                               (byte) 0x20, (byte) 0x15, (byte) 0xC7, (byte) 0x49, (byte) 0x80, (byte) 0xD3, (byte) 0x02, (byte) 0x4A,
+                               (byte) 0x61, (byte) 0xEF};
                return Arrays.equals(defaultKey, salt);
        }
 }
index 0f19d02..ee99f96 100644 (file)
@@ -1,47 +1,37 @@
 /*
  * The Shibboleth License, Version 1. Copyright (c) 2002 University Corporation for Advanced Internet Development, Inc.
- * All rights reserved
- * 
- * 
- * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
- * following conditions are met:
- * 
- * Redistributions of source code must retain the above copyright notice, this list of conditions and the following
- * disclaimer.
- * 
- * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided with the distribution, if any, must include the
- * following acknowledgment: "This product includes software developed by the University Corporation for Advanced
- * Internet Development <http://www.ucaid.edu> Internet2 Project. Alternately, this acknowledegement may appear in the
- * software itself, if and wherever such third-party acknowledgments normally appear.
- * 
- * Neither the name of Shibboleth nor the names of its contributors, nor Internet2, nor the University Corporation for
- * Advanced Internet Development, Inc., nor UCAID may be used to endorse or promote products derived from this software
- * without specific prior written permission. For written permission, please contact shibboleth@shibboleth.org
- * 
- * Products derived from this software may not be called Shibboleth, Internet2, UCAID, or the University Corporation
- * for Advanced Internet Development, nor may Shibboleth appear in their name, without prior written permission of the
- * University Corporation for Advanced Internet Development.
- * 
- * 
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND WITH ALL FAULTS. ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
- * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK OF SATISFACTORY QUALITY, PERFORMANCE,
- * ACCURACY, AND EFFORT IS WITH LICENSEE. IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY
- * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * All rights reserved Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met: Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution, if any, must include the following acknowledgment: "This product includes software
+ * developed by the University Corporation for Advanced Internet Development <http://www.ucaid.edu> Internet2 Project.
+ * Alternately, this acknowledegement may appear in the software itself, if and wherever such third-party
+ * acknowledgments normally appear. Neither the name of Shibboleth nor the names of its contributors, nor Internet2, nor
+ * the University Corporation for Advanced Internet Development, Inc., nor UCAID may be used to endorse or promote
+ * products derived from this software without specific prior written permission. For written permission, please contact
+ * shibboleth@shibboleth.org Products derived from this software may not be called Shibboleth, Internet2, UCAID, or the
+ * University Corporation for Advanced Internet Development, nor may Shibboleth appear in their name, without prior
+ * written permission of the University Corporation for Advanced Internet Development. THIS SOFTWARE IS PROVIDED BY THE
+ * COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE
+ * DISCLAIMED AND THE ENTIRE RISK OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. IN NO
+ * EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC.
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 package edu.internet2.middleware.shibboleth.common;
 
+import java.io.BufferedInputStream;
 import java.io.BufferedReader;
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
+import java.io.StringReader;
 import java.security.AlgorithmParameters;
 import java.security.GeneralSecurityException;
 import java.security.KeyFactory;
@@ -65,7 +55,9 @@ import java.security.spec.PKCS8EncodedKeySpec;
 import java.security.spec.RSAPrivateCrtKeySpec;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Enumeration;
 import java.util.Hashtable;
+import java.util.List;
 
 import javax.crypto.BadPaddingException;
 import javax.crypto.Cipher;
@@ -77,16 +69,22 @@ import javax.crypto.spec.PBEKeySpec;
 import javax.crypto.spec.SecretKeySpec;
 
 import org.apache.log4j.Logger;
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.DEREncodable;
+import org.bouncycastle.asn1.DERInteger;
+import org.bouncycastle.asn1.DERObject;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.util.ASN1Dump;
+import org.bouncycastle.util.encoders.Base64;
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
 
-import sun.misc.BASE64Decoder;
-import sun.security.util.DerValue;
-
 /**
- * Uses {@link CredentialResolver} implementations to create {@link Credential}s.
- *
+ * Uses {@link CredentialResolver}implementations to create {@link Credential}s.
+ * 
  * @author Walter Hoehn
  */
 public class Credentials {
@@ -97,10 +95,12 @@ public class Credentials {
        private Hashtable data = new Hashtable();
        private boolean singleMode = false;
 
-        /**
-         * Creates credentials based on XML configuration.
-         * @param e DOM representation of credentials configuration
-         */
+       /**
+        * Creates credentials based on XML configuration.
+        * 
+        * @param e
+        *            DOM representation of credentials configuration
+        */
        public Credentials(Element e) {
 
                if (e != null && e.getLocalName().equals("Credential")) {
@@ -121,7 +121,8 @@ public class Credentials {
                                                if (singleMode) {
                                                        credentialId = "SINGLE";
                                                } else {
-                                                       log.error("Found credential that was not labeled with a unique \"Id\" attribute. Skipping.");
+                                                       log
+                                                                       .error("Found credential that was not labeled with a unique \"Id\" attribute. Skipping.");
                                                }
                                        }
 
@@ -135,26 +136,26 @@ public class Credentials {
                                } catch (CredentialFactoryException cfe) {
                                        log.error("Could not load credential, skipping: " + cfe.getMessage());
                                } catch (ClassCastException cce) {
-                                       log.error("Problem realizing credential configuration" + cce.getMessage());
+                                       log.error("Problem realizing credential configuration " + cce.getMessage());
                                }
                        }
                }
        }
 
        public boolean containsCredential(String identifier) {
+
                return data.containsKey(identifier);
        }
 
        public Credential getCredential(String identifier) {
 
                // Default if there is only one credential
-               if ((identifier == null || identifier.equals("")) && data.size() == 1) {
-                       return (Credential) data.values().iterator().next();
-               }
+               if ((identifier == null || identifier.equals("")) && data.size() == 1) { return (Credential) data.values()
+                               .iterator().next(); }
 
                return (Credential) data.get(identifier);
        }
-       
+
        public Credential getCredential() {
 
                return (Credential) data.values().iterator().next();
@@ -165,21 +166,15 @@ public class Credentials {
                private static Logger log = Logger.getLogger(CredentialFactory.class.getName());
 
                public static Credential loadCredential(Element e) throws CredentialFactoryException {
-                       if (e.getLocalName().equals("KeyInfo")) {
-                               return new KeyInfoCredentialResolver().loadCredential(e);
-                       }
 
-                       if (e.getLocalName().equals("FileResolver")) {
-                               return new FileCredentialResolver().loadCredential(e);
-                       }
+                       if (e.getLocalName().equals("KeyInfo")) { return new KeyInfoCredentialResolver().loadCredential(e); }
 
-                       if (e.getLocalName().equals("KeyStoreResolver")) {
-                               return new KeystoreCredentialResolver().loadCredential(e);
-                       }
+                       if (e.getLocalName().equals("FileResolver")) { return new FileCredentialResolver().loadCredential(e); }
 
-                       if (e.getLocalName().equals("CustomResolver")) {
-                               return new CustomCredentialResolver().loadCredential(e);
-                       }
+                       if (e.getLocalName().equals("KeyStoreResolver")) { return new KeystoreCredentialResolver()
+                                       .loadCredential(e); }
+
+                       if (e.getLocalName().equals("CustomResolver")) { return new CustomCredentialResolver().loadCredential(e); }
 
                        log.error("Unrecognized Credential Resolver type: " + e.getTagName());
                        throw new CredentialFactoryException("Failed to load credential.");
@@ -190,28 +185,43 @@ public class Credentials {
 }
 
 class KeyInfoCredentialResolver implements CredentialResolver {
+
        private static Logger log = Logger.getLogger(KeyInfoCredentialResolver.class.getName());
+
        KeyInfoCredentialResolver() throws CredentialFactoryException {
+
                log.error("Credential Resolver (KeyInfoCredentialResolver) not implemented");
                throw new CredentialFactoryException("Failed to load credential.");
        }
 
        public Credential loadCredential(Element e) {
+
                return null;
        }
 }
 
 /**
  * Loads a credential from a file. Supports DER, PEM, encrypted PEM, PKCS8, and encrypted PKCS8 for RSA and DSA.
+ * 
  * @author Walter Hoehn
+ * @author Chad La Joie
  */
+
 class FileCredentialResolver implements CredentialResolver {
 
        private static Logger log = Logger.getLogger(FileCredentialResolver.class.getName());
 
-       private static String DSAKey_OID = "1.2.840.10040.4.1";
-       private static String RSAKey_OID = "1.2.840.113549.1.1.1";
-
+       /**
+        * Reads a private key, and certificate information, specified by the configuration element, and creates a security
+        * credential which can then be used for operations such a assertion signing. DER and PEM encoded keys (both
+        * none-encrypted and encrypted) and PEM encoded certificated are supported.
+        * 
+        * @param e
+        *            the credentials configuration element
+        * @throws CredentialFactoryException
+        *             thrown if an error is encountered during any step of the credential creation, exact error specified
+        *             in exception message
+        */
        public Credential loadCredential(Element e) throws CredentialFactoryException {
 
                if (!e.getLocalName().equals("FileResolver")) {
@@ -219,1149 +229,1556 @@ class FileCredentialResolver implements CredentialResolver {
                        throw new CredentialFactoryException("Failed to initialize Credential Resolver.");
                }
 
-               //Load the key
-               String keyFormat = getKeyFormat(e);
-               String keyPath = getKeyPath(e);
-               String password = getKeyPassword(e);
-               log.debug("Key Format: (" + keyFormat + ").");
-               log.debug("Key Path: (" + keyPath + ").");
-
-               PrivateKey key = null; 
-
-               if (keyFormat.equals("DER")) {
-                       InputStream keyStream = null;
-                       try {
-                               keyStream = new ShibResource(keyPath, this.getClass()).getInputStream();
-                               key = getDERKey(keyStream, password);
-                       } catch (IOException ioe) {
-                               log.error("Could not load resource from specified location (" + keyPath + "): " + e);
-                               throw new CredentialFactoryException("Unable to load private key.");
-                       } finally {
-                               if (keyStream != null) {
-                                       try {
-                                               keyStream.close();
-                                       } catch (IOException e1) {
-                                               // ignore
-                                       }
-                               }
-                       }
-                       
-               } else if (keyFormat.equals("PEM")) {
-                       InputStream keyStream = null;
-                       try {
-                               keyStream = new ShibResource(keyPath, this.getClass()).getInputStream();
-                               key = getPEMKey(keyStream, password);
-                       } catch (IOException ioe) {
-                               log.error("Could not load resource from specified location (" + keyPath + "): " + e);
-                               throw new CredentialFactoryException("Unable to load private key.");
-                       } finally {
-                               if (keyStream != null) {
-                                       try {
-                                               keyStream.close();
-                                       } catch (IOException e1) {
-                                               // ignore
-                                       }
-                               }
-                       }
-                       
-               } else {
-                       log.error("File credential resolver only supports (DER) and (PEM) formats.");
-                       throw new CredentialFactoryException("Failed to initialize Credential Resolver.");
-               }
-
+               PrivateKey key = getPrivateKey(e);
                if (key == null) {
                        log.error("Failed to load private key.");
-                       throw new CredentialFactoryException("Failed to initialize Credential Resolver.");
+                       throw new CredentialFactoryException("Failed to load private key.");
                }
-               log.info("Successfully loaded private key.");
-
-               
-               ArrayList certChain = new ArrayList();
-               String certPath = getCertPath(e);
-               
-               if (certPath == null || certPath.equals("")) {
-                       log.info("No certificates specified.");
-               } else {
-
-               String certFormat = getCertFormat(e);
-               //A placeholder in case we want to make this configurable
-               String certType = "X.509";
 
-               log.debug("Certificate Format: (" + certFormat + ").");
-               log.debug("Certificate Path: (" + certPath + ").");
+               List certChain = getCertificateChain(e, key);
 
-               //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.");
-                       throw new CredentialFactoryException("Failed to initialize Credential Resolver.");
+               Credential credential = new Credential(((X509Certificate[]) certChain.toArray(new X509Certificate[0])), key);
+               if (log.isDebugEnabled()) {
+                       log.debug("Credential created");
                }
 
+               return credential;
+       }
 
-               ArrayList allCerts = new ArrayList();
-
-               try {
-                       Certificate[] certsFromPath =
-                               loadCertificates(new ShibResource(certPath, this.getClass()).getInputStream(), certType);
+       /**
+        * Gets the private key for the credentials. Keys can be in either DER or PEM format and either password protected
+        * (encrypted) or not.
+        * 
+        * @param credentialConfigElement
+        *            the credential configuration element
+        * @return the private key
+        * @throws CredentialFactoryException
+        *             thrown if the private key file can not be found, the private key format can not be determined, or
+        *             some IO error occurs reading from the private key file
+        */
+       private PrivateKey getPrivateKey(Element credentialConfigElement) throws CredentialFactoryException {
 
-                       allCerts.addAll(Arrays.asList(certsFromPath));
+               String keyPath = getKeyPath(credentialConfigElement);
+               String password = getKeyPassword(credentialConfigElement);
 
-                       //Find the end-entity cert first
-                       if (certsFromPath == null || certsFromPath.length == 0) {
-                               log.error("File at (" + certPath + ") did not contain any valid certificates.");
-                               throw new CredentialFactoryException("File did not contain any valid certificates.");
+               PrivateKey key = null;
+               InputStream keyStream = null;
+               try {
+                       if (log.isDebugEnabled()) {
+                               log.debug("Attempting to load private key from file " + keyPath);
                        }
+                       keyStream = new ShibResource(keyPath, this.getClass()).getInputStream();
+                       int encoding = getEncodingFormat(keyStream);
+                       EncodedKey encodedKey;
+
+                       switch (encoding) {
+                               case EncodedKey.DER_ENCODING :
+                                       if (log.isDebugEnabled()) {
+                                               log.debug("Private key in file " + keyPath + " determined to be DER encoded");
+                                       }
+                                       encodedKey = new DERKey(keyStream, password);
+                                       return encodedKey.getPrivateKey();
 
-                       if (certsFromPath.length == 1) {
-                               log.debug("Certificate file only contains 1 certificate.");
-                               log.debug("Ensure that it matches the private key.");
-                               if (!isMatchingKey(certsFromPath[0].getPublicKey(), key)) {
-                                       log.error("Certificate does not match the private key.");
-                                       throw new CredentialFactoryException("File did not contain any valid certificates.");
-                               }
-                               certChain.add(certsFromPath[0]);
-                               log.debug(
-                                       "Successfully identified the end entity cert: "
-                                               + ((X509Certificate) certChain.get(0)).getSubjectDN());
-
-                       } else {
-                               log.debug("Certificate file contains multiple certificates.");
-                               log.debug("Trying to determine the end-entity cert by matching against the private key.");
-                               for (int i = 0; certsFromPath.length > i; i++) {
-                                       if (isMatchingKey(certsFromPath[i].getPublicKey(), key)) {
-                                               log.debug("Found matching end cert: " + ((X509Certificate) certsFromPath[i]).getSubjectDN());
-                                               certChain.add(certsFromPath[i]);
+                               case EncodedKey.PEM_ENCODING :
+                                       if (log.isDebugEnabled()) {
+                                               log.debug("Private key in file " + keyPath + " determined to be PEM encoded");
                                        }
-                               }
-                               if (certChain.size() < 1) {
-                                       log.error("No certificate in chain that matches specified private key");
-                                       throw new CredentialFactoryException("No certificate in chain that matches specified private key");
-                               }
-                               if (certChain.size() > 1) {
-                                       log.error("More than one certificate in chain that matches specified private key");
-                                       throw new CredentialFactoryException("More than one certificate in chain that matches specified private key");
-                               }
-                               log.debug(
-                                       "Successfully identified the end entity cert: "
-                                               + ((X509Certificate) certChain.get(0)).getSubjectDN());
-                       }
+                                       encodedKey = new PEMKey(keyStream, password);
+                                       return encodedKey.getPrivateKey();
 
-                       //Now load additional certs and construct a chain
-                       String[] caPaths = getCAPaths(e);
-                       if (caPaths != null && caPaths.length > 0) {
-                               log.debug("Attempting to load certificates from (" + caPaths.length + ") CA certificate files.");
-                               for (int i = 0; i < caPaths.length; i++) {
-                                       allCerts.addAll(
-                                               Arrays.asList(
-                                                       loadCertificates(
-                                                               new ShibResource(caPaths[i], this.getClass()).getInputStream(),
-                                                               certType)));
-                               }
+                               default :
+                                       log.error("Unable to determine format of private key specified in file " + keyPath);
+                                       throw new CredentialFactoryException("Unable to determine private key format.");
                        }
-
-                       log.debug("Attempting to construct a certificate chain.");
-                       walkChain((X509Certificate[]) allCerts.toArray(new X509Certificate[0]), certChain);
-
-                       log.debug("Verifying that each link in the cert chain is signed appropriately");
-                       for (int i = 0; i < certChain.size() - 1; i++) {
-                               PublicKey pubKey = ((X509Certificate) certChain.get(i + 1)).getPublicKey();
+               } catch (IOException ioe) {
+                       log.error("Could not load credential from specified file (" + keyPath + "): " + ioe);
+                       throw new CredentialFactoryException("Unable to load private key.");
+               } finally {
+                       if (keyStream != null) {
                                try {
-                                       ((X509Certificate) certChain.get(i)).verify(pubKey);
-                               } catch (Exception se) {
-                                       log.error("Certificate chain cannot be verified: " + se);
-                                       throw new CredentialFactoryException("Certificate chain cannot be verified: " + se);
+                                       keyStream.close();
+                               } catch (IOException e1) {
+                                       // ignore
                                }
                        }
-                       log.debug("All signatures verified. Certificate chain creation successful.");
-                       log.info("Successfully loaded certificates.");
-               
-
-               } catch (IOException p) {
-                       log.error("Could not load resource from specified location (" + certPath + "): " + p);
-                       throw new CredentialFactoryException("Unable to load certificates.");
                }
+       }
+
+       /**
+        * Determines whether the given stream is, which may be either a key is DER or PEM encoded. The input stream is
+        * reset to the last mark point after the determination is complete.
+        * 
+        * @param encodedStream
+        *            the key input stream
+        * @return -1 if the format can not be determined, PEM_ENCODING for a PEM encoded key, DER_ENCODING for a DER
+        *         encoded key
+        */
+       private int getEncodingFormat(InputStream encodedStream) throws CredentialFactoryException {
+
+               try {
+                       // Need to mark the stream and then reset it, after getting the
+                       // first byte so that the private key decoder starts reading at
+                       // the correct position
+                       encodedStream.mark(2);
+                       int firstByte = encodedStream.read();
+                       encodedStream.reset();
+
+                       // PEM encoded keys must start with a "-", a decimal value of 45
+                       if (firstByte == 45) { return EncodedKey.PEM_ENCODING; }
+
+                       // DER encoded keys must start with a decimal value of 48
+                       if (firstByte == 48) { return EncodedKey.DER_ENCODING; }
+
+                       // Can not determine type
+                       return -1;
+
+               } catch (IOException e) {
+                       log.error("Recieved error attempting to read private key file:" + e);
+                       return -1;
                }
-               return new Credential(((X509Certificate[]) certChain.toArray(new X509Certificate[0])), key);
        }
 
-       private PrivateKey getDERKey(InputStream inStream, String password)
-               throws CredentialFactoryException, IOException {
+       /**
+        * Gets the complete certificate chain specified by the configuration element. Currently only X.509 certificates are
+        * supported.
+        * 
+        * @param credentialConfigElement
+        *            the Credentials configuration element
+        * @param key
+        *            the private key for the certificate
+        * @return the certificate chain as a list of certificates
+        * @throws CredentialFactoryException
+        *             thrown if the certificate files is not found, can not be parsed, or an IOException occurs whils
+        *             reading the file
+        */
+       private List getCertificateChain(Element credentialConfigElement, PrivateKey key) throws CredentialFactoryException {
+
+               List certChain = new ArrayList();
+               String certPath = getCertPath(credentialConfigElement);
 
-               byte[] inputBuffer = new byte[8];
-               int i;
-               ByteContainer inputBytes = new ByteContainer(800);
-               do {
-                       i = inStream.read(inputBuffer);
-                       for (int j = 0; j < i; j++) {
-                               inputBytes.append(inputBuffer[j]);
+               if (certPath == null || certPath.equals("")) {
+                       if (log.isInfoEnabled()) {
+                               log.info("No certificates specified.");
                        }
-               } while (i > -1);
-
-               //Examine the ASN.1 Structure to auto-detect the format
-               //This gets a tad nasty
-               try {
-                       DerValue root = new DerValue(inputBytes.toByteArray());
-                       if (root.tag != DerValue.tag_Sequence) {
-                               log.error("Unexpected data type.  Unable to determine key type from data.");
-                               throw new CredentialFactoryException("Unable to load private key.");
+               } else {
+                       if (log.isDebugEnabled()) {
+                               log.debug("Certificate Path: (" + certPath + ").");
                        }
 
-                       DerValue[] childValues = new DerValue[3];
+                       // A placeholder in case we want to make this configurable
+                       String certType = "X.509";
 
-                       if (root.data.available() == 0) {
-                               log.error("Unexpected data type.  Unable to determine key type from data.");
-                               throw new CredentialFactoryException("Unable to load private key.");
+                       // 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.");
+                               throw new CredentialFactoryException("Only X.509 certificates are supported");
                        }
 
-                       childValues[0] = root.data.getDerValue();
+                       ArrayList allCerts = new ArrayList();
 
-                       if (childValues[0].tag == DerValue.tag_Sequence) {
+                       try {
+                               Certificate[] certsFromPath = loadCertificates(new ShibResource(certPath, this.getClass())
+                                               .getInputStream(), certType);
 
-                               //May be encrypted pkcs8... dig further
-                               if (root.data.available() == 0) {
-                                       log.error("Unexpected data type.  Unable to determine key type from data.");
-                                       throw new CredentialFactoryException("Unable to load private key.");
-                               }
-                               childValues[1] = root.data.getDerValue();
-                               if (childValues[1].tag != DerValue.tag_OctetString) {
-                                       log.error("Unexpected data type.  Unable to determine key type from data.");
-                                       throw new CredentialFactoryException("Unable to load private key.");
-                               }
+                               allCerts.addAll(Arrays.asList(certsFromPath));
 
-                               if (childValues[0].data.available() == 0) {
-                                       log.error("Unexpected data type.  Unable to determine key type from data.");
-                                       throw new CredentialFactoryException("Unable to load private key.");
-                               }
-                               DerValue grandChild = childValues[0].data.getDerValue();
-                               if (grandChild.tag != DerValue.tag_ObjectId) {
-                                       log.error("Unexpected data type.  Unable to determine key type from data.");
-                                       throw new CredentialFactoryException("Unable to load private key.");
+                               // Find the end-entity cert first
+                               if (certsFromPath == null || certsFromPath.length == 0) {
+                                       log.error("File at (" + certPath + ") did not contain any valid certificates.");
+                                       throw new CredentialFactoryException("File did not contain any valid certificates.");
                                }
 
-                               log.debug("Key appears to be formatted as encrypted PKCS8. Loading...");
-                               return getEncryptedPkcs8Key(inputBytes.toByteArray(), password.toCharArray());
-
-                       } else if (childValues[0].tag == DerValue.tag_Integer) {
-
-                               //May be pkcs8, rsa, or dsa... dig further
-                               if (root.data.available() == 0) {
-                                       log.error("Unexpected data type.  Unable to determine key type from data.");
-                                       throw new CredentialFactoryException("Unable to load private key.");
-                               }
-                               childValues[1] = root.data.getDerValue();
-                               if (childValues[1].tag == DerValue.tag_Sequence) {
-                                       //May be pkcs8... dig further
-                                       if (root.data.available() == 0) {
-                                               log.error("Unexpected data type.  Unable to determine key type from data.");
-                                               throw new CredentialFactoryException("Unable to load private key.");
-                                       }
-                                       childValues[2] = root.data.getDerValue();
-                                       if (childValues[2].tag != DerValue.tag_OctetString) {
-                                               log.error("Unexpected data type.  Unable to determine key type from data.");
-                                               throw new CredentialFactoryException("Unable to load private key.");
+                               if (certsFromPath.length == 1) {
+                                       if (log.isDebugEnabled()) {
+                                               log.debug("Certificate file only contains 1 certificate.");
+                                               log.debug("Ensuring that it matches the private key.");
                                        }
-
-                                       if (childValues[1].data.available() == 0) {
-                                               log.error("Unexpected data type.  Unable to determine key type from data.");
-                                               throw new CredentialFactoryException("Unable to load private key.");
+                                       if (!isMatchingKey(certsFromPath[0].getPublicKey(), key)) {
+                                               log.error("Certificate file " + certPath
+                                                               + "only contained one certificate and it does not match the private key.");
+                                               throw new CredentialFactoryException(
+                                                               "No certificate in chain that matches specified private key");
                                        }
-                                       DerValue grandChild = childValues[1].data.getDerValue();
-                                       if (grandChild.tag != DerValue.tag_ObjectId) {
-                                               log.error("Unexpected data type.  Unable to determine key type from data.");
-                                               throw new CredentialFactoryException("Unable to load private key.");
+                                       certChain.add(certsFromPath[0]);
+                                       if (log.isDebugEnabled()) {
+                                               log.debug("Successfully identified the end entity cert: "
+                                                               + ((X509Certificate) certChain.get(0)).getSubjectDN());
                                        }
 
-                                       log.debug("Key appears to be formatted as PKCS8. Loading...");
-                                       return getRSAPkcs8DerKey(inputBytes.toByteArray());
-
-                               } else if (childValues[1].tag == DerValue.tag_Integer) {
-
-                                       //May be rsa or dsa... dig further
-                                       if (root.data.available() == 0
-                                               || root.data.getDerValue().tag != DerValue.tag_Integer
-                                               || root.data.available() == 0
-                                               || root.data.getDerValue().tag != DerValue.tag_Integer
-                                               || root.data.available() == 0
-                                               || root.data.getDerValue().tag != DerValue.tag_Integer
-                                               || root.data.available() == 0
-                                               || root.data.getDerValue().tag != DerValue.tag_Integer) {
-                                               log.error("Unexpected data type.  Unable to determine key type from data.");
-                                               throw new CredentialFactoryException("Unable to load private key.");
+                               } else {
+                                       if (log.isDebugEnabled()) {
+                                               log.debug("Certificate file contains multiple certificates.");
+                                               log
+                                                               .debug("Trying to determine the end-entity cert by the matching certificates against the private key.");
                                        }
-
-                                       if (root.data.available() == 0) {
-
-                                               log.debug("Key appears to be DSA. Loading...");
-                                               return getDSARawDerKey(inputBytes.toByteArray());
-
-                                       } else {
-
-                                               DerValue dsaOverrun = root.data.getDerValue();
-                                               if (dsaOverrun.tag != DerValue.tag_Integer) {
-                                                       log.error("Unexpected data type.  Unable to determine key type from data.");
-                                                       throw new CredentialFactoryException("Unable to load private key.");
+                                       for (int i = 0; certsFromPath.length > i; i++) {
+                                               if (isMatchingKey(certsFromPath[i].getPublicKey(), key)) {
+                                                       if (log.isDebugEnabled()) {
+                                                               log.debug("Found matching end cert: "
+                                                                               + ((X509Certificate) certsFromPath[i]).getSubjectDN());
+                                                       }
+                                                       certChain.add(certsFromPath[i]);
                                                }
-
-                                               log.debug("Key appears to be RSA. Loading...");
-                                               return getRSARawDerKey(inputBytes.toByteArray());
                                        }
-
-                               } else {
-                                       log.error("Unexpected data type.  Unable to determine key type from data.");
-                                       throw new CredentialFactoryException("Unable to load private key.");
+                                       if (certChain.size() < 1) {
+                                               log.error("Certificate file " + certPath
+                                                               + "only contained multiple certificates and none matched the private key.");
+                                               throw new CredentialFactoryException(
+                                                               "No certificate in chain that matches specified private key");
+                                       }
+                                       if (certChain.size() > 1) {
+                                               log.error("More than one certificate in chain that matches specified private key");
+                                               throw new CredentialFactoryException(
+                                                               "More than one certificate in chain that matches specified private key");
+                                       }
+                                       if (log.isDebugEnabled()) {
+                                               log.debug("Successfully identified the end entity cert: "
+                                                               + ((X509Certificate) certChain.get(0)).getSubjectDN());
+                                       }
                                }
 
-                       } else {
-                               log.error("Unexpected data type.  Unable to determine key type from data.");
-                               throw new CredentialFactoryException("Unable to load private key.");
-                       }
-
-               } catch (CredentialFactoryException e) {
-                       log.error("Invalid DER encoding for key: " + e);
-                       throw new CredentialFactoryException("Unable to load private key.");
-               }
-
-       }
+                               // Now load additional certs and construct a chain
+                               String[] caPaths = getCAPaths(credentialConfigElement);
+                               if (caPaths != null && caPaths.length > 0) {
+                                       if (log.isDebugEnabled()) {
+                                               log
+                                                               .debug("Attempting to load certificates from (" + caPaths.length
+                                                                               + ") CA certificate files.");
+                                       }
+                                       for (int i = 0; i < caPaths.length; i++) {
+                                               allCerts.addAll(Arrays.asList(loadCertificates(new ShibResource(caPaths[i], this.getClass())
+                                                               .getInputStream(), certType)));
+                                       }
+                               }
 
-       private PrivateKey getPEMKey(InputStream inStream, String password)
-               throws CredentialFactoryException, IOException {
+                               if (log.isDebugEnabled()) {
+                                       log.debug("Attempting to construct a certificate chain.");
+                               }
+                               walkChain((X509Certificate[]) allCerts.toArray(new X509Certificate[0]), certChain);
 
-               byte[] inputBuffer = new byte[8];
-               int i;
-               ByteContainer inputBytes = new ByteContainer(800);
-               do {
-                       i = inStream.read(inputBuffer);
-                       for (int j = 0; j < i; j++) {
-                               inputBytes.append(inputBuffer[j]);
-                       }
-               } while (i > -1);
-
-               BufferedReader in =
-                       new BufferedReader(new InputStreamReader(new ByteArrayInputStream(inputBytes.toByteArray())));
-               String str;
-               while ((str = in.readLine()) != null) {
-
-                       if (str.matches("^.*-----BEGIN PRIVATE KEY-----.*$")) {
-                               log.debug("Key appears to be in PKCS8 format.");
-                               in.close();
-                               return getPkcs8Key(
-                                       singleDerFromPEM(
-                                               inputBytes.toByteArray(),
-                                               "-----BEGIN PRIVATE KEY-----",
-                                               "-----END PRIVATE KEY-----"));
-
-                       } else if (str.matches("^.*-----BEGIN RSA PRIVATE KEY-----.*$")) {
-                               String nextStr = in.readLine();
-                               if (nextStr != null && nextStr.matches("^.*Proc-Type: 4,ENCRYPTED.*$")) {
-                                       log.debug("Key appears to be encrypted RSA in raw format.");
-                                       return getRawEncryptedPemKey(inputBytes.toByteArray(), password);
+                               if (log.isDebugEnabled()) {
+                                       log.debug("Verifying that each link in the cert chain is signed appropriately");
+                               }
+                               for (int i = 0; i < certChain.size() - 1; i++) {
+                                       PublicKey pubKey = ((X509Certificate) certChain.get(i + 1)).getPublicKey();
+                                       try {
+                                               ((X509Certificate) certChain.get(i)).verify(pubKey);
+                                       } catch (Exception se) {
+                                               log.error("Certificate chain cannot be verified: " + se);
+                                               throw new CredentialFactoryException("Certificate chain cannot be verified: " + se);
+                                       }
+                               }
+                               if (log.isDebugEnabled()) {
+                                       log.debug("All signatures verified. Certificate chain creation successful.");
                                }
 
-                               in.close();
-                               log.debug("Key appears to be RSA in raw format.");
-                               return getRSARawDerKey(
-                                       singleDerFromPEM(
-                                               inputBytes.toByteArray(),
-                                               "-----BEGIN RSA PRIVATE KEY-----",
-                                               "-----END RSA PRIVATE KEY-----"));
-
-                       } else if (str.matches("^.*-----BEGIN DSA PRIVATE KEY-----.*$")) {
-                               String nextStr = in.readLine();
-                               if (nextStr != null && nextStr.matches("^.*Proc-Type: 4,ENCRYPTED.*$")) {
-                                       log.debug("Key appears to be encrypted DSA in raw format.");
-                                       return getRawEncryptedPemKey(inputBytes.toByteArray(), password);
+                               if (log.isInfoEnabled()) {
+                                       log.info("Successfully loaded certificates.");
                                }
-                               in.close();
-                               log.debug("Key appears to be DSA in raw format.");
-                               return getDSARawDerKey(
-                                       singleDerFromPEM(
-                                               inputBytes.toByteArray(),
-                                               "-----BEGIN DSA PRIVATE KEY-----",
-                                               "-----END DSA PRIVATE KEY-----"));
-
-                       } else if (str.matches("^.*-----BEGIN ENCRYPTED PRIVATE KEY-----.*$")) {
-                               in.close();
-                               log.debug("Key appears to be in encrypted PKCS8 format.");
-                               return getEncryptedPkcs8Key(
-                                       singleDerFromPEM(
-                                               inputBytes.toByteArray(),
-                                               "-----BEGIN ENCRYPTED PRIVATE KEY-----",
-                                               "-----END ENCRYPTED PRIVATE KEY-----"),
-                                       password.toCharArray());
+                       } catch (IOException ioe) {
+                               log.error("Could not load resource from specified location (" + certPath + "): " + ioe);
+                               throw new CredentialFactoryException("Unable to load certificates.");
                        }
                }
-               in.close();
-               log.error("Unsupported formatting.  Available PEM types are PKCS8, Raw RSA, and Raw DSA.");
-               throw new CredentialFactoryException("Failed to initialize Credential Resolver.");
 
+               return certChain;
        }
 
-       private PrivateKey getRSAPkcs8DerKey(byte[] bytes) throws CredentialFactoryException {
-
-               try {
-                       KeyFactory keyFactory = KeyFactory.getInstance("RSA");
-                       PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(bytes);
-                       return keyFactory.generatePrivate(keySpec);
+       /**
+        * Gets the private key password from the Credentials configuration element if one exists.
+        * 
+        * @param e
+        *            the credentials configuration element
+        * @return the password if one is given or an empty string if one is not
+        * @throws CredentialFactoryException
+        *             thrown if no Key element is present in the configuration
+        */
+       private String getKeyPassword(Element e) throws CredentialFactoryException {
 
-               } catch (Exception e) {
-                       log.error("Unable to load private key: " + e);
-                       throw new CredentialFactoryException("Unable to load private key.");
+               NodeList keyElements = e.getElementsByTagNameNS(Credentials.credentialsNamespace, "Key");
+               if (keyElements.getLength() < 1) {
+                       log.error("Key not specified.");
+                       throw new CredentialFactoryException("File Credential Resolver requires a <Key> specification.");
                }
-       }
 
-       private PrivateKey getDSAPkcs8DerKey(byte[] bytes) throws CredentialFactoryException {
-
-               try {
-                       KeyFactory keyFactory = KeyFactory.getInstance("DSA");
-                       PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(bytes);
-                       return keyFactory.generatePrivate(keySpec);
+               if (keyElements.getLength() > 1) {
+                       log.error("Multiple Key path specifications, using first.");
+               }
 
-               } catch (Exception e) {
-                       log.error("Unable to load private key: " + e);
-                       throw new CredentialFactoryException("Unable to load private key.");
+               String password = ((Element) keyElements.item(0)).getAttribute("password");
+               if (password == null) {
+                       password = "";
                }
+               return password;
        }
 
-       private PrivateKey getRSARawDerKey(byte[] bytes) throws CredentialFactoryException {
-
-               try {
-                       DerValue root = new DerValue(bytes);
-                       if (root.tag != DerValue.tag_Sequence) {
-                               log.error("Unexpected data type.  Unable to load data as an RSA key.");
-                               throw new CredentialFactoryException("Unable to load private key.");
-                       }
-
-                       DerValue[] childValues = new DerValue[10];
-                       childValues[0] = root.data.getDerValue();
-                       childValues[1] = root.data.getDerValue();
-                       childValues[2] = root.data.getDerValue();
-                       childValues[3] = root.data.getDerValue();
-                       childValues[4] = root.data.getDerValue();
-                       childValues[5] = root.data.getDerValue();
-                       childValues[6] = root.data.getDerValue();
-                       childValues[7] = root.data.getDerValue();
-                       childValues[8] = root.data.getDerValue();
-
-                       //This data is optional.
-                       if (root.data.available() != 0) {
-                               childValues[9] = root.data.getDerValue();
-                               if (root.data.available() != 0) {
-                                       log.error("Data overflow.  Unable to load data as an RSA key.");
-                                       throw new CredentialFactoryException("Unable to load private key.");
-                               }
-                       }
+       /**
+        * Gets the certificate path from the Credentials configuration element. If multiple paths are specified only the
+        * first one is used.
+        * 
+        * @param e
+        *            the credentials configuration element
+        * @return the certificate path, or null if non is specificed
+        * @throws CredentialFactoryException
+        *             thrown if no Path element is given or it's empty
+        */
+       private String getCertPath(Element e) throws CredentialFactoryException {
 
-                       if (childValues[0].tag != DerValue.tag_Integer
-                               || childValues[1].tag != DerValue.tag_Integer
-                               || childValues[2].tag != DerValue.tag_Integer
-                               || childValues[3].tag != DerValue.tag_Integer
-                               || childValues[4].tag != DerValue.tag_Integer
-                               || childValues[5].tag != DerValue.tag_Integer
-                               || childValues[6].tag != DerValue.tag_Integer
-                               || childValues[7].tag != DerValue.tag_Integer
-                               || childValues[8].tag != DerValue.tag_Integer) {
-                               log.error("Unexpected data type.  Unable to load data as an RSA key.");
-                               throw new CredentialFactoryException("Unable to load private key.");
+               NodeList certificateElements = e.getElementsByTagNameNS(Credentials.credentialsNamespace, "Certificate");
+               if (certificateElements.getLength() < 1) {
+                       if (log.isDebugEnabled()) {
+                               log.debug("No <Certificate> element found.");
                        }
+                       return null;
+               }
 
-                       RSAPrivateCrtKeySpec keySpec =
-                               new RSAPrivateCrtKeySpec(
-                                       childValues[1].getBigInteger(),
-                                       childValues[2].getBigInteger(),
-                                       childValues[3].getBigInteger(),
-                                       childValues[4].getBigInteger(),
-                                       childValues[5].getBigInteger(),
-                                       childValues[6].getBigInteger(),
-                                       childValues[7].getBigInteger(),
-                                       childValues[8].getBigInteger());
-
-                       KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+               NodeList pathElements = ((Element) certificateElements.item(0)).getElementsByTagNameNS(
+                               Credentials.credentialsNamespace, "Path");
 
-                       return keyFactory.generatePrivate(keySpec);
+               if (pathElements.getLength() < 1) {
+                       log.error("Certificate path not specified.");
+                       throw new CredentialFactoryException(
+                                       "File Credential Resolver requires a <Certificate><Path/></Certificate> specification, none was specified.");
+               }
 
-               } catch (IOException e) {
-                       log.error("Invalid DER encoding for RSA key: " + e);
-                       throw new CredentialFactoryException("Unable to load private key.");
-               } catch (GeneralSecurityException e) {
-                       log.error("Unable to marshall private key: " + e);
-                       throw new CredentialFactoryException("Unable to load private key.");
+               if (pathElements.getLength() > 1) {
+                       log.error("Multiple Certificate path specifications, using first.");
+               }
+               Node tnode = pathElements.item(0).getFirstChild();
+               String path = null;
+               if (tnode != null && tnode.getNodeType() == Node.TEXT_NODE) {
+                       path = tnode.getNodeValue();
+               }
+               if (path == null || path.equals("")) {
+                       log.error("Certificate path was empty.");
+                       throw new CredentialFactoryException(
+                                       "File Credential Resolver requires a <Certificate><Path/></Certificate> specification, the specified one was empty.");
                }
 
+               return path;
        }
-       private PrivateKey getRawEncryptedPemKey(byte[] bytes, String password) throws CredentialFactoryException {
-
-               try {
-                       String algorithm = null;
-                       String algParams = null;
-
-                       BufferedReader in = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(bytes)));
-                       String str;
-                       boolean insideBase64 = false;
-                       StringBuffer base64Key = null;
-                       while ((str = in.readLine()) != null) {
 
-                               if (insideBase64) {
-                                       if (str.matches("^.*Proc-Type: 4,ENCRYPTED.*$")) {
-                                               continue;
-                                       }
+       /**
+        * Get the CA certificate paths from the Credentials configuration element. Paths should be delimited with the
+        * operating system path delimiter. If multiple Certificate elements are found only the first is used.
+        * 
+        * @param e
+        *            the credentials configuration element
+        * @return an array of CA certificate paths, or null if no certificate path was specified
+        * @throws CredentialFactoryException
+        *             no certificate path was specified
+        */
+       private String[] getCAPaths(Element e) throws CredentialFactoryException {
 
-                                       if (str.matches("^.*DEK-Info:.*$")) {
-                                               String[] components = str.split(":\\s");
-                                               if (components.length != 2) {
-                                                       log.error("Encrypted key did not contain DEK-Info specification.");
-                                                       throw new CredentialFactoryException("Unable to load private key.");
-                                               }
-                                               String[] cryptData = components[1].split(",");
-                                               if (cryptData.length != 2
-                                                       || cryptData[0] == null
-                                                       || cryptData[0].equals("")
-                                                       || cryptData[1] == null
-                                                       || cryptData[1].equals("")) {
-                                                       log.error("Encrypted key did not contain a proper DEK-Info specification.");
-                                                       throw new CredentialFactoryException("Unable to load private key.");
-                                               }
-                                               algorithm = cryptData[0];
-                                               algParams = cryptData[1];
-                                               continue;
-                                       }
-                                       if (str.equals("")) {
-                                               continue;
-                                       }
+               NodeList certificateElements = e.getElementsByTagNameNS(Credentials.credentialsNamespace, "Certificate");
+               if (certificateElements.getLength() < 1) {
+                       log.error("Certificate not specified.");
+                       throw new CredentialFactoryException("File Credential Resolver requires a <Certificate> specification.");
+               }
+               if (certificateElements.getLength() > 1) {
+                       log.error("Multiple Certificate path specifications, using first.");
+               }
 
-                                       if (str.matches("^.*-----END [DR]SA PRIVATE KEY-----.*$")) {
-                                               break;
-                                       }
-                                       {
-                                               base64Key.append(str);
-                                       }
-                               } else if (str.matches("^.*-----BEGIN [DR]SA PRIVATE KEY-----.*$")) {
-                                       insideBase64 = true;
-                                       base64Key = new StringBuffer();
-                               }
+               NodeList pathElements = ((Element) certificateElements.item(0)).getElementsByTagNameNS(
+                               Credentials.credentialsNamespace, "CAPath");
+               if (pathElements.getLength() < 1) {
+                       if (log.isDebugEnabled()) {
+                               log.debug("No CA Certificate paths specified.");
                        }
-                       in.close();
-                       if (base64Key == null || base64Key.length() == 0) {
-                               log.error("Could not find Base 64 encoded entity.");
-                               throw new IOException("Could not find Base 64 encoded entity.");
+                       return null;
+               }
+               ArrayList paths = new ArrayList();
+               for (int i = 0; i < pathElements.getLength(); i++) {
+                       Node tnode = pathElements.item(i).getFirstChild();
+                       String path = null;
+                       if (tnode != null && tnode.getNodeType() == Node.TEXT_NODE) {
+                               path = tnode.getNodeValue();
                        }
-
-                       BASE64Decoder decoder = new BASE64Decoder();
-                       byte[] encryptedBytes = decoder.decodeBuffer(base64Key.toString());
-
-                       byte[] ivBytes = new byte[8];
-                       for (int j = 0; j < 8; j++) {
-                               ivBytes[j] = (byte) Integer.parseInt(algParams.substring(j * 2, j * 2 + 2), 16);
+                       if (path != null && !(path.equals(""))) {
+                               paths.add(path);
                        }
-                       IvParameterSpec paramSpec = new IvParameterSpec(ivBytes);
-
-                       if ((!algorithm.equals("DES-CBC")) && (!algorithm.equals("DES-EDE3-CBC"))) {
-                               log.error(
-                                       "Connot decrypt key with algorithm ("
-                                               + algorithm
-                                               + ").  Supported algorithms for raw (OpenSSL) keys are (DES-CBC) and (DES-EDE3-CBC).");
-                               throw new CredentialFactoryException("Unable to load private key.");
+                       if (paths.isEmpty()) {
+                               if (log.isDebugEnabled()) {
+                                       log.debug("No CA Certificate paths specified.");
+                               }
                        }
+               }
+               return (String[]) paths.toArray(new String[0]);
+       }
 
-                       byte[] keyBuffer = new byte[24];
-                       //The key generation method (with the IV used as the salt, and
-                       //the single proprietary iteration)
-                       //is the reason we can't use the pkcs5 providers to read the
-                       // OpenSSL encrypted format
-
-                       MessageDigest md = MessageDigest.getInstance("MD5");
-                       md.update(password.getBytes());
-                       md.update(paramSpec.getIV());
-                       byte[] digested = md.digest();
-                       System.arraycopy(digested, 0, keyBuffer, 0, 16);
-
-                       md.update(digested);
-                       md.update(password.getBytes());
-                       md.update(paramSpec.getIV());
-                       digested = md.digest();
-                       System.arraycopy(digested, 0, keyBuffer, 16, 8);
-
-                       SecretKeySpec keySpec = null;
-                       Cipher cipher = null;
-                       if (algorithm.equals("DES-CBC")) {
-                               //Special handling!!!
-                               //For DES, we use the same key generation,
-                               //then just chop off the end :-)
-                               byte[] desBuff = new byte[8];
-                               System.arraycopy(keyBuffer, 0, desBuff, 0, 8);
-                               keySpec = new SecretKeySpec(desBuff, "DES");
-                               cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
-                       }
-                       if (algorithm.equals("DES-EDE3-CBC")) {
-                               keySpec = new SecretKeySpec(keyBuffer, "DESede");
-                               cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
-                       }
-
-                       cipher.init(Cipher.DECRYPT_MODE, keySpec, paramSpec);
-                       byte[] decrypted = cipher.doFinal(encryptedBytes);
-
-                       return getDERKey(new ByteArrayInputStream(decrypted), password);
+       /**
+        * Gets the path to the private key from the Credentials configuration element. If more than one is specified only
+        * the first one is used.
+        * 
+        * @param e
+        *            the credentials configuration element
+        * @return path to the private key
+        * @throws CredentialFactoryException
+        *             thrown if no path is specified or it's null.
+        */
+       private String getKeyPath(Element e) throws CredentialFactoryException {
 
-               } catch (IOException ioe) {
-                       log.error("Could not decode Base 64: " + ioe);
-                       throw new CredentialFactoryException("Unable to load private key.");
+               NodeList keyElements = e.getElementsByTagNameNS(Credentials.credentialsNamespace, "Key");
+               if (keyElements.getLength() < 1) {
+                       log.error("Key not specified.");
+                       throw new CredentialFactoryException("File Credential Resolver requires a <Key> specification.");
+               }
+               if (keyElements.getLength() > 1) {
+                       log.error("Multiple Key path specifications, using first.");
+               }
 
-               } catch (BadPaddingException e) {
-                       log.debug(e.getMessage());
-                       log.error("Incorrect password to unlock private key.");
-                       throw new CredentialFactoryException("Unable to load private key.");
-               } catch (Exception e) {
-                       log.error(
-                               "Unable to decrypt private key.  Installed JCE implementations don't support the necessary algorithm: "
-                                       + e);
-                       throw new CredentialFactoryException("Unable to load private key.");
+               NodeList pathElements = ((Element) keyElements.item(0)).getElementsByTagNameNS(
+                               Credentials.credentialsNamespace, "Path");
+               if (pathElements.getLength() < 1) {
+                       log.error("Key path not specified.");
+                       throw new CredentialFactoryException(
+                                       "File Credential Resolver requires a <Key><Path/></Certificate> specification.");
+               }
+               if (pathElements.getLength() > 1) {
+                       log.error("Multiple Key path specifications, using first.");
                }
+               Node tnode = pathElements.item(0).getFirstChild();
+               String path = null;
+               if (tnode != null && tnode.getNodeType() == Node.TEXT_NODE) {
+                       path = tnode.getNodeValue();
+               }
+               if (path == null || path.equals("")) {
+                       log.error("Key path is empty.");
+                       throw new CredentialFactoryException(
+                                       "File Credential Resolver requires a <Key><Path/></Certificate> specification.");
+               }
+               return path;
        }
 
-       private PrivateKey getDSARawDerKey(byte[] bytes) throws CredentialFactoryException {
+       /**
+        * Loads a specified bundle of certs individually and returns an array of {@link Certificate}objects. This is
+        * needed because the standard {@link CertificateFactory#getCertificates(InputStream)}method bails out when it has
+        * trouble loading any cert and cannot handle "comments".
+        */
+       private Certificate[] loadCertificates(InputStream inStream, String certType) throws CredentialFactoryException {
+
+               ArrayList certificates = new ArrayList();
 
                try {
-                       DerValue root = new DerValue(bytes);
-                       if (root.tag != DerValue.tag_Sequence) {
-                               log.error("Unexpected data type.  Unable to load data as an DSA key.");
-                               throw new CredentialFactoryException("Unable to load private key.");
-                       }
-
-                       DerValue[] childValues = new DerValue[6];
-                       childValues[0] = root.data.getDerValue();
-                       childValues[1] = root.data.getDerValue();
-                       childValues[2] = root.data.getDerValue();
-                       childValues[3] = root.data.getDerValue();
-                       childValues[4] = root.data.getDerValue();
-                       childValues[5] = root.data.getDerValue();
+                       CertificateFactory certFactory = CertificateFactory.getInstance(certType);
 
-                       if (root.data.available() != 0) {
-                               log.error("Data overflow.  Unable to load data as an DSA key.");
-                               throw new CredentialFactoryException("Unable to load private key.");
-                       }
+                       BufferedReader in = new BufferedReader(new InputStreamReader(inStream));
+                       String str;
+                       boolean insideCert = false;
+                       StringBuffer rawCert = null;
+                       while ((str = in.readLine()) != null) {
 
-                       if (childValues[0].tag != DerValue.tag_Integer
-                               || childValues[1].tag != DerValue.tag_Integer
-                               || childValues[2].tag != DerValue.tag_Integer
-                               || childValues[3].tag != DerValue.tag_Integer
-                               || childValues[4].tag != DerValue.tag_Integer
-                               || childValues[5].tag != DerValue.tag_Integer) {
-                               log.error("Unexpected data type.  Unable to load data as an DSA key.");
-                               throw new CredentialFactoryException("Unable to load private key.");
+                               if (insideCert) {
+                                       rawCert.append(str);
+                                       rawCert.append(System.getProperty("line.separator"));
+                                       if (str.matches("^.*-----END CERTIFICATE-----.*$")) {
+                                               insideCert = false;
+                                               try {
+                                                       Certificate cert = certFactory.generateCertificate(new ByteArrayInputStream(rawCert
+                                                                       .toString().getBytes()));
+                                                       certificates.add(cert);
+                                               } catch (CertificateException ce) {
+                                                       log.warn("Failed to load a certificate from the certificate bundle: " + ce);
+                                                       if (log.isDebugEnabled()) {
+                                                               if (log.isDebugEnabled()) {
+                                                                       log.debug("Dump of bad certificate: " + System.getProperty("line.separator")
+                                                                                       + rawCert.toString());
+                                                               }
+                                                       }
+                                               }
+                                               continue;
+                                       }
+                               } else if (str.matches("^.*-----BEGIN CERTIFICATE-----.*$")) {
+                                       insideCert = true;
+                                       rawCert = new StringBuffer();
+                                       rawCert.append(str);
+                                       rawCert.append(System.getProperty("line.separator"));
+                               }
                        }
-
-                       DSAPrivateKeySpec keySpec =
-                               new DSAPrivateKeySpec(
-                                       childValues[5].getBigInteger(),
-                                       childValues[1].getBigInteger(),
-                                       childValues[2].getBigInteger(),
-                                       childValues[3].getBigInteger());
-
-                       KeyFactory keyFactory = KeyFactory.getInstance("DSA");
-
-                       return keyFactory.generatePrivate(keySpec);
-
-               } catch (IOException e) {
-                       log.error("Invalid DER encoding for DSA key: " + e);
-                       throw new CredentialFactoryException("Unable to load private key.");
-               } catch (GeneralSecurityException e) {
-                       log.error("Unable to marshall private key: " + e);
-                       throw new CredentialFactoryException("Unable to load private key.");
+                       in.close();
+               } catch (IOException p) {
+                       log.error("Could not load resource from specified location: " + p);
+                       throw new CredentialFactoryException("Unable to load certificates.");
+               } catch (CertificateException p) {
+                       log.error("Problem loading certificate factory: " + p);
+                       throw new CredentialFactoryException("Unable to load certificates.");
                }
 
+               return (Certificate[]) certificates.toArray(new Certificate[0]);
        }
 
-       private PrivateKey getPkcs8Key(byte[] bytes) throws CredentialFactoryException {
+       /**
+        * 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
+        */
+       protected void walkChain(X509Certificate[] chainSource, List chainDest) throws CredentialFactoryException {
 
-               try {
-                       DerValue root = new DerValue(bytes);
-                       if (root.tag != DerValue.tag_Sequence) {
-                               log.error("Unexpected data type.  Unable to load data as a PKCS8 formatted key.");
-                               throw new CredentialFactoryException("Unable to load private key.");
+               X509Certificate currentCert = (X509Certificate) chainDest.get(chainDest.size() - 1);
+               if (currentCert.getSubjectDN().equals(currentCert.getIssuerDN())) {
+                       if (log.isDebugEnabled()) {
+                               log.debug("Found self-signed root cert: " + currentCert.getSubjectDN());
                        }
+                       return;
+               } else {
+                       for (int i = 0; chainSource.length > i; i++) {
+                               if (currentCert.getIssuerDN().equals(chainSource[i].getSubjectDN())) {
+                                       chainDest.add(chainSource[i]);
+                                       walkChain(chainSource, chainDest);
+                                       return;
+                               }
+                       }
+                       if (log.isDebugEnabled()) {
+                               log.debug("Certificate chain is incomplete.");
+                       }
+               }
+       }
 
-                       DerValue[] childValues = new DerValue[2];
-                       childValues[0] = root.data.getDerValue();
-                       childValues[1] = root.data.getDerValue();
+       /**
+        * 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
+        */
+       protected boolean isMatchingKey(PublicKey pubKey, PrivateKey privKey) {
 
-                       if (childValues[0].tag != DerValue.tag_Integer || childValues[1].tag != DerValue.tag_Sequence) {
-                               log.error("Unexpected data type.  Unable to load data as a PKCS8 formatted key.");
-                               throw new CredentialFactoryException("Unable to load private key.");
+               try {
+                       String controlString = "asdf";
+                       if (log.isDebugEnabled()) {
+                               log.debug("Checking for matching private key/public key pair");
                        }
 
-                       DerValue grandChild = childValues[1].data.getDerValue();
-                       if (grandChild.tag != DerValue.tag_ObjectId) {
-                               log.error("Unexpected data type.  Unable to load data as a PKCS8 formatted key.");
-                               throw new CredentialFactoryException("Unable to load private key.");
+                       Signature signature = null;
+                       try {
+                               signature = Signature.getInstance(privKey.getAlgorithm());
+                       } catch (NoSuchAlgorithmException nsae) {
+                               if (log.isDebugEnabled()) {
+                                       log.debug("No provider for (RSA) signature, attempting (MD5withRSA).");
+                               }
+                               if (privKey.getAlgorithm().equals("RSA")) {
+                                       signature = Signature.getInstance("MD5withRSA");
+                               } else {
+                                       throw nsae;
+                               }
                        }
-
-                       String keyOID = grandChild.getOID().toString();
-                       if (keyOID.equals(FileCredentialResolver.RSAKey_OID)) {
-                               log.debug("Found RSA key in PKCS8.");
-                               return getRSAPkcs8DerKey(bytes);
-                       } else if (keyOID.equals(FileCredentialResolver.DSAKey_OID)) {
-                               log.debug("Found DSA key in PKCS8.");
-                               return getDSAPkcs8DerKey(bytes);
-                       } else {
-                               log.error("Unexpected key type.  Only RSA and DSA keys are supported in PKCS8 format.");
-                               throw new CredentialFactoryException("Unable to load private key.");
+                       signature.initSign(privKey);
+                       signature.update(controlString.getBytes());
+                       byte[] sigBytes = signature.sign();
+                       signature.initVerify(pubKey);
+                       signature.update(controlString.getBytes());
+                       if (signature.verify(sigBytes)) {
+                               if (log.isDebugEnabled()) {
+                                       log.debug("Found match.");
+                               }
+                               return true;
                        }
-
-               } catch (IOException e) {
-                       log.error("Invalid DER encoding for PKCS8 formatted key: " + e);
-                       throw new CredentialFactoryException("Unable to load private key.");
+               } catch (Exception e) {
+                       log.warn(e);
+               }
+               if (log.isDebugEnabled()) {
+                       log.debug("This pair does not match.");
                }
+               return false;
        }
 
-       private PrivateKey getEncryptedPkcs8Key(byte[] bytes, char[] password) throws CredentialFactoryException {
+       /**
+        * Auto-enlarging container for bytes.
+        */
+       // Sure makes you wish bytes were first class objects.
+       private class ByteContainer {
 
-               try {
+               private byte[] buffer;
+               private int cushion;
+               private int currentSize = 0;
 
-                       //Convince the JCE provider that it does know how to do
-                       // pbeWithMD5AndDES-CBC
-                       Provider provider = Security.getProvider("SunJCE");
-                       if (provider != null) {
-                               provider.setProperty("Alg.Alias.AlgorithmParameters.1.2.840.113549.1.5.3", "PBE");
-                               provider.setProperty("Alg.Alias.SecretKeyFactory.1.2.840.113549.1.5.3", "PBEWithMD5AndDES");
-                               provider.setProperty("Alg.Alias.Cipher.1.2.840.113549.1.5.3", "PBEWithMD5AndDES");
-                       }
+               private ByteContainer(int initSize, int growBy) {
 
-                       EncryptedPrivateKeyInfo encryptedKeyInfo = new EncryptedPrivateKeyInfo(bytes);
-                       AlgorithmParameters params = encryptedKeyInfo.getAlgParameters();
+                       buffer = new byte[initSize];
+                       this.cushion = growBy;
+               }
 
-                       if (params == null) {
-                               log.error(
-                                       "Unable to decrypt private key.  Installed JCE implementations don't support the ("
-                                               + encryptedKeyInfo.getAlgName()
-                                               + ") algorithm.");
-                               throw new CredentialFactoryException("Unable to load private key.");
+               private void grow() {
+
+                       int newSize = currentSize + cushion;
+                       byte[] b = new byte[newSize];
+                       int toCopy = Math.min(currentSize, newSize);
+                       int i;
+                       for (i = 0; i < toCopy; i++) {
+                               b[i] = buffer[i];
                        }
+                       buffer = b;
+               }
 
-                       SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(encryptedKeyInfo.getAlgName());
-                       PBEKeySpec passwordSpec = new PBEKeySpec(password);
-                       SecretKey key = keyFactory.generateSecret(passwordSpec);
+               /**
+                * Returns an array of the bytes in the container.
+                * <p>
+                */
 
-                       Cipher cipher = Cipher.getInstance(encryptedKeyInfo.getAlgName());
-                       cipher.init(Cipher.DECRYPT_MODE, key, params);
-                       PKCS8EncodedKeySpec decrypted = encryptedKeyInfo.getKeySpec(cipher);
+               private byte[] toByteArray() {
 
-                       return getPkcs8Key(decrypted.getEncoded());
+                       byte[] b = new byte[currentSize];
+                       System.arraycopy(buffer, 0, b, 0, currentSize);
+                       return b;
+               }
 
-               } catch (IOException e) {
-                       e.printStackTrace();
-                       log.error("Invalid DER encoding for PKCS8 formatted encrypted key: " + e);
-                       throw new CredentialFactoryException("Unable to load private key.");
-               } catch (InvalidKeySpecException e) {
-                       log.debug(e.getMessage());
-                       log.error("Incorrect password to unlock private key.");
-                       throw new CredentialFactoryException("Unable to load private key.");
-               } catch (Exception e) {
-                       log.error(
-                               "Unable to decrypt private key.  Installed JCE implementations don't support the necessary algorithm: "
-                                       + e);
-                       throw new CredentialFactoryException("Unable to load private key.");
+               /**
+                * Add one byte to the end of the container.
+                */
+
+               private void append(byte b) {
+
+                       if (currentSize == buffer.length) {
+                               grow();
+                       }
+                       buffer[currentSize] = b;
+                       currentSize++;
                }
 
        }
 
-       private byte[] singleDerFromPEM(byte[] bytes, String beginToken, String endToken) throws IOException {
+       /**
+        * Abstract class representing private keys encoded in formats like PEM and DER.
+        * 
+        * @author Chad La Joie
+        */
+       private abstract class EncodedKey {
 
-               try {
+               /**
+                * DER encoded key
+                */
+               public static final int DER_ENCODING = 0;
 
-                       BufferedReader in = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(bytes)));
-                       String str;
-                       boolean insideBase64 = false;
-                       StringBuffer base64Key = null;
-                       while ((str = in.readLine()) != null) {
+               /**
+                * PEM encoded key
+                */
+               public static final int PEM_ENCODING = 1;
 
-                               if (insideBase64) {
-                                       if (str.matches("^.*" + endToken + ".*$")) {
-                                               break;
-                                       }
-                                       {
-                                               base64Key.append(str);
-                                       }
-                               } else if (str.matches("^.*" + beginToken + ".*$")) {
-                                       insideBase64 = true;
-                                       base64Key = new StringBuffer();
-                               }
-                       }
-                       in.close();
-                       if (base64Key == null || base64Key.length() == 0) {
-                               log.error("Could not find Base 64 encoded entity.");
-                               throw new IOException("Could not find Base 64 encoded entity.");
-                       }
+               /**
+                * OID for DSA keys
+                */
+               public final static String DSAKey_OID = "1.2.840.10040.4.1";
 
-                       try {
-                               BASE64Decoder decoder = new BASE64Decoder();
-                               return decoder.decodeBuffer(base64Key.toString());
-                       } catch (IOException ioe) {
-                               log.error("Could not decode Base 64: " + ioe);
-                               throw new IOException("Could not decode Base 64.");
-                       }
+               /**
+                * OID for RSA keys
+                */
+               public final static String RSAKey_OID = "1.2.840.113549.1.1.1";
 
-               } catch (IOException e) {
-                       log.error("Could not load resource from specified location: " + e);
-                       throw new IOException("Could not load resource from specified location.");
-               }
+               /**
+                * PKCS8 key format
+                */
+               public final static int PKCS8 = 0;
 
-       }
+               /**
+                * RSA key format
+                */
+               public final static int RSA = 1;
 
-       private String getCertFormat(Element e) throws CredentialFactoryException {
+               /**
+                * DSA key format
+                */
+               public final static int DSA = 2;
 
-               NodeList certificateElements = e.getElementsByTagNameNS(Credentials.credentialsNamespace, "Certificate");
-               if (certificateElements.getLength() < 1) {
-                       log.error("Certificate not specified.");
-                       throw new CredentialFactoryException("File Credential Resolver requires a <Certificate> specification.");
-               }
-               if (certificateElements.getLength() > 1) {
-                       log.error("Multiple Certificate path specifications, using first.");
-               }
+               /**
+                * Key encryption algorithim DES-CDC
+                */
+               public final static int DES_CBC = 0;
 
-               String format = ((Element) certificateElements.item(0)).getAttribute("format");
-               if (format == null || format.equals("")) {
-                       log.debug("No format specified for certificate, using default (PEM) format.");
-                       format = "PEM";
-               }
+               /**
+                * Key encryption algorithim DES-EDE3-CBC
+                */
+               public final static int DES_EDE3_CBC = 1;
 
-               if ((!format.equals("PEM")) && (!format.equals("DER"))) {
-                       log.error("File credential resolver only supports the (DER) and (PEM) formats.");
-                       throw new CredentialFactoryException("Failed to initialize Credential Resolver.");
-               }
+               /**
+                * Format of the PEM encoded key
+                */
+               private int format = -1;
 
-               return format;
-       }
+               /**
+                * Is the key encrypted?
+                */
+               private boolean encrypted;
 
-       private String getKeyFormat(Element e) throws CredentialFactoryException {
+               /**
+                * Password for the encrypted key
+                */
+               private String keyPassword;
 
-               NodeList keyElements = e.getElementsByTagNameNS(Credentials.credentialsNamespace, "Key");
-               if (keyElements.getLength() < 1) {
-                       log.error("Key not specified.");
-                       throw new CredentialFactoryException("File Credential Resolver requires a <Key> specification.");
-               }
-               if (keyElements.getLength() > 1) {
-                       log.error("Multiple Key path specifications, using first.");
+               /**
+                * Encryption algorithim used for this key
+                */
+               private int encAlgo = -1;
+
+               /**
+                * Initialization vector for the encryption algorithim
+                */
+               private String initVector = "";
+
+               /**
+                * DER encoded key
+                */
+               private byte[] keyBytes;
+
+               /**
+                * Gets the format (PKCS8, RSA, DSA) of the key.
+                * 
+                * @return format of the key
+                */
+               public int getFormat() {
+
+                       return format;
                }
 
-               String format = ((Element) keyElements.item(0)).getAttribute("format");
-               if (format == null || format.equals("")) {
-                       log.debug("No format specified for certificate, using default (PEM) format.");
-                       format = "PEM";
+               /**
+                * Sets the format (PKCS8, RSA, DSA) of the key.
+                * 
+                * @param format
+                *            the format of the key
+                */
+               public void setFormat(int format) {
+
+                       this.format = format;
                }
 
-               if (!((format.equals("DER")) || (format.equals("PEM")))) {
-                       log.error("File credential resolver currently only supports (DER) and (PEM) formats.");
-                       throw new CredentialFactoryException("Failed to initialize Credential Resolver.");
+               /**
+                * Gets whether this PEM key is encrypted.
+                * 
+                * @return true if this key is encrypted, false if not
+                */
+               public boolean isEncrypted() {
+
+                       return encrypted;
                }
-               return format;
-       }
 
-       private String getKeyPassword(Element e) throws CredentialFactoryException {
+               /**
+                * Sets whether the key is encrypted.
+                * 
+                * @param encrypted
+                *            whether the key is encrypted
+                */
+               public void setEncrypted(boolean encrypted) {
 
-               NodeList keyElements = e.getElementsByTagNameNS(Credentials.credentialsNamespace, "Key");
-               if (keyElements.getLength() < 1) {
-                       log.error("Key not specified.");
-                       throw new CredentialFactoryException("File Credential Resolver requires a <Key> specification.");
+                       this.encrypted = encrypted;
                }
 
-               if (keyElements.getLength() > 1) {
-                       log.error("Multiple Key path specifications, using first.");
+               /**
+                * Gets the password to decrypt this key
+                * 
+                * @return the password to decrypt this key
+                */
+               public String getEncryptionPassword() {
+
+                       return keyPassword;
                }
 
-               String password = ((Element) keyElements.item(0)).getAttribute("password");
-               if (password == null) {
-                       password = "";
+               /**
+                * Sets the password to decrypt this key
+                * 
+                * @param keyPassword
+                *            the password to decrypt this key
+                */
+               public void setEncryptionPassword(String keyPassword) {
+
+                       this.keyPassword = keyPassword;
                }
-               return password;
-       }
 
-       private String getCertPath(Element e) throws CredentialFactoryException {
+               /**
+                * Gets the encryption algorithim used to encrypt the private key.
+                * 
+                * @return the encryption algorithim used to encrypt the private key
+                */
+               public int getEncryptionAlgorithim() {
 
-               NodeList certificateElements = e.getElementsByTagNameNS(Credentials.credentialsNamespace, "Certificate");
-               if (certificateElements.getLength() < 1) {
-                       log.debug("No <Certificate> element found.");
-                       return null;
+                       return encAlgo;
                }
-               if (certificateElements.getLength() > 1) {
-                       log.error("Multiple Certificate path specifications, using first.");
+
+               /**
+                * Sets the encryption algorithim used to encrypt the private key.
+                * 
+                * @param encAlgo
+                *            the encryption algorithim used to encrypt the private key
+                */
+               public void setEncryptionAlgorithim(int encAlgo) {
+
+                       this.encAlgo = encAlgo;
                }
 
-               NodeList pathElements =
-                       ((Element) certificateElements.item(0)).getElementsByTagNameNS(Credentials.credentialsNamespace, "Path");
-               if (pathElements.getLength() < 1) {
-                       log.error("Certificate path not specified.");
-                       throw new CredentialFactoryException("File Credential Resolver requires a <Certificate><Path/></Certificate> specification.");
+               /**
+                * Gets the initialization vector used in the encryption of the private key.
+                * 
+                * @return the initialization vector used in the encryption of the private key
+                */
+               public String getInitializationVector() {
+
+                       return initVector;
                }
-               if (pathElements.getLength() > 1) {
-                       log.error("Multiple Certificate path specifications, using first.");
+
+               /**
+                * Sets the initialization vector used in the encryption of the private key.
+                * 
+                * @param initVector
+                *            ets the initialization vector used in the encryption of the private key
+                */
+               public void setInitializationVector(String initVector) {
+
+                       this.initVector = initVector;
                }
-               Node tnode = pathElements.item(0).getFirstChild();
-               String path = null;
-               if (tnode != null && tnode.getNodeType() == Node.TEXT_NODE) {
-                       path = tnode.getNodeValue();
+
+               /**
+                * Gets the private key as bytes.
+                * 
+                * @return the private key as bytes.
+                */
+               public byte[] getKeyBytes() {
+
+                       return keyBytes;
                }
-               if (path == null || path.equals("")) {
-                       log.error("Certificate path not specified.");
-                       throw new CredentialFactoryException("File Credential Resolver requires a <Certificate><Path/></Certificate> specification.");
+
+               /**
+                * Sets the private key as bytes.
+                * 
+                * @param keyBytes
+                *            the private key as bytes
+                */
+               public void setKeyBytes(byte[] keyBytes) {
+
+                       this.keyBytes = keyBytes;
                }
-               return path;
+
+               /**
+                * Gets the private key from this encoded key.
+                * 
+                * @return the private key from this encoded key
+                */
+               public abstract PrivateKey getPrivateKey() throws CredentialFactoryException;
        }
 
-       private String[] getCAPaths(Element e) throws CredentialFactoryException {
+       /**
+        * Represents a PEM formatted cryptographic key. Used to determine it's format (PKCS8, RSA, DSA), whether it's been
+        * encrypted or not, get the Base64 encoded key, and then decoded the key into the DER encoded key.
+        * 
+        * @author Chad La Joie
+        */
+       private class PEMKey extends EncodedKey {
 
-               NodeList certificateElements = e.getElementsByTagNameNS(Credentials.credentialsNamespace, "Certificate");
-               if (certificateElements.getLength() < 1) {
-                       log.error("Certificate not specified.");
-                       throw new CredentialFactoryException("File Credential Resolver requires a <Certificate> specification.");
+               /**
+                * DER encoded key
+                */
+               private DERKey derKey;
+
+               /**
+                * Constructor
+                * 
+                * @param pemKey
+                *            the PEM key
+                * @throws CredentialFactoryException
+                * @throws CredentialFactoryException
+                * @throws IOException
+                */
+               public PEMKey(String pemKey, String password) throws IOException, CredentialFactoryException {
+
+                       setEncryptionPassword(password);
+                       BufferedReader keyReader = new BufferedReader(new StringReader(pemKey));
+                       parsePEMKey(keyReader);
                }
-               if (certificateElements.getLength() > 1) {
-                       log.error("Multiple Certificate path specifications, using first.");
+
+               /**
+                * Constructor
+                * 
+                * @param pemKeyStream
+                *            and input stream with the PEM key
+                * @throws CredentialFactoryException
+                * @throws CredentialFactoryException
+                * @throws IOException
+                */
+               public PEMKey(InputStream pemKeyStream, String password) throws IOException, CredentialFactoryException {
+
+                       setEncryptionPassword(password);
+                       BufferedReader keyReader = new BufferedReader(new InputStreamReader(pemKeyStream));
+                       parsePEMKey(keyReader);
                }
 
-               NodeList pathElements =
-                       ((Element) certificateElements.item(0)).getElementsByTagNameNS(Credentials.credentialsNamespace, "CAPath");
-               if (pathElements.getLength() < 1) {
-                       log.debug("No CA Certificate paths specified.");
-                       return null;
+               /**
+                * Gets the private key from this PEM encoded key
+                * 
+                * @throws CredentialFactoryException
+                */
+               public PrivateKey getPrivateKey() throws CredentialFactoryException {
+
+                       return derKey.getPrivateKey();
                }
-               ArrayList paths = new ArrayList();
-               for (int i = 0; i < pathElements.getLength(); i++) {
-                       Node tnode = pathElements.item(i).getFirstChild();
-                       String path = null;
-                       if (tnode != null && tnode.getNodeType() == Node.TEXT_NODE) {
-                               path = tnode.getNodeValue();
+
+               /**
+                * Parses the PEM key to determine its format, whether it's encrypted, then extract the Base64 encoded key and
+                * decodes it into the DER encoded key.
+                * 
+                * @param keyReader
+                *            the PEM key
+                * @throws IOException
+                *             thrown if there is problem reading the key
+                * @throws CredentialFactoryException
+                */
+               private void parsePEMKey(BufferedReader keyReader) throws IOException, CredentialFactoryException {
+
+                       if (log.isDebugEnabled()) {
+                               log.debug("Parsing PEM enocded private key");
                        }
-                       if (path != null && !(path.equals(""))) {
-                               paths.add(path);
+                       String currentLine = keyReader.readLine();
+
+                       if (currentLine.matches("^.*-----BEGIN PRIVATE KEY-----.*$")) {
+                               if (log.isDebugEnabled()) {
+                                       log.debug("Key appears to be in PKCS8 format.");
+                               }
+
+                               setFormat(PKCS8);
+                               setEncrypted(false);
+
+                       } else if (currentLine.matches("^.*-----BEGIN ENCRYPTED PRIVATE KEY-----.*$")) {
+                               if (log.isDebugEnabled()) {
+                                       log.debug("Key appears to be in encrypted PKCS8 format.");
+                               }
+                               setFormat(PKCS8);
+                               setEncrypted(true);
+
+                       } else if (currentLine.matches("^.*-----BEGIN RSA PRIVATE KEY-----.*$")) {
+                               setFormat(RSA);
+
+                               // Mark the stream, if it's not encrypted we need to reset
+                               // or lose the first line of the base64 key
+                               keyReader.mark(100);
+                               currentLine = keyReader.readLine();
+                               if (currentLine.matches("^.*Proc-Type: 4,ENCRYPTED.*$")) {
+                                       if (log.isDebugEnabled()) {
+                                               log.debug("Key appears to be encrypted RSA in raw format.");
+                                       }
+                                       setEncrypted(true);
+                               } else {
+                                       if (log.isDebugEnabled()) {
+                                               log.debug("Key appears to be RSA in raw format.");
+                                       }
+                                       keyReader.reset();
+                                       setEncrypted(false);
+                               }
+
+                       } else if (currentLine.matches("^.*-----BEGIN DSA PRIVATE KEY-----.*$")) {
+                               setFormat(DSA);
+
+                               // Mark the stream, if it's not encrypted we need to reset
+                               // or lose the first line of the base64 key
+                               keyReader.mark(100);
+                               currentLine = keyReader.readLine();
+                               if (currentLine.matches("^.*Proc-Type: 4,ENCRYPTED.*$")) {
+                                       if (log.isDebugEnabled()) {
+                                               log.debug("Key appears to be encrypted DSA in raw format.");
+                                       }
+                                       setEncrypted(true);
+                               } else {
+                                       if (log.isDebugEnabled()) {
+                                               log.debug("Key appears to be DSA in raw format.");
+                                       }
+                                       keyReader.reset();
+                                       setEncrypted(false);
+                               }
                        }
-                       if (paths.isEmpty()) {
-                               log.debug("No CA Certificate paths specified.");
+
+                       // Key is an encrypted RSA or DSA key, need to get the algorithim used
+                       if (isEncrypted() && (getFormat() == RSA || getFormat() == DSA)) {
+                               if (log.isDebugEnabled()) {
+                                       log.debug("Key data is encrypted RSA or DSA, inspecting encryption properties");
+                               }
+                               currentLine = keyReader.readLine();
+                               String[] components = currentLine.split(":\\s");
+                               if (components.length != 2) {
+                                       log.error("Encrypted key did not contain DEK-Info specification.");
+                                       // throw new CredentialFactoryException("Unable to load private key.");
+                               }
+                               String[] cryptData = components[1].split(",");
+                               if (cryptData.length != 2 || cryptData[0] == null || cryptData[0].equals("") || cryptData[1] == null
+                                               || cryptData[1].equals("")) {
+                                       log.error("Encrypted key did not contain a proper DEK-Info specification.");
+                                       // throw new CredentialFactoryException("Unable to load private key.");
+                               }
+                               if (cryptData[0].equals("DES-CBC")) {
+                                       if (log.isDebugEnabled()) {
+                                               log.debug("Key encryption method determined to be DES-CBC");
+                                       }
+                                       setEncryptionAlgorithim(DES_CBC);
+                               } else if (cryptData[0].equals("DES-EDE3-CBC")) {
+                                       if (log.isDebugEnabled()) {
+                                               log.debug("Key encryption method determined to be DES-EDE3-CBC");
+                                       }
+                                       setEncryptionAlgorithim(DES_EDE3_CBC);
+                               } else {
+                                       setEncryptionAlgorithim(-1);
+                                       log.error("Key encryption method unknown: " + cryptData[0]);
+                               }
+
+                               if (log.isDebugEnabled()) {
+                                       log.debug("Key encryption algorithim initialization vector determined to be " + cryptData[1]);
+                               }
+                               setInitializationVector(cryptData[1]);
+                       }
+
+                       // Now that we've parsed the headers, get the base64 encoded key itself
+                       StringBuffer keyBuf = new StringBuffer();
+                       while ((currentLine = keyReader.readLine()) != null) {
+                               if (currentLine.matches("^.*END.*$")) {
+                                       break;
+                               }
+
+                               keyBuf.append(currentLine);
+                       }
+
+                       String base64Key = keyBuf.toString();
+                       if (log.isDebugEnabled()) {
+                               log.debug("Base64 encoded key: " + base64Key);
+                       }
+
+                       // Base64 decode the key, gives teh DER encoded key data
+                       if (log.isDebugEnabled()) {
+                               log.debug("Base64 decoding key");
+                       }
+                       setKeyBytes(Base64.decode(base64Key));
+
+                       // If the key was a raw RSA/DSA encrypted we need to decrypt it now
+                       // If it was a PKCS8 key we'll decrypt it when we parse it's DER data
+                       if (isEncrypted() && (getFormat() == RSA || getFormat() == DSA)) {
+                               if (log.isDebugEnabled()) {
+                                       log.debug("Decrypting RSA/DSA key");
+                               }
+                               decryptKey();
+                       }
+
+                       // We now have a key encoded in DER format
+                       if (log.isDebugEnabled()) {
+                               log.debug("PEM key has been decoded into DER encoded data, processing it as DER key");
+                       }
+                       derKey = new DERKey(getKeyBytes(), getEncryptionPassword());
+
+                       // Close the reader, we're done
+                       keyReader.close();
+               }
+
+               /**
+                * Decrypts an encrypted private key.
+                * 
+                * @throws CredentialFactoryException
+                */
+               private void decryptKey() throws CredentialFactoryException {
+
+                       try {
+                               byte[] ivBytes = new byte[8];
+                               for (int j = 0; j < 8; j++) {
+                                       ivBytes[j] = (byte) Integer.parseInt(getInitializationVector().substring(j * 2, j * 2 + 2), 16);
+                               }
+                               IvParameterSpec paramSpec = new IvParameterSpec(ivBytes);
+
+                               byte[] keyBuffer = new byte[24];
+                               // The key generation method (with the IV used as the salt, and
+                               // the single proprietary iteration)
+                               // is the reason we can't use the pkcs5 providers to read the
+                               // OpenSSL encrypted format
+
+                               byte[] keyPass = getEncryptionPassword().getBytes();
+                               MessageDigest md = MessageDigest.getInstance("MD5");
+                               md.update(keyPass);
+                               md.update(paramSpec.getIV());
+                               byte[] digested = md.digest();
+                               System.arraycopy(digested, 0, keyBuffer, 0, 16);
+
+                               md.update(digested);
+                               md.update(keyPass);
+                               md.update(paramSpec.getIV());
+                               digested = md.digest();
+                               System.arraycopy(digested, 0, keyBuffer, 16, 8);
+
+                               SecretKeySpec keySpec = null;
+                               Cipher cipher = null;
+                               if (getEncryptionAlgorithim() == EncodedKey.DES_CBC) {
+                                       // Special handling!!!
+                                       // For DES, we use the same key generation,
+                                       // then just chop off the end :-)
+                                       byte[] desBuff = new byte[8];
+                                       System.arraycopy(keyBuffer, 0, desBuff, 0, 8);
+                                       keySpec = new SecretKeySpec(desBuff, "DES");
+                                       cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
+                               }
+                               if (getEncryptionAlgorithim() == EncodedKey.DES_EDE3_CBC) {
+                                       keySpec = new SecretKeySpec(keyBuffer, "DESede");
+                                       cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
+                               }
+
+                               cipher.init(Cipher.DECRYPT_MODE, keySpec, paramSpec);
+                               byte[] decrypted = cipher.doFinal(getKeyBytes());
+
+                               setEncrypted(false);
+                               setKeyBytes(decrypted);
+                       } catch (BadPaddingException e) {
+                               log.error("Incorrect password to unlock private key.", e);
+                               throw new CredentialFactoryException("Unable to load private key.");
+                       } catch (Exception e) {
+                               log
+                                               .error("Unable to decrypt private key.  Installed JCE implementations don't support the necessary algorithm: "
+                                                               + e);
+                               throw new CredentialFactoryException("Unable to load private key.");
                        }
                }
-               return (String[]) paths.toArray(new String[0]);
        }
 
-       private String getKeyPath(Element e) throws CredentialFactoryException {
+       /**
+        * Represents a DER formatted cryptographic key. Used to determine it's format (PKCS8, RSA, DSA), whether it's been
+        * encrypted or not
+        * 
+        * @author Chad La Joie
+        */
+       private class DERKey extends EncodedKey {
 
-               NodeList keyElements = e.getElementsByTagNameNS(Credentials.credentialsNamespace, "Key");
-               if (keyElements.getLength() < 1) {
-                       log.error("Key not specified.");
-                       throw new CredentialFactoryException("File Credential Resolver requires a <Key> specification.");
-               }
-               if (keyElements.getLength() > 1) {
-                       log.error("Multiple Key path specifications, using first.");
-               }
+               DERSequence rootDerTag;
 
-               NodeList pathElements =
-                       ((Element) keyElements.item(0)).getElementsByTagNameNS(Credentials.credentialsNamespace, "Path");
-               if (pathElements.getLength() < 1) {
-                       log.error("Key path not specified.");
-                       throw new CredentialFactoryException("File Credential Resolver requires a <Key><Path/></Certificate> specification.");
+               /**
+                * Constructor.
+                * 
+                * @param derKeyStream
+                *            the inputstream that contains the key
+                * @throws IOException
+                *             thrown if there is an error reading from the stream
+                * @throws CredentialFactoryException
+                *             thrown if there is an error parsing the stream data
+                */
+               public DERKey(InputStream derKeyStream, String password) throws IOException, CredentialFactoryException {
+
+                       setEncryptionPassword(password);
+                       ByteContainer derKey = new ByteContainer(600, 50);
+
+                       for (int i = derKeyStream.read(); i != -1; i = derKeyStream.read()) {
+                               derKey.append((byte) i);
+                       }
+
+                       setKeyBytes(derKey.toByteArray());
+                       parseDerKey();
                }
-               if (pathElements.getLength() > 1) {
-                       log.error("Multiple Key path specifications, using first.");
+
+               public DERKey(byte[] derKey, String password) throws IOException, CredentialFactoryException {
+
+                       setEncryptionPassword(password);
+                       setKeyBytes(derKey);
+                       parseDerKey();
                }
-               Node tnode = pathElements.item(0).getFirstChild();
-               String path = null;
-               if (tnode != null && tnode.getNodeType() == Node.TEXT_NODE) {
-                       path = tnode.getNodeValue();
+
+               public PrivateKey getPrivateKey() throws CredentialFactoryException {
+
+                       switch (getFormat()) {
+                               case EncodedKey.PKCS8 :
+                                       if (!isEncrypted()) {
+                                               return getPkcs8Key();
+                                       } else {
+                                               return getEncryptedPkcs8Key();
+                                       }
+
+                               case EncodedKey.RSA :
+                                       return getRSARawDerKey();
+
+                               case EncodedKey.DSA :
+                                       return getDSARawDerKey();
+
+                               default :
+                                       throw new CredentialFactoryException("Unable to determine format of DER encoded private key");
+                       }
                }
-               if (path == null || path.equals("")) {
-                       log.error("Key path not specified.");
-                       throw new CredentialFactoryException("File Credential Resolver requires a <Key><Path/></Certificate> specification.");
+
+               /**
+                * Takes a set of ASN.1 encoded bytes and converts them into a DER object.
+                * 
+                * @param keyBytes
+                *            the ASN.1 encoded bytes
+                * @return the DER object
+                * @throws IOException
+                *             thrown if the bytes aren't ASN.1 encoded
+                */
+               private DERObject getRootDerTag(byte[] keyBytes) throws IOException {
+
+                       InputStream derKeyStream = new BufferedInputStream(new ByteArrayInputStream(getKeyBytes()));
+                       ASN1InputStream asn1Stream = new ASN1InputStream(derKeyStream);
+                       DERObject derObject = asn1Stream.readObject();
+                       derKeyStream.close();
+                       asn1Stream.close();
+
+                       return derObject;
                }
-               return path;
-       }
 
-       /**
-        * Loads a specified bundle of certs individually and returns an array of {@link Certificate} objects. This
-        * is needed because the standard {@link CertificateFactory#getCertificates(InputStream)} method bails out
-        * when it has trouble loading any cert and cannot handle "comments".
-        */
-       private Certificate[] loadCertificates(InputStream inStream, String certType) throws CredentialFactoryException {
+               /**
+                * Parse the key stream and determines data about the key.
+                * 
+                * @param derKeyStream
+                *            the inputstream that contains the key
+                * @throws IOExceptionthrown
+                *             if there is an error reading from the stream
+                * @throws CredentialFactoryException
+                *             thrown if there is an error parsing the stream data
+                */
+               private void parseDerKey() throws IOException, CredentialFactoryException {
 
-               ArrayList certificates = new ArrayList();
+                       if (log.isDebugEnabled()) {
+                               log.debug("Starting to parse " + getKeyBytes().length + " byte DER formatted key.");
+                       }
+                       DERObject derObject = getRootDerTag(getKeyBytes());
 
-               try {
-                       CertificateFactory certFactory = CertificateFactory.getInstance(certType);
+                       if (log.isDebugEnabled()) {
+                               log
+                                               .debug("Parsed ASN.1 object which has the following structure:\n"
+                                                               + ASN1Dump.dumpAsString(derObject));
+                       }
 
-                       BufferedReader in = new BufferedReader(new InputStreamReader(inStream));
-                       String str;
-                       boolean insideCert = false;
-                       StringBuffer rawCert = null;
-                       while ((str = in.readLine()) != null) {
+                       // All supported key formats start with a DER sequence tag
+                       if (!(derObject instanceof DERSequence)) {
+                               log.error("Private key is not in valid DER format, it does not start with a DER sequence");
+                               throw new CredentialFactoryException("Private key is not in valid DER format");
+                       }
+                       DERSequence rootSeq = (DERSequence) derObject;
+                       if (rootSeq.size() < 2) {
+                               // Valid key in any format will have at least two tags under the root
+                               log.error("Private key is not in valid DER format; does not contain more than 2 ASN.1 tags");
+                               throw new CredentialFactoryException("Private key is not in valid DER format");
+                       }
 
-                               if (insideCert) {
-                                       rawCert.append(str);
-                                       rawCert.append(System.getProperty("line.separator"));
-                                       if (str.matches("^.*-----END CERTIFICATE-----.*$")) {
-                                               insideCert = false;
-                                               try {
-                                                       Certificate cert =
-                                                               certFactory.generateCertificate(
-                                                                       new ByteArrayInputStream(rawCert.toString().getBytes()));
-                                                       certificates.add(cert);
-                                               } catch (CertificateException ce) {
-                                                       log.warn("Failed to load a certificate from the certificate bundle: " + ce);
+                       DERObject firstChild = rootSeq.getObjectAt(0).getDERObject();
+
+                       if (firstChild instanceof DERSequence) {
+                               if (log.isDebugEnabled()) {
+                                       log.debug("First ASN.1 tag is a sequence, checking to see if this is an encrypted PKCS8 key");
+                               }
+                               // Might be encrypted PKCS8, lets check some more
+                               DERSequence firstChildSeq = (DERSequence) firstChild;
+                               DERObject grandChildObj = firstChildSeq.getObjectAt(0).getDERObject();
+                               DERObject secondChild = rootSeq.getObjectAt(1).getDERObject();
+
+                               // Encrypted PKCS8 have an octet string as the second child (from the root)
+                               // and an object identifier as the child of the first child from the root
+                               if (secondChild instanceof DEROctetString && grandChildObj instanceof DERObjectIdentifier) {
+                                       if (log.isDebugEnabled()) {
+                                               log.debug("DER encoded key determined to be encrypted PKCS8");
+                                       }
+                                       rootDerTag = rootSeq;
+                                       setFormat(PKCS8);
+                                       setEncrypted(true);
+                               }
+                       } else if (firstChild instanceof DERInteger) {
+                               if (log.isDebugEnabled()) {
+                                       log
+                                                       .debug("First child ASN.1 tag is a Integer, checking to see if this is an PKCS8, RSA, or DSA key");
+                               }
+                               // Might be unencrypted PKCS8, RSA, or DSA
+
+                               // Check to see if it's PKCS8 with contains an
+                               // Integer, then Sequence, then OctetString
+                               if (rootSeq.size() == 3) {
+                                       if (log.isDebugEnabled()) {
+                                               log.debug("First ASN.1 sequence tag has 3 children, checking to see if this is an PKCS8 key");
+                                       }
+                                       if (rootSeq.getObjectAt(0).getDERObject() instanceof DERInteger
+                                                       && rootSeq.getObjectAt(1).getDERObject() instanceof DERSequence
+                                                       && rootSeq.getObjectAt(2).getDERObject() instanceof DEROctetString) {
+                                               if (log.isDebugEnabled()) {
+                                                       log.debug("DER encoded key determined to be PKCS8");
+                                               }
+                                               rootDerTag = rootSeq;
+                                               setFormat(PKCS8);
+                                               setEncrypted(false);
+                                       }
+                               } else {
+                                       // Might be RSA or DSA. DSA will have 6 Integers
+                                       // under the root sequences, RSA will have 9
+                                       Enumeration children = rootSeq.getObjects();
+                                       DERObject child;
+                                       boolean allInts = true;
+
+                                       while (children.hasMoreElements()) {
+                                               child = ((DEREncodable) children.nextElement()).getDERObject();
+                                               if (!(child instanceof DERInteger)) {
+                                                       allInts = false;
+                                               }
+                                       }
+
+                                       if (rootSeq.size() == 6) {
+                                               if (log.isDebugEnabled()) {
+                                                       log.debug("First ASN.1 sequence tag has 6 children, checking to see if this is an DSA key");
+                                               }
+                                               // DSA keys have six integer tags in the root sequence
+                                               if (allInts) {
                                                        if (log.isDebugEnabled()) {
-                                                               log.debug(
-                                                                       "Dump of bad certificate: "
-                                                                               + System.getProperty("line.separator")
-                                                                               + rawCert.toString());
+                                                               log.debug("DER encoded key determined to be raw DSA");
                                                        }
+                                                       rootDerTag = rootSeq;
+                                                       setFormat(DSA);
+                                                       setEncrypted(false);
+                                               }
+                                       } else if (rootSeq.size() == 9) {
+                                               if (log.isDebugEnabled()) {
+                                                       log.debug("First ASN.1 sequence tag has 9 children, checking to see if this is an DSA key");
+                                               }
+                                               // RSA (PKCS1) keys have 9 integer tags in the root sequence
+                                               if (allInts) {
+                                                       if (log.isDebugEnabled()) {
+                                                               log.debug("DER encoded key determined to be raw RSA");
+                                                       }
+                                                       rootDerTag = rootSeq;
+                                                       setFormat(RSA);
+                                                       setEncrypted(false);
                                                }
-                                               continue;
                                        }
-                               } else if (str.matches("^.*-----BEGIN CERTIFICATE-----.*$")) {
-                                       insideCert = true;
-                                       rawCert = new StringBuffer();
-                                       rawCert.append(str);
-                                       rawCert.append(System.getProperty("line.separator"));
                                }
                        }
-                       in.close();
-               } catch (IOException p) {
-                       log.error("Could not load resource from specified location: " + p);
-                       throw new CredentialFactoryException("Unable to load certificates.");
-               } catch (CertificateException p) {
-                       log.error("Problem loading certificate factory: " + p);
-                       throw new CredentialFactoryException("Unable to load certificates.");
+
+                       // If we don't know what the format is now then the stream wasn't a valid DER encoded key
+                       if (getFormat() == -1) {
+                               log.error("Private key is not in valid DER format");
+                               throw new CredentialFactoryException("Private key is not in valid DER format");
+                       }
                }
 
-               return (Certificate[]) certificates.toArray(new Certificate[0]);
-       }
+               /**
+                * Gets the private key from a encrypted PKCS8 formatted key.
+                * 
+                * @param bytes
+                *            the PKCS8 formatted key
+                * @param password
+                *            the password to decrypt the key
+                * @return the private key
+                * @throws CredentialFactoryException
+                *             thrown is there is an error loading the private key
+                */
+               private PrivateKey getEncryptedPkcs8Key() throws CredentialFactoryException {
 
-       /**
-        * 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
-        */
-       protected void walkChain(X509Certificate[] chainSource, ArrayList chainDest) throws CredentialFactoryException {
+                       if (log.isDebugEnabled()) {
+                               log.debug("Beginning to decrypt encrypted PKCS8 key");
+                       }
+                       try {
+                               // Convince the JCE provider that it does know how to do
+                               // pbeWithMD5AndDES-CBC
+                               Provider provider = Security.getProvider("SunJCE");
+                               if (provider != null) {
+                                       provider.setProperty("Alg.Alias.AlgorithmParameters.1.2.840.113549.1.5.3", "PBE");
+                                       provider.setProperty("Alg.Alias.SecretKeyFactory.1.2.840.113549.1.5.3", "PBEWithMD5AndDES");
+                                       provider.setProperty("Alg.Alias.Cipher.1.2.840.113549.1.5.3", "PBEWithMD5AndDES");
+                               }
 
-               X509Certificate currentCert = (X509Certificate) chainDest.get(chainDest.size() - 1);
-               if (currentCert.getSubjectDN().equals(currentCert.getIssuerDN())) {
-                       log.debug("Found self-signed root cert: " + currentCert.getSubjectDN());
-                       return;
-               } else {
-                       for (int i = 0; chainSource.length > i; i++) {
-                               if (currentCert.getIssuerDN().equals(chainSource[i].getSubjectDN())) {
-                                       chainDest.add(chainSource[i]);
-                                       walkChain(chainSource, chainDest);
-                                       return;
+                               if (log.isDebugEnabled()) {
+                                       log.debug("Inspecting key properties");
+                               }
+                               EncryptedPrivateKeyInfo encryptedKeyInfo = new EncryptedPrivateKeyInfo(getKeyBytes());
+                               if (log.isDebugEnabled()) {
+                                       log.debug("Key encryption Algorithim: " + encryptedKeyInfo.getAlgName());
+                                       log.debug("Key encryption parameters: " + encryptedKeyInfo.getAlgParameters());
+                               }
+
+                               AlgorithmParameters params = encryptedKeyInfo.getAlgParameters();
+
+                               if (params == null) {
+                                       log.error("Unable to decrypt private key.  Installed JCE implementations don't support the ("
+                                                       + encryptedKeyInfo.getAlgName() + ") algorithm.");
+                                       throw new CredentialFactoryException("Unable to load private key; " + encryptedKeyInfo.getAlgName()
+                                                       + " is not a supported by this JCE");
+                               }
+
+                               if (log.isDebugEnabled()) {
+                                       log.debug("Key encryption properties determined, decrypting key");
                                }
+                               SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(encryptedKeyInfo.getAlgName());
+                               PBEKeySpec passwordSpec = new PBEKeySpec(getEncryptionPassword().toCharArray());
+                               SecretKey key = keyFactory.generateSecret(passwordSpec);
+
+                               Cipher cipher = Cipher.getInstance(encryptedKeyInfo.getAlgName());
+                               cipher.init(Cipher.DECRYPT_MODE, key, params);
+                               PKCS8EncodedKeySpec decrypted = encryptedKeyInfo.getKeySpec(cipher);
+
+                               if (log.isDebugEnabled()) {
+                                       log.debug("Key decrypted, key format now non-encrypted PKCS8");
+                               }
+
+                               setEncrypted(false);
+                               setKeyBytes(decrypted.getEncoded());
+
+                               // Now that we've decrypted the key we've changed the ASN.1 structure
+                               // and so need to reread it.
+                               rootDerTag = (DERSequence) getRootDerTag(getKeyBytes());
+
+                               return getPkcs8Key();
+
+                       } catch (IOException e) {
+                               log.error("Invalid DER encoding for PKCS8 formatted encrypted key: " + e);
+                               throw new CredentialFactoryException("Unable to load private key; invalid key format.");
+                       } catch (InvalidKeySpecException e) {
+                               log.error("Incorrect password to unlock private key.", e);
+                               throw new CredentialFactoryException("Unable to load private key; incorrect key decryption password");
+                       } catch (GeneralSecurityException e) {
+                               log.error("JCE does not support algorithim to decrypt key: " + e);
+                               throw new CredentialFactoryException(
+                                               "Unable to load private key; JCE does not support algorithim to decrypt key");
                        }
-                       log.debug("Certificate chain is incomplete.");
                }
-       }
 
-       /**
-        * 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
-        */
-       protected boolean isMatchingKey(PublicKey pubKey, PrivateKey privKey) {
+               /**
+                * Gets the private key from a PKCS8 formatted key.
+                * 
+                * @param bytes
+                *            the PKCS8 formatted key
+                * @return the private key
+                * @throws CredentialFactoryException
+                *             thrown is there is an error loading the private key
+                */
+               private PrivateKey getPkcs8Key() throws CredentialFactoryException {
 
-               try {
-                       String controlString = "asdf";
-                       log.debug("Checking for matching private key/public key pair");
-                       Signature signature = null;
-                       try {
-                               signature = Signature.getInstance(privKey.getAlgorithm());
-                       } catch (NoSuchAlgorithmException nsae) {
-                               log.debug("No provider for (RSA) signature, attempting (MD5withRSA).");
-                               if (privKey.getAlgorithm().equals("RSA")) {
-                                       signature = Signature.getInstance("MD5withRSA");
-                               } else {
-                                       throw nsae;
-                               }
+                       if (log.isDebugEnabled()) {
+                               log.debug("Reading unecrypted PKCS8 key to determine if key is RSA or DSA");
                        }
-                       signature.initSign(privKey);
-                       signature.update(controlString.getBytes());
-                       byte[] sigBytes = signature.sign();
-                       signature.initVerify(pubKey);
-                       signature.update(controlString.getBytes());
-                       if (signature.verify(sigBytes)) {
-                               log.debug("Found match.");
-                               return true;
+                       DERSequence childSeq = (DERSequence) rootDerTag.getObjectAt(1).getDERObject();
+                       DERObjectIdentifier derOID = (DERObjectIdentifier) childSeq.getObjectAt(0).getDERObject();
+                       String keyOID = derOID.getId();
+
+                       if (keyOID.equals(EncodedKey.RSAKey_OID)) {
+                               if (log.isDebugEnabled()) {
+                                       log.debug("Found RSA key in PKCS8.");
+                               }
+                               return getRSAPkcs8DerKey();
+                       } else if (keyOID.equals(EncodedKey.DSAKey_OID)) {
+                               if (log.isDebugEnabled()) {
+                                       log.debug("Found DSA key in PKCS8.");
+                               }
+                               return getDSAPkcs8DerKey();
+                       } else {
+                               log.error("Unexpected key type.  Only RSA and DSA keys are supported in PKCS8 format.");
+                               throw new CredentialFactoryException("Unable to load private key; unexpected key type in PKCS8");
                        }
-               } catch (Exception e) {
-                       log.warn(e);
                }
-               log.debug("This pair does not match.");
-               return false;
-       }
 
-       /**
-        * Auto-enlarging container for bytes.
-        */
+               /**
+                * Gets a private key from a raw RSA PKCS8 formated DER encoded key.
+                * 
+                * @param bytes
+                *            the encoded key
+                * @return the private key
+                * @throws CredentialFactoryException
+                *             thrown if the private key can not be read
+                */
+               private PrivateKey getRSAPkcs8DerKey() throws CredentialFactoryException {
 
-       // Sure makes you wish bytes were first class objects.
+                       if (log.isDebugEnabled()) {
+                               log.debug("Constructing PrivateKey from PKCS8 encoded RSA key data");
+                       }
+                       try {
+                               KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+                               PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(getKeyBytes());
+                               return keyFactory.generatePrivate(keySpec);
 
-       private class ByteContainer {
+                       } catch (Exception e) {
+                               log.error("Unable to load private key: " + e);
+                               throw new CredentialFactoryException("Unable to load private key.");
+                       }
+               }
 
-               private byte[] buffer;
-               private int cushion;
-               private int currentSize = 0;
+               /**
+                * Gets a private key from a raw DSA PKCS8 formated DER encoded key.
+                * 
+                * @param bytes
+                *            the encoded key
+                * @return the private key
+                * @throws CredentialFactoryException
+                *             thrown if the private key can not be read
+                */
+               private PrivateKey getDSAPkcs8DerKey() throws CredentialFactoryException {
 
-               private ByteContainer(int cushion) {
-                       buffer = new byte[cushion];
-                       this.cushion = cushion;
-               }
+                       if (log.isDebugEnabled()) {
+                               log.debug("Constructing PrivateKey from PKCS8 encoded DSA key data");
+                       }
 
-               private void grow() {
-                       log.debug("Growing ByteContainer.");
-                       int newSize = currentSize + cushion;
-                       byte[] b = new byte[newSize];
-                       int toCopy = Math.min(currentSize, newSize);
-                       int i;
-                       for (i = 0; i < toCopy; i++) {
-                               b[i] = buffer[i];
+                       try {
+                               KeyFactory keyFactory = KeyFactory.getInstance("DSA");
+                               PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(getKeyBytes());
+                               return keyFactory.generatePrivate(keySpec);
+
+                       } catch (Exception e) {
+                               log.error("Unable to load private key: " + e);
+                               throw new CredentialFactoryException("Unable to load private key.");
                        }
-                       buffer = b;
                }
 
                /**
-                * Returns an array of the bytes in the container.
-                * <p>
+                * Converts a raw RSA key encoded in DER format into a private key object.
+                * 
+                * @param key
+                *            the DER encoded key
+                * @return the private key
+                * @throws CredentialFactoryException
+                *             thrown if a key can not be constructed from the input
                 */
+               private PrivateKey getRSARawDerKey() throws CredentialFactoryException {
 
-               private byte[] toByteArray() {
-                       byte[] b = new byte[currentSize];
-                       for (int i = 0; i < currentSize; i++) {
-                               b[i] = buffer[i];
+                       if (log.isDebugEnabled()) {
+                               log.debug("Constructing PrivateKey from raw RSA key data");
+                       }
+                       try {
+                               RSAPrivateCrtKeySpec keySpec = new RSAPrivateCrtKeySpec(((DERInteger) rootDerTag.getObjectAt(1))
+                                               .getValue(), ((DERInteger) rootDerTag.getObjectAt(2)).getValue(), ((DERInteger) rootDerTag
+                                               .getObjectAt(3)).getValue(), ((DERInteger) rootDerTag.getObjectAt(4)).getValue(),
+                                               ((DERInteger) rootDerTag.getObjectAt(5)).getValue(), ((DERInteger) rootDerTag.getObjectAt(6))
+                                                               .getValue(), ((DERInteger) rootDerTag.getObjectAt(7)).getValue(),
+                                               ((DERInteger) rootDerTag.getObjectAt(8)).getValue());
+
+                               KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+
+                               return keyFactory.generatePrivate(keySpec);
+
+                       } catch (GeneralSecurityException e) {
+                               log.error("Unable to marshall private key: " + e);
+                               throw new CredentialFactoryException("Unable to load private key.");
                        }
-                       return b;
                }
 
                /**
-                * Add one byte to the end of the container.
+                * Converts a raw DSA key encoded in DER format into a private key object.
+                * 
+                * @param derKey
+                *            DER encoded DSA key
+                * @return the private key
+                * @throws CredentialFactoryException
+                *             thrown if a key can not be constructed from the input
                 */
+               private PrivateKey getDSARawDerKey() throws CredentialFactoryException {
 
-               private void append(byte b) {
-                       if (currentSize == buffer.length) {
-                               grow();
+                       if (log.isDebugEnabled()) {
+                               log.debug("Constructing PrivateKey from raw DSA key data");
                        }
-                       buffer[currentSize] = b;
-                       currentSize++;
-               }
 
-       }
+                       try {
+                               DSAPrivateKeySpec keySpec = new DSAPrivateKeySpec(((DERInteger) rootDerTag.getObjectAt(5)).getValue(),
+                                               ((DERInteger) rootDerTag.getObjectAt(1)).getValue(), ((DERInteger) rootDerTag.getObjectAt(2))
+                                                               .getValue(), ((DERInteger) rootDerTag.getObjectAt(3)).getValue());
 
+                               KeyFactory keyFactory = KeyFactory.getInstance("DSA");
+
+                               return keyFactory.generatePrivate(keySpec);
+                       } catch (GeneralSecurityException e) {
+                               log.error("Unable to marshall private key: " + e);
+                               throw new CredentialFactoryException("Unable to load private key.");
+                       }
+               }
+       }
 }
 
 /**
- * Loads a credential from a Java keystore. 
+ * Loads a credential from a Java keystore.
+ * 
  * @author Walter Hoehn
  */
+
 class KeystoreCredentialResolver implements CredentialResolver {
 
        private static Logger log = Logger.getLogger(KeystoreCredentialResolver.class.getName());
@@ -1392,17 +1809,13 @@ class KeystoreCredentialResolver implements CredentialResolver {
 
                        PrivateKey privateKey = (PrivateKey) keyStore.getKey(alias, keyPassword.toCharArray());
 
-                       if (privateKey == null) {
-                               throw new CredentialFactoryException("No key entry was found with an alias of (" + alias + ").");
-                       }
+                       if (privateKey == null) { throw new CredentialFactoryException("No key entry was found with an alias of ("
+                                       + alias + ")."); }
 
                        Certificate[] certificates = keyStore.getCertificateChain(certAlias);
-                       if (certificates == null) {
-                               throw new CredentialFactoryException(
+                       if (certificates == null) { throw new CredentialFactoryException(
                                        "An error occurred while reading the java keystore: No certificate found with the specified alias ("
-                                               + certAlias
-                                               + ").");
-                       }
+                                                       + certAlias + ")."); }
 
                        X509Certificate[] x509Certs = new X509Certificate[certificates.length];
                        for (int i = 0; i < certificates.length; i++) {
@@ -1410,9 +1823,8 @@ class KeystoreCredentialResolver implements CredentialResolver {
                                        x509Certs[i] = (X509Certificate) certificates[i];
                                } else {
                                        throw new CredentialFactoryException(
-                                               "The KeyStore Credential Resolver can only load X509 certificates.  Found an unsupported certificate of type ("
-                                                       + certificates[i]
-                                                       + ").");
+                                                       "The KeyStore Credential Resolver can only load X509 certificates.  Found an unsupported certificate of type ("
+                                                                       + certificates[i] + ").");
                                }
                        }
 
@@ -1423,13 +1835,13 @@ class KeystoreCredentialResolver implements CredentialResolver {
                } catch (NoSuchAlgorithmException nsae) {
                        throw new CredentialFactoryException("Appropriate JCE provider not found in the java environment: " + nsae);
                } catch (CertificateException ce) {
-                       throw new CredentialFactoryException(
-                               "The java keystore contained a certificate that could not be loaded: " + ce);
+                       throw new CredentialFactoryException("The java keystore contained a certificate that could not be loaded: "
+                                       + ce);
                } catch (IOException ioe) {
                        throw new CredentialFactoryException("An error occurred while reading the java keystore: " + ioe);
                } catch (UnrecoverableKeyException uke) {
                        throw new CredentialFactoryException(
-                               "An error occurred while attempting to load the key from the java keystore: " + uke);
+                                       "An error occurred while attempting to load the key from the java keystore: " + uke);
                }
 
        }
@@ -1507,7 +1919,8 @@ class KeystoreCredentialResolver implements CredentialResolver {
                NodeList passwordElements = e.getElementsByTagNameNS(Credentials.credentialsNamespace, "StorePassword");
                if (passwordElements.getLength() < 1) {
                        log.error("KeyStore password not specified.");
-                       throw new CredentialFactoryException("KeyStore Credential Resolver requires an <StorePassword> specification.");
+                       throw new CredentialFactoryException(
+                                       "KeyStore Credential Resolver requires an <StorePassword> specification.");
                }
                if (passwordElements.getLength() > 1) {
                        log.error("Multiple KeyStore password specifications, using first.");
@@ -1519,7 +1932,8 @@ class KeystoreCredentialResolver implements CredentialResolver {
                }
                if (password == null || password.equals("")) {
                        log.error("KeyStore password not specified.");
-                       throw new CredentialFactoryException("KeyStore Credential Resolver requires an <StorePassword> specification.");
+                       throw new CredentialFactoryException(
+                                       "KeyStore Credential Resolver requires an <StorePassword> specification.");
                }
                return password;
        }
@@ -1529,7 +1943,8 @@ class KeystoreCredentialResolver implements CredentialResolver {
                NodeList passwords = e.getElementsByTagNameNS(Credentials.credentialsNamespace, "KeyPassword");
                if (passwords.getLength() < 1) {
                        log.error("KeyStore key password not specified.");
-                       throw new CredentialFactoryException("KeyStore Credential Resolver requires an <KeyPassword> specification.");
+                       throw new CredentialFactoryException(
+                                       "KeyStore Credential Resolver requires an <KeyPassword> specification.");
                }
                if (passwords.getLength() > 1) {
                        log.error("Multiple KeyStore key password specifications, using first.");
@@ -1541,7 +1956,8 @@ class KeystoreCredentialResolver implements CredentialResolver {
                }
                if (password == null || password.equals("")) {
                        log.error("KeyStore key password not specified.");
-                       throw new CredentialFactoryException("KeyStore Credential Resolver requires an <KeyPassword> specification.");
+                       throw new CredentialFactoryException(
+                                       "KeyStore Credential Resolver requires an <KeyPassword> specification.");
                }
                return password;
        }
@@ -1549,9 +1965,10 @@ class KeystoreCredentialResolver implements CredentialResolver {
 
 /**
  * Uses implementation specified in the configuration to load a credential.
- *
+ * 
  * @author Walter Hoehn
  */
+
 class CustomCredentialResolver implements CredentialResolver {
 
        private static Logger log = Logger.getLogger(CustomCredentialResolver.class.getName());
@@ -1573,8 +1990,9 @@ class CustomCredentialResolver implements CredentialResolver {
                        return ((CredentialResolver) Class.forName(className).newInstance()).loadCredential(e);
 
                } catch (Exception loaderException) {
-                       log.error(
-                               "Failed to load Custom Credential Resolver implementation class: " + loaderException.getMessage());
+                       log
+                                       .error("Failed to load Custom Credential Resolver implementation class: "
+                                                       + loaderException.getMessage());
                        throw new CredentialFactoryException("Failed to initialize Credential Resolver.");
                }
 
@@ -1585,6 +2003,7 @@ class CustomCredentialResolver implements CredentialResolver {
 class CredentialFactoryException extends Exception {
 
        CredentialFactoryException(String message) {
+
                super(message);
        }
 }
index 407d555..bc771b7 100644 (file)
@@ -48,7 +48,6 @@ import edu.internet2.middleware.shibboleth.common.ShibbolethConfigurationExcepti
 import edu.internet2.middleware.shibboleth.idp.IdPProtocolHandler;
 import edu.internet2.middleware.shibboleth.idp.IdPProtocolSupport;
 import edu.internet2.middleware.shibboleth.metadata.EntityDescriptor;
-import edu.internet2.middleware.shibboleth.metadata.KeyDescriptor;
 import edu.internet2.middleware.shibboleth.metadata.RoleDescriptor;
 
 /**
index ed4b87b..4f034a4 100644 (file)
@@ -43,6 +43,7 @@ import javax.servlet.http.HttpServletResponse;
 import javax.xml.namespace.QName;
 
 import org.apache.log4j.Logger;
+import org.bouncycastle.util.encoders.Base64;
 import org.opensaml.SAMLAssertion;
 import org.opensaml.SAMLAttribute;
 import org.opensaml.SAMLAttributeStatement;
@@ -62,7 +63,6 @@ import org.opensaml.artifact.Artifact;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 
-import sun.misc.BASE64Decoder;
 import edu.internet2.middleware.shibboleth.aa.AAException;
 import edu.internet2.middleware.shibboleth.common.AuthNPrincipal;
 import edu.internet2.middleware.shibboleth.common.NameIdentifierMappingException;
@@ -460,12 +460,8 @@ public class ShibbolethV1SSOHandler extends SSOHandler implements IdPProtocolHan
                req.setAttribute("assertion", new String(buf, "ASCII"));
 
                if (log.isDebugEnabled()) {
-                       try {
-                               log.debug("Dumping generated SAML Response:" + System.getProperty("line.separator")
-                                               + new String(new BASE64Decoder().decodeBuffer(new String(buf, "ASCII")), "UTF8"));
-                       } catch (IOException e) {
-                               log.error("Encountered an error while decoding SAMLReponse for logging purposes.");
-                       }
+                       log.debug("Dumping generated SAML Response:" + System.getProperty("line.separator")
+                                       + new String(Base64.decode(buf)));
                }
 
                RequestDispatcher rd = req.getRequestDispatcher("/IdP.jsp");
index 5c0bfbf..2536ba5 100755 (executable)
@@ -68,7 +68,7 @@ import org.apache.log4j.Level;
 import org.apache.log4j.LogManager;
 import org.apache.log4j.Logger;
 import org.apache.log4j.PatternLayout;
-import sun.misc.BASE64Encoder;
+import org.bouncycastle.util.encoders.Base64;
 
 /**
  * Extension utility for use alongside Sun's keytool program. Performs useful functions not found in original.
@@ -190,8 +190,8 @@ public class ExtKeyTool {
                        if (rfc) {
                                log.debug("Dumping with rfc encoding");
                                outStream.println("-----BEGIN PRIVATE KEY-----");
-                               BASE64Encoder encoder = new BASE64Encoder();
-                               encoder.encodeBuffer(key.getEncoded(), outStream);
+                               outStream.println(Base64.encode(key.getEncoded()));
+
                                outStream.println("-----END PRIVATE KEY-----");
                        } else {
                                log.debug("Dumping with default encoding.");
@@ -213,47 +213,44 @@ public class ExtKeyTool {
                }
        }
 
-    /**
-     * Attempts to unmarshall a secret key from a given stream.
-     * 
-     * @param keyStream
-     *            the <code>InputStream</code> suppying the key
-     * @param algorithm
-     *            the key algorithm
-     * @throws ExtKeyToolException
-     *             if there a problem unmarshalling the key
-     */
-
-    protected SecretKey readSecretKey(String provider, InputStream keyStream, String algorithm)
-            throws ExtKeyToolException {
-
-        try {
-            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(algorithm, provider);
-
-            byte[] inputBuffer = new byte[8];
-            int i;
-            ByteContainer inputBytes = new ByteContainer(400);
-            do {
-                i = keyStream.read(inputBuffer);
-                for (int j = 0; j < i; j++) {
-                    inputBytes.append(inputBuffer[j]);
-                }
-            } while (i > -1);
-
-            KeySpec keySpec = null;
-            if (algorithm.equals("DESede"))
-                keySpec = new DESedeKeySpec(inputBytes.toByteArray());
-            else if (algorithm.equals("DES"))
-                keySpec = new DESKeySpec(inputBytes.toByteArray());
-            return keyFactory.generateSecret(keySpec);
-
-        } catch (Exception e) {
-            log.error("Problem reading secret key: " + e.getMessage());
-            throw new ExtKeyToolException(
-                    "Problem reading secret key.  Keys should be DER encoded native format.");
-        }
-    }
-    
+       /**
+        * Attempts to unmarshall a secret key from a given stream.
+        * 
+        * @param keyStream
+        *            the <code>InputStream</code> suppying the key
+        * @param algorithm
+        *            the key algorithm
+        * @throws ExtKeyToolException
+        *             if there a problem unmarshalling the key
+        */
+
+       protected SecretKey readSecretKey(String provider, InputStream keyStream, String algorithm)
+                       throws ExtKeyToolException {
+
+               try {
+                       SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(algorithm, provider);
+
+                       byte[] inputBuffer = new byte[8];
+                       int i;
+                       ByteContainer inputBytes = new ByteContainer(400);
+                       do {
+                               i = keyStream.read(inputBuffer);
+                               for (int j = 0; j < i; j++) {
+                                       inputBytes.append(inputBuffer[j]);
+                               }
+                       } while (i > -1);
+
+                       KeySpec keySpec = null;
+                       if (algorithm.equals("DESede")) keySpec = new DESedeKeySpec(inputBytes.toByteArray());
+                       else if (algorithm.equals("DES")) keySpec = new DESKeySpec(inputBytes.toByteArray());
+                       return keyFactory.generateSecret(keySpec);
+
+               } catch (Exception e) {
+                       log.error("Problem reading secret key: " + e.getMessage());
+                       throw new ExtKeyToolException("Problem reading secret key.  Keys should be DER encoded native format.");
+               }
+       }
+
        /**
         * Boolean indication of whether a given private key and public key form a valid keypair.
         * 
@@ -444,8 +441,8 @@ public class ExtKeyTool {
         *            password used to verify the integrity of the old keystore and save the new keystore
         * @param keyPassword
         *            the password for saving the key
-     * @param secret
-     *            indicates this is a secret key import
+        * @param secret
+        *            indicates this is a secret key import
         * @return an OutputStream containing the new keystore
         * @throws ExtKeyToolException
         *             if there a problem importing the key
@@ -477,38 +474,37 @@ public class ExtKeyTool {
                        }
                        keyStore.deleteEntry(keyAlias);
 
-            if (secret) {
-                log.info("Reading secret key.");
-                if (keyAlgorithm == null) {
-                    keyAlgorithm = "AES";
-                }
-                log.debug("Using key algorithm: (" + keyAlgorithm + ")");
-                SecretKey key = readSecretKey(provider, keyStream, keyAlgorithm);
-                keyStore.setKeyEntry(keyAlias, key, keyPassword, null);
-            }
-            else {
-                       log.info("Reading private key.");
-                       if (keyAlgorithm == null) {
-                               keyAlgorithm = "RSA";
-                       }
-                       log.debug("Using key algorithm: (" + keyAlgorithm + ")");
-                       PrivateKey key = readPrivateKey(provider, keyStream, keyAlgorithm);
-    
-                       log.info("Reading certificate chain.");
-    
-                       CertificateFactory certFactory = CertificateFactory.getInstance("X.509", provider);
-                       Collection chain = certFactory.generateCertificates(new BufferedInputStream(chainStream));
-                       if (chain.isEmpty()) {
-                               log.error("Input did not contain any valid certificates.");
-                               throw new ExtKeyToolException("Input did not contain any valid certificates.");
-                       }
-    
-                       X509Certificate[] verifiedChain = linkChain(keyAlgorithm, (X509Certificate[]) chain
-                                       .toArray(new X509Certificate[0]), key);
-
-                keyStore.setKeyEntry(keyAlias, key, keyPassword, verifiedChain);
-            }
-            
+                       if (secret) {
+                               log.info("Reading secret key.");
+                               if (keyAlgorithm == null) {
+                                       keyAlgorithm = "AES";
+                               }
+                               log.debug("Using key algorithm: (" + keyAlgorithm + ")");
+                               SecretKey key = readSecretKey(provider, keyStream, keyAlgorithm);
+                               keyStore.setKeyEntry(keyAlias, key, keyPassword, null);
+                       } else {
+                               log.info("Reading private key.");
+                               if (keyAlgorithm == null) {
+                                       keyAlgorithm = "RSA";
+                               }
+                               log.debug("Using key algorithm: (" + keyAlgorithm + ")");
+                               PrivateKey key = readPrivateKey(provider, keyStream, keyAlgorithm);
+
+                               log.info("Reading certificate chain.");
+
+                               CertificateFactory certFactory = CertificateFactory.getInstance("X.509", provider);
+                               Collection chain = certFactory.generateCertificates(new BufferedInputStream(chainStream));
+                               if (chain.isEmpty()) {
+                                       log.error("Input did not contain any valid certificates.");
+                                       throw new ExtKeyToolException("Input did not contain any valid certificates.");
+                               }
+
+                               X509Certificate[] verifiedChain = linkChain(keyAlgorithm, (X509Certificate[]) chain
+                                               .toArray(new X509Certificate[0]), key);
+
+                               keyStore.setKeyEntry(keyAlias, key, keyPassword, verifiedChain);
+                       }
+
                        ByteArrayOutputStream keyStoreOutStream = new ByteArrayOutputStream();
                        keyStore.store(keyStoreOutStream, keyStorePassword);
                        log.info("Key Store saved to stream.");
@@ -548,18 +544,17 @@ public class ExtKeyTool {
 
                        String flags = args[i];
 
-                       //parse actions
+                       // parse actions
                        if (flags.equalsIgnoreCase("-exportkey")) {
                                parsedArguments.setProperty("command", "exportKey");
                        } else if (flags.equalsIgnoreCase("-importkey")) {
                                parsedArguments.setProperty("command", "importKey");
                        }
 
-                       //parse specifiers
-            else if (flags.equalsIgnoreCase("-secret")) {
-                parsedArguments.setProperty("secret", "true");
-            }
-                       else if (flags.equalsIgnoreCase("-alias")) {
+                       // parse specifiers
+                       else if (flags.equalsIgnoreCase("-secret")) {
+                               parsedArguments.setProperty("secret", "true");
+                       } else if (flags.equalsIgnoreCase("-alias")) {
                                if (++i == args.length) { throw new IllegalArgumentException("The argument -alias requires a parameter"); }
                                parsedArguments.setProperty("alias", args[i]);
                        } else if (flags.equalsIgnoreCase("-keyfile")) {
@@ -599,7 +594,7 @@ public class ExtKeyTool {
                                parsedArguments.setProperty("keyAlgorithm", args[i]);
                        }
 
-                       //options
+                       // options
                        else if (flags.equalsIgnoreCase("-v")) {
                                parsedArguments.setProperty("verbose", "true");
                        } else if (flags.equalsIgnoreCase("-rfc")) {
@@ -688,7 +683,7 @@ public class ExtKeyTool {
 
        private void run(Properties arguments) throws ExtKeyToolException {
 
-               //common for all actions
+               // common for all actions
                char[] storePassword = null;
                if (arguments.getProperty("storePass", null) != null) {
                        storePassword = arguments.getProperty("storePass").toCharArray();
@@ -701,7 +696,7 @@ public class ExtKeyTool {
                        providerName = "SUN";
                }
 
-               //export key action
+               // export key action
                if (arguments.getProperty("command").equals("exportKey")) {
 
                        boolean rfc = false;
@@ -730,7 +725,7 @@ public class ExtKeyTool {
                        }
                        outStream.close();
 
-                       //import action
+                       // import action
                } else if (arguments.getProperty("command").equals("importKey")) {
 
                        InputStream keyInStream = null;
@@ -751,24 +746,16 @@ public class ExtKeyTool {
                                } catch (FileNotFoundException e) {
                                        throw new ExtKeyToolException("Could not open cert file." + e.getMessage());
                                }
-                       } else if (!arguments.getProperty("secret").equalsIgnoreCase("true")){
-                               throw new IllegalArgumentException("Certificate file must be specified.");
-                       }
+                       } else if (!arguments.getProperty("secret").equalsIgnoreCase("true")) { throw new IllegalArgumentException(
+                                       "Certificate file must be specified."); }
 
                        try {
 
-                               ByteArrayOutputStream keyStoreOutStream = importKey(
-                        providerName,
-                        arguments.getProperty("keyAlgorithm", null),
-                        keyInStream,
-                        certInStream,
-                        new FileInputStream(resolveKeyStore(arguments.getProperty("keyStore", null))),
-                        arguments.getProperty("storeType", null),
-                        arguments.getProperty("alias", null),
-                        storePassword,
-                        resolveKeyPass(arguments.getProperty("keyPass", null), storePassword),
-                        arguments.getProperty("secret","false").equalsIgnoreCase("true")
-                        );
+                               ByteArrayOutputStream keyStoreOutStream = importKey(providerName, arguments.getProperty("keyAlgorithm",
+                                               null), keyInStream, certInStream, new FileInputStream(resolveKeyStore(arguments.getProperty(
+                                               "keyStore", null))), arguments.getProperty("storeType", null), arguments.getProperty("alias",
+                                               null), storePassword, resolveKeyPass(arguments.getProperty("keyPass", null), storePassword),
+                                               arguments.getProperty("secret", "false").equalsIgnoreCase("true"));
 
                                keyInStream.close();
                                // A quick sanity check before we overwrite the old keystore