簡體   English   中英

使用JAVA等效的OpenSSL命令

[英]OpenSSL command equivalent using JAVA

所以我有一個非常基本的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();

所以這可以工作,並加密一個加密了Hello World的小文件。 我不相信這是AES-256-CBC,並且當我使用FileUtils.readFileToByteArray(tarGz)時它不起作用,盡管調試器中的結果字節數組的大小正確為大約94MB。 這對我來說真的很奇怪,它適用於"Hello World".toByteArray()而不是FileUtils.readAllBytes(tarGz) 另外作為旁注,使用IOUtils.copyByteArrayInputStream可以工作,而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)
~~~~

如果有人有任何建議,或者我需要提供更多信息,請告訴我。 我現在相當困難,我正在討論編寫一個腳本來簡單地運行openssl命令並從java運行該腳本...

結論

通過閱讀@ dave-thompson-085的答案后,我意識到我找不到想要做的事情有一個很好的理由。 因此,我決定繼續使用流程構建器從java調用openssl進程。 我能夠從上面重新創建openssl命令作為java中的Process,啟動它並使用以下代碼運行它:

  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();

希望這可以幫助那些想要嘗試這個的人,也許可以節省一些時間!

首先,要明確: openssl smime命令實際上處理S / MIME CMS(aka 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次。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM