112112import org .bouncycastle .crypto .CipherParameters ;
113113import org .bouncycastle .crypto .InvalidCipherTextException ;
114114import org .bouncycastle .crypto .PBEParametersGenerator ;
115+ import org .bouncycastle .asn1 .nist .NISTObjectIdentifiers ;
116+ import org .bouncycastle .crypto .engines .AESEngine ;
115117import org .bouncycastle .crypto .engines .DESedeEngine ;
116118import org .bouncycastle .crypto .engines .RC2Engine ;
117119import org .bouncycastle .crypto .generators .OpenSSLPBEParametersGenerator ;
@@ -360,14 +362,15 @@ else if ( line.indexOf(BEG_STRING_PKCS8) != -1 ) {
360362 try {
361363 byte [] bytes = readBase64Bytes (reader , BEF_E + PEM_STRING_PKCS8 );
362364 EncryptedPrivateKeyInfo eIn = EncryptedPrivateKeyInfo .getInstance (bytes );
363- AlgorithmIdentifier algId = eIn .getEncryptionAlgorithm ();
364- PrivateKey privKey ;
365- if (algId .getAlgorithm ().toString ().equals ("1.2.840.113549.1.5.13" )) { // PBES2
366- privKey = derivePrivateKeyPBES2 (eIn , algId , passwd );
367- } else {
368- privKey = derivePrivateKeyPBES1 (eIn , algId , passwd );
369- }
370- return new KeyPair (null , privKey );
365+ org .bouncycastle .pkcs .PKCS8EncryptedPrivateKeyInfo encInfo =
366+ new org .bouncycastle .pkcs .PKCS8EncryptedPrivateKeyInfo (eIn );
367+ org .bouncycastle .operator .InputDecryptorProvider decryptor =
368+ new org .bouncycastle .pkcs .jcajce .JcePKCSPBEInputDecryptorProviderBuilder ()
369+ .setProvider (SecurityHelper .getSecurityProvider ())
370+ .build (passwd );
371+ final PrivateKeyInfo keyInfo = encInfo .decryptPrivateKeyInfo (decryptor );
372+ final Type type = getPrivateKeyType (keyInfo .getPrivateKeyAlgorithm ());
373+ return org .jruby .ext .openssl .impl .PKey .readPrivateKey (type , keyInfo );
371374 }
372375 catch (Exception e ) {
373376 throw mapReadException ("problem creating private key: " , e );
@@ -377,6 +380,43 @@ else if ( line.indexOf(BEG_STRING_PKCS8) != -1 ) {
377380 return null ;
378381 }
379382
383+ /**
384+ * Attempt to read a private key from DER-encoded PKCS#8 bytes (PrivateKeyInfo or
385+ * EncryptedPrivateKeyInfo). Returns null if the input cannot be parsed as either format.
386+ */
387+ public static KeyPair readPrivateKeyFromDER (final byte [] input , final char [] passwd ) throws IOException {
388+ // Try as unencrypted PKCS#8 PrivateKeyInfo
389+ try {
390+ final PrivateKeyInfo keyInfo = PrivateKeyInfo .getInstance (ASN1Primitive .fromByteArray (input ));
391+ if (keyInfo != null ) {
392+ final Type type = getPrivateKeyType (keyInfo .getPrivateKeyAlgorithm ());
393+ return org .jruby .ext .openssl .impl .PKey .readPrivateKey (type , keyInfo );
394+ }
395+ } catch (Exception e ) {
396+ // Not a PrivateKeyInfo - try as EncryptedPrivateKeyInfo
397+ }
398+ // Try as encrypted PKCS#8 EncryptedPrivateKeyInfo
399+ if (passwd != null ) {
400+ try {
401+ EncryptedPrivateKeyInfo eIn = EncryptedPrivateKeyInfo .getInstance (ASN1Primitive .fromByteArray (input ));
402+ if (eIn != null ) {
403+ org .bouncycastle .pkcs .PKCS8EncryptedPrivateKeyInfo encInfo =
404+ new org .bouncycastle .pkcs .PKCS8EncryptedPrivateKeyInfo (eIn );
405+ org .bouncycastle .operator .InputDecryptorProvider decryptor =
406+ new org .bouncycastle .pkcs .jcajce .JcePKCSPBEInputDecryptorProviderBuilder ()
407+ .setProvider (SecurityHelper .getSecurityProvider ())
408+ .build (passwd );
409+ final PrivateKeyInfo keyInfo = encInfo .decryptPrivateKeyInfo (decryptor );
410+ final Type type = getPrivateKeyType (keyInfo .getPrivateKeyAlgorithm ());
411+ return org .jruby .ext .openssl .impl .PKey .readPrivateKey (type , keyInfo );
412+ }
413+ } catch (Exception e ) {
414+ throw new IOException ("Could not decrypt PKCS#8 key: " + e .getMessage (), e );
415+ }
416+ }
417+ return null ;
418+ }
419+
380420 private static IOException mapReadException (final String message , final Exception ex ) {
381421 if ( ex instanceof PasswordRequiredException ) {
382422 return (PasswordRequiredException ) ex ;
@@ -416,12 +456,20 @@ private static PrivateKey derivePrivateKeyPBES2(EncryptedPrivateKeyInfo eIn, Alg
416456
417457 EncryptionScheme scheme = pbeParams .getEncryptionScheme ();
418458 BufferedBlockCipher cipher ;
419- if ( scheme .getAlgorithm ().equals ( PKCSObjectIdentifiers .RC2_CBC ) ) {
459+ ASN1ObjectIdentifier encOid = scheme .getAlgorithm ();
460+ if ( encOid .equals ( PKCSObjectIdentifiers .RC2_CBC ) ) {
420461 RC2CBCParameter rc2Params = RC2CBCParameter .getInstance (scheme );
421462 byte [] iv = rc2Params .getIV ();
422463 CipherParameters param = new ParametersWithIV (cipherParams , iv );
423464 cipher = new PaddedBufferedBlockCipher (new CBCBlockCipher (new RC2Engine ()));
424465 cipher .init (false , param );
466+ } else if ( encOid .equals ( NISTObjectIdentifiers .id_aes128_CBC ) ||
467+ encOid .equals ( NISTObjectIdentifiers .id_aes192_CBC ) ||
468+ encOid .equals ( NISTObjectIdentifiers .id_aes256_CBC ) ) {
469+ byte [] iv = ASN1OctetString .getInstance ( scheme .getParameters () ).getOctets ();
470+ CipherParameters param = new ParametersWithIV (cipherParams , iv );
471+ cipher = new PaddedBufferedBlockCipher (new CBCBlockCipher (new AESEngine ()));
472+ cipher .init (false , param );
425473 } else {
426474 byte [] iv = ASN1OctetString .getInstance ( scheme .getParameters () ).getOctets ();
427475 CipherParameters param = new ParametersWithIV (cipherParams , iv );
@@ -1053,6 +1101,18 @@ public static void writeECPrivateKey(Writer _out, ECPrivateKey obj, CipherSpec c
10531101 }
10541102 }
10551103
1104+ /** Writes a PKCS#8 unencrypted private key in PEM format ("PRIVATE KEY"). */
1105+ public static void writePKCS8PrivateKey (Writer _out , byte [] pkcs8Bytes ) throws IOException {
1106+ BufferedWriter out = makeBuffered (_out );
1107+ writePemPlain (out , PEM_STRING_PKCS8INF , pkcs8Bytes );
1108+ }
1109+
1110+ /** Writes a PKCS#8 encrypted private key in PEM format ("ENCRYPTED PRIVATE KEY"). */
1111+ public static void writeEncryptedPKCS8PrivateKey (Writer _out , byte [] encryptedPKCS8Bytes ) throws IOException {
1112+ BufferedWriter out = makeBuffered (_out );
1113+ writePemPlain (out , PEM_STRING_PKCS8 , encryptedPKCS8Bytes );
1114+ }
1115+
10561116 public static void writeECParameters (Writer _out , ASN1ObjectIdentifier obj , CipherSpec cipher , char [] passwd ) throws IOException {
10571117 assert (obj != null );
10581118 final String PEM_STRING_EC = "EC PARAMETERS" ;
0 commit comments