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