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
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        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}