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 }