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}