directory-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From plusplusjia...@apache.org
Subject [15/21] directory-kerby git commit: Merge from pkinit-support branch.
Date Wed, 16 Dec 2015 06:17:02 GMT
http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/0a19b1d7/kerby-pkix/src/main/java/org/apache/commons/ssl/KeyStoreBuilder.java
----------------------------------------------------------------------
diff --git a/kerby-pkix/src/main/java/org/apache/commons/ssl/KeyStoreBuilder.java b/kerby-pkix/src/main/java/org/apache/commons/ssl/KeyStoreBuilder.java
new file mode 100644
index 0000000..1b3267a
--- /dev/null
+++ b/kerby-pkix/src/main/java/org/apache/commons/ssl/KeyStoreBuilder.java
@@ -0,0 +1,692 @@
+/*
+ * $HeadURL: http://juliusdavies.ca/svn/not-yet-commons-ssl/tags/commons-ssl-0.3.16/src/java/org/apache/commons/ssl/KeyStoreBuilder.java $
+ * $Revision: 180 $
+ * $Date: 2014-09-23 11:33:47 -0700 (Tue, 23 Sep 2014) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.commons.ssl;
+
+import org.apache.kerby.asn1.type.Asn1Integer;
+import org.apache.kerby.asn1.type.Asn1Sequence;
+import org.apache.kerby.util.Util;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.security.interfaces.DSAParams;
+import java.security.interfaces.DSAPrivateKey;
+import java.security.interfaces.RSAPrivateCrtKey;
+import java.security.interfaces.RSAPublicKey;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Builds Java Key Store files out of pkcs12 files, or out of pkcs8 files +
+ * certificate chains.  Also supports OpenSSL style private keys (encrypted or
+ * unencrypted).
+ *
+ * @author Credit Union Central of British Columbia
+ * @author <a href="http://www.cucbc.com/">www.cucbc.com</a>
+ * @author <a href="mailto:juliusdavies@cucbc.com">juliusdavies@cucbc.com</a>
+ * @since 4-Nov-2006
+ */
+public class KeyStoreBuilder {
+    private static final String PKCS7_ENCRYPTED = "1.2.840.113549.1.7.6";
+
+    public static KeyStore build(byte[] jksOrCerts, char[] password)
+        throws IOException, CertificateException, KeyStoreException,
+        NoSuchAlgorithmException, InvalidKeyException,
+        NoSuchProviderException, ProbablyBadPasswordException,
+        UnrecoverableKeyException {
+        return build(jksOrCerts, null, password);
+    }
+
+    public static KeyStore build(byte[] jksOrCerts, byte[] privateKey,
+                                 char[] password)
+        throws IOException, CertificateException, KeyStoreException,
+        NoSuchAlgorithmException, InvalidKeyException,
+        NoSuchProviderException, ProbablyBadPasswordException,
+        UnrecoverableKeyException {
+        return build(jksOrCerts, privateKey, password, null);
+    }
+
+
+    public static KeyStore build(byte[] jksOrCerts, byte[] privateKey,
+                                 char[] jksPassword, char[] keyPassword)
+        throws IOException, CertificateException, KeyStoreException,
+        NoSuchAlgorithmException, InvalidKeyException,
+        NoSuchProviderException, ProbablyBadPasswordException,
+        UnrecoverableKeyException {
+
+        if (keyPassword == null || keyPassword.length <= 0) {
+            keyPassword = jksPassword;
+        }
+
+        BuildResult br1 = parse(jksOrCerts, jksPassword, keyPassword);
+        BuildResult br2 = null;
+        KeyStore jks = null;
+        if (br1.jks != null) {
+            jks = br1.jks;
+        } else if (privateKey != null && privateKey.length > 0) {
+            br2 = parse(privateKey, jksPassword, keyPassword);
+            if (br2.jks != null) {
+                jks = br2.jks;
+            }
+        }
+
+        // If we happened to find a JKS file, let's just return that.
+        // JKS files get priority (in case some weirdo specifies both a PKCS12
+        // and a JKS file!).
+        if (jks != null) {
+            // Make sure the keystore we found is not corrupt.
+            br1 = validate(jks, keyPassword);
+            if (br1 == null) {
+                return jks;
+            }
+        }
+
+        List keys = br1.keys;
+        List chains = br1.chains;        
+        boolean atLeastOneNotSet = keys == null || chains == null || keys.isEmpty() || chains.isEmpty();
+        if (atLeastOneNotSet && br2 != null) {
+            if (br2.keys != null && !br2.keys.isEmpty()) {
+                // Notice that the key from build-result-2 gets priority over the
+                // key from build-result-1 (if both had valid keys).
+                keys = br2.keys;
+            }
+            if (chains == null || chains.isEmpty()) {
+                chains = br2.chains;
+            }
+        }
+
+        atLeastOneNotSet = keys == null || chains == null || keys.isEmpty() || chains.isEmpty();
+        if (atLeastOneNotSet) {
+            String missing = "";
+            if (keys == null) {
+                missing = " [Private key missing (bad password?)]";
+            }
+            if (chains == null) {
+                missing += " [Certificate chain missing]";
+            }
+            throw new KeyStoreException("Can't build keystore:" + missing);
+        } else {
+            KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
+            ks.load(null, jksPassword);
+            Iterator keysIt = keys.iterator();
+            Iterator chainsIt = chains.iterator();
+            int i = 1;
+            while (keysIt.hasNext() && chainsIt.hasNext()) {
+                Key key = (Key) keysIt.next();
+                Certificate[] c = (Certificate[]) chainsIt.next();
+                X509Certificate theOne = buildChain(key, c);
+                String alias = "alias_" + i++;
+                // The theOne is not null, then our chain was probably altered.
+                // Need to trim out the newly introduced null entries at the end of
+                // our chain.
+                if (theOne != null) {
+                    c = Certificates.trimChain(c);
+                    alias = Certificates.getCN(theOne);
+                    alias = alias.replace(' ', '_');
+                }
+                ks.setKeyEntry(alias, key, keyPassword, c);
+            }
+            return ks;
+        }
+    }
+
+    /**
+     * Builds the chain up such that chain[ 0 ] contains the public key
+     * corresponding to the supplied private key.
+     *
+     * @param key   private key
+     * @param chain array of certificates to build chain from
+     * @return theOne!
+     * @throws java.security.KeyStoreException        no certificates correspond to private key
+     * @throws java.security.cert.CertificateException     java libraries complaining
+     * @throws java.security.NoSuchAlgorithmException java libraries complaining
+     * @throws java.security.InvalidKeyException      java libraries complaining
+     * @throws java.security.NoSuchProviderException  java libraries complaining
+     */
+    public static X509Certificate buildChain(Key key, Certificate[] chain)
+        throws CertificateException, KeyStoreException,
+        NoSuchAlgorithmException, InvalidKeyException,
+        NoSuchProviderException {
+        X509Certificate theOne = null;
+        if (key instanceof RSAPrivateCrtKey) {
+            final RSAPrivateCrtKey rsa = (RSAPrivateCrtKey) key;
+            BigInteger publicExponent = rsa.getPublicExponent();
+            BigInteger modulus = rsa.getModulus();
+            for (int i = 0; i < chain.length; i++) {
+                X509Certificate c = (X509Certificate) chain[i];
+                PublicKey pub = c.getPublicKey();
+                if (pub instanceof RSAPublicKey) {
+                    RSAPublicKey certKey = (RSAPublicKey) pub;
+                    BigInteger pe = certKey.getPublicExponent();
+                    BigInteger mod = certKey.getModulus();
+                    if (publicExponent.equals(pe) && modulus.equals(mod)) {
+                        theOne = c;
+                    }
+                }
+            }
+            if (theOne == null) {
+                throw new KeyStoreException("Can't build keystore: [No certificates belong to the private-key]");
+            }
+            X509Certificate[] newChain;
+            newChain = X509CertificateChainBuilder.buildPath(theOne, chain);
+            Arrays.fill(chain, null);
+            System.arraycopy(newChain, 0, chain, 0, newChain.length);
+        }
+        return theOne;
+    }
+
+    public static BuildResult validate(KeyStore jks, char[] keyPass)
+        throws CertificateException, KeyStoreException,
+        NoSuchAlgorithmException, InvalidKeyException,
+        NoSuchProviderException, UnrecoverableKeyException {
+        Enumeration en = jks.aliases();
+        boolean atLeastOneSuccess = false;
+        boolean atLeastOneFailure = false;
+
+        List keys = new LinkedList();
+        List chains = new LinkedList();
+        while (en.hasMoreElements()) {
+            String alias = (String) en.nextElement();
+            if (jks.isKeyEntry(alias)) {
+                try {
+                    PrivateKey key = (PrivateKey) jks.getKey(alias, keyPass);
+                    // No Exception thrown, so we're good!
+                    atLeastOneSuccess = true;
+                    Certificate[] chain = jks.getCertificateChain(alias);
+                    X509Certificate[] c;
+                    if (chain != null) {
+                        c = Certificates.x509ifyChain(chain);
+                        X509Certificate theOne = buildChain(key, c);
+                        // The theOne is not null, then our chain was probably
+                        // altered.  Need to trim out the newly introduced null
+                        // entries at the end of our chain.
+                        if (theOne != null) {
+                            c = (X509Certificate[]) Certificates.trimChain(c);
+                            jks.deleteEntry(alias);
+                            jks.setKeyEntry(alias, key, keyPass, c);
+                        }
+                        keys.add(key);
+                        chains.add(c);
+                    }
+                } catch (GeneralSecurityException gse) {
+                    atLeastOneFailure = true;
+                    // This is not the key you're looking for.
+                }
+            }
+        }
+        if (!atLeastOneSuccess) {
+            throw new KeyStoreException("No private keys found in keystore!");
+        }
+        // The idea is a bit hacky:  if we return null, all is cool.  If
+        // we return a list, we're telling upstairs to abandon the JKS and
+        // build a new one from the BuildResults we provide.
+        // (Sun's builtin SSL refuses to deal with keystores where not all
+        // keys can be decrypted).
+        return atLeastOneFailure ? new BuildResult(keys, chains, null) : null;
+    }
+
+    public static class BuildResult {
+        protected final List keys;
+        protected final List chains;
+        protected final KeyStore jks;
+
+        protected BuildResult(List keys, List chains, KeyStore jks) {
+            if (keys == null || keys.isEmpty()) {
+                this.keys = null;
+            } else {
+                this.keys = Collections.unmodifiableList(keys);
+            }
+            this.jks = jks;
+            List x509Chains = new LinkedList();
+            if (chains != null) {
+                Iterator it = chains.iterator();
+                while (it.hasNext()) {
+                    Certificate[] chain = (Certificate[]) it.next();
+                    if (chain != null && chain.length > 0) {
+                        int len = chain.length;
+                        X509Certificate[] x509 = new X509Certificate[len];
+                        for (int i = 0; i < x509.length; i++) {
+                            x509[i] = (X509Certificate) chain[i];
+                        }
+                        x509Chains.add(x509);
+                    }
+                }
+            }
+            if (x509Chains == null || x509Chains.isEmpty()) {
+                this.chains = null;
+            } else {
+                this.chains = Collections.unmodifiableList(x509Chains);
+            }
+        }
+    }
+
+
+    public static BuildResult parse(byte[] stuff, char[] jksPass,
+                                    char[] keyPass)
+            throws IOException, CertificateException, KeyStoreException,
+            ProbablyBadPasswordException {
+
+        return parse(stuff, jksPass, keyPass, false);
+    }
+
+    static BuildResult parse(byte[] stuff, char[] jksPass,
+                             char[] keyPass, boolean forTrustMaterial)
+        throws IOException, CertificateException, KeyStoreException,
+        ProbablyBadPasswordException {
+        CertificateFactory cf = CertificateFactory.getInstance("X.509");
+        Key key = null;
+        Certificate[] chain = null;
+        try {
+            PKCS8Key pkcs8Key = new PKCS8Key(stuff, jksPass);
+            key = pkcs8Key.getPrivateKey();
+        } catch (ProbablyBadPasswordException pbpe) {
+            throw pbpe;
+        } catch (GeneralSecurityException gse) {
+            // no luck
+            System.err.println(gse);
+        }
+
+        List pemItems = PEMUtil.decode(stuff);
+        Iterator it = pemItems.iterator();
+        LinkedList certificates = new LinkedList();
+        while (it.hasNext()) {
+            PEMItem item = (PEMItem) it.next();
+            byte[] derBytes = item.getDerBytes();
+            String type = item.pemType.trim().toUpperCase();
+            if (type.startsWith("CERT")
+                || type.startsWith("X509")
+                || type.startsWith("PKCS7")) {
+                ByteArrayInputStream in = new ByteArrayInputStream(derBytes);
+                X509Certificate c = (X509Certificate) cf.generateCertificate(in);
+                certificates.add(c);
+            }
+            chain = toChain(certificates);
+        }
+
+        if (chain != null || key != null) {
+            List chains = chain != null ? Collections.singletonList(chain) : null;
+            List keys = key != null ? Collections.singletonList(key) : null;
+            return new BuildResult(keys, chains, null);
+        }
+
+        boolean isProbablyPKCS12 = false;
+        boolean isASN = false;
+        PkcsStructure asn1 = null;
+        try {
+            asn1 = PkcsUtil.analyze(stuff);
+            isASN = true;
+            isProbablyPKCS12 = asn1.oids.contains(PKCS7_ENCRYPTED);
+            if (!isProbablyPKCS12 && asn1.bigPayload != null) {
+                asn1 = PkcsUtil.analyze(asn1.bigPayload);
+                isProbablyPKCS12 = asn1.oids.contains(PKCS7_ENCRYPTED);
+            }
+        } catch (Exception e) {
+            // isProbablyPKCS12 and isASN are set properly by now.
+            System.err.println(e);
+        }
+
+        ByteArrayInputStream stuffStream = new ByteArrayInputStream(stuff);
+        // Try default keystore... then try others.
+        BuildResult br = tryJKS(KeyStore.getDefaultType(), stuffStream, jksPass, keyPass, forTrustMaterial);
+        if (br == null) {
+            br = tryJKS("jks", stuffStream, jksPass, keyPass, forTrustMaterial);
+            if (br == null) {
+                br = tryJKS("jceks", stuffStream, jksPass, keyPass, forTrustMaterial);
+                if (br == null) {
+                    br = tryJKS("BKS", stuffStream, jksPass, keyPass, forTrustMaterial);
+                    if (br == null) {
+                        br = tryJKS("UBER", stuffStream, jksPass, keyPass, forTrustMaterial);
+                    }
+                }
+            }
+        }
+        if (br != null) {
+            return br;
+        }
+        if (isASN && isProbablyPKCS12) {
+            br = tryJKS("pkcs12", stuffStream, jksPass, null, forTrustMaterial);
+        }
+
+        if (br == null) {
+            // Okay, it's ASN.1, but it's not PKCS12.  Only one possible
+            // interesting things remains:  X.509.
+            stuffStream.reset();
+
+            try {
+                certificates = new LinkedList();
+                Collection certs = cf.generateCertificates(stuffStream);
+                it = certs.iterator();
+                while (it.hasNext()) {
+                    X509Certificate x509 = (X509Certificate) it.next();
+                    certificates.add(x509);
+                }
+                chain = toChain(certificates);
+                if (chain != null && chain.length > 0) {
+                    List chains = Collections.singletonList(chain);
+                    return new BuildResult(null, chains, null);
+                }
+            } catch (CertificateException ce) {
+                // oh well
+                System.err.println(ce);
+            }
+
+            stuffStream.reset();
+            // Okay, still no luck.  Maybe it's an ASN.1 DER stream
+            // containing only a single certificate?  (I don't completely
+            // trust CertificateFactory.generateCertificates).
+            try {
+                Certificate c = cf.generateCertificate(stuffStream);
+                X509Certificate x509 = (X509Certificate) c;
+                chain = toChain(Collections.singleton(x509));
+                if (chain != null && chain.length > 0) {
+                    List chains = Collections.singletonList(chain);
+                    return new BuildResult(null, chains, null);
+                }
+            } catch (CertificateException ce) {
+                // oh well
+                System.err.println(ce);
+            }
+        }
+
+        br = tryJKS("pkcs12", stuffStream, jksPass, null, forTrustMaterial);
+        if (br != null) {
+            // no exception thrown, so must be PKCS12.
+            /*
+            Hmm, well someone finally reported this bug!   And they want the library to be quiet....
+            Commenting out for now, maybe investigate why it's happening one day....
+
+            System.out.println("Please report bug!");
+            System.out.println("PKCS12 detection failed to realize this was PKCS12!");
+            System.out.println(asn1);
+            */
+            return br;
+        }
+        throw new KeyStoreException("failed to extract any certificates or private keys - maybe bad password?");
+    }
+
+    private static BuildResult tryJKS(
+            String keystoreType, ByteArrayInputStream in, char[] jksPassword, char[] keyPassword,
+            boolean forTrustMaterial
+    ) throws ProbablyBadPasswordException {
+        in.reset();
+        if (keyPassword == null || keyPassword.length <= 0) {
+            keyPassword = jksPassword;
+        }
+
+        keystoreType = keystoreType.trim().toLowerCase();
+        boolean isPKCS12 = "pkcs12".equalsIgnoreCase(keystoreType);
+        try {
+            Key key = null;
+            Certificate[] chain = null;
+            UnrecoverableKeyException uke = null;
+            KeyStore jksKeyStore = KeyStore.getInstance(keystoreType);
+            jksKeyStore.load(in, jksPassword);
+            Enumeration en = jksKeyStore.aliases();
+            while (en.hasMoreElements()) {
+                String alias = (String) en.nextElement();
+                if (jksKeyStore.isKeyEntry(alias)) {
+                    try {
+                        if (keyPassword != null) {
+                            key = jksKeyStore.getKey(alias, keyPassword);
+                        }
+                        if (key instanceof PrivateKey) {
+                            chain = jksKeyStore.getCertificateChain(alias);
+                            break;
+                        }
+                    } catch (UnrecoverableKeyException e) {
+                        uke = e;  // We might throw this one later. 
+                    } catch (GeneralSecurityException gse) {
+                        // Swallow... keep looping.
+                        System.err.println(gse);
+                    }
+                }
+                if (isPKCS12 && en.hasMoreElements()) {
+                    System.out.println("what kind of weird pkcs12 file has more than one alias?");
+                }
+            }
+            if (key == null && uke != null && !forTrustMaterial) {
+                // If we're trying to load KeyMaterial, then we *need* that key we spotted.
+                // But if we're trying to load TrustMaterial, then we're fine, and we can ignore the key.
+                throw new ProbablyBadPasswordException("Probably bad JKS-Key password: " + uke);
+            }
+            if (isPKCS12) {
+                // PKCS12 is supposed to be just a key and a chain, anyway.
+                jksKeyStore = null;
+            }
+
+            List keys = Collections.singletonList(key);
+            List chains = Collections.singletonList(chain);
+            return new BuildResult(keys, chains, jksKeyStore);
+        } catch (ProbablyBadPasswordException pbpe) {
+            throw pbpe;
+        } catch (GeneralSecurityException gse) {
+            // swallow it, return null
+            return null;
+        } catch (IOException ioe) {
+            String msg = ioe.getMessage();
+            msg = msg != null ? msg.trim().toLowerCase() : "";
+            if (isPKCS12) {
+                int x = msg.indexOf("failed to decrypt");
+                int y = msg.indexOf("verify mac");
+                x = Math.max(x, y);
+                if (x >= 0) {
+                    throw new ProbablyBadPasswordException("Probably bad PKCS12 password: " + ioe);
+                }
+            } else {
+                int x = msg.indexOf("password");
+                if (x >= 0) {
+                    throw new ProbablyBadPasswordException("Probably bad JKS password: " + ioe);
+                }
+            }
+            // swallow it, return null.
+            return null;
+        }
+    }
+
+    private static X509Certificate[] toChain(Collection certs) {
+        if (certs != null && !certs.isEmpty()) {
+            X509Certificate[] x509Chain = new X509Certificate[certs.size()];
+            certs.toArray(x509Chain);
+            return x509Chain;
+        } else {
+            return null;
+        }
+    }
+
+
+    public static void main(String[] args) throws Exception {
+        if (args.length < 2) {
+            System.out.println("KeyStoreBuilder:  creates '[alias].jks' (Java Key Store)");
+            System.out.println("    -topk8 mode:  creates '[alias].pem' (x509 chain + unencrypted pkcs8)");
+            System.out.println("[alias] will be set to the first CN value of the X509 certificate.");
+            System.out.println("-------------------------------------------------------------------");
+            System.out.println("Usage1: [password] [file:pkcs12]");
+            System.out.println("Usage2: [password] [file:private-key] [file:certificate-chain]");
+            System.out.println("Usage3: -topk8 [password] [file:jks]");
+            System.out.println("-------------------------------------------------------------------");
+            System.out.println("[private-key] can be openssl format, or pkcs8.");
+            System.out.println("[password] decrypts [private-key], and also encrypts outputted JKS file.");
+            System.out.println("All files can be PEM or DER.");
+            System.exit(1);
+        }
+        char[] password = args[0].toCharArray();
+        boolean toPKCS8 = false;
+        if ("-topk8".equalsIgnoreCase(args[0])) {
+            toPKCS8 = true;
+            password = args[1].toCharArray();
+            args[1] = args[2];
+            args[2] = null;
+        }
+
+        FileInputStream fin1 = new FileInputStream(args[1]);
+        byte[] bytes1 = Util.streamToBytes(fin1);
+        byte[] bytes2 = null;
+        if (args.length > 2 && args[2] != null) {
+            FileInputStream fin2 = new FileInputStream(args[2]);
+            bytes2 = Util.streamToBytes(fin2);
+        }
+
+        KeyStore ks = build(bytes1, bytes2, password);
+        Enumeration en = ks.aliases();
+        String alias = "keystorebuilder";
+
+        // We're going to assume that the biggest key is the one we want
+        // to convert to PKCS8 (PEM).  That's until someone figures out a
+        // better way to deal with this annoying situation (more than 1
+        // key in the KeyStore).
+        int biggestKey = 0;
+        while (en.hasMoreElements()) {
+            String s = (String) en.nextElement();
+            try {
+                PrivateKey pk = (PrivateKey) ks.getKey(s, password);
+                byte[] encoded = pk.getEncoded();
+                int len = encoded != null ? encoded.length : 0;
+                if (len >= biggestKey) {
+                    biggestKey = len;
+                    alias = s;
+                }
+            } catch (Exception e) {
+                // oh well, try next one.
+                System.err.println(e);
+            }
+        }
+
+        String suffix = toPKCS8 ? ".pem" : ".jks";
+        String fileName = alias;
+        Certificate[] chain = ks.getCertificateChain(alias);
+        if (chain != null && chain[0] != null) {
+            String cn = Certificates.getCN((X509Certificate) chain[0]);
+            cn = cn != null ? cn.trim() : "";
+            if (!"".equals(cn)) {
+                fileName = cn;
+            }
+        }
+
+        File f = new File(fileName + suffix);
+        int count = 1;
+        while (f.exists()) {
+            f = new File(alias + "_" + count + suffix);
+            count++;
+        }
+
+        FileOutputStream fout = new FileOutputStream(f);
+        if (toPKCS8) {
+            List pemItems = new LinkedList();
+            PrivateKey key = (PrivateKey) ks.getKey(alias, password);
+            chain = ks.getCertificateChain(alias);
+            byte[] pkcs8DerBytes = null;
+            if (key instanceof RSAPrivateCrtKey) {
+                RSAPrivateCrtKey rsa = (RSAPrivateCrtKey) key;
+                Asn1Sequence seq = new Asn1Sequence();
+                seq.addItem(new Asn1Integer(BigInteger.ZERO));
+                seq.addItem(new Asn1Integer(rsa.getModulus()));
+                seq.addItem(new Asn1Integer(rsa.getPublicExponent()));
+                seq.addItem(new Asn1Integer(rsa.getPrivateExponent()));
+                seq.addItem(new Asn1Integer(rsa.getPrimeP()));
+                seq.addItem(new Asn1Integer(rsa.getPrimeQ()));
+                seq.addItem(new Asn1Integer(rsa.getPrimeExponentP()));
+                seq.addItem(new Asn1Integer(rsa.getPrimeExponentQ()));
+                seq.addItem(new Asn1Integer(rsa.getCrtCoefficient()));
+                byte[] derBytes = seq.encode();
+                PKCS8Key pkcs8 = new PKCS8Key(derBytes, null);
+                pkcs8DerBytes = pkcs8.getDecryptedBytes();
+            } else if (key instanceof DSAPrivateKey) {
+                DSAPrivateKey dsa = (DSAPrivateKey) key;
+                DSAParams params = dsa.getParams();
+                BigInteger g = params.getG();
+                BigInteger p = params.getP();
+                BigInteger q = params.getQ();
+                BigInteger x = dsa.getX();
+                BigInteger y = q.modPow(x, p);
+
+                Asn1Sequence seq = new Asn1Sequence();
+                seq.addItem(new Asn1Integer(BigInteger.ZERO));
+                seq.addItem(new Asn1Integer(p));
+                seq.addItem(new Asn1Integer(q));
+                seq.addItem(new Asn1Integer(g));
+                seq.addItem(new Asn1Integer(y));
+                seq.addItem(new Asn1Integer(x));
+                byte[] derBytes = seq.encode();
+                PKCS8Key pkcs8 = new PKCS8Key(derBytes, null);
+                pkcs8DerBytes = pkcs8.getDecryptedBytes();
+            }
+            if (chain != null && chain.length > 0) {
+                for (int i = 0; i < chain.length; i++) {
+                    X509Certificate x509 = (X509Certificate) chain[i];
+                    byte[] derBytes = x509.getEncoded();
+                    PEMItem item = new PEMItem(derBytes, "CERTIFICATE");
+                    pemItems.add(item);
+                }
+            }
+            if (pkcs8DerBytes != null) {
+                PEMItem item = new PEMItem(pkcs8DerBytes, "PRIVATE KEY");
+                pemItems.add(item);
+            }
+            byte[] pem = PEMUtil.encode(pemItems);
+            fout.write(pem);
+        } else {
+            // If we're not converting to unencrypted PKCS8 style PEM,
+            // then we are converting to Sun JKS.  It happens right here:
+            KeyStore jks = KeyStore.getInstance(KeyStore.getDefaultType());
+            jks.load(null, password);
+            jks.setKeyEntry(alias, ks.getKey(alias, password), password, ks.getCertificateChain(alias));
+            jks.store(fout, password);
+        }
+        fout.flush();
+        fout.close();
+        System.out.println("Successfuly wrote: [" + f.getPath() + "]");
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/0a19b1d7/kerby-pkix/src/main/java/org/apache/commons/ssl/OpenSSL.java
----------------------------------------------------------------------
diff --git a/kerby-pkix/src/main/java/org/apache/commons/ssl/OpenSSL.java b/kerby-pkix/src/main/java/org/apache/commons/ssl/OpenSSL.java
new file mode 100644
index 0000000..0eda1db
--- /dev/null
+++ b/kerby-pkix/src/main/java/org/apache/commons/ssl/OpenSSL.java
@@ -0,0 +1,725 @@
+/*
+ * $HeadURL: http://juliusdavies.ca/svn/not-yet-commons-ssl/tags/commons-ssl-0.3.16/src/java/org/apache/commons/ssl/OpenSSL.java $
+ * $Revision: 144 $
+ * $Date: 2009-05-25 11:14:29 -0700 (Mon, 25 May 2009) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.commons.ssl;
+
+import org.apache.kerby.util.Base64;
+import org.apache.kerby.util.Base64InputStream;
+import org.apache.kerby.util.Hex;
+import org.apache.kerby.util.Util;
+
+import javax.crypto.Cipher;
+import javax.crypto.CipherInputStream;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.GeneralSecurityException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.StringTokenizer;
+
+/**
+ * Class for encrypting or decrypting data with a password (PBE - password
+ * based encryption).  Compatible with "openssl enc" unix utility.  An OpenSSL
+ * compatible cipher name must be specified along with the password (try "man enc" on a
+ * unix box to see what's possible).  Some examples:
+ * <ul><li>des, des3, des-ede3-cbc
+ * <li>aes128, aes192, aes256, aes-256-cbc
+ * <li>rc2, rc4, bf</ul>
+ * <pre>
+ * <em style="color: green;">// Encrypt!</em>
+ * byte[] encryptedData = OpenSSL.encrypt( "des3", password, data );
+ * </pre>
+ * <p/>
+ * If you want to specify a raw key and iv directly (without using PBE), use
+ * the methods that take byte[] key, byte[] iv.  Those byte[] arrays can be
+ * the raw binary, or they can be ascii (hex representation: '0' - 'F').  If
+ * you want to use PBE to derive the key and iv, then use the methods that
+ * take char[] password.
+ * <p/>
+ * This class is able to decrypt files encrypted with "openssl" unix utility.
+ * <p/>
+ * The "openssl" unix utility is able to decrypt files encrypted by this class.
+ * <p/>
+ * This class is also able to encrypt and decrypt its own files.
+ *
+ * @author <a href="mailto:juliusdavies@cucbc.com">juliusdavies@gmail.com</a>
+ * @since 18-Oct-2007
+ */
+public class OpenSSL {
+
+
+    /**
+     * Decrypts data using a password and an OpenSSL compatible cipher
+     * name.
+     *
+     * @param cipher    The OpenSSL compatible cipher to use (try "man enc" on a
+     *                  unix box to see what's possible).  Some examples:
+     *                  <ul><li>des, des3, des-ede3-cbc
+     *                  <li>aes128, aes192, aes256, aes-256-cbc
+     *                  <li>rc2, rc4, bf</ul>
+     * @param pwd       password to use for this PBE decryption
+     * @param encrypted byte array to decrypt.  Can be raw, or base64.
+     * @return decrypted bytes
+     * @throws java.io.IOException              problems with encrypted bytes (unlikely!)
+     * @throws java.security.GeneralSecurityException problems decrypting
+     */
+    public static byte[] decrypt(String cipher, char[] pwd, byte[] encrypted)
+        throws IOException, GeneralSecurityException {
+        ByteArrayInputStream in = new ByteArrayInputStream(encrypted);
+        InputStream decrypted = decrypt(cipher, pwd, in);
+        return Util.streamToBytes(decrypted);
+    }
+
+    /**
+     * Decrypts data using a password and an OpenSSL compatible cipher
+     * name.
+     *
+     * @param cipher    The OpenSSL compatible cipher to use (try "man enc" on a
+     *                  unix box to see what's possible).  Some examples:
+     *                  <ul><li>des, des3, des-ede3-cbc
+     *                  <li>aes128, aes192, aes256, aes-256-cbc
+     *                  <li>rc2, rc4, bf</ul>
+     * @param pwd       password to use for this PBE decryption
+     * @param encrypted InputStream to decrypt.  Can be raw, or base64.
+     * @return decrypted bytes as an InputStream
+     * @throws java.io.IOException              problems with InputStream
+     * @throws java.security.GeneralSecurityException problems decrypting
+     */
+    public static InputStream decrypt(String cipher, char[] pwd,
+                                      InputStream encrypted)
+        throws IOException, GeneralSecurityException {
+        CipherInfo cipherInfo = lookup(cipher);
+        boolean salted = false;
+
+        // First 16 bytes of raw binary will hopefully be OpenSSL's
+        // "Salted__[8 bytes of hex]" thing.  Might be in Base64, though.
+        byte[] saltLine = Util.streamToBytes(encrypted, 16);
+        if (saltLine.length <= 0) {
+            throw new IOException("encrypted InputStream is empty");
+        }
+        String firstEightBytes = "";
+        if (saltLine.length >= 8) {
+            firstEightBytes = new String(saltLine, 0, 8);
+        }
+        if ("SALTED__".equalsIgnoreCase(firstEightBytes)) {
+            salted = true;
+        } else {
+            // Maybe the reason we didn't find the salt is because we're in
+            // base64.
+            if (Base64.isArrayByteBase64(saltLine)) {
+                InputStream head = new ByteArrayInputStream(saltLine);
+                // Need to put that 16 byte "saltLine" back into the Stream.
+                encrypted = new ComboInputStream(head, encrypted);
+                encrypted = new Base64InputStream(encrypted);
+                saltLine = Util.streamToBytes(encrypted, 16);
+
+                if (saltLine.length >= 8) {
+                    firstEightBytes = new String(saltLine, 0, 8);
+                }
+                if ("SALTED__".equalsIgnoreCase(firstEightBytes)) {
+                    salted = true;
+                }
+            }
+        }
+
+        byte[] salt = null;
+        if (salted) {
+            salt = new byte[8];
+            System.arraycopy(saltLine, 8, salt, 0, 8);
+        } else {
+            // Encrypted data wasn't salted.  Need to put the "saltLine" we
+            // extracted back into the stream.
+            InputStream head = new ByteArrayInputStream(saltLine);
+            encrypted = new ComboInputStream(head, encrypted);
+        }
+
+        int keySize = cipherInfo.keySize;
+        int ivSize = cipherInfo.ivSize;
+        boolean des2 = cipherInfo.des2;
+        DerivedKey dk = deriveKey(pwd, salt, keySize, ivSize, des2);
+        Cipher c = PKCS8Key.generateCipher(
+            cipherInfo.javaCipher, cipherInfo.blockMode, dk, des2, null, true
+        );
+
+        return new CipherInputStream(encrypted, c);
+    }
+
+    /**
+     * Encrypts data using a password and an OpenSSL compatible cipher
+     * name.
+     *
+     * @param cipher The OpenSSL compatible cipher to use (try "man enc" on a
+     *               unix box to see what's possible).  Some examples:
+     *               <ul><li>des, des3, des-ede3-cbc
+     *               <li>aes128, aes192, aes256, aes-256-cbc
+     *               <li>rc2, rc4, bf</ul>
+     * @param pwd    password to use for this PBE encryption
+     * @param data   byte array to encrypt
+     * @return encrypted bytes as an array in base64.  First 16 bytes include the
+     *         special OpenSSL "Salted__" info encoded into base64.
+     * @throws java.io.IOException              problems with the data byte array
+     * @throws java.security.GeneralSecurityException problems encrypting
+     */
+    public static byte[] encrypt(String cipher, char[] pwd, byte[] data)
+        throws IOException, GeneralSecurityException {
+        // base64 is the default output format.
+        return encrypt(cipher, pwd, data, true);
+    }
+
+    /**
+     * Encrypts data using a password and an OpenSSL compatible cipher
+     * name.
+     *
+     * @param cipher The OpenSSL compatible cipher to use (try "man enc" on a
+     *               unix box to see what's possible).  Some examples:
+     *               <ul><li>des, des3, des-ede3-cbc
+     *               <li>aes128, aes192, aes256, aes-256-cbc
+     *               <li>rc2, rc4, bf</ul>
+     * @param pwd    password to use for this PBE encryption
+     * @param data   InputStream to encrypt
+     * @return encrypted bytes as an InputStream.  First 16 bytes include the
+     *         special OpenSSL "Salted__" info encoded into base64.
+     * @throws java.io.IOException              problems with the data InputStream
+     * @throws java.security.GeneralSecurityException problems encrypting
+     */
+    public static InputStream encrypt(String cipher, char[] pwd,
+                                      InputStream data)
+        throws IOException, GeneralSecurityException {
+        // base64 is the default output format.
+        return encrypt(cipher, pwd, data, true);
+    }
+
+    /**
+     * Encrypts data using a password and an OpenSSL compatible cipher
+     * name.
+     *
+     * @param cipher   The OpenSSL compatible cipher to use (try "man enc" on a
+     *                 unix box to see what's possible).  Some examples:
+     *                 <ul><li>des, des3, des-ede3-cbc
+     *                 <li>aes128, aes192, aes256, aes-256-cbc
+     *                 <li>rc2, rc4, bf</ul>
+     * @param pwd      password to use for this PBE encryption
+     * @param data     byte array to encrypt
+     * @param toBase64 true if resulting InputStream should contain base64,
+     *                 <br>false if InputStream should contain raw binary.
+     * @return encrypted bytes as an array.  First 16 bytes include the
+     *         special OpenSSL "Salted__" info.
+     * @throws java.io.IOException              problems with the data byte array
+     * @throws java.security.GeneralSecurityException problems encrypting
+     */
+    public static byte[] encrypt(String cipher, char[] pwd, byte[] data,
+                                 boolean toBase64)
+        throws IOException, GeneralSecurityException {
+        // we use a salt by default.
+        return encrypt(cipher, pwd, data, toBase64, true);
+    }
+
+    /**
+     * Encrypts data using a password and an OpenSSL compatible cipher
+     * name.
+     *
+     * @param cipher   The OpenSSL compatible cipher to use (try "man enc" on a
+     *                 unix box to see what's possible).  Some examples:
+     *                 <ul><li>des, des3, des-ede3-cbc
+     *                 <li>aes128, aes192, aes256, aes-256-cbc
+     *                 <li>rc2, rc4, bf</ul>
+     * @param pwd      password to use for this PBE encryption
+     * @param data     InputStream to encrypt
+     * @param toBase64 true if resulting InputStream should contain base64,
+     *                 <br>false if InputStream should contain raw binary.
+     * @return encrypted bytes as an InputStream.  First 16 bytes include the
+     *         special OpenSSL "Salted__" info.
+     * @throws java.io.IOException              problems with the data InputStream
+     * @throws java.security.GeneralSecurityException problems encrypting
+     */
+    public static InputStream encrypt(String cipher, char[] pwd,
+                                      InputStream data, boolean toBase64)
+        throws IOException, GeneralSecurityException {
+        // we use a salt by default.
+        return encrypt(cipher, pwd, data, toBase64, true);
+    }
+
+    /**
+     * Encrypts data using a password and an OpenSSL compatible cipher
+     * name.
+     *
+     * @param cipher   The OpenSSL compatible cipher to use (try "man enc" on a
+     *                 unix box to see what's possible).  Some examples:
+     *                 <ul><li>des, des3, des-ede3-cbc
+     *                 <li>aes128, aes192, aes256, aes-256-cbc
+     *                 <li>rc2, rc4, bf</ul>
+     * @param pwd      password to use for this PBE encryption
+     * @param data     byte array to encrypt
+     * @param toBase64 true if resulting InputStream should contain base64,
+     *                 <br>false if InputStream should contain raw binary.
+     * @param useSalt  true if a salt should be used to derive the key.
+     *                 <br>false otherwise.  (Best security practises
+     *                 always recommend using a salt!).
+     * @return encrypted bytes as an array.  First 16 bytes include the
+     *         special OpenSSL "Salted__" info if <code>useSalt</code> is true.
+     * @throws java.io.IOException              problems with the data InputStream
+     * @throws java.security.GeneralSecurityException problems encrypting
+     */
+    public static byte[] encrypt(String cipher, char[] pwd, byte[] data,
+                                 boolean toBase64, boolean useSalt)
+        throws IOException, GeneralSecurityException {
+        ByteArrayInputStream in = new ByteArrayInputStream(data);
+        InputStream encrypted = encrypt(cipher, pwd, in, toBase64, useSalt);
+        return Util.streamToBytes(encrypted);
+    }
+
+    /**
+     * Encrypts data using a password and an OpenSSL compatible cipher
+     * name.
+     *
+     * @param cipher   The OpenSSL compatible cipher to use (try "man enc" on a
+     *                 unix box to see what's possible).  Some examples:
+     *                 <ul><li>des, des3, des-ede3-cbc
+     *                 <li>aes128, aes192, aes256, aes-256-cbc
+     *                 <li>rc2, rc4, bf</ul>
+     * @param pwd      password to use for this PBE encryption
+     * @param data     InputStream to encrypt
+     * @param toBase64 true if resulting InputStream should contain base64,
+     *                 <br>false if InputStream should contain raw binary.
+     * @param useSalt  true if a salt should be used to derive the key.
+     *                 <br>false otherwise.  (Best security practises
+     *                 always recommend using a salt!).
+     * @return encrypted bytes as an InputStream.  First 16 bytes include the
+     *         special OpenSSL "Salted__" info if <code>useSalt</code> is true.
+     * @throws java.io.IOException              problems with the data InputStream
+     * @throws java.security.GeneralSecurityException problems encrypting
+     */
+    public static InputStream encrypt(String cipher, char[] pwd,
+                                      InputStream data, boolean toBase64,
+                                      boolean useSalt)
+        throws IOException, GeneralSecurityException {
+        CipherInfo cipherInfo = lookup(cipher);
+        byte[] salt = null;
+        if (useSalt) {
+            SecureRandom rand = SecureRandom.getInstance("SHA1PRNG");
+            salt = new byte[8];
+            rand.nextBytes(salt);
+        }
+
+        int keySize = cipherInfo.keySize;
+        int ivSize = cipherInfo.ivSize;
+        boolean des2 = cipherInfo.des2;
+        DerivedKey dk = deriveKey(pwd, salt, keySize, ivSize, des2);
+        Cipher c = PKCS8Key.generateCipher(
+            cipherInfo.javaCipher, cipherInfo.blockMode, dk, des2, null, false
+        );
+
+        InputStream cipherStream = new CipherInputStream(data, c);
+
+        if (useSalt) {
+            byte[] saltLine = new byte[16];
+            byte[] salted = "Salted__".getBytes();
+            System.arraycopy(salted, 0, saltLine, 0, salted.length);
+            System.arraycopy(salt, 0, saltLine, salted.length, salt.length);
+            InputStream head = new ByteArrayInputStream(saltLine);
+            cipherStream = new ComboInputStream(head, cipherStream);
+        }
+        if (toBase64) {
+            cipherStream = new Base64InputStream(cipherStream, true);
+        }
+        return cipherStream;
+    }
+
+
+    public static byte[] decrypt(String cipher, byte[] key, byte[] iv,
+                                 byte[] encrypted)
+        throws IOException, GeneralSecurityException {
+        ByteArrayInputStream in = new ByteArrayInputStream(encrypted);
+        InputStream decrypted = decrypt(cipher, key, iv, in);
+        return Util.streamToBytes(decrypted);
+    }
+
+    public static InputStream decrypt(String cipher, byte[] key, byte[] iv,
+                                      InputStream encrypted)
+        throws IOException, GeneralSecurityException {
+        CipherInfo cipherInfo = lookup(cipher);
+        byte[] firstLine = Util.streamToBytes(encrypted, 16);
+        if (Base64.isArrayByteBase64(firstLine)) {
+            InputStream head = new ByteArrayInputStream(firstLine);
+            // Need to put that 16 byte "firstLine" back into the Stream.
+            encrypted = new ComboInputStream(head, encrypted);
+            encrypted = new Base64InputStream(encrypted);
+        } else {
+            // Encrypted data wasn't base64.  Need to put the "firstLine" we
+            // extracted back into the stream.
+            InputStream head = new ByteArrayInputStream(firstLine);
+            encrypted = new ComboInputStream(head, encrypted);
+        }
+
+        int keySize = cipherInfo.keySize;
+        int ivSize = cipherInfo.ivSize;
+        // Looks like key is in hex
+        if (key.length == keySize / 4) {
+            key = Hex.decode(key);
+        }
+        // Looks like IV is in hex
+        if (iv.length == ivSize / 4) {
+            iv = Hex.decode(iv);
+        }
+        DerivedKey dk = new DerivedKey(key, iv);
+        Cipher c = PKCS8Key.generateCipher(cipherInfo.javaCipher,
+            cipherInfo.blockMode,
+            dk, cipherInfo.des2, null, true);
+        return new CipherInputStream(encrypted, c);
+    }
+
+    public static byte[] encrypt(String cipher, byte[] key, byte[] iv,
+                                 byte[] data)
+        throws IOException, GeneralSecurityException {
+        return encrypt(cipher, key, iv, data, true);
+    }
+
+    public static byte[] encrypt(String cipher, byte[] key, byte[] iv,
+                                 byte[] data, boolean toBase64)
+        throws IOException, GeneralSecurityException {
+        ByteArrayInputStream in = new ByteArrayInputStream(data);
+        InputStream encrypted = encrypt(cipher, key, iv, in, toBase64);
+        return Util.streamToBytes(encrypted);
+    }
+
+
+    public static InputStream encrypt(String cipher, byte[] key, byte[] iv,
+                                      InputStream data)
+        throws IOException, GeneralSecurityException {
+        return encrypt(cipher, key, iv, data, true);
+    }
+
+    public static InputStream encrypt(String cipher, byte[] key, byte[] iv,
+                                      InputStream data, boolean toBase64)
+        throws IOException, GeneralSecurityException {
+        CipherInfo cipherInfo = lookup(cipher);
+        int keySize = cipherInfo.keySize;
+        int ivSize = cipherInfo.ivSize;
+        if (key.length == keySize / 4) {
+            key = Hex.decode(key);
+        }
+        if (iv.length == ivSize / 4) {
+            iv = Hex.decode(iv);
+        }
+        DerivedKey dk = new DerivedKey(key, iv);
+        Cipher c = PKCS8Key.generateCipher(cipherInfo.javaCipher,
+            cipherInfo.blockMode,
+            dk, cipherInfo.des2, null, false);
+
+        InputStream cipherStream = new CipherInputStream(data, c);
+        if (toBase64) {
+            cipherStream = new Base64InputStream(cipherStream, true);
+        }
+        return cipherStream;
+    }
+
+
+    public static DerivedKey deriveKey(char[] password, byte[] salt,
+                                       int keySize, boolean des2)
+        throws NoSuchAlgorithmException {
+        return deriveKey(password, salt, keySize, 0, des2);
+    }
+
+    @SuppressWarnings("PMD.UselessParentheses")
+    public static DerivedKey deriveKey(char[] password, byte[] salt,
+                                       int keySize, int ivSize, boolean des2)
+        throws NoSuchAlgorithmException {
+        if (des2) {
+            keySize = 128;
+        }
+        MessageDigest md = MessageDigest.getInstance("MD5");
+        byte[] pwdAsBytes = new byte[password.length];
+        for (int i = 0; i < password.length; i++) {
+            pwdAsBytes[i] = (byte) password[i];
+        }
+
+        md.reset();
+        byte[] keyAndIv = new byte[(keySize / 8) + (ivSize / 8)];
+        if (salt == null || salt.length == 0) {
+            // Unsalted!  Bad idea!
+            salt = null;
+        }
+        byte[] result;
+        int currentPos = 0;
+        while (currentPos < keyAndIv.length) {
+            md.update(pwdAsBytes);
+            if (salt != null) {
+                // First 8 bytes of salt ONLY!  That wasn't obvious to me
+                // when using AES encrypted private keys in "Traditional
+                // SSLeay Format".
+                //
+                // Example:
+                // DEK-Info: AES-128-CBC,8DA91D5A71988E3D4431D9C2C009F249
+                //
+                // Only the first 8 bytes are salt, but the whole thing is
+                // re-used again later as the IV.  MUCH gnashing of teeth!
+                md.update(salt, 0, 8);
+            }
+            result = md.digest();
+            int stillNeed = keyAndIv.length - currentPos;
+            // Digest gave us more than we need.  Let's truncate it.
+            if (result.length > stillNeed) {
+                byte[] b = new byte[stillNeed];
+                System.arraycopy(result, 0, b, 0, b.length);
+                result = b;
+            }
+            System.arraycopy(result, 0, keyAndIv, currentPos, result.length);
+            currentPos += result.length;
+            if (currentPos < keyAndIv.length) {
+                // Next round starts with a hash of the hash.
+                md.reset();
+                md.update(result);
+            }
+        }
+        if (des2) {
+            keySize = 192;
+            byte[] buf = new byte[keyAndIv.length + 8];
+            // Make space where 3rd key needs to go (16th - 24th bytes):
+            System.arraycopy(keyAndIv, 0, buf, 0, 16);
+            if (ivSize > 0) {
+                System.arraycopy(keyAndIv, 16, buf, 24, keyAndIv.length - 16);
+            }
+            keyAndIv = buf;
+            // copy first 8 bytes into last 8 bytes to create 2DES key.
+            System.arraycopy(keyAndIv, 0, keyAndIv, 16, 8);
+        }
+        if (ivSize == 0) {
+            // if ivSize == 0, then "keyAndIv" array is actually all key.
+
+            // Must be "Traditional SSLeay Format" encrypted private key in
+            // PEM.  The "salt" in its entirety (not just first 8 bytes) will
+            // probably be re-used later as the IV (initialization vector).
+            return new DerivedKey(keyAndIv, salt);
+        } else {
+            byte[] key = new byte[keySize / 8];
+            byte[] iv = new byte[ivSize / 8];
+            System.arraycopy(keyAndIv, 0, key, 0, key.length);
+            System.arraycopy(keyAndIv, key.length, iv, 0, iv.length);
+            return new DerivedKey(key, iv);
+        }
+    }
+
+
+    public static class CipherInfo {
+        public final String javaCipher;
+        public final String blockMode;
+        public final int keySize;
+        public final int ivSize;
+        public final boolean des2;
+
+        public CipherInfo(String javaCipher, String blockMode, int keySize,
+                          int ivSize, boolean des2) {
+            this.javaCipher = javaCipher;
+            this.blockMode = blockMode;
+            this.keySize = keySize;
+            this.ivSize = ivSize;
+            this.des2 = des2;
+        }
+
+        public String toString() {
+            return javaCipher + "/" + blockMode + " " + keySize + "bit  des2=" + des2;
+        }
+    }
+
+    /**
+     * Converts the way OpenSSL names its ciphers into a Java-friendly naming.
+     *
+     * @param openSSLCipher OpenSSL cipher name, e.g. "des3" or "des-ede3-cbc".
+     *                      Try "man enc" on a unix box to see what's possible.
+     * @return CipherInfo object with the Java-friendly cipher information.
+     */
+    public static CipherInfo lookup(String openSSLCipher) {
+        openSSLCipher = openSSLCipher.trim();
+        if (openSSLCipher.charAt(0) == '-') {
+            openSSLCipher = openSSLCipher.substring(1);
+        }
+        String javaCipher = openSSLCipher.toUpperCase();
+        String blockMode = "CBC";
+        int keySize = -1;
+        int ivSize = 64;
+        boolean des2 = false;
+
+
+        StringTokenizer st = new StringTokenizer(openSSLCipher, "-");
+        if (st.hasMoreTokens()) {
+            javaCipher = st.nextToken().toUpperCase();
+            if (st.hasMoreTokens()) {
+                // Is this the middle token?  Or the last token?
+                String tok = st.nextToken();
+                if (st.hasMoreTokens()) {
+                    try {
+                        keySize = Integer.parseInt(tok);
+                    } catch (NumberFormatException nfe) {
+                        // I guess 2nd token isn't an integer
+                        String upper = tok.toUpperCase();
+                        if (upper.startsWith("EDE3")) {
+                            javaCipher = "DESede";
+                        } else if (upper.startsWith("EDE")) {
+                            javaCipher = "DESede";
+                            des2 = true;
+                        }
+                    }
+                    blockMode = st.nextToken().toUpperCase();
+                } else {
+                    try {
+                        keySize = Integer.parseInt(tok);
+                    } catch (NumberFormatException nfe) {
+                        // It's the last token, so must be mode (usually "CBC").
+                        blockMode = tok.toUpperCase();
+                        if (blockMode.startsWith("EDE3")) {
+                            javaCipher = "DESede";
+                            blockMode = "ECB";
+                        } else if (blockMode.startsWith("EDE")) {
+                            javaCipher = "DESede";
+                            blockMode = "ECB";
+                            des2 = true;
+                        }
+                    }
+                }
+            }
+        }
+        if (javaCipher.startsWith("BF")) {
+            javaCipher = "Blowfish";
+        } else if (javaCipher.startsWith("TWOFISH")) {
+            javaCipher = "Twofish";
+            ivSize = 128;
+        } else if (javaCipher.startsWith("IDEA")) {
+            javaCipher = "IDEA";
+        } else if (javaCipher.startsWith("CAST6")) {
+            javaCipher = "CAST6";
+            ivSize = 128;
+        } else if (javaCipher.startsWith("CAST")) {
+            javaCipher = "CAST5";
+        } else if (javaCipher.startsWith("GOST")) {
+            keySize = 256;
+        } else if (javaCipher.startsWith("DESX")) {
+            javaCipher = "DESX";
+        } else if ("DES3".equals(javaCipher)) {
+            javaCipher = "DESede";
+        } else if ("DES2".equals(javaCipher)) {
+            javaCipher = "DESede";
+            des2 = true;
+        } else if (javaCipher.startsWith("RIJNDAEL")) {
+            javaCipher = "Rijndael";
+            ivSize = 128;
+        } else if (javaCipher.startsWith("SEED")) {
+            javaCipher = "SEED";
+            ivSize = 128;
+        } else if (javaCipher.startsWith("SERPENT")) {
+            javaCipher = "Serpent";
+            ivSize = 128;
+        } else if (javaCipher.startsWith("Skipjack")) {
+            javaCipher = "Skipjack";
+            ivSize = 128;
+        } else if (javaCipher.startsWith("RC6")) {
+            javaCipher = "RC6";
+            ivSize = 128;
+        } else if (javaCipher.startsWith("TEA")) {
+            javaCipher = "TEA";
+        } else if (javaCipher.startsWith("XTEA")) {
+            javaCipher = "XTEA";
+        } else if (javaCipher.startsWith("AES")) {
+            if (javaCipher.startsWith("AES128")) {
+                keySize = 128;
+            } else if (javaCipher.startsWith("AES192")) {
+                keySize = 192;
+            } else if (javaCipher.startsWith("AES256")) {
+                keySize = 256;
+            }
+            javaCipher = "AES";
+            ivSize = 128;
+        } else if (javaCipher.startsWith("CAMELLIA")) {
+            if (javaCipher.startsWith("CAMELLIA128")) {
+                keySize = 128;
+            } else if (javaCipher.startsWith("CAMELLIA192")) {
+                keySize = 192;
+            } else if (javaCipher.startsWith("CAMELLIA256")) {
+                keySize = 256;
+            }
+            javaCipher = "CAMELLIA";
+            ivSize = 128;
+        }
+        if (keySize == -1) {
+            if (javaCipher.startsWith("DESede")) {
+                keySize = 192;
+            } else if (javaCipher.startsWith("DES")) {
+                keySize = 64;
+            } else {
+                // RC2, RC4, RC5 and Blowfish ?
+                keySize = 128;
+            }
+        }
+        return new CipherInfo(javaCipher, blockMode, keySize, ivSize, des2);
+    }
+
+
+    /**
+     * @param args command line arguments: [password] [cipher] [file-to-decrypt]
+     *             <br>[cipher] == OpenSSL cipher name, e.g. "des3" or "des-ede3-cbc".
+     *             Try "man enc" on a unix box to see what's possible.
+     * @throws java.io.IOException              problems with the [file-to-decrypt]
+     * @throws java.security.GeneralSecurityException decryption problems
+     */
+    public static void main(String[] args)
+        throws IOException, GeneralSecurityException {
+        if (args.length < 3) {
+            System.out.println(Version.versionString());
+            System.out.println("Pure-java utility to decrypt files previously encrypted by \'openssl enc\'");
+            System.out.println();
+            System.out.println("Usage:  java -cp commons-ssl.jar org.apache.commons.ssl.OpenSSL [args]");
+            System.out.println("        [args]   == [password] [cipher] [file-to-decrypt]");
+            System.out.println("        [cipher] == des, des3, des-ede3-cbc, aes256, rc2, rc4, bf, bf-cbc, etc...");
+            System.out.println("                    Try 'man enc' on a unix box to see what's possible.");
+            System.out.println();
+            System.out.println("This utility can handle base64 or raw, salted or unsalted.");
+            System.out.println();
+            System.exit(1);
+        }
+        char[] password = args[0].toCharArray();
+
+        InputStream in = new FileInputStream(args[2]);
+        in = decrypt(args[1], password, in);
+
+        // in = encrypt( args[ 1 ], pwdAsBytes, in, true );
+
+        in = new BufferedInputStream(in);
+        BufferedOutputStream bufOut = new BufferedOutputStream(System.out);
+        Util.pipeStream(in, bufOut, false);
+        bufOut.flush();
+        System.out.flush();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/0a19b1d7/kerby-pkix/src/main/java/org/apache/commons/ssl/PBETestCreate.java
----------------------------------------------------------------------
diff --git a/kerby-pkix/src/main/java/org/apache/commons/ssl/PBETestCreate.java b/kerby-pkix/src/main/java/org/apache/commons/ssl/PBETestCreate.java
new file mode 100644
index 0000000..0aef858
--- /dev/null
+++ b/kerby-pkix/src/main/java/org/apache/commons/ssl/PBETestCreate.java
@@ -0,0 +1,79 @@
+package org.apache.commons.ssl;
+
+import org.apache.kerby.util.Util;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Properties;
+import java.util.TreeSet;
+
+/**
+ * @author Julius Davies
+ * @since 4-Jul-2007
+ */
+public class PBETestCreate {
+
+    public static void main(String[] args) throws Exception {
+        FileInputStream in = new FileInputStream(args[0]);
+        Properties p = new Properties();
+        p.load(in);
+        in.close();
+
+        String targetDir = p.getProperty("target");
+        File dir = new File(targetDir);
+        dir.mkdirs();
+        if (!dir.exists()) {
+            throw new IOException(dir.getCanonicalPath() + " doesn't exist!");
+        }
+
+        TreeSet ciphers = new TreeSet();
+        Iterator it = p.entrySet().iterator();
+        while (it.hasNext()) {
+            Map.Entry entry = (Map.Entry) it.next();
+            String key = (String) entry.getKey();
+            if (!"target".equalsIgnoreCase(key)) {
+                ciphers.add(key);
+                ciphers.add(key + "-cbc");
+                ciphers.add(key + "-cfb");
+                ciphers.add(key + "-cfb1");
+                ciphers.add(key + "-cfb8");
+                ciphers.add(key + "-ecb");
+                ciphers.add(key + "-ofb");
+            }
+        }
+
+        byte[] toEncrypt = "Hello World!".getBytes("UTF-8");
+        char[] pwd = "changeit".toCharArray();
+        it = ciphers.iterator();
+        while (it.hasNext()) {
+            String cipher = (String) it.next();
+            String cipherPadded = Util.pad(cipher, 15, false);
+            String fileNameBase64 = cipher + ".base64";
+            String fileNameRaw = cipher + ".raw";
+            String d = dir.getCanonicalPath() + "/";
+            try {
+                byte[] base64 = OpenSSL.encrypt(cipher, pwd, toEncrypt, true);
+                FileOutputStream out = new FileOutputStream(d + fileNameBase64);
+                out.write(base64);
+                out.close();
+            } catch (Exception e) {
+                System.err.println("FAILURE \t" + cipherPadded + "\t" + fileNameBase64 + "\t" + e);
+            }
+
+            try {
+                byte[] raw = OpenSSL.encrypt(cipher, pwd, toEncrypt, false);
+                FileOutputStream out = new FileOutputStream(d + fileNameRaw);
+                out.write(raw);
+                out.close();
+            } catch (Exception e) {
+                System.err.println("FAILURE \t" + cipherPadded + "\t" + fileNameRaw + "\t" + e);
+            }
+
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/0a19b1d7/kerby-pkix/src/main/java/org/apache/commons/ssl/PEMItem.java
----------------------------------------------------------------------
diff --git a/kerby-pkix/src/main/java/org/apache/commons/ssl/PEMItem.java b/kerby-pkix/src/main/java/org/apache/commons/ssl/PEMItem.java
new file mode 100644
index 0000000..846340a
--- /dev/null
+++ b/kerby-pkix/src/main/java/org/apache/commons/ssl/PEMItem.java
@@ -0,0 +1,106 @@
+/*
+ * $HeadURL: http://juliusdavies.ca/svn/not-yet-commons-ssl/tags/commons-ssl-0.3.16/src/java/org/apache/commons/ssl/PEMItem.java $
+ * $Revision: 121 $
+ * $Date: 2007-11-13 21:26:57 -0800 (Tue, 13 Nov 2007) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.commons.ssl;
+
+import org.apache.kerby.util.Hex;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.StringTokenizer;
+import java.util.TreeMap;
+
+/**
+ * @author Credit Union Central of British Columbia
+ * @author <a href="http://www.cucbc.com/">www.cucbc.com</a>
+ * @author <a href="mailto:juliusdavies@cucbc.com">juliusdavies@cucbc.com</a>
+ * @since 13-Aug-2006
+ */
+public class PEMItem {
+    public static final String DEK_INFO = "dek-info";
+
+    private final byte[] derBytes;
+    public final String pemType;
+    public final Map properties;
+
+    public final String dekInfo;
+    public final byte[] iv;
+    public final String cipher;
+    public final boolean des2;
+    public final String mode;
+    public final int keySizeInBits;
+
+    public PEMItem(byte[] derBytes, String type) {
+        this(derBytes, type, null);
+    }
+
+    public PEMItem(byte[] derBytes, String type, Map properties) {
+        this.derBytes = derBytes;
+        this.pemType = type;
+        if (properties == null) {
+            properties = new TreeMap(); // empty map
+        }
+        this.properties = Collections.unmodifiableMap(properties);
+        String di = (String) properties.get(DEK_INFO);
+        String diCipher = "";
+        String diIV = "";
+        if (di != null) {
+            StringTokenizer st = new StringTokenizer(di, ",");
+            if (st.hasMoreTokens()) {
+                diCipher = st.nextToken().trim().toLowerCase();
+            }
+            if (st.hasMoreTokens()) {
+                diIV = st.nextToken().trim().toLowerCase();
+            }
+        }
+        this.dekInfo = diCipher;
+        this.iv = Hex.decode(diIV);
+        if (!"".equals(diCipher)) {
+            OpenSSL.CipherInfo cipherInfo = OpenSSL.lookup(diCipher);
+            this.cipher = cipherInfo.javaCipher;
+            this.mode = cipherInfo.blockMode;
+            this.keySizeInBits = cipherInfo.keySize;
+            this.des2 = cipherInfo.des2;
+        } else {
+            this.mode = "";
+            cipher = "UNKNOWN";
+            keySizeInBits = -1;
+            des2 = false;
+        }
+    }
+
+    public byte[] getDerBytes() {
+        byte[] b = new byte[derBytes.length];
+        System.arraycopy(derBytes, 0, b, 0, derBytes.length);
+        return b;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/0a19b1d7/kerby-pkix/src/main/java/org/apache/commons/ssl/PEMUtil.java
----------------------------------------------------------------------
diff --git a/kerby-pkix/src/main/java/org/apache/commons/ssl/PEMUtil.java b/kerby-pkix/src/main/java/org/apache/commons/ssl/PEMUtil.java
new file mode 100644
index 0000000..a062d62
--- /dev/null
+++ b/kerby-pkix/src/main/java/org/apache/commons/ssl/PEMUtil.java
@@ -0,0 +1,259 @@
+/*
+ * $HeadURL: http://juliusdavies.ca/svn/not-yet-commons-ssl/tags/commons-ssl-0.3.16/src/java/org/apache/commons/ssl/PEMUtil.java $
+ * $Revision: 153 $
+ * $Date: 2009-09-15 22:40:53 -0700 (Tue, 15 Sep 2009) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.commons.ssl;
+
+import org.apache.kerby.util.Base64;
+import org.apache.kerby.util.ByteArrayReadLine;
+import org.apache.kerby.util.Util;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.PublicKey;
+import java.security.interfaces.DSAPublicKey;
+import java.security.interfaces.RSAPrivateCrtKey;
+import java.security.interfaces.RSAPublicKey;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Credit Union Central of British Columbia
+ * @author <a href="http://www.cucbc.com/">www.cucbc.com</a>
+ * @author <a href="mailto:juliusdavies@cucbc.com">juliusdavies@cucbc.com</a>
+ * @since 13-Aug-2006
+ */
+public class PEMUtil {
+    static final String LINE_SEPARATOR = System.getProperty("line.separator");
+
+    public static byte[] encode(Collection items) throws IOException {
+        final byte[] lineSeparatorBytes = LINE_SEPARATOR.getBytes("UTF-8");
+        ByteArrayOutputStream out = new ByteArrayOutputStream(8192);
+        Iterator it = items.iterator();
+        while (it.hasNext()) {
+            PEMItem item = (PEMItem) it.next();
+            out.write("-----BEGIN ".getBytes("UTF-8"));
+            out.write(item.pemType.getBytes("UTF-8"));
+            out.write("-----".getBytes("UTF-8"));
+            out.write(lineSeparatorBytes);
+
+            byte[] derBytes = item.getDerBytes();
+            ByteArrayInputStream bin = new ByteArrayInputStream(derBytes);
+            byte[] line = Util.streamToBytes(bin, 48);
+            while (line.length == 48) {
+                byte[] base64Line = Base64.encodeBase64(line);
+                out.write(base64Line);
+                out.write(lineSeparatorBytes);
+                line = Util.streamToBytes(bin, 48);
+            }
+            if (line.length > 0) {
+                byte[] base64Line = Base64.encodeBase64(line);
+                out.write(base64Line);
+                out.write(lineSeparatorBytes);
+            }
+            out.write("-----END ".getBytes("UTF-8"));
+            out.write(item.pemType.getBytes("UTF-8"));
+            out.write("-----".getBytes("UTF-8"));
+            out.write(lineSeparatorBytes);
+        }
+        return out.toByteArray();
+    }
+
+    public static List decode(byte[] pemBytes) {
+        LinkedList pemItems = new LinkedList();
+        ByteArrayInputStream in = new ByteArrayInputStream(pemBytes);
+        ByteArrayReadLine readLine = new ByteArrayReadLine(in);
+        String line = readLine.next();
+        while (line != null) {
+            int len = 0;
+            byte[] decoded;
+            ArrayList listOfByteArrays = new ArrayList(64);
+            Map properties = new HashMap();
+            String type = "[unknown]";
+            while (line != null && !beginBase64(line)) {
+                line = readLine.next();
+            }
+            if (line != null) {
+                String upperLine = line.toUpperCase();
+                int x = upperLine.indexOf("-BEGIN") + "-BEGIN".length();
+                int y = upperLine.indexOf("-", x);
+                type = upperLine.substring(x, y).trim();
+                line = readLine.next();
+            }
+            while (line != null && !endBase64(line)) {
+                line = Util.trim(line);
+                if (!"".equals(line)) {
+                    int x = line.indexOf(':');
+                    if (x > 0) {
+                        String k = line.substring(0, x).trim();
+                        String v = "";
+                        if (line.length() > x + 1) {
+                            v = line.substring(x + 1).trim();
+                        }
+                        properties.put(k.toLowerCase(), v.toLowerCase());
+                    } else {
+                        byte[] base64 = line.getBytes();
+                        byte[] rawBinary = Base64.decodeBase64(base64);
+                        listOfByteArrays.add(rawBinary);
+                        len += rawBinary.length;
+                    }
+                }
+                line = readLine.next();
+            }
+            if (line != null) {
+                line = readLine.next();
+            }
+
+            if (!listOfByteArrays.isEmpty()) {
+                decoded = new byte[len];
+                int pos = 0;
+                Iterator it = listOfByteArrays.iterator();
+                while (it.hasNext()) {
+                    byte[] oneLine = (byte[]) it.next();
+                    System.arraycopy(oneLine, 0, decoded, pos, oneLine.length);
+                    pos += oneLine.length;
+                }
+                PEMItem item = new PEMItem(decoded, type, properties);
+                pemItems.add(item);
+            }
+        }
+
+        // closing ByteArrayInputStream is a NO-OP
+        // in.close();
+
+        return pemItems;
+    }
+
+    private static boolean beginBase64(String line) {
+        line = line != null ? line.trim().toUpperCase() : "";
+        int x = line.indexOf("-BEGIN");
+        return x > 0 && startsAndEndsWithDashes(line);
+    }
+
+    private static boolean endBase64(String line) {
+        line = line != null ? line.trim().toUpperCase() : "";
+        int x = line.indexOf("-END");
+        return x > 0 && startsAndEndsWithDashes(line);
+    }
+
+    private static boolean startsAndEndsWithDashes(String line) {
+        line = Util.trim(line);
+        char c = line.charAt(0);
+        char d = line.charAt(line.length() - 1);
+        return c == '-' && d == '-';
+    }
+
+    public static String formatRSAPrivateKey(RSAPrivateCrtKey key) {
+        StringBuffer buf = new StringBuffer(2048);
+        buf.append("Private-Key:");
+        buf.append(LINE_SEPARATOR);
+        buf.append("modulus:");
+        buf.append(LINE_SEPARATOR);
+        buf.append(formatBigInteger(key.getModulus(), 129 * 2));
+        buf.append(LINE_SEPARATOR);
+        buf.append("publicExponent: ");
+        buf.append(key.getPublicExponent());
+        buf.append(LINE_SEPARATOR);
+        buf.append("privateExponent:");
+        buf.append(LINE_SEPARATOR);
+        buf.append(formatBigInteger(key.getPrivateExponent(), 128 * 2));
+        buf.append(LINE_SEPARATOR);
+        buf.append("prime1:");
+        buf.append(LINE_SEPARATOR);
+        buf.append(formatBigInteger(key.getPrimeP(), 65 * 2));
+        buf.append(LINE_SEPARATOR);
+        buf.append("prime2:");
+        buf.append(LINE_SEPARATOR);
+        buf.append(formatBigInteger(key.getPrimeQ(), 65 * 2));
+        buf.append(LINE_SEPARATOR);
+        buf.append("exponent1:");
+        buf.append(LINE_SEPARATOR);
+        buf.append(formatBigInteger(key.getPrimeExponentP(), 65 * 2));
+        buf.append(LINE_SEPARATOR);
+        buf.append("exponent2:");
+        buf.append(LINE_SEPARATOR);
+        buf.append(formatBigInteger(key.getPrimeExponentQ(), 65 * 2));
+        buf.append(LINE_SEPARATOR);
+        buf.append("coefficient:");
+        buf.append(LINE_SEPARATOR);
+        buf.append(formatBigInteger(key.getCrtCoefficient(), 65 * 2));
+        return buf.toString();
+    }
+
+    public static String formatBigInteger(BigInteger bi, int length) {
+        String s = bi.toString(16);
+        StringBuffer buf = new StringBuffer(s.length());
+        int zeroesToAppend = length - s.length();
+        int count = 0;
+        buf.append("    ");
+        for (int i = 0; i < zeroesToAppend; i++) {
+            count++;
+            buf.append('0');
+            if (i % 2 == 1) {
+                buf.append(':');
+            }
+        }
+        for (int i = 0; i < s.length() - 2; i++) {
+            count++;
+            buf.append(s.charAt(i));
+            if (i % 2 == 1) {
+                buf.append(':');
+            }
+            if (count % 30 == 0) {
+                buf.append(LINE_SEPARATOR);
+                buf.append("    ");
+            }
+        }
+        buf.append(s.substring(s.length() - 2));
+        return buf.toString();
+    }
+
+    public static String toPem(PublicKey key) throws IOException {
+        PEMItem item = null;
+        if (key instanceof RSAPublicKey) {
+            item = new PEMItem(key.getEncoded(), "PUBLIC KEY");
+        } else if (key instanceof DSAPublicKey) {
+            item = new PEMItem(key.getEncoded(), "PUBLIC KEY");
+        } else {
+            throw new IOException("Not an RSA or DSA key");
+        }
+        byte[] pem = encode(Collections.singleton(item));
+        return new String(pem, "UTF-8");
+    }
+
+}


Mime
View raw message