【问题标题】:Java Encryption with OpenSSL key使用 OpenSSL 密钥进行 Java 加密
【发布时间】:2013-04-08 11:20:24
【问题描述】:

我有一种情况,我按照他们网站上列出的说明使用 OpenSSL 生成了一个公钥/私钥对,用于gdcmanon。具体来说,我使用以下命令为 gdcmanon 生成密钥

$ openssl genrsa -out CA_key.pem
$ openssl req -new -key CA_key.pem -x509 -days 365 -out CA_cert.cer

然后我能够按照他们的指示使用

加密文件
gdcmanon -c CA_cert.cer -e original.dcm original_anonymized.dcm

并使用解密文件

gdcmanon -k CA_key.pem -d original_anonymized.dcm orginal_deanonymized.dcm

然后我想使用该密钥来解码 Java 中相应 DICOM 文件中的一些信息。在努力获得 Java 中的密钥之后,我找到了this 页面,并且能够通过以下调用创建一个不会导致我的 Java 程序崩溃的密钥

openssl pkcs8 -topk8 -inform PEM -outform DER -in CA_key.pem -out CA_key.pkcs8.pem -nocrypt

经过所有这些,以及大量阅读,我已经生成了以下 Java 代码

public static String decode(byte[] encryptedData) {
    Key key = readPEMKey(new File("CA_key.pkcs8.pem"));
    //Key key = readPEMKey(new File("CA_key.pem"));
    try {
        Cipher c = Cipher.getInstance("RSA");
        c.init(Cipher.DECRYPT_MODE, key);
        byte[] decValue = c.doFinal(encryptedData);
        String decryptedValue = new String(decValue);
        return decryptedValue;
    }
    catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

private static Key readPEMKey(File key) {
    DataInputStream dis = null;
    BufferedReader reader = null;
    try {
        /*
        reader = new BufferedReader(new FileReader(key));
        String privKeyPEM = "";
        String line;
        while ((line = reader.readLine()) != null) {
            if (!line.equals("-----BEGIN RSA PRIVATE KEY-----") && !line.equals("-----END RSA PRIVATE KEY-----"))
            privKeyPEM += line + "\n";
        }
        byte[] encoded = new BASE64Decoder().decodeBuffer(privKeyPEM);
        */

        dis = new DataInputStream(new BufferedInputStream(new FileInputStream(key)));
        byte[] encoded = new byte[(int) key.length()];
        dis.readFully(encoded);

        // PKCS8 decode the encoded RSA private key
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);
        KeyFactory kf = KeyFactory.getInstance("RSA");
        PrivateKey privKey = kf.generatePrivate(keySpec);
        return privKey;
    }
    catch (Exception e) {
        e.printStackTrace();
    }
    finally {
        if (reader != null) {try {reader.close();} catch (Exception e) {e.printStackTrace();}}
        if (dis != null) {try {dis.close();} catch (Exception e) {e.printStackTrace();}}
    }
    return null;
}

我尝试了多种方法来读取和处理用于以各种方式解码的私钥(从注释掉的部分可以看出),但是,我还没有找到可以通过 encryptedData 解密的解决方案。如果我将密码与 RSA 算法一起使用,我会得到以下堆栈跟踪

javax.crypto.IllegalBlockSizeException: Data must not be longer than 256 bytes
at com.sun.crypto.provider.RSACipher.a(DashoA13*..)
at com.sun.crypto.provider.RSACipher.engineDoFinal(DashoA13*..)
at javax.crypto.Cipher.doFinal(DashoA13*..)
at my.app.decode(Application.java:124)

这在 byte[] decValue = c.doFinal(encryptedData); 行失败

如果我使用带有 AES 算法的密码,我会得到以下堆栈跟踪

java.security.InvalidKeyException: No installed provider supports this key: sun.security.rsa.RSAPrivateCrtKeyImpl
at javax.crypto.Cipher.a(DashoA13*..)
at javax.crypto.Cipher.init(DashoA13*..)
at javax.crypto.Cipher.init(DashoA13*..)
at my.app.Application.decode(Application.java:123)

这在 c.init(Cipher.DECRYPT_MODE, key); 行失败;

我已经安装了 Java 6 的 JCE(我正在使用)。我不知道我做错了什么。有人可以指出我正确的方向吗?

谢谢

更新

我在这方面做了很多工作,也尝试了 Bouncy Castle,但还没有结果。这是我在使用 Bouncy Castle 时的第一次尝试。

BufferedReader reader = new BufferedReader(new FileReader(new File("CA_key.pem")));
PEMParser parser = new PEMParser(reader);
PEMKeyPair keyPair = (PEMKeyPair)parser.readObject();
AsymmetricKeyParameter privKeyParams = PrivateKeyFactory.createKey(keyPair.getPrivateKeyInfo());
AsymmetricKeyParameter publicKeyParams = PublicKeyFactory.createKey(keyPair.getPublicKeyInfo());
parser.close();

RSAEngine e = new RSAEngine();
e.init(false, publicKeyParams);
byte[] decValue = e.processBlock(encryptedData, 0, encryptedData.length);

我再次遇到了 processBlock 方法的异常 org.bouncycastle.crypto.DataLengthException:输入对于 RSA 密码来说太大。 我认为这与我之前遇到的问题相同。我在这里真的很茫然,因为 gdcmanon 工具可以仅使用 CA_key.pem 文件清楚地解密此字符串,因为这是唯一的输入(要解密的文件除外)。我一直在 gdcmanon 源代码中四处寻找,看起来它以某种方式使用 AES 256 密钥,但我只是不知道如何从我生成的 RSA 密钥中获得它。有人可以指出我正确的方向吗?

编辑

我一直在浏览 gdcmanon 源代码,看起来他们使用加密消息语法 (CMS)。这对我来说是全新的,但看起来 BC 有一堆与 CMS 相关的类。具体来说,gdcmanon 正在使用一种名为 PKCS7_dataDecode 的 OpenSSL 方法来解密数据。我还没有找到关于如何在 Java 中做到这一点的好例子。有什么想法吗?

谢谢

【问题讨论】:

  • 2048 位 RSA 私钥无法解密超过 256 字节的数据,因此您的 encryptedData 参数太大。您当然不应该为您的密码使用AES 算法,因此您可以忽略其他错误。我会专注于理解为什么您对解密的输入比预期的要大。
  • 根据 gdcmanon 教程,我以为我制作了 AES 密钥,但老实说我不确定。我对这个级别的加密非常陌生,所以我不确定我做错了什么。我知道 gdcmanon 可以仅使用他们教程页面中指定的 CA_key.pem 文件很好地解密此消息,所以这是我认为我需要的唯一信息。

标签: java encryption aes rsa


【解决方案1】:

经过一番苦苦挣扎,我终于有了以下似乎可以工作的代码。如果有人能告诉我在没有 BC 的情况下如何做到这一点,那就太棒了,但现在我很高兴它可以工作。

public static String decode(byte[] encryptedData, String keyFile) {
    BufferedReader reader = null;
    try {
        reader = new BufferedReader(new FileReader(new File(keyFile)));
        PEMParser parser = new PEMParser(reader);
        PEMKeyPair keyPair = (PEMKeyPair)parser.readObject();
        AsymmetricKeyParameter privKeyParams = PrivateKeyFactory.createKey(keyPair.getPrivateKeyInfo());
        parser.close();

        CMSEnvelopedData data = new CMSEnvelopedData(encryptedData);
        RecipientInformationStore recipients = data.getRecipientInfos();

        Iterator it = recipients.getRecipients().iterator();

        if (it.hasNext()) {
            RecipientInformation recipient = (RecipientInformation)it.next();
            byte[] recData = recipient.getContent(new BcRSAKeyTransEnvelopedRecipient(privKeyParams));
            String decryptedValue = new String(recData);
            return decryptedValue;
        }
    }
    catch (Exception e) {
        e.printStackTrace();
    }
    finally {
        if (reader != null) {try {reader.close();} catch (Exception e) {e.printStackTrace();}}
    }
    return null;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-03-15
    • 2014-07-12
    • 1970-01-01
    • 2014-04-01
    • 1970-01-01
    • 2021-02-02
    • 2020-07-23
    • 2014-05-31
    相关资源
    最近更新 更多