/** c: PEM_read_PrivateKey + PEM_read_bio_PrivateKey CAUTION: KeyPair#getPublic() may be null. */
 public static KeyPair readPrivateKey(Reader in, char[] password) throws IOException {
   BufferedReader _in = makeBuffered(in);
   String line;
   while ((line = _in.readLine()) != null) {
     if (line.indexOf(BEF_G + PEM_STRING_RSA) != -1) {
       try {
         return readKeyPair(_in, password, "RSA", BEF_E + PEM_STRING_RSA);
       } catch (Exception e) {
         throw new IOException("problem creating RSA private key: " + e.toString());
       }
     } else if (line.indexOf(BEF_G + PEM_STRING_DSA) != -1) {
       try {
         return readKeyPair(_in, password, "DSA", BEF_E + PEM_STRING_DSA);
       } catch (Exception e) {
         throw new IOException("problem creating DSA private key: " + e.toString());
       }
     } else if (line.indexOf(BEF_G + PEM_STRING_ECPRIVATEKEY) != -1) {
       throw new IOException("EC private key not supported");
     } else if (line.indexOf(BEF_G + PEM_STRING_PKCS8INF) != -1) {
       try {
         byte[] bytes = readBytes(_in, BEF_E + PEM_STRING_PKCS8INF);
         PrivateKeyInfo info =
             new PrivateKeyInfo((ASN1Sequence) new ASN1InputStream(bytes).readObject());
         String type = getPrivateKeyTypeFromObjectId(info.getAlgorithmId().getObjectId());
         return org.jruby.ext.openssl.impl.PKey.readPrivateKey(
             info.getPrivateKey().getDEREncoded(), type);
       } catch (Exception e) {
         throw new IOException("problem creating private key: " + e.toString());
       }
     } else if (line.indexOf(BEF_G + PEM_STRING_PKCS8) != -1) {
       try {
         byte[] bytes = readBytes(_in, BEF_E + PEM_STRING_PKCS8);
         org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo eIn =
             new org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo(
                 (ASN1Sequence) new ASN1InputStream(bytes).readObject());
         AlgorithmIdentifier algId = eIn.getEncryptionAlgorithm();
         PrivateKey privKey;
         if (algId.getAlgorithm().toString().equals("1.2.840.113549.1.5.13")) { // PBES2
           privKey = derivePrivateKeyPBES2(eIn, algId, password);
         } else {
           privKey = derivePrivateKeyPBES1(eIn, algId, password);
         }
         return new KeyPair(null, privKey);
       } catch (Exception e) {
         throw new IOException("problem creating private key: " + e.toString());
       }
     }
   }
   return null;
 }
  private static PrivateKey derivePrivateKeyPBES2(
      org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo eIn,
      AlgorithmIdentifier algId,
      char[] password)
      throws GeneralSecurityException, InvalidCipherTextException {
    PBES2Parameters pbeParams = new PBES2Parameters((ASN1Sequence) algId.getParameters());
    CipherParameters cipherParams = extractPBES2CipherParams(password, pbeParams);

    EncryptionScheme scheme = pbeParams.getEncryptionScheme();
    BufferedBlockCipher cipher;
    if (scheme.getAlgorithm().equals(PKCSObjectIdentifiers.RC2_CBC)) {
      RC2CBCParameter rc2Params = new RC2CBCParameter((ASN1Sequence) scheme.getObject());
      byte[] iv = rc2Params.getIV();
      CipherParameters param = new ParametersWithIV(cipherParams, iv);
      cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new RC2Engine()));
      cipher.init(false, param);
    } else {
      byte[] iv = ((ASN1OctetString) scheme.getObject()).getOctets();
      CipherParameters param = new ParametersWithIV(cipherParams, iv);
      cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new DESedeEngine()));
      cipher.init(false, param);
    }

    byte[] data = eIn.getEncryptedData();
    byte[] out = new byte[cipher.getOutputSize(data.length)];
    int len = cipher.processBytes(data, 0, data.length, out, 0);
    len += cipher.doFinal(out, len);
    byte[] pkcs8 = new byte[len];
    System.arraycopy(out, 0, pkcs8, 0, len);
    KeyFactory fact = KeyFactory.getInstance("RSA"); // It seems to work for both RSA and DSA.
    return fact.generatePrivate(new PKCS8EncodedKeySpec(pkcs8));
  }
  private AlgorithmParameters getParameters() throws NoSuchAlgorithmException {
    AlgorithmParameters ap = AlgorithmParameters.getInstance(this.getAlgName());
    ByteArrayOutputStream bOut = new ByteArrayOutputStream();
    DEROutputStream dOut = new DEROutputStream(bOut);

    try {
      dOut.writeObject(infoObj.getEncryptionAlgorithm().getParameters());
      dOut.close();

      ap.init(bOut.toByteArray());
    } catch (IOException e) {
      throw new NoSuchAlgorithmException("unable to parse parameters");
    }

    return ap;
  }
  private static PrivateKey derivePrivateKeyPBES1(
      org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo eIn,
      AlgorithmIdentifier algId,
      char[] password)
      throws GeneralSecurityException {
    PKCS12PBEParams pkcs12Params = new PKCS12PBEParams((ASN1Sequence) algId.getParameters());
    PBEParameterSpec pbeParams =
        new PBEParameterSpec(pkcs12Params.getIV(), pkcs12Params.getIterations().intValue());

    String algorithm = ASN1Registry.o2a(algId.getAlgorithm());
    algorithm = (algorithm.split("-"))[0];
    Cipher cipher = OpenSSLReal.getCipherBC(algorithm); // need to use BC for PBEParameterSpec.

    SecretKeyFactory fact =
        OpenSSLReal.getSecretKeyFactoryBC(algorithm); // need to use BC for PKCS12PBEParams.
    SecretKey key = fact.generateSecret(new PBEKeySpec(password));

    cipher.init(Cipher.UNWRAP_MODE, key, pbeParams);
    // wrappedKeyAlgorithm is unknown ("")
    return (PrivateKey) cipher.unwrap(eIn.getEncryptedData(), "", Cipher.PRIVATE_KEY);
  }
 /**
  * Returns a copy of the encrypted data.
  *
  * @returns a copy of the encrypted data.
  */
 public byte[] getEncryptedData() {
   return infoObj.getEncryptedData();
 }
 /**
  * Returns the encryption algorithm.
  *
  * @returns the algorithm name.
  */
 public String getAlgName() {
   return infoObj.getEncryptionAlgorithm().getObjectId().getId();
 }