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