001/*
002 * $HeadURL: file:///opt/dev/not-yet-commons-ssl-SVN-repo/tags/commons-ssl-0.3.17/src/java/org/apache/commons/ssl/PKCS8Key.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 java.io.ByteArrayInputStream;
035import java.io.ByteArrayOutputStream;
036import java.io.File;
037import java.io.FileInputStream;
038import java.io.IOException;
039import java.io.InputStream;
040import java.math.BigInteger;
041import java.security.GeneralSecurityException;
042import java.security.InvalidAlgorithmParameterException;
043import java.security.InvalidKeyException;
044import java.security.KeyFactory;
045import java.security.MessageDigest;
046import java.security.NoSuchAlgorithmException;
047import java.security.PrivateKey;
048import java.security.PublicKey;
049import java.security.interfaces.DSAParams;
050import java.security.interfaces.DSAPrivateKey;
051import java.security.interfaces.RSAPrivateCrtKey;
052import java.security.spec.DSAPublicKeySpec;
053import java.security.spec.KeySpec;
054import java.security.spec.PKCS8EncodedKeySpec;
055import java.security.spec.RSAPublicKeySpec;
056import java.util.Arrays;
057import java.util.Collections;
058import java.util.Iterator;
059import java.util.List;
060
061import javax.crypto.BadPaddingException;
062import javax.crypto.Cipher;
063import javax.crypto.IllegalBlockSizeException;
064import javax.crypto.Mac;
065import javax.crypto.NoSuchPaddingException;
066import javax.crypto.SecretKey;
067import javax.crypto.spec.IvParameterSpec;
068import javax.crypto.spec.RC2ParameterSpec;
069import javax.crypto.spec.RC5ParameterSpec;
070import javax.crypto.spec.SecretKeySpec;
071
072import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1Encodable;
073import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1EncodableVector;
074import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1Integer;
075import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1ObjectIdentifier;
076import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1OutputStream;
077import org.apache.commons.ssl.org.bouncycastle.asn1.DERNull;
078import org.apache.commons.ssl.org.bouncycastle.asn1.DEROctetString;
079import org.apache.commons.ssl.org.bouncycastle.asn1.DERSequence;
080
081/**
082 * Utility for decrypting PKCS8 private keys.  Way easier to use than
083 * javax.crypto.EncryptedPrivateKeyInfo since all you need is the byte[] array
084 * and the password.  You don't need to know anything else about the PKCS8
085 * key you pass in.
086 * </p><p>
087 * Can handle base64 PEM, or raw DER.
088 * Can handle PKCS8 Version 1.5 and 2.0.
089 * Can also handle OpenSSL encrypted or unencrypted private keys (DSA or RSA).
090 * </p><p>
091 * The PKCS12 key derivation (the "pkcs12()" method) comes from BouncyCastle.
092 * </p>
093 *
094 * @author Credit Union Central of British Columbia
095 * @author <a href="http://www.cucbc.com/">www.cucbc.com</a>
096 * @author <a href="mailto:juliusdavies@cucbc.com">juliusdavies@cucbc.com</a>
097 * @author <a href="bouncycastle.org">bouncycastle.org</a>
098 * @since 7-Nov-2006
099 */
100public class PKCS8Key {
101    public final static String RSA_OID = "1.2.840.113549.1.1.1";
102    public final static String DSA_OID = "1.2.840.10040.4.1";
103
104    public final static String PKCS8_UNENCRYPTED = "PRIVATE KEY";
105    public final static String PKCS8_ENCRYPTED = "ENCRYPTED PRIVATE KEY";
106    public final static String OPENSSL_RSA = "RSA PRIVATE KEY";
107    public final static String OPENSSL_DSA = "DSA PRIVATE KEY";
108
109    private final PrivateKey privateKey;
110    private final byte[] decryptedBytes;
111    private final String transformation;
112    private final int keySize;
113    private final boolean isDSA;
114    private final boolean isRSA;
115
116    static {
117        JavaImpl.load();
118    }
119
120    /**
121     * @param in       pkcs8 file to parse (pem or der, encrypted or unencrypted)
122     * @param password password to decrypt the pkcs8 file.  Ignored if the
123     *                 supplied pkcs8 is already unencrypted.
124     * @throws GeneralSecurityException If a parsing or decryption problem
125     *                                  occured.
126     * @throws IOException              If the supplied InputStream could not be read.
127     */
128    public PKCS8Key(final InputStream in, char[] password)
129        throws GeneralSecurityException, IOException {
130        this(Util.streamToBytes(in), password);
131    }
132
133    /**
134     * @param in       pkcs8 file to parse (pem or der, encrypted or unencrypted)
135     * @param password password to decrypt the pkcs8 file.  Ignored if the
136     *                 supplied pkcs8 is already unencrypted.
137     * @throws GeneralSecurityException If a parsing or decryption problem
138     *                                  occured.
139     */
140    public PKCS8Key(final ByteArrayInputStream in, char[] password)
141        throws GeneralSecurityException {
142        this(Util.streamToBytes(in), password);
143    }
144
145    /**
146     * @param encoded  pkcs8 file to parse (pem or der, encrypted or unencrypted)
147     * @param password password to decrypt the pkcs8 file.  Ignored if the
148     *                 supplied pkcs8 is already unencrypted.
149     * @throws GeneralSecurityException If a parsing or decryption problem
150     *                                  occured.
151     */
152    public PKCS8Key(final byte[] encoded, char[] password)
153        throws GeneralSecurityException {
154        DecryptResult decryptResult =
155            new DecryptResult("UNENCRYPTED", 0, encoded);
156
157        List pemItems = PEMUtil.decode(encoded);
158        PEMItem keyItem = null;
159        byte[] derBytes = null;
160        if (pemItems.isEmpty()) {
161            // must be DER encoded - PEMUtil wasn't able to extract anything.
162            derBytes = encoded;
163        } else {
164            Iterator it = pemItems.iterator();
165            boolean opensslRSA = false;
166            boolean opensslDSA = false;
167
168            while (it.hasNext()) {
169                PEMItem item = (PEMItem) it.next();
170                String type = item.pemType.trim().toUpperCase();
171                boolean plainPKCS8 = type.startsWith(PKCS8_UNENCRYPTED);
172                boolean encryptedPKCS8 = type.startsWith(PKCS8_ENCRYPTED);
173                boolean rsa = type.startsWith(OPENSSL_RSA);
174                boolean dsa = type.startsWith(OPENSSL_DSA);
175                if (plainPKCS8 || encryptedPKCS8 || rsa || dsa) {
176                    opensslRSA = opensslRSA || rsa;
177                    opensslDSA = opensslDSA || dsa;
178                    if (derBytes != null) {
179                        throw new ProbablyNotPKCS8Exception("More than one pkcs8 or OpenSSL key found in the supplied PEM Base64 stream");
180                    }
181                    derBytes = item.getDerBytes();
182                    keyItem = item;
183                    decryptResult = new DecryptResult("UNENCRYPTED", 0, derBytes);
184                }
185            }
186            // after the loop is finished, did we find anything?
187            if (derBytes == null) {
188                throw new ProbablyNotPKCS8Exception("No pkcs8 or OpenSSL key found in the supplied PEM Base64 stream");
189            }
190
191            if (opensslDSA || opensslRSA) {
192                String c = keyItem.cipher.trim();
193                boolean encrypted = !"UNKNOWN".equals(c) && !"".equals(c);
194                if (encrypted) {
195                    decryptResult = opensslDecrypt(keyItem, password);
196                }
197
198                String oid = RSA_OID;
199                if (opensslDSA) {
200                    oid = DSA_OID;
201                }
202                derBytes = formatAsPKCS8(decryptResult.bytes, oid, null);
203
204                String tf = decryptResult.transformation;
205                int ks = decryptResult.keySize;
206                decryptResult = new DecryptResult(tf, ks, derBytes);
207            }
208        }
209
210        ASN1Structure pkcs8;
211        try {
212            pkcs8 = ASN1Util.analyze(derBytes);
213        }
214        catch (Exception e) {
215            throw new ProbablyNotPKCS8Exception("asn1 parse failure: " + e);
216        }
217
218        String oid = RSA_OID;
219        // With the OpenSSL unencrypted private keys in DER format, the only way
220        // to even have a hope of guessing what we've got (DSA or RSA?) is to
221        // count the number of DERIntegers occurring in the first DERSequence.
222        int derIntegerCount = -1;
223        if (pkcs8.derIntegers != null) {
224            derIntegerCount = pkcs8.derIntegers.size();
225        }
226        switch (derIntegerCount) {
227            case 6:
228                oid = DSA_OID;
229            case 9:
230                derBytes = formatAsPKCS8(derBytes, oid, pkcs8);
231                pkcs8.oid1 = oid;
232
233                String tf = decryptResult.transformation;
234                int ks = decryptResult.keySize;
235                decryptResult = new DecryptResult(tf, ks, derBytes);
236                break;
237            default:
238                break;
239        }
240
241        oid = pkcs8.oid1;
242        if (!oid.startsWith("1.2.840.113549.1")) {
243            boolean isOkay = false;
244            if (oid.startsWith("1.2.840.10040.4.")) {
245                String s = oid.substring("1.2.840.10040.4.".length());
246                // 1.2.840.10040.4.1 -- id-dsa
247                // 1.2.840.10040.4.3 -- id-dsa-with-sha1
248                isOkay = s.equals("1") || s.startsWith("1.") ||
249                         s.equals("3") || s.startsWith("3.");
250            }
251            if (!isOkay) {
252                throw new ProbablyNotPKCS8Exception("Valid ASN.1, but not PKCS8 or OpenSSL format.  OID=" + oid);
253            }
254        }
255
256        boolean isRSA = RSA_OID.equals(oid);
257        boolean isDSA = DSA_OID.equals(oid);
258        boolean encrypted = !isRSA && !isDSA;
259        byte[] decryptedPKCS8 = encrypted ? null : derBytes;
260
261        if (encrypted) {
262            decryptResult = decryptPKCS8(pkcs8, password);
263            decryptedPKCS8 = decryptResult.bytes;
264        }
265        if (encrypted) {
266            try {
267                pkcs8 = ASN1Util.analyze(decryptedPKCS8);
268            }
269            catch (Exception e) {
270                throw new ProbablyBadPasswordException("Decrypted stream not ASN.1.  Probably bad decryption password.");
271            }
272            oid = pkcs8.oid1;
273            isDSA = DSA_OID.equals(oid);
274        }
275
276        KeySpec spec = new PKCS8EncodedKeySpec(decryptedPKCS8);
277        String type = "RSA";
278        PrivateKey pk;
279        try {
280            KeyFactory KF;
281            if (isDSA) {
282                type = "DSA";
283                KF = KeyFactory.getInstance("DSA");
284            } else {
285                KF = KeyFactory.getInstance("RSA");
286            }
287            pk = KF.generatePrivate(spec);
288        }
289        catch (Exception e) {
290            throw new ProbablyBadPasswordException("Cannot create " + type + " private key from decrypted stream.  Probably bad decryption password. " + e);
291        }
292        if (pk != null) {
293            this.privateKey = pk;
294            this.isDSA = isDSA;
295            this.isRSA = !isDSA;
296            this.decryptedBytes = decryptedPKCS8;
297            this.transformation = decryptResult.transformation;
298            this.keySize = decryptResult.keySize;
299        } else {
300            throw new GeneralSecurityException("KeyFactory.generatePrivate() returned null and didn't throw exception!");
301        }
302    }
303
304    public boolean isRSA() {
305        return isRSA;
306    }
307
308    public boolean isDSA() {
309        return isDSA;
310    }
311
312    public String getTransformation() {
313        return transformation;
314    }
315
316    public int getKeySize() {
317        return keySize;
318    }
319
320    public byte[] getDecryptedBytes() {
321        return decryptedBytes;
322    }
323
324    public PrivateKey getPrivateKey() {
325        return privateKey;
326    }
327
328    public PublicKey getPublicKey() throws GeneralSecurityException {
329        if (privateKey instanceof DSAPrivateKey) {
330            DSAPrivateKey dsa = (DSAPrivateKey) privateKey;
331            DSAParams params = dsa.getParams();
332            BigInteger g = params.getG();
333            BigInteger p = params.getP();
334            BigInteger q = params.getQ();
335            BigInteger x = dsa.getX();
336            BigInteger y = q.modPow( x, p );
337            DSAPublicKeySpec dsaKeySpec = new DSAPublicKeySpec(y, p, q, g);
338            return KeyFactory.getInstance("DSA").generatePublic(dsaKeySpec);
339        } else if (privateKey instanceof RSAPrivateCrtKey) {
340            RSAPrivateCrtKey rsa = (RSAPrivateCrtKey) privateKey;
341            RSAPublicKeySpec rsaKeySpec = new RSAPublicKeySpec(
342                    rsa.getModulus(),
343                    rsa.getPublicExponent()
344            );
345            return KeyFactory.getInstance("RSA").generatePublic(rsaKeySpec);
346        } else {
347            throw new GeneralSecurityException("Not an RSA or DSA key");
348        }
349    }
350
351    public static class DecryptResult {
352        public final String transformation;
353        public final int keySize;
354        public final byte[] bytes;
355
356        protected DecryptResult(String transformation, int keySize,
357                                byte[] decryptedBytes) {
358            this.transformation = transformation;
359            this.keySize = keySize;
360            this.bytes = decryptedBytes;
361        }
362    }
363
364    private static DecryptResult opensslDecrypt(final PEMItem item,
365                                                final char[] password)
366        throws GeneralSecurityException {
367        final String cipher = item.cipher;
368        final String mode = item.mode;
369        final int keySize = item.keySizeInBits;
370        final byte[] salt = item.iv;
371        final boolean des2 = item.des2;
372        final DerivedKey dk = OpenSSL.deriveKey(password, salt, keySize, des2);
373        return decrypt(cipher, mode, dk, des2, null, item.getDerBytes());
374    }
375
376    public static Cipher generateCipher(String cipher, String mode,
377                                        final DerivedKey dk,
378                                        final boolean des2,
379                                        final byte[] iv,
380                                        final boolean decryptMode)
381        throws NoSuchAlgorithmException, NoSuchPaddingException,
382        InvalidKeyException, InvalidAlgorithmParameterException {
383        if (des2 && dk.key.length >= 24) {
384            // copy first 8 bytes into last 8 bytes to create 2DES key.
385            System.arraycopy(dk.key, 0, dk.key, 16, 8);
386        }
387
388        final int keySize = dk.key.length * 8;
389        cipher = cipher.trim();
390        String cipherUpper = cipher.toUpperCase();
391        mode = mode.trim().toUpperCase();
392        // Is the cipher even available?
393        Cipher.getInstance(cipher);
394        String padding = "PKCS5Padding";
395        if (mode.startsWith("CFB") || mode.startsWith("OFB")) {
396            padding = "NoPadding";
397        }
398
399        String transformation = cipher + "/" + mode + "/" + padding;
400        if (cipherUpper.startsWith("RC4")) {
401            // RC4 does not take mode or padding.
402            transformation = cipher;
403        }
404
405        SecretKey secret = new SecretKeySpec(dk.key, cipher);
406        IvParameterSpec ivParams;
407        if (iv != null) {
408            ivParams = new IvParameterSpec(iv);
409        } else {
410            ivParams = dk.iv != null ? new IvParameterSpec(dk.iv) : null;
411        }
412
413        Cipher c = Cipher.getInstance(transformation);
414        int cipherMode = Cipher.ENCRYPT_MODE;
415        if (decryptMode) {
416            cipherMode = Cipher.DECRYPT_MODE;
417        }
418
419        // RC2 requires special params to inform engine of keysize.
420        if (cipherUpper.startsWith("RC2")) {
421            RC2ParameterSpec rcParams;
422            if (mode.startsWith("ECB") || ivParams == null) {
423                // ECB doesn't take an IV.
424                rcParams = new RC2ParameterSpec(keySize);
425            } else {
426                rcParams = new RC2ParameterSpec(keySize, ivParams.getIV());
427            }
428            c.init(cipherMode, secret, rcParams);
429        } else if (cipherUpper.startsWith("RC5")) {
430            RC5ParameterSpec rcParams;
431            if (mode.startsWith("ECB") || ivParams == null) {
432                // ECB doesn't take an IV.
433                rcParams = new RC5ParameterSpec(16, 12, 32);
434            } else {
435                rcParams = new RC5ParameterSpec(16, 12, 32, ivParams.getIV());
436            }
437            c.init(cipherMode, secret, rcParams);
438        } else if (mode.startsWith("ECB") || cipherUpper.startsWith("RC4")) {
439            // RC4 doesn't require any params.
440            // Any cipher using ECB does not require an IV.
441            c.init(cipherMode, secret);
442        } else {
443            // DES, DESede, AES, BlowFish require IVParams (when in CBC, CFB,
444            // or OFB mode).  (In ECB mode they don't require IVParams).
445            c.init(cipherMode, secret, ivParams);
446        }
447        return c;
448    }
449
450    public static DecryptResult decrypt(String cipher, String mode,
451                                        final DerivedKey dk,
452                                        final boolean des2,
453                                        final byte[] iv,
454                                        final byte[] encryptedBytes)
455
456        throws NoSuchAlgorithmException, NoSuchPaddingException,
457        InvalidKeyException, InvalidAlgorithmParameterException,
458        IllegalBlockSizeException, BadPaddingException {
459        Cipher c = generateCipher(cipher, mode, dk, des2, iv, true);
460        final String transformation = c.getAlgorithm();
461        final int keySize = dk.key.length * 8;
462        byte[] decryptedBytes = c.doFinal(encryptedBytes);
463        return new DecryptResult(transformation, keySize, decryptedBytes);
464    }
465
466    private static DecryptResult decryptPKCS8(ASN1Structure pkcs8,
467                                              char[] password)
468        throws GeneralSecurityException {
469        boolean isVersion1 = true;
470        boolean isVersion2 = false;
471        boolean usePKCS12PasswordPadding = false;
472        boolean use2DES = false;
473        String cipher = null;
474        String hash = null;
475        int keySize = -1;
476        // Almost all PKCS8 encrypted keys use CBC.  Looks like the AES OID's can
477        // support different modes, and RC4 doesn't use any mode at all!
478        String mode = "CBC";
479
480        // In PKCS8 Version 2 the IV is stored in the ASN.1 structure for
481        // us, so we don't need to derive it.  Just leave "ivSize" set to 0 for
482        // those ones.
483        int ivSize = 0;
484
485        String oid = pkcs8.oid1;
486        if (oid.startsWith("1.2.840.113549.1.12."))  // PKCS12 key derivation!
487        {
488            usePKCS12PasswordPadding = true;
489
490            // Let's trim this OID to make life a little easier.
491            oid = oid.substring("1.2.840.113549.1.12.".length());
492
493            if (oid.equals("1.1") || oid.startsWith("1.1.")) {
494                // 1.2.840.113549.1.12.1.1
495                hash = "SHA1";
496                cipher = "RC4";
497                keySize = 128;
498            } else if (oid.equals("1.2") || oid.startsWith("1.2.")) {
499                // 1.2.840.113549.1.12.1.2
500                hash = "SHA1";
501                cipher = "RC4";
502                keySize = 40;
503            } else if (oid.equals("1.3") || oid.startsWith("1.3.")) {
504                // 1.2.840.113549.1.12.1.3
505                hash = "SHA1";
506                cipher = "DESede";
507                keySize = 192;
508            } else if (oid.equals("1.4") || oid.startsWith("1.4.")) {
509                // DES2 !!!
510
511                // 1.2.840.113549.1.12.1.4
512                hash = "SHA1";
513                cipher = "DESede";
514                keySize = 192;
515                use2DES = true;
516                // later on we'll copy the first 8 bytes of the 24 byte DESede key
517                // over top the last 8 bytes, making the key look like K1-K2-K1
518                // instead of the usual K1-K2-K3.
519            } else if (oid.equals("1.5") || oid.startsWith("1.5.")) {
520                // 1.2.840.113549.1.12.1.5
521                hash = "SHA1";
522                cipher = "RC2";
523                keySize = 128;
524            } else if (oid.equals("1.6") || oid.startsWith("1.6.")) {
525                // 1.2.840.113549.1.12.1.6
526                hash = "SHA1";
527                cipher = "RC2";
528                keySize = 40;
529            }
530        } else if (oid.startsWith("1.2.840.113549.1.5.")) {
531            // Let's trim this OID to make life a little easier.
532            oid = oid.substring("1.2.840.113549.1.5.".length());
533
534            if (oid.equals("1") || oid.startsWith("1.")) {
535                // 1.2.840.113549.1.5.1 -- pbeWithMD2AndDES-CBC
536                hash = "MD2";
537                cipher = "DES";
538                keySize = 64;
539            } else if (oid.equals("3") || oid.startsWith("3.")) {
540                // 1.2.840.113549.1.5.3 -- pbeWithMD5AndDES-CBC
541                hash = "MD5";
542                cipher = "DES";
543                keySize = 64;
544            } else if (oid.equals("4") || oid.startsWith("4.")) {
545                // 1.2.840.113549.1.5.4 -- pbeWithMD2AndRC2_CBC
546                hash = "MD2";
547                cipher = "RC2";
548                keySize = 64;
549            } else if (oid.equals("6") || oid.startsWith("6.")) {
550                // 1.2.840.113549.1.5.6 -- pbeWithMD5AndRC2_CBC
551                hash = "MD5";
552                cipher = "RC2";
553                keySize = 64;
554            } else if (oid.equals("10") || oid.startsWith("10.")) {
555                // 1.2.840.113549.1.5.10 -- pbeWithSHA1AndDES-CBC
556                hash = "SHA1";
557                cipher = "DES";
558                keySize = 64;
559            } else if (oid.equals("11") || oid.startsWith("11.")) {
560                // 1.2.840.113549.1.5.11 -- pbeWithSHA1AndRC2_CBC
561                hash = "SHA1";
562                cipher = "RC2";
563                keySize = 64;
564            } else if (oid.equals("12") || oid.startsWith("12.")) {
565                // 1.2.840.113549.1.5.12 - id-PBKDF2 - Key Derivation Function
566                isVersion2 = true;
567            } else if (oid.equals("13") || oid.startsWith("13.")) {
568                // 1.2.840.113549.1.5.13 - id-PBES2: PBES2 encryption scheme
569                isVersion2 = true;
570            } else if (oid.equals("14") || oid.startsWith("14.")) {
571                // 1.2.840.113549.1.5.14 - id-PBMAC1 message authentication scheme
572                isVersion2 = true;
573            }
574        }
575        if (isVersion2) {
576            isVersion1 = false;
577            hash = "HmacSHA1";
578            oid = pkcs8.oid2;
579
580            // really ought to be:
581            //
582            // if ( oid.startsWith( "1.2.840.113549.1.5.12" ) )
583            //
584            // but all my tests still pass, and I figure this to be more robust:
585            if (pkcs8.oid3 != null) {
586                oid = pkcs8.oid3;
587            }
588            if (oid.startsWith("1.3.6.1.4.1.3029.1.2")) {
589                // 1.3.6.1.4.1.3029.1.2 - Blowfish
590                cipher = "Blowfish";
591                mode = "CBC";
592                keySize = 128;
593            } else if (oid.startsWith("1.3.14.3.2.")) {
594                oid = oid.substring("1.3.14.3.2.".length());
595                if (oid.equals("6") || oid.startsWith("6.")) {
596                    // 1.3.14.3.2.6 - desECB
597                    cipher = "DES";
598                    mode = "ECB";
599                    keySize = 64;
600                } else if (oid.equals("7") || oid.startsWith("7.")) {
601                    // 1.3.14.3.2.7 - desCBC
602                    cipher = "DES";
603                    mode = "CBC";
604                    keySize = 64;
605                } else if (oid.equals("8") || oid.startsWith("8.")) {
606                    // 1.3.14.3.2.8 - desOFB
607                    cipher = "DES";
608                    mode = "OFB";
609                    keySize = 64;
610                } else if (oid.equals("9") || oid.startsWith("9.")) {
611                    // 1.3.14.3.2.9 - desCFB
612                    cipher = "DES";
613                    mode = "CFB";
614                    keySize = 64;
615                } else if (oid.equals("17") || oid.startsWith("17.")) {
616                    // 1.3.14.3.2.17 - desEDE
617                    cipher = "DESede";
618                    mode = "CBC";
619                    keySize = 192;
620
621                    // If the supplied IV is all zeroes, then this is DES2
622                    // (Well, that's what happened when I played with OpenSSL!)
623                    if (allZeroes(pkcs8.iv)) {
624                        mode = "ECB";
625                        use2DES = true;
626                        pkcs8.iv = null;
627                    }
628                }
629            }
630
631            // AES
632            // 2.16.840.1.101.3.4.1.1  - id-aes128-ECB
633            // 2.16.840.1.101.3.4.1.2  - id-aes128-CBC
634            // 2.16.840.1.101.3.4.1.3  - id-aes128-OFB
635            // 2.16.840.1.101.3.4.1.4  - id-aes128-CFB
636            // 2.16.840.1.101.3.4.1.21 - id-aes192-ECB
637            // 2.16.840.1.101.3.4.1.22 - id-aes192-CBC
638            // 2.16.840.1.101.3.4.1.23 - id-aes192-OFB
639            // 2.16.840.1.101.3.4.1.24 - id-aes192-CFB
640            // 2.16.840.1.101.3.4.1.41 - id-aes256-ECB
641            // 2.16.840.1.101.3.4.1.42 - id-aes256-CBC
642            // 2.16.840.1.101.3.4.1.43 - id-aes256-OFB
643            // 2.16.840.1.101.3.4.1.44 - id-aes256-CFB
644            else if (oid.startsWith("2.16.840.1.101.3.4.1.")) {
645                cipher = "AES";
646                if (pkcs8.iv == null) {
647                    ivSize = 128;
648                }
649                oid = oid.substring("2.16.840.1.101.3.4.1.".length());
650                int x = oid.indexOf('.');
651                int finalDigit;
652                if (x >= 0) {
653                    finalDigit = Integer.parseInt(oid.substring(0, x));
654                } else {
655                    finalDigit = Integer.parseInt(oid);
656                }
657                switch (finalDigit % 10) {
658                    case 1:
659                        mode = "ECB";
660                        break;
661                    case 2:
662                        mode = "CBC";
663                        break;
664                    case 3:
665                        mode = "OFB";
666                        break;
667                    case 4:
668                        mode = "CFB";
669                        break;
670                    default:
671                        throw new RuntimeException("Unknown AES final digit: " + finalDigit);
672                }
673                switch (finalDigit / 10) {
674                    case 0:
675                        keySize = 128;
676                        break;
677                    case 2:
678                        keySize = 192;
679                        break;
680                    case 4:
681                        keySize = 256;
682                        break;
683                    default:
684                        throw new RuntimeException("Unknown AES final digit: " + finalDigit);
685                }
686            } else if (oid.startsWith("1.2.840.113549.3.")) {
687                // Let's trim this OID to make life a little easier.
688                oid = oid.substring("1.2.840.113549.3.".length());
689
690                if (oid.equals("2") || oid.startsWith("2.")) {
691                    // 1.2.840.113549.3.2 - RC2-CBC
692                    // Note:  keysize determined in PKCS8 Version 2.0 ASN.1 field.
693                    cipher = "RC2";
694                    keySize = pkcs8.keySize * 8;
695                } else if (oid.equals("4") || oid.startsWith("4.")) {
696                    // 1.2.840.113549.3.4 - RC4
697                    // Note:  keysize determined in PKCS8 Version 2.0 ASN.1 field.
698                    cipher = "RC4";
699                    keySize = pkcs8.keySize * 8;
700                } else if (oid.equals("7") || oid.startsWith("7.")) {
701                    // 1.2.840.113549.3.7 - DES-EDE3-CBC
702                    cipher = "DESede";
703                    keySize = 192;
704                } else if (oid.equals("9") || oid.startsWith("9.")) {
705                    // 1.2.840.113549.3.9 - RC5 CBC Pad
706                    // Note:  keysize determined in PKCS8 Version 2.0 ASN.1 field.
707                    keySize = pkcs8.keySize * 8;
708                    cipher = "RC5";
709
710                    // Need to find out more about RC5.
711                    // How do I create the RC5ParameterSpec?
712                    // (int version, int rounds, int wordSize, byte[] iv)
713                }
714            }
715        }
716
717        // The pkcs8 structure has been thoroughly examined.  If we don't have
718        // a cipher or hash at this point, then we don't support the file we
719        // were given.
720        if (cipher == null || hash == null) {
721            throw new ProbablyNotPKCS8Exception("Unsupported PKCS8 format. oid1=[" + pkcs8.oid1 + "], oid2=[" + pkcs8.oid2 + "]");
722        }
723
724        // In PKCS8 Version 1.5 we need to derive an 8 byte IV.  In those cases
725        // the ASN.1 structure doesn't have the IV, anyway, so I can use that
726        // to decide whether to derive one or not.
727        //
728        // Note:  if AES, then IV has to be 16 bytes.
729        if (pkcs8.iv == null) {
730            ivSize = 64;
731        }
732
733        byte[] salt = pkcs8.salt;
734        int ic = pkcs8.iterationCount;
735
736        // PKCS8 converts the password to a byte[] array using a simple
737        // cast.  This byte[] array is ignored if we're using the PKCS12
738        // key derivation, since that employs a different technique.
739        byte[] pwd = new byte[password.length];
740        for (int i = 0; i < pwd.length; i++) {
741            pwd[i] = (byte) password[i];
742        }
743
744        DerivedKey dk;
745        if (usePKCS12PasswordPadding) {
746            MessageDigest md = MessageDigest.getInstance(hash);
747            dk = deriveKeyPKCS12(password, salt, ic, keySize, ivSize, md);
748        } else {
749            if (isVersion1) {
750                MessageDigest md = MessageDigest.getInstance(hash);
751                dk = deriveKeyV1(pwd, salt, ic, keySize, ivSize, md);
752            } else {
753                Mac mac = Mac.getInstance(hash);
754                dk = deriveKeyV2(pwd, salt, ic, keySize, ivSize, mac);
755            }
756        }
757
758
759        return decrypt(cipher, mode, dk, use2DES, pkcs8.iv, pkcs8.bigPayload);
760    }
761
762
763    public static DerivedKey deriveKeyV1(byte[] password, byte[] salt,
764                                         int iterations, int keySizeInBits,
765                                         int ivSizeInBits, MessageDigest md) {
766        int keySize = keySizeInBits / 8;
767        int ivSize = ivSizeInBits / 8;
768        md.reset();
769        md.update(password);
770        byte[] result = md.digest(salt);
771        for (int i = 1; i < iterations; i++) {
772            // Hash of the hash for each of the iterations.
773            result = md.digest(result);
774        }
775        byte[] key = new byte[keySize];
776        byte[] iv = new byte[ivSize];
777        System.arraycopy(result, 0, key, 0, key.length);
778        System.arraycopy(result, key.length, iv, 0, iv.length);
779        return new DerivedKey(key, iv);
780    }
781
782    public static DerivedKey deriveKeyPKCS12(char[] password, byte[] salt,
783                                             int iterations, int keySizeInBits,
784                                             int ivSizeInBits,
785                                             MessageDigest md) {
786        byte[] pwd;
787        if (password.length > 0) {
788            pwd = new byte[(password.length + 1) * 2];
789            for (int i = 0; i < password.length; i++) {
790                pwd[i * 2] = (byte) (password[i] >>> 8);
791                pwd[i * 2 + 1] = (byte) password[i];
792            }
793        } else {
794            pwd = new byte[0];
795        }
796        int keySize = keySizeInBits / 8;
797        int ivSize = ivSizeInBits / 8;
798        byte[] key = pkcs12(1, keySize, salt, pwd, iterations, md);
799        byte[] iv = pkcs12(2, ivSize, salt, pwd, iterations, md);
800        return new DerivedKey(key, iv);
801    }
802
803    /**
804     * This PKCS12 key derivation code comes from BouncyCastle.
805     *
806     * @param idByte         1 == key, 2 == iv
807     * @param n              keysize or ivsize
808     * @param salt           8 byte salt
809     * @param password       password
810     * @param iterationCount iteration-count
811     * @param md             The message digest to use
812     * @return byte[] the derived key
813     */
814    private static byte[] pkcs12(int idByte, int n, byte[] salt,
815                                 byte[] password, int iterationCount,
816                                 MessageDigest md) {
817        int u = md.getDigestLength();
818        // sha1, md2, md5 all use 512 bits.  But future hashes might not.
819        int v = 512 / 8;
820        md.reset();
821        byte[] D = new byte[v];
822        byte[] dKey = new byte[n];
823        for (int i = 0; i != D.length; i++) {
824            D[i] = (byte) idByte;
825        }
826        byte[] S;
827        if ((salt != null) && (salt.length != 0)) {
828            S = new byte[v * ((salt.length + v - 1) / v)];
829            for (int i = 0; i != S.length; i++) {
830                S[i] = salt[i % salt.length];
831            }
832        } else {
833            S = new byte[0];
834        }
835        byte[] P;
836        if ((password != null) && (password.length != 0)) {
837            P = new byte[v * ((password.length + v - 1) / v)];
838            for (int i = 0; i != P.length; i++) {
839                P[i] = password[i % password.length];
840            }
841        } else {
842            P = new byte[0];
843        }
844        byte[] I = new byte[S.length + P.length];
845        System.arraycopy(S, 0, I, 0, S.length);
846        System.arraycopy(P, 0, I, S.length, P.length);
847        byte[] B = new byte[v];
848        int c = (n + u - 1) / u;
849        for (int i = 1; i <= c; i++) {
850            md.update(D);
851            byte[] result = md.digest(I);
852            for (int j = 1; j != iterationCount; j++) {
853                result = md.digest(result);
854            }
855            for (int j = 0; j != B.length; j++) {
856                B[j] = result[j % result.length];
857            }
858            for (int j = 0; j < (I.length / v); j++) {
859                /*
860                     * add a + b + 1, returning the result in a. The a value is treated
861                     * as a BigInteger of length (b.length * 8) bits. The result is
862                     * modulo 2^b.length in case of overflow.
863                     */
864                int aOff = j * v;
865                int bLast = B.length - 1;
866                int x = (B[bLast] & 0xff) + (I[aOff + bLast] & 0xff) + 1;
867                I[aOff + bLast] = (byte) x;
868                x >>>= 8;
869                for (int k = B.length - 2; k >= 0; k--) {
870                    x += (B[k] & 0xff) + (I[aOff + k] & 0xff);
871                    I[aOff + k] = (byte) x;
872                    x >>>= 8;
873                }
874            }
875            if (i == c) {
876                System.arraycopy(result, 0, dKey, (i - 1) * u, dKey.length - ((i - 1) * u));
877            } else {
878                System.arraycopy(result, 0, dKey, (i - 1) * u, result.length);
879            }
880        }
881        return dKey;
882    }
883
884    public static DerivedKey deriveKeyV2(byte[] password, byte[] salt,
885                                         int iterations, int keySizeInBits,
886                                         int ivSizeInBits, Mac mac)
887        throws InvalidKeyException {
888        int keySize = keySizeInBits / 8;
889        int ivSize = ivSizeInBits / 8;
890
891        // Because we're using an Hmac, we need to initialize with a SecretKey.
892        // HmacSHA1 doesn't need SecretKeySpec's 2nd parameter, hence the "N/A".
893        SecretKeySpec sk = new SecretKeySpec(password, "N/A");
894        mac.init(sk);
895        int macLength = mac.getMacLength();
896        int derivedKeyLength = keySize + ivSize;
897        int blocks = (derivedKeyLength + macLength - 1) / macLength;
898        byte[] blockIndex = new byte[4];
899        byte[] finalResult = new byte[blocks * macLength];
900        for (int i = 1; i <= blocks; i++) {
901            int offset = (i - 1) * macLength;
902            blockIndex[0] = (byte) (i >>> 24);
903            blockIndex[1] = (byte) (i >>> 16);
904            blockIndex[2] = (byte) (i >>> 8);
905            blockIndex[3] = (byte) i;
906            mac.reset();
907            mac.update(salt);
908            byte[] result = mac.doFinal(blockIndex);
909            System.arraycopy(result, 0, finalResult, offset, result.length);
910            for (int j = 1; j < iterations; j++) {
911                mac.reset();
912                result = mac.doFinal(result);
913                for (int k = 0; k < result.length; k++) {
914                    finalResult[offset + k] ^= result[k];
915                }
916            }
917        }
918        byte[] key = new byte[keySize];
919        byte[] iv = new byte[ivSize];
920        System.arraycopy(finalResult, 0, key, 0, key.length);
921        System.arraycopy(finalResult, key.length, iv, 0, iv.length);
922        return new DerivedKey(key, iv);
923    }
924
925    public static byte[] formatAsPKCS8(byte[] privateKey, String oid,
926                                       ASN1Structure pkcs8) {
927        ASN1Integer derZero = new ASN1Integer(BigInteger.ZERO);
928        ASN1EncodableVector outterVec = new ASN1EncodableVector();
929        ASN1EncodableVector innerVec = new ASN1EncodableVector();
930        DEROctetString octetsToAppend;
931        try {
932            ASN1ObjectIdentifier derOID = new ASN1ObjectIdentifier(oid);
933            innerVec.add(derOID);
934            if (DSA_OID.equals(oid)) {
935                if (pkcs8 == null) {
936                    try {
937                        pkcs8 = ASN1Util.analyze(privateKey);
938                    }
939                    catch (Exception e) {
940                        throw new RuntimeException("asn1 parse failure " + e);
941                    }
942                }
943                if (pkcs8.derIntegers == null || pkcs8.derIntegers.size() < 6) {
944                    throw new RuntimeException("invalid DSA key - can't find P, Q, G, X");
945                }
946
947                ASN1Integer[] ints = new ASN1Integer[pkcs8.derIntegers.size()];
948                pkcs8.derIntegers.toArray(ints);
949                ASN1Integer p = ints[1];
950                ASN1Integer q = ints[2];
951                ASN1Integer g = ints[3];
952                ASN1Integer x = ints[5];
953
954                byte[] encodedX = encode(x);
955                octetsToAppend = new DEROctetString(encodedX);
956                ASN1EncodableVector pqgVec = new ASN1EncodableVector();
957                pqgVec.add(p);
958                pqgVec.add(q);
959                pqgVec.add(g);
960                DERSequence pqg = new DERSequence(pqgVec);
961                innerVec.add(pqg);
962            } else {
963                innerVec.add(DERNull.INSTANCE);
964                octetsToAppend = new DEROctetString(privateKey);
965            }
966
967            DERSequence inner = new DERSequence(innerVec);
968            outterVec.add(derZero);
969            outterVec.add(inner);
970            outterVec.add(octetsToAppend);
971            DERSequence outter = new DERSequence(outterVec);
972            return encode(outter);
973        }
974        catch (IOException ioe) {
975            throw JavaImpl.newRuntimeException(ioe);
976        }
977    }
978
979    private static boolean allZeroes(byte[] b) {
980        for (int i = 0; i < b.length; i++) {
981            if (b[i] != 0) {
982                return false;
983            }
984        }
985        return true;
986    }
987
988    public static byte[] encode(ASN1Encodable der) throws IOException {
989        ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
990        ASN1OutputStream out = new ASN1OutputStream(baos);
991        out.writeObject(der);
992        out.close();
993        return baos.toByteArray();
994    }
995
996    public static void main(String[] args) throws Exception {
997        String password = "changeit";
998        if (args.length == 0) {
999            System.out.println("Usage1:  [password] [file:private-key]      Prints decrypted PKCS8 key (base64).");
1000            System.out.println("Usage2:  [password] [file1] [file2] etc...  Checks that all private keys are equal.");
1001            System.out.println("Usage2 assumes that all files can be decrypted with the same password.");
1002        } else if (args.length == 1 || args.length == 2) {
1003            FileInputStream in = new FileInputStream(args[args.length - 1]);
1004            if (args.length == 2) {
1005                password = args[0];
1006            }
1007            byte[] bytes = Util.streamToBytes(in);
1008            PKCS8Key key = new PKCS8Key(bytes, password.toCharArray());
1009            PEMItem item = new PEMItem(key.getDecryptedBytes(), "PRIVATE KEY");
1010            byte[] pem = PEMUtil.encode(Collections.singleton(item));
1011            System.out.write(pem);
1012        } else {
1013            byte[] original = null;
1014            File f = new File(args[0]);
1015            int i = 0;
1016            if (!f.exists()) {
1017                // File0 doesn't exist, so it must be a password!
1018                password = args[0];
1019                i++;
1020            }
1021            for (; i < args.length; i++) {
1022                FileInputStream in = new FileInputStream(args[i]);
1023                byte[] bytes = Util.streamToBytes(in);
1024                PKCS8Key key = null;
1025                try {
1026                    key = new PKCS8Key(bytes, password.toCharArray());
1027                }
1028                catch (Exception e) {
1029                    System.out.println(" FAILED! " + args[i] + " " + e);
1030                }
1031                if (key != null) {
1032                    byte[] decrypted = key.getDecryptedBytes();
1033                    int keySize = key.getKeySize();
1034                    String keySizeStr = "" + keySize;
1035                    if (keySize < 10) {
1036                        keySizeStr = "  " + keySizeStr;
1037                    } else if (keySize < 100) {
1038                        keySizeStr = " " + keySizeStr;
1039                    }
1040                    StringBuffer buf = new StringBuffer(key.getTransformation());
1041                    int maxLen = "Blowfish/CBC/PKCS5Padding".length();
1042                    for (int j = buf.length(); j < maxLen; j++) {
1043                        buf.append(' ');
1044                    }
1045                    String transform = buf.toString();
1046                    String type = key.isDSA() ? "DSA" : "RSA";
1047
1048                    if (original == null) {
1049                        original = decrypted;
1050                        System.out.println("   SUCCESS    \t" + type + "\t" + transform + "\t" + keySizeStr + "\t" + args[i]);
1051                    } else {
1052                        boolean identical = Arrays.equals(original, decrypted);
1053                        if (!identical) {
1054                            System.out.println("***FAILURE*** \t" + type + "\t" + transform + "\t" + keySizeStr + "\t" + args[i]);
1055                        } else {
1056                            System.out.println("   SUCCESS    \t" + type + "\t" + transform + "\t" + keySizeStr + "\t" + args[i]);
1057                        }
1058                    }
1059                }
1060            }
1061        }
1062    }
1063
1064}