Bra*_*out 1 java encryption openssl certificate
所以我有一个非常基本的 openssl 命令提供给我openssl smime -encrypt -binary -aes-256-cbc -in $inPath -out $encryptedPath -outform DER $pubCert,这个命令也可以正常工作并输出一个加密文件。我需要在 java 应用程序中使用此命令的等效项,最好不要调用进程并使用 openssl 本身(只是因为我觉得这可能是不好的做法)。
我已经研究了很多,似乎没有任何等效的东西可以找到。我尝试了几件事,但大多数似乎都不起作用。奇怪的是......我能够使用我编写的代码获得一个简单的“Hello World”字符串来加密(尽管我不相信它会正确加密它,因为我将密码设置为“RSA”而不是“ AES") 但是当字节数组来自一个文件时,它默默地失败了,只写了 0 个字节。现在这就是我的代码的样子。
Cipher aes = Cipher.getInstance("RSA");
CertificateFactory certF = CertificateFactory.getInstance("X.509");
File public_cert = new File( getClass().getClassLoader().getResource("public.crt").getFile());
FileInputStream certIS = new FileInputStream(public_cert);
X509Certificate cert = (X509Certificate) certF.generateCertificate(certIS);
certIS.close();
aes.init(Cipher.ENCRYPT_MODE, cert);
File tarGz = new File("C:\\volatile\\generic.tar.gz");
FileInputStream fis = new FileInputStream(tarGz);
byte[] tarGzBytes = FileUtils.readFileToByteArray(tarGz);
tarGzBytes = "Hello World".getBytes();
ByteArrayInputStream bais = new ByteArrayInputStream("Hello World".getBytes());
File encFile = new File("C:\\volatile\\generic.tar.gz.enc");
FileOutputStream enc = new FileOutputStream(encFile);
CipherOutputStream cos = new CipherOutputStream(enc, aes);
cos.write(tarGzBytes);
//IOUtils.copy(fis, cos);
//IOUtils.copy(bais, cos);
cos.flush();
cos.close();
Run Code Online (Sandbox Code Playgroud)
所以这是有效的,并加密一个小文件,其中Hello World加密。我不相信这是 AES-256-CBC,当我使用FileUtils.readFileToByteArray(tarGz). 这对我来说似乎很奇怪,它适用于"Hello World".toByteArray()而不是FileUtils.readAllBytes(tarGz). 另外作为旁注,ByteArrayInputStreamusingIOUtils.copy有效,而FileInputStream版本也写入 0 个字节。
另外,当我将密码模式设置为AES/CBC/PKCS5Padding(因为我在网上发现一些建议将其设置为该模式并且它看起来更像我想要的)时,我收到以下错误消息:
java.security.InvalidKeyException: No installed provider supports this key: sun.security.rsa.RSAPublicKeyImpl
at javax.crypto.Cipher.chooseProvider(Cipher.java:892)
at javax.crypto.Cipher.init(Cipher.java:1724)
~~~~
Run Code Online (Sandbox Code Playgroud)
如果有人有任何建议,或者我需要提供更多信息,请告诉我。我现在相当卡住了,此时我正在讨论编写一个脚本来简单地运行 openssl 命令并从 java 运行该脚本......
通读@dave-thompson-085 的回答后,我意识到有一个很好的理由为什么我找不到我想做的事情。因此,我决定继续openssl使用进程构建器从 java调用进程。我能够从上面重新创建 openssl 命令作为 java 中的进程,启动它并使用以下代码运行它:
File cert = new File(getClass().getClassLoader().getResource("public.crt").getFile());
ProcessBuilder openSslBuilder = new ProcessBuilder("openssl", "smime", "-encrypt", "-binary",
"-aes-256-cbc", "-in", "C:\\volatile\\generic.tar.gz", "-out",
"C:\\volatile\\generic.tar.gz.enc", "-outform", "DER", cert.getPath());
Process openssl = openSslBuilder.start();
openssl.waitFor();
System.out.println(openssl.exitValue());
openssl.destroy();
Run Code Online (Sandbox Code Playgroud)
希望这可以帮助其他想要尝试此操作的人,并可能为某人节省大量时间!
首先,要明确:该openssl smime命令实际上处理 S/MIME和CMS(又名 PKCS7)格式;这些是相关但不同的标准,它们基本上对基本相同的加密操作使用不同的文件格式。随着-outform DER你实际上是在做CMS / PKCS7。
第二个也是更基本的:CMS/PKCS7 和 S/MIME,以及大多数其他常见的加密方案,如 PGP,实际上进行混合加密。您的数据实际上并未使用 RSA 加密;相反,您的数据使用对称算法(此处为 AES-256-CBC,因为您选择了该算法)使用随机生成的密钥加密,称为 DEK(数据加密密钥)和DEK使用接收者的公钥(从他们的证书获得)用 RSA 加密,并且这两个结果加上大量元数据被安排成一个相当复杂的数据结构。接收者可以解析消息以提取这些片段,然后使用 RSA 和他们的私钥来解密 DEK,然后使用 DEK 对数据进行 AES 解密。请注意,您始终对 RSA 使用 RSA 密钥,对 AES 使用 AES 密钥;对称密钥几乎都是比特,只是大小不同,但包括 RSA(还有 DH、DSA、ECC 等)在内的公钥加密密钥要复杂得多,不能混合。
尝试像您一样直接使用 RSA 加密数据,除了会出错之外,一般不会起作用,因为 RSA 只能加密有限数量的数据,具体取决于所使用的密钥大小,通常约为 100-200 字节。对称加密也有一些限制,但通常要大得多;AES-CBC 适用于大约 250,000,000,000,000,000 字节。
如果您想自己实现这一点,您需要阅读CMS 标准,特别是使用 KeyTransRecipientInfo(用于 RSA)的 EnvelopedData 部分,并结合ASN.1 BER/DER 编码规则。这不是一项简单的工作,尽管如果您愿意付出努力,它可以完成。
如果您可以在 Java 中使用第三方库,则来自https://www.bouncycastle.org的“bcpkix”jar具有支持 CMS 的例程以及其他一些内容。如果您正在编写自己或在您的部门中运行的程序,这通常很容易。如果要将其交付给可能不喜欢管理依赖项的外部用户或客户,则可能不会。
也就是说,在我的书中运行另一个程序来做某事不一定是不好的做法,并且可以直接从 java 完成(没有脚本)。除非您(需要)经常这样做,例如每秒 100 次。
| 归档时间: |
|
| 查看次数: |
1248 次 |
| 最近记录: |