001    /*
002     * $HeadURL: http://juliusdavies.ca/svn/not-yet-commons-ssl/tags/commons-ssl-0.3.11/src/java/org/apache/commons/ssl/KeyStoreBuilder.java $
003     * $Revision: 154 $
004     * $Date: 2009-09-16 22:18:47 -0700 (Wed, 16 Sep 2009) $
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    
032    package org.apache.commons.ssl;
033    
034    import org.apache.commons.ssl.asn1.ASN1EncodableVector;
035    import org.apache.commons.ssl.asn1.DERInteger;
036    import org.apache.commons.ssl.asn1.DERSequence;
037    
038    import java.io.ByteArrayInputStream;
039    import java.io.File;
040    import java.io.FileInputStream;
041    import java.io.FileOutputStream;
042    import java.io.IOException;
043    import java.math.BigInteger;
044    import java.security.GeneralSecurityException;
045    import java.security.InvalidKeyException;
046    import java.security.Key;
047    import java.security.KeyStore;
048    import java.security.KeyStoreException;
049    import java.security.NoSuchAlgorithmException;
050    import java.security.NoSuchProviderException;
051    import java.security.PrivateKey;
052    import java.security.PublicKey;
053    import java.security.UnrecoverableKeyException;
054    import java.security.cert.Certificate;
055    import java.security.cert.CertificateException;
056    import java.security.cert.CertificateFactory;
057    import java.security.cert.X509Certificate;
058    import java.security.interfaces.DSAParams;
059    import java.security.interfaces.DSAPrivateKey;
060    import java.security.interfaces.RSAPrivateCrtKey;
061    import java.security.interfaces.RSAPublicKey;
062    import java.util.Arrays;
063    import java.util.Collection;
064    import java.util.Collections;
065    import java.util.Enumeration;
066    import java.util.Iterator;
067    import java.util.LinkedList;
068    import 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     */
080    public 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            CertificateFactory cf = CertificateFactory.getInstance("X.509");
320            Key key = null;
321            Certificate[] chain = null;
322            try {
323                PKCS8Key pkcs8Key = new PKCS8Key(stuff, jksPass);
324                key = pkcs8Key.getPrivateKey();
325            }
326            catch (ProbablyBadPasswordException pbpe) {
327                throw pbpe;
328            }
329            catch (GeneralSecurityException gse) {
330                // no luck
331            }
332    
333            List pemItems = PEMUtil.decode(stuff);
334            Iterator it = pemItems.iterator();
335            LinkedList certificates = new LinkedList();
336            while (it.hasNext()) {
337                PEMItem item = (PEMItem) it.next();
338                byte[] derBytes = item.getDerBytes();
339                String type = item.pemType.trim().toUpperCase();
340                if (type.startsWith("CERT") ||
341                    type.startsWith("X509") ||
342                    type.startsWith("PKCS7")) {
343                    ByteArrayInputStream in = new ByteArrayInputStream(derBytes);
344                    X509Certificate c = (X509Certificate) cf.generateCertificate(in);
345                    certificates.add(c);
346                }
347                chain = toChain(certificates);
348            }
349    
350            if (chain != null || key != null) {
351                List chains = chain != null ? Collections.singletonList(chain) : null;
352                List keys = key != null ? Collections.singletonList(key) : null;
353                return new BuildResult(keys, chains, null);
354            }
355    
356            boolean isProbablyPKCS12 = false;
357            boolean isASN = false;
358            ASN1Structure asn1 = null;
359            try {
360                asn1 = ASN1Util.analyze(stuff);
361                isASN = true;
362                isProbablyPKCS12 = asn1.oids.contains(PKCS7_ENCRYPTED);
363                if (!isProbablyPKCS12 && asn1.bigPayload != null) {
364                    asn1 = ASN1Util.analyze(asn1.bigPayload);
365                    isProbablyPKCS12 = asn1.oids.contains(PKCS7_ENCRYPTED);
366                }
367            }
368            catch (Exception e) {
369                // isProbablyPKCS12 and isASN are set properly by now.
370            }
371    
372            ByteArrayInputStream stuffStream = new ByteArrayInputStream(stuff);
373            // Try default keystore... then try others.
374            BuildResult br = tryJKS(KeyStore.getDefaultType(), stuffStream, jksPass, keyPass);
375            if (br == null) {
376                br = tryJKS("jks", stuffStream, jksPass, keyPass);
377                if (br == null) {
378                    br = tryJKS("jceks", stuffStream, jksPass, keyPass);
379                    if (br == null) {
380                        br = tryJKS("BKS", stuffStream, jksPass, keyPass);
381                        if (br == null) {
382                            br = tryJKS("UBER", stuffStream, jksPass, keyPass);
383                        }
384                    }
385                }
386            }
387            if (br != null) {
388                return br;
389            }
390            if (isASN) {
391                if (isProbablyPKCS12) {
392                    return tryJKS("pkcs12", stuffStream, jksPass, null);
393                }
394            } else {
395                // Okay, it's ASN.1, but it's not PKCS12.  Only one possible
396                // interesting things remains:  X.509.
397                stuffStream.reset();
398    
399                try {
400                    certificates = new LinkedList();
401                    Collection certs = cf.generateCertificates(stuffStream);
402                    it = certs.iterator();
403                    while (it.hasNext()) {
404                        X509Certificate x509 = (X509Certificate) it.next();
405                        certificates.add(x509);
406                    }
407                    chain = toChain(certificates);
408                    if (chain != null && chain.length > 0) {
409                        List chains = Collections.singletonList(chain);
410                        return new BuildResult(null, chains, null);
411                    }
412                }
413                catch (CertificateException ce) {
414                    // oh well
415                }
416    
417                stuffStream.reset();
418                // Okay, still no luck.  Maybe it's an ASN.1 DER stream
419                // containing only a single certificate?  (I don't completely
420                // trust CertificateFactory.generateCertificates).
421                try {
422                    Certificate c = cf.generateCertificate(stuffStream);
423                    X509Certificate x509 = (X509Certificate) c;
424                    chain = toChain(Collections.singleton(x509));
425                    if (chain != null && chain.length > 0) {
426                        List chains = Collections.singletonList(chain);
427                        return new BuildResult(null, chains, null);
428                    }
429                }
430                catch (CertificateException ce) {
431                    // oh well
432                }
433            }
434    
435            br = tryJKS("pkcs12", stuffStream, jksPass, null);
436            if (br != null) {
437                // no exception thrown, so must be PKCS12.
438                System.out.println("Please report bug!");
439                System.out.println("PKCS12 detection failed to realize this was PKCS12!");
440                System.out.println(asn1);
441                return br;
442            }
443            throw new KeyStoreException("failed to extract any certificates or private keys - maybe bad password?");
444        }
445    
446        private static BuildResult tryJKS(String keystoreType,
447                                          ByteArrayInputStream in,
448                                          char[] jksPassword, char[] keyPassword)
449            throws ProbablyBadPasswordException {
450            in.reset();
451            if (keyPassword == null || keyPassword.length <= 0) {
452                keyPassword = jksPassword;
453            }
454    
455            keystoreType = keystoreType.trim().toLowerCase();
456            boolean isPKCS12 = "pkcs12".equalsIgnoreCase(keystoreType);
457            try {
458                Key key = null;
459                Certificate[] chain = null;
460                UnrecoverableKeyException uke = null;
461                KeyStore jksKeyStore = KeyStore.getInstance(keystoreType);
462                jksKeyStore.load(in, jksPassword);
463                Enumeration en = jksKeyStore.aliases();
464                while (en.hasMoreElements()) {
465                    String alias = (String) en.nextElement();
466                    if (jksKeyStore.isKeyEntry(alias)) {
467                        try {
468                            key = jksKeyStore.getKey(alias, keyPassword);
469                            if (key != null && key instanceof PrivateKey) {
470                                chain = jksKeyStore.getCertificateChain(alias);
471                                break;
472                            }
473                        } catch (UnrecoverableKeyException e) {
474                            uke = e;  // We might throw this one later. 
475                        } catch (GeneralSecurityException gse) {
476                            // Swallow... keep looping.
477                        }
478                    }
479                    if (isPKCS12 && en.hasMoreElements()) {
480                        System.out.println("what kind of weird pkcs12 file has more than one alias?");
481                    }
482                }
483                if (key == null && uke != null) {
484                    throw new ProbablyBadPasswordException("Probably bad JKS-Key password: " + uke);
485                }
486                if (isPKCS12) {
487                    // PKCS12 is supposed to be just a key and a chain, anyway.
488                    jksKeyStore = null;
489                }
490    
491                List keys = Collections.singletonList(key);
492                List chains = Collections.singletonList(chain);
493                return new BuildResult(keys, chains, jksKeyStore);
494            }
495            catch (ProbablyBadPasswordException pbpe) {
496                throw pbpe;
497            }
498            catch (GeneralSecurityException gse) {
499                // swallow it, return null
500                return null;
501            }
502            catch (IOException ioe) {
503                String msg = ioe.getMessage();
504                msg = msg != null ? msg.trim().toLowerCase() : "";
505                if (isPKCS12) {
506                    int x = msg.indexOf("failed to decrypt");
507                    int y = msg.indexOf("verify mac");
508                    x = Math.max(x, y);
509                    if (x >= 0) {
510                        throw new ProbablyBadPasswordException("Probably bad PKCS12 password: " + ioe);
511                    }
512                } else {
513                    int x = msg.indexOf("password");
514                    if (x >= 0) {
515                        throw new ProbablyBadPasswordException("Probably bad JKS password: " + ioe);
516                    }
517                }
518                // swallow it, return null.
519                return null;
520            }
521        }
522    
523        private static X509Certificate[] toChain(Collection certs) {
524            if (certs != null && !certs.isEmpty()) {
525                X509Certificate[] x509Chain = new X509Certificate[certs.size()];
526                certs.toArray(x509Chain);
527                return x509Chain;
528            } else {
529                return null;
530            }
531        }
532    
533    
534        public static void main(String[] args) throws Exception {
535            if (args.length < 2) {
536                System.out.println("KeyStoreBuilder:  creates '[alias].jks' (Java Key Store)");
537                System.out.println("    -topk8 mode:  creates '[alias].pem' (x509 chain + unencrypted pkcs8)");
538                System.out.println("[alias] will be set to the first CN value of the X509 certificate.");
539                System.out.println("-------------------------------------------------------------------");
540                System.out.println("Usage1: [password] [file:pkcs12]");
541                System.out.println("Usage2: [password] [file:private-key] [file:certificate-chain]");
542                System.out.println("Usage3: -topk8 [password] [file:jks]");
543                System.out.println("-------------------------------------------------------------------");
544                System.out.println("[private-key] can be openssl format, or pkcs8.");
545                System.out.println("[password] decrypts [private-key], and also encrypts outputted JKS file.");
546                System.out.println("All files can be PEM or DER.");
547                System.exit(1);
548            }
549            char[] password = args[0].toCharArray();
550            boolean toPKCS8 = false;
551            if ("-topk8".equalsIgnoreCase(args[0])) {
552                toPKCS8 = true;
553                password = args[1].toCharArray();
554                args[1] = args[2];
555                args[2] = null;
556            }
557    
558            FileInputStream fin1 = new FileInputStream(args[1]);
559            byte[] bytes1 = Util.streamToBytes(fin1);
560            byte[] bytes2 = null;
561            if (args.length > 2 && args[2] != null) {
562                FileInputStream fin2 = new FileInputStream(args[2]);
563                bytes2 = Util.streamToBytes(fin2);
564            }
565    
566            KeyStore ks = build(bytes1, bytes2, password);
567            Enumeration en = ks.aliases();
568            String alias = "keystorebuilder";
569    
570            // We're going to assume that the biggest key is the one we want
571            // to convert to PKCS8 (PEM).  That's until someone figures out a
572            // better way to deal with this annoying situation (more than 1
573            // key in the KeyStore).
574            int biggestKey = 0;
575            while (en.hasMoreElements()) {
576                String s = (String) en.nextElement();
577                try {
578                    PrivateKey pk = (PrivateKey) ks.getKey(s, password);
579                    byte[] encoded = pk.getEncoded();
580                    int len = encoded != null ? encoded.length : 0;
581                    if (len >= biggestKey) {
582                        biggestKey = len;
583                        alias = s;
584                    }
585                } catch (Exception e) {
586                    // oh well, try next one.
587                }
588            }
589    
590            String suffix = toPKCS8 ? ".pem" : ".jks";
591            String fileName = alias;
592            Certificate[] chain = ks.getCertificateChain(alias);
593            if (chain != null && chain[0] != null) {
594                String cn = Certificates.getCN((X509Certificate) chain[0]);
595                cn = cn != null ? cn.trim() : "";
596                if (!"".equals(cn)) {
597                    fileName = cn;
598                }
599            }
600    
601            File f = new File(fileName + suffix);
602            int count = 1;
603            while (f.exists()) {
604                f = new File(alias + "_" + count + suffix);
605                count++;
606            }
607    
608            FileOutputStream fout = new FileOutputStream(f);
609            if (toPKCS8) {
610                List pemItems = new LinkedList();
611                PrivateKey key = (PrivateKey) ks.getKey(alias, password);
612                chain = ks.getCertificateChain(alias);
613                byte[] pkcs8DerBytes = null;
614                if (key instanceof RSAPrivateCrtKey) {
615                    RSAPrivateCrtKey rsa = (RSAPrivateCrtKey) key;
616                    ASN1EncodableVector vec = new ASN1EncodableVector();
617                    vec.add(new DERInteger(BigInteger.ZERO));
618                    vec.add(new DERInteger(rsa.getModulus()));
619                    vec.add(new DERInteger(rsa.getPublicExponent()));
620                    vec.add(new DERInteger(rsa.getPrivateExponent()));
621                    vec.add(new DERInteger(rsa.getPrimeP()));
622                    vec.add(new DERInteger(rsa.getPrimeQ()));
623                    vec.add(new DERInteger(rsa.getPrimeExponentP()));
624                    vec.add(new DERInteger(rsa.getPrimeExponentQ()));
625                    vec.add(new DERInteger(rsa.getCrtCoefficient()));
626                    DERSequence seq = new DERSequence(vec);
627                    byte[] derBytes = PKCS8Key.encode(seq);
628                    PKCS8Key pkcs8 = new PKCS8Key(derBytes, null);
629                    pkcs8DerBytes = pkcs8.getDecryptedBytes();
630                } else if (key instanceof DSAPrivateKey) {
631                    DSAPrivateKey dsa = (DSAPrivateKey) key;
632                    DSAParams params = dsa.getParams();
633                    BigInteger g = params.getG();
634                    BigInteger p = params.getP();
635                    BigInteger q = params.getQ();
636                    BigInteger x = dsa.getX();
637                    BigInteger y = q.modPow(x, p);
638    
639                    ASN1EncodableVector vec = new ASN1EncodableVector();
640                    vec.add(new DERInteger(BigInteger.ZERO));
641                    vec.add(new DERInteger(p));
642                    vec.add(new DERInteger(q));
643                    vec.add(new DERInteger(g));
644                    vec.add(new DERInteger(y));
645                    vec.add(new DERInteger(x));
646                    DERSequence seq = new DERSequence(vec);
647                    byte[] derBytes = PKCS8Key.encode(seq);
648                    PKCS8Key pkcs8 = new PKCS8Key(derBytes, null);
649                    pkcs8DerBytes = pkcs8.getDecryptedBytes();
650                }
651                if (chain != null && chain.length > 0) {
652                    for (int i = 0; i < chain.length; i++) {
653                        X509Certificate x509 = (X509Certificate) chain[i];
654                        byte[] derBytes = x509.getEncoded();
655                        PEMItem item = new PEMItem(derBytes, "CERTIFICATE");
656                        pemItems.add(item);
657                    }
658                }
659                if (pkcs8DerBytes != null) {
660                    PEMItem item = new PEMItem(pkcs8DerBytes, "PRIVATE KEY");
661                    pemItems.add(item);
662                }
663                byte[] pem = PEMUtil.encode(pemItems);
664                fout.write(pem);
665            } else {
666                // If we're not converting to unencrypted PKCS8 style PEM,
667                // then we are converting to Sun JKS.  It happens right here:
668                KeyStore jks = KeyStore.getInstance(KeyStore.getDefaultType());
669                jks.load(null, password);
670                jks.setKeyEntry(alias, ks.getKey(alias, password), password, ks.getCertificateChain(alias));
671                jks.store(fout, password);
672            }
673            fout.flush();
674            fout.close();
675            System.out.println("Successfuly wrote: [" + f.getPath() + "]");
676        }
677    
678    
679    }