    001/*
    002 * $HeadURL: http://juliusdavies.ca/svn/not-yet-commons-ssl/tags/commons-ssl-0.3.15/src/java/org/apache/commons/ssl/KeyStoreBuilder.java $
    003 * $Revision: 176 $
    004 * $Date: 2014-09-07 11:36:28 -0700 (Sun, 07 Sep 2014) $
    005 *
    006 * ====================================================================
    007 * Licensed to the Apache Software Foundation (ASF) under one
    008 * or more contributor license agreements.  See the NOTICE file
    009 * distributed with this work for additional information
    010 * regarding copyright ownership.  The ASF licenses this file
    011 * to you under the Apache License, Version 2.0 (the
    012 * "License"); you may not use this file except in compliance
    013 * with the License.  You may obtain a copy of the License at
    014 *
    015 *   http://www.apache.org/licenses/LICENSE-2.0
    016 *
    017 * Unless required by applicable law or agreed to in writing,
    018 * software distributed under the License is distributed on an
    019 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    020 * KIND, either express or implied.  See the License for the
    021 * specific language governing permissions and limitations
    022 * under the License.
    023 * ====================================================================
    024 *
    025 * This software consists of voluntary contributions made by many
    026 * individuals on behalf of the Apache Software Foundation.  For more
    027 * information on the Apache Software Foundation, please see
    028 * <http://www.apache.org/>.
    029 *
    030 */
    031
    032package org.apache.commons.ssl;
    033
    034import org.apache.commons.ssl.asn1.ASN1EncodableVector;
    035import org.apache.commons.ssl.asn1.DERInteger;
    036import org.apache.commons.ssl.asn1.DERSequence;
    037
    038import java.io.ByteArrayInputStream;
    039import java.io.File;
    040import java.io.FileInputStream;
    041import java.io.FileOutputStream;
    042import java.io.IOException;
    043import java.math.BigInteger;
    044import java.security.GeneralSecurityException;
    045import java.security.InvalidKeyException;
    046import java.security.Key;
    047import java.security.KeyStore;
    048import java.security.KeyStoreException;
    049import java.security.NoSuchAlgorithmException;
    050import java.security.NoSuchProviderException;
    051import java.security.PrivateKey;
    052import java.security.PublicKey;
    053import java.security.UnrecoverableKeyException;
    054import java.security.cert.Certificate;
    055import java.security.cert.CertificateException;
    056import java.security.cert.CertificateFactory;
    057import java.security.cert.X509Certificate;
    058import java.security.interfaces.DSAParams;
    059import java.security.interfaces.DSAPrivateKey;
    060import java.security.interfaces.RSAPrivateCrtKey;
    061import java.security.interfaces.RSAPublicKey;
    062import java.util.Arrays;
    063import java.util.Collection;
    064import java.util.Collections;
    065import java.util.Enumeration;
    066import java.util.Iterator;
    067import java.util.LinkedList;
    068import java.util.List;
    069
    070/**
    071 * Builds Java Key Store files out of pkcs12 files, or out of pkcs8 files +
    072 * certificate chains.  Also supports OpenSSL style private keys (encrypted or
    073 * unencrypted).
    074 *
    075 * @author Credit Union Central of British Columbia
    076 * @author <a href="http://www.cucbc.com/">www.cucbc.com</a>
    077 * @author <a href="mailto:juliusdavies@cucbc.com">juliusdavies@cucbc.com</a>
    078 * @since 4-Nov-2006
    079 */
    080public class KeyStoreBuilder {
    081    private final static String PKCS7_ENCRYPTED = "1.2.840.113549.1.7.6";
    082
    083    public static KeyStore build(byte[] jksOrCerts, char[] password)
    084        throws IOException, CertificateException, KeyStoreException,
    085        NoSuchAlgorithmException, InvalidKeyException,
    086        NoSuchProviderException, ProbablyBadPasswordException,
    087        UnrecoverableKeyException {
    088        return build(jksOrCerts, null, password);
    089    }
    090
    091    public static KeyStore build(byte[] jksOrCerts, byte[] privateKey,
    092                                 char[] password)
    093        throws IOException, CertificateException, KeyStoreException,
    094        NoSuchAlgorithmException, InvalidKeyException,
    095        NoSuchProviderException, ProbablyBadPasswordException,
    096        UnrecoverableKeyException {
    097        return build(jksOrCerts, privateKey, password, null);
    098    }
    099
    100
    101    public static KeyStore build(byte[] jksOrCerts, byte[] privateKey,
    102                                 char[] jksPassword, char[] keyPassword)
    103        throws IOException, CertificateException, KeyStoreException,
    104        NoSuchAlgorithmException, InvalidKeyException,
    105        NoSuchProviderException, ProbablyBadPasswordException,
    106        UnrecoverableKeyException {
    107
    108        if (keyPassword == null || keyPassword.length <= 0) {
    109            keyPassword = jksPassword;
    110        }
    111
    112        BuildResult br1 = parse(jksOrCerts, jksPassword, keyPassword);
    113        BuildResult br2 = null;
    114        KeyStore jks = null;
    115        if (br1.jks != null) {
    116            jks = br1.jks;
    117        } else if (privateKey != null && privateKey.length > 0) {
    118            br2 = parse(privateKey, jksPassword, keyPassword);
    119            if (br2.jks != null) {
    120                jks = br2.jks;
    121            }
    122        }
    123
    124        // If we happened to find a JKS file, let's just return that.
    125        // JKS files get priority (in case some weirdo specifies both a PKCS12
    126        // and a JKS file!).
    127        if (jks != null) {
    128            // Make sure the keystore we found is not corrupt.
    129            br1 = validate(jks, keyPassword);
    130            if (br1 == null) {
    131                return jks;
    132            }
    133        }
    134
    135        List keys = br1.keys;
    136        List chains = br1.chains;
    137        boolean atLeastOneNotSet = keys == null || chains == null || keys.isEmpty() || chains.isEmpty();
    138        if (atLeastOneNotSet && br2 != null) {
    139            if (br2.keys != null && !br2.keys.isEmpty()) {
    140                // Notice that the key from build-result-2 gets priority over the
    141                // key from build-result-1 (if both had valid keys).
    142                keys = br2.keys;
    143            }
    144            if (chains == null || chains.isEmpty()) {
    145                chains = br2.chains;
    146            }
    147        }
    148
    149        atLeastOneNotSet = keys == null || chains == null || keys.isEmpty() || chains.isEmpty();
    150        if (atLeastOneNotSet) {
    151            String missing = "";
    152            if (keys == null) {
    153                missing = " [Private key missing (bad password?)]";
    154            }
    155            if (chains == null) {
    156                missing += " [Certificate chain missing]";
    157            }
    158            throw new KeyStoreException("Can't build keystore:" + missing);
    159        } else {
    160            KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
    161            ks.load(null, jksPassword);
    162            Iterator keysIt = keys.iterator();
    163            Iterator chainsIt = chains.iterator();
    164            int i = 1;
    165            while (keysIt.hasNext() && chainsIt.hasNext()) {
    166                Key key = (Key) keysIt.next();
    167                Certificate[] c = (Certificate[]) chainsIt.next();
    168                X509Certificate theOne = buildChain(key, c);
    169                String alias = "alias_" + i++;
    170                // The theOne is not null, then our chain was probably altered.
    171                // Need to trim out the newly introduced null entries at the end of
    172                // our chain.
    173                if (theOne != null) {
    174                    c = Certificates.trimChain(c);
    175                    alias = Certificates.getCN(theOne);
    176                    alias = alias.replace(' ', '_');
    177                }
    178                ks.setKeyEntry(alias, key, keyPassword, c);
    179            }
    180            return ks;
    181        }
    182    }
    183
    184    /**
    185     * Builds the chain up such that chain[ 0 ] contains the public key
    186     * corresponding to the supplied private key.
    187     *
    188     * @param key   private key
    189     * @param chain array of certificates to build chain from
    190     * @return theOne!
    191     * @throws KeyStoreException        no certificates correspond to private key
    192     * @throws CertificateException     java libraries complaining
    193     * @throws NoSuchAlgorithmException java libraries complaining
    194     * @throws InvalidKeyException      java libraries complaining
    195     * @throws NoSuchProviderException  java libraries complaining
    196     */
    197    public static X509Certificate buildChain(Key key, Certificate[] chain)
    198        throws CertificateException, KeyStoreException,
    199        NoSuchAlgorithmException, InvalidKeyException,
    200        NoSuchProviderException {
    201        X509Certificate theOne = null;
    202        if (key instanceof RSAPrivateCrtKey) {
    203            final RSAPrivateCrtKey rsa = (RSAPrivateCrtKey) key;
    204            BigInteger publicExponent = rsa.getPublicExponent();
    205            BigInteger modulus = rsa.getModulus();
    206            for (int i = 0; i < chain.length; i++) {
    207                X509Certificate c = (X509Certificate) chain[i];
    208                PublicKey pub = c.getPublicKey();
    209                if (pub instanceof RSAPublicKey) {
    210                    RSAPublicKey certKey = (RSAPublicKey) pub;
    211                    BigInteger pe = certKey.getPublicExponent();
    212                    BigInteger mod = certKey.getModulus();
    213                    if (publicExponent.equals(pe) && modulus.equals(mod)) {
    214                        theOne = c;
    215                    }
    216                }
    217            }
    218            if (theOne == null) {
    219                throw new KeyStoreException("Can't build keystore: [No certificates belong to the private-key]");
    220            }
    221            X509Certificate[] newChain;
    222            newChain = X509CertificateChainBuilder.buildPath(theOne, chain);
    223            Arrays.fill(chain, null);
    224            System.arraycopy(newChain, 0, chain, 0, newChain.length);
    225        }
    226        return theOne;
    227    }
    228
    229    public static BuildResult validate(KeyStore jks, char[] keyPass)
    230        throws CertificateException, KeyStoreException,
    231        NoSuchAlgorithmException, InvalidKeyException,
    232        NoSuchProviderException, UnrecoverableKeyException {
    233        Enumeration en = jks.aliases();
    234        boolean atLeastOneSuccess = false;
    235        boolean atLeastOneFailure = false;
    236
    237        List keys = new LinkedList();
    238        List chains = new LinkedList();
    239        while (en.hasMoreElements()) {
    240            String alias = (String) en.nextElement();
    241            if (jks.isKeyEntry(alias)) {
    242                try {
    243                    PrivateKey key = (PrivateKey) jks.getKey(alias, keyPass);
    244                    // No Exception thrown, so we're good!
    245                    atLeastOneSuccess = true;
    246                    Certificate[] chain = jks.getCertificateChain(alias);
    247                    X509Certificate[] c;
    248                    if (chain != null) {
    249                        c = Certificates.x509ifyChain(chain);
    250                        X509Certificate theOne = buildChain(key, c);
    251                        // The theOne is not null, then our chain was probably
    252                        // altered.  Need to trim out the newly introduced null
    253                        // entries at the end of our chain.
    254                        if (theOne != null) {
    255                            c = (X509Certificate[]) Certificates.trimChain(c);
    256                            jks.deleteEntry(alias);
    257                            jks.setKeyEntry(alias, key, keyPass, c);
    258                        }
    259                        keys.add(key);
    260                        chains.add(c);
    261                    }
    262                } catch (GeneralSecurityException gse) {
    263                    atLeastOneFailure = true;
    264                    // This is not the key you're looking for.
    265                }
    266            }
    267        }
    268        if (!atLeastOneSuccess) {
    269            throw new KeyStoreException("No private keys found in keystore!");
    270        }
    271        // The idea is a bit hacky:  if we return null, all is cool.  If
    272        // we return a list, we're telling upstairs to abandon the JKS and
    273        // build a new one from the BuildResults we provide.
    274        // (Sun's builtin SSL refuses to deal with keystores where not all
    275        // keys can be decrypted).
    276        return atLeastOneFailure ? new BuildResult(keys, chains, null) : null;
    277    }
    278
    279    public static class BuildResult {
    280        protected final List keys;
    281        protected final List chains;
    282        protected final KeyStore jks;
    283
    284        protected BuildResult(List keys, List chains, KeyStore jks) {
    285            if (keys == null || keys.isEmpty()) {
    286                this.keys = null;
    287            } else {
    288                this.keys = Collections.unmodifiableList(keys);
    289            }
    290            this.jks = jks;
    291            List x509Chains = new LinkedList();
    292            if (chains != null) {
    293                Iterator it = chains.iterator();
    294                while (it.hasNext()) {
    295                    Certificate[] chain = (Certificate[]) it.next();
    296                    if (chain != null && chain.length > 0) {
    297                        int len = chain.length;
    298                        X509Certificate[] x509 = new X509Certificate[len];
    299                        for (int i = 0; i < x509.length; i++) {
    300                            x509[i] = (X509Certificate) chain[i];
    301                        }
    302                        x509Chains.add(x509);
    303                    }
    304                }
    305            }
    306            if (x509Chains == null || x509Chains.isEmpty()) {
    307                this.chains = null;
    308            } else {
    309                this.chains = Collections.unmodifiableList(x509Chains);
    310            }
    311        }
    312    }
    313
    314
    315    public static BuildResult parse(byte[] stuff, char[] jksPass,
    316                                    char[] keyPass)
    317            throws IOException, CertificateException, KeyStoreException,
    318            ProbablyBadPasswordException {
    319
    320        return parse(stuff, jksPass, keyPass, false);
    321    }
    322
    323    static BuildResult parse(byte[] stuff, char[] jksPass,
    324                             char[] keyPass, boolean forTrustMaterial)
    325        throws IOException, CertificateException, KeyStoreException,
    326        ProbablyBadPasswordException {
    327        CertificateFactory cf = CertificateFactory.getInstance("X.509");
    328        Key key = null;
    329        Certificate[] chain = null;
    330        try {
    331            PKCS8Key pkcs8Key = new PKCS8Key(stuff, jksPass);
    332            key = pkcs8Key.getPrivateKey();
    333        }
    334        catch (ProbablyBadPasswordException pbpe) {
    335            throw pbpe;
    336        }
    337        catch (GeneralSecurityException gse) {
    338            // no luck
    339        }
    340
    341        List pemItems = PEMUtil.decode(stuff);
    342        Iterator it = pemItems.iterator();
    343        LinkedList certificates = new LinkedList();
    344        while (it.hasNext()) {
    345            PEMItem item = (PEMItem) it.next();
    346            byte[] derBytes = item.getDerBytes();
    347            String type = item.pemType.trim().toUpperCase();
    348            if (type.startsWith("CERT") ||
    349                type.startsWith("X509") ||
    350                type.startsWith("PKCS7")) {
    351                ByteArrayInputStream in = new ByteArrayInputStream(derBytes);
    352                X509Certificate c = (X509Certificate) cf.generateCertificate(in);
    353                certificates.add(c);
    354            }
    355            chain = toChain(certificates);
    356        }
    357
    358        if (chain != null || key != null) {
    359            List chains = chain != null ? Collections.singletonList(chain) : null;
    360            List keys = key != null ? Collections.singletonList(key) : null;
    361            return new BuildResult(keys, chains, null);
    362        }
    363
    364        boolean isProbablyPKCS12 = false;
    365        boolean isASN = false;
    366        ASN1Structure asn1 = null;
    367        try {
    368            asn1 = ASN1Util.analyze(stuff);
    369            isASN = true;
    370            isProbablyPKCS12 = asn1.oids.contains(PKCS7_ENCRYPTED);
    371            if (!isProbablyPKCS12 && asn1.bigPayload != null) {
    372                asn1 = ASN1Util.analyze(asn1.bigPayload);
    373                isProbablyPKCS12 = asn1.oids.contains(PKCS7_ENCRYPTED);
    374            }
    375        }
    376        catch (Exception e) {
    377            // isProbablyPKCS12 and isASN are set properly by now.
    378        }
    379
    380        ByteArrayInputStream stuffStream = new ByteArrayInputStream(stuff);
    381        // Try default keystore... then try others.
    382        BuildResult br = tryJKS(KeyStore.getDefaultType(), stuffStream, jksPass, keyPass, forTrustMaterial);
    383        if (br == null) {
    384            br = tryJKS("jks", stuffStream, jksPass, keyPass, forTrustMaterial);
    385            if (br == null) {
    386                br = tryJKS("jceks", stuffStream, jksPass, keyPass, forTrustMaterial);
    387                if (br == null) {
    388                    br = tryJKS("BKS", stuffStream, jksPass, keyPass, forTrustMaterial);
    389                    if (br == null) {
    390                        br = tryJKS("UBER", stuffStream, jksPass, keyPass, forTrustMaterial);
    391                    }
    392                }
    393            }
    394        }
    395        if (br != null) {
    396            return br;
    397        }
    398        if (isASN) {
    399            if (isProbablyPKCS12) {
    400                return tryJKS("pkcs12", stuffStream, jksPass, null, forTrustMaterial);
    401            }
    402        } else {
    403            // Okay, it's ASN.1, but it's not PKCS12.  Only one possible
    404            // interesting things remains:  X.509.
    405            stuffStream.reset();
    406
    407            try {
    408                certificates = new LinkedList();
    409                Collection certs = cf.generateCertificates(stuffStream);
    410                it = certs.iterator();
    411                while (it.hasNext()) {
    412                    X509Certificate x509 = (X509Certificate) it.next();
    413                    certificates.add(x509);
    414                }
    415                chain = toChain(certificates);
    416                if (chain != null && chain.length > 0) {
    417                    List chains = Collections.singletonList(chain);
    418                    return new BuildResult(null, chains, null);
    419                }
    420            }
    421            catch (CertificateException ce) {
    422                // oh well
    423            }
    424
    425            stuffStream.reset();
    426            // Okay, still no luck.  Maybe it's an ASN.1 DER stream
    427            // containing only a single certificate?  (I don't completely
    428            // trust CertificateFactory.generateCertificates).
    429            try {
    430                Certificate c = cf.generateCertificate(stuffStream);
    431                X509Certificate x509 = (X509Certificate) c;
    432                chain = toChain(Collections.singleton(x509));
    433                if (chain != null && chain.length > 0) {
    434                    List chains = Collections.singletonList(chain);
    435                    return new BuildResult(null, chains, null);
    436                }
    437            }
    438            catch (CertificateException ce) {
    439                // oh well
    440            }
    441        }
    442
    443        br = tryJKS("pkcs12", stuffStream, jksPass, null, forTrustMaterial);
    444        if (br != null) {
    445            // no exception thrown, so must be PKCS12.
    446            /*
    447            Hmm, well someone finally reported this bug!   And they want the library to be quiet....
    448            Commenting out for now, maybe investigate why it's happening one day....
    449
    450            System.out.println("Please report bug!");
    451            System.out.println("PKCS12 detection failed to realize this was PKCS12!");
    452            System.out.println(asn1);
    453            */
    454            return br;
    455        }
    456        throw new KeyStoreException("failed to extract any certificates or private keys - maybe bad password?");
    457    }
    458
    459    private static BuildResult tryJKS(
    460            String keystoreType, ByteArrayInputStream in, char[] jksPassword, char[] keyPassword,
    461            boolean forTrustMaterial
    462    ) throws ProbablyBadPasswordException {
    463        in.reset();
    464        if (keyPassword == null || keyPassword.length <= 0) {
    465            keyPassword = jksPassword;
    466        }
    467
    468        keystoreType = keystoreType.trim().toLowerCase();
    469        boolean isPKCS12 = "pkcs12".equalsIgnoreCase(keystoreType);
    470        try {
    471            Key key = null;
    472            Certificate[] chain = null;
    473            UnrecoverableKeyException uke = null;
    474            KeyStore jksKeyStore = KeyStore.getInstance(keystoreType);
    475            jksKeyStore.load(in, jksPassword);
    476            Enumeration en = jksKeyStore.aliases();
    477            while (en.hasMoreElements()) {
    478                String alias = (String) en.nextElement();
    479                if (jksKeyStore.isKeyEntry(alias)) {
    480                    try {
    481                        if (keyPassword != null) {
    482                            key = jksKeyStore.getKey(alias, keyPassword);
    483                        }
    484                        if (key instanceof PrivateKey) {
    485                            chain = jksKeyStore.getCertificateChain(alias);
    486                            break;
    487                        }
    488                    } catch (UnrecoverableKeyException e) {
    489                        uke = e;  // We might throw this one later.
    490                    } catch (GeneralSecurityException gse) {
    491                        // Swallow... keep looping.
    492                    }
    493                }
    494                if (isPKCS12 && en.hasMoreElements()) {
    495                    System.out.println("what kind of weird pkcs12 file has more than one alias?");
    496                }
    497            }
    498            if (key == null && uke != null) {
    499                // If we're trying to load KeyMaterial, then we *need* that key we spotted.
    500                // But if we're trying to load TrustMaterial, then we're fine, and we can ignore the key.
    501                if (!forTrustMaterial) {
    502                    throw new ProbablyBadPasswordException("Probably bad JKS-Key password: " + uke);
    503                }
    504            }
    505            if (isPKCS12) {
    506                // PKCS12 is supposed to be just a key and a chain, anyway.
    507                jksKeyStore = null;
    508            }
    509
    510            List keys = Collections.singletonList(key);
    511            List chains = Collections.singletonList(chain);
    512            return new BuildResult(keys, chains, jksKeyStore);
    513        }
    514        catch (ProbablyBadPasswordException pbpe) {
    515            throw pbpe;
    516        }
    517        catch (GeneralSecurityException gse) {
    518            // swallow it, return null
    519            return null;
    520        }
    521        catch (IOException ioe) {
    522            String msg = ioe.getMessage();
    523            msg = msg != null ? msg.trim().toLowerCase() : "";
    524            if (isPKCS12) {
    525                int x = msg.indexOf("failed to decrypt");
    526                int y = msg.indexOf("verify mac");
    527                x = Math.max(x, y);
    528                if (x >= 0) {
    529                    throw new ProbablyBadPasswordException("Probably bad PKCS12 password: " + ioe);
    530                }
    531            } else {
    532                int x = msg.indexOf("password");
    533                if (x >= 0) {
    534                    throw new ProbablyBadPasswordException("Probably bad JKS password: " + ioe);
    535                }
    536            }
    537            // swallow it, return null.
    538            return null;
    539        }
    540    }
    541
    542    private static X509Certificate[] toChain(Collection certs) {
    543        if (certs != null && !certs.isEmpty()) {
    544            X509Certificate[] x509Chain = new X509Certificate[certs.size()];
    545            certs.toArray(x509Chain);
    546            return x509Chain;
    547        } else {
    548            return null;
    549        }
    550    }
    551
    552
    553    public static void main(String[] args) throws Exception {
    554        if (args.length < 2) {
    555            System.out.println("KeyStoreBuilder:  creates '[alias].jks' (Java Key Store)");
    556            System.out.println("    -topk8 mode:  creates '[alias].pem' (x509 chain + unencrypted pkcs8)");
    557            System.out.println("[alias] will be set to the first CN value of the X509 certificate.");
    558            System.out.println("-------------------------------------------------------------------");
    559            System.out.println("Usage1: [password] [file:pkcs12]");
    560            System.out.println("Usage2: [password] [file:private-key] [file:certificate-chain]");
    561            System.out.println("Usage3: -topk8 [password] [file:jks]");
    562            System.out.println("-------------------------------------------------------------------");
    563            System.out.println("[private-key] can be openssl format, or pkcs8.");
    564            System.out.println("[password] decrypts [private-key], and also encrypts outputted JKS file.");
    565            System.out.println("All files can be PEM or DER.");
    566            System.exit(1);
    567        }
    568        char[] password = args[0].toCharArray();
    569        boolean toPKCS8 = false;
    570        if ("-topk8".equalsIgnoreCase(args[0])) {
    571            toPKCS8 = true;
    572            password = args[1].toCharArray();
    573            args[1] = args[2];
    574            args[2] = null;
    575        }
    576
    577        FileInputStream fin1 = new FileInputStream(args[1]);
    578        byte[] bytes1 = Util.streamToBytes(fin1);
    579        byte[] bytes2 = null;
    580        if (args.length > 2 && args[2] != null) {
    581            FileInputStream fin2 = new FileInputStream(args[2]);
    582            bytes2 = Util.streamToBytes(fin2);
    583        }
    584
    585        KeyStore ks = build(bytes1, bytes2, password);
    586        Enumeration en = ks.aliases();
    587        String alias = "keystorebuilder";
    588
    589        // We're going to assume that the biggest key is the one we want
    590        // to convert to PKCS8 (PEM).  That's until someone figures out a
    591        // better way to deal with this annoying situation (more than 1
    592        // key in the KeyStore).
    593        int biggestKey = 0;
    594        while (en.hasMoreElements()) {
    595            String s = (String) en.nextElement();
    596            try {
    597                PrivateKey pk = (PrivateKey) ks.getKey(s, password);
    598                byte[] encoded = pk.getEncoded();
    599                int len = encoded != null ? encoded.length : 0;
    600                if (len >= biggestKey) {
    601                    biggestKey = len;
    602                    alias = s;
    603                }
    604            } catch (Exception e) {
    605                // oh well, try next one.
    606            }
    607        }
    608
    609        String suffix = toPKCS8 ? ".pem" : ".jks";
    610        String fileName = alias;
    611        Certificate[] chain = ks.getCertificateChain(alias);
    612        if (chain != null && chain[0] != null) {
    613            String cn = Certificates.getCN((X509Certificate) chain[0]);
    614            cn = cn != null ? cn.trim() : "";
    615            if (!"".equals(cn)) {
    616                fileName = cn;
    617            }
    618        }
    619
    620        File f = new File(fileName + suffix);
    621        int count = 1;
    622        while (f.exists()) {
    623            f = new File(alias + "_" + count + suffix);
    624            count++;
    625        }
    626
    627        FileOutputStream fout = new FileOutputStream(f);
    628        if (toPKCS8) {
    629            List pemItems = new LinkedList();
    630            PrivateKey key = (PrivateKey) ks.getKey(alias, password);
    631            chain = ks.getCertificateChain(alias);
    632            byte[] pkcs8DerBytes = null;
    633            if (key instanceof RSAPrivateCrtKey) {
    634                RSAPrivateCrtKey rsa = (RSAPrivateCrtKey) key;
    635                ASN1EncodableVector vec = new ASN1EncodableVector();
    636                vec.add(new DERInteger(BigInteger.ZERO));
    637                vec.add(new DERInteger(rsa.getModulus()));
    638                vec.add(new DERInteger(rsa.getPublicExponent()));
    639                vec.add(new DERInteger(rsa.getPrivateExponent()));
    640                vec.add(new DERInteger(rsa.getPrimeP()));
    641                vec.add(new DERInteger(rsa.getPrimeQ()));
    642                vec.add(new DERInteger(rsa.getPrimeExponentP()));
    643                vec.add(new DERInteger(rsa.getPrimeExponentQ()));
    644                vec.add(new DERInteger(rsa.getCrtCoefficient()));
    645                DERSequence seq = new DERSequence(vec);
    646                byte[] derBytes = PKCS8Key.encode(seq);
    647                PKCS8Key pkcs8 = new PKCS8Key(derBytes, null);
    648                pkcs8DerBytes = pkcs8.getDecryptedBytes();
    649            } else if (key instanceof DSAPrivateKey) {
    650                DSAPrivateKey dsa = (DSAPrivateKey) key;
    651                DSAParams params = dsa.getParams();
    652                BigInteger g = params.getG();
    653                BigInteger p = params.getP();
    654                BigInteger q = params.getQ();
    655                BigInteger x = dsa.getX();
    656                BigInteger y = q.modPow(x, p);
    657
    658                ASN1EncodableVector vec = new ASN1EncodableVector();
    659                vec.add(new DERInteger(BigInteger.ZERO));
    660                vec.add(new DERInteger(p));
    661                vec.add(new DERInteger(q));
    662                vec.add(new DERInteger(g));
    663                vec.add(new DERInteger(y));
    664                vec.add(new DERInteger(x));
    665                DERSequence seq = new DERSequence(vec);
    666                byte[] derBytes = PKCS8Key.encode(seq);
    667                PKCS8Key pkcs8 = new PKCS8Key(derBytes, null);
    668                pkcs8DerBytes = pkcs8.getDecryptedBytes();
    669            }
    670            if (chain != null && chain.length > 0) {
    671                for (int i = 0; i < chain.length; i++) {
    672                    X509Certificate x509 = (X509Certificate) chain[i];
    673                    byte[] derBytes = x509.getEncoded();
    674                    PEMItem item = new PEMItem(derBytes, "CERTIFICATE");
    675                    pemItems.add(item);
    676                }
    677            }
    678            if (pkcs8DerBytes != null) {
    679                PEMItem item = new PEMItem(pkcs8DerBytes, "PRIVATE KEY");
    680                pemItems.add(item);
    681            }
    682            byte[] pem = PEMUtil.encode(pemItems);
    683            fout.write(pem);
    684        } else {
    685            // If we're not converting to unencrypted PKCS8 style PEM,
    686            // then we are converting to Sun JKS.  It happens right here:
    687            KeyStore jks = KeyStore.getInstance(KeyStore.getDefaultType());
    688            jks.load(null, password);
    689            jks.setKeyEntry(alias, ks.getKey(alias, password), password, ks.getCertificateChain(alias));
    690            jks.store(fout, password);
    691        }
    692        fout.flush();
    693        fout.close();
    694        System.out.println("Successfuly wrote: [" + f.getPath() + "]");
    695    }
    696
    697
    698}