这是涵盖 Java 加密算法的三部分博客系列的第 3 部分。该系列涵盖了如何实现以下内容:
第 3 篇文章详细介绍了如何实现公钥/私钥、非对称、RSA–4096 加密。让我们开始吧。
这篇文章仅供参考。在使用所提供的任何信息之前,请认真思考。从中吸取教训,但最终做出自己的决定需要您自担风险。
我使用以下主要技术完成了这篇文章的所有工作。您也许可以使用不同的技术或版本做同样的事情,但不能保证。
访问我的 GitHub 页面,查看我所有的开源项目。这篇文章的代码位于项目中:thoth-cryptography
非对称算法基于 2 个密钥:公钥和私钥。公钥负责加密,私钥负责解密。公钥可以自由分发。使用公钥,任何客户端都可以加密只有您使用私钥才能解密的消息(非对称算法,n.d. 第 3 段)。
非对称算法是互联网的主力军。 SSH、OpenPGP、SSL 和 TLS 等协议依赖于非对称算法(Rouse,2016 年,第 2 段)。任何使用 Web 浏览器进行网上银行之类的事情的人都天生就知道非对称算法的重要性。
截至今天所做的研究似乎表明最好和最安全的公钥/私钥、非对称加密算法如下(Sheth,2017 年,“选择正确的算法”,第 2 段):
RSA 不是块密码,因此 ECB 模式没有多大意义,但是,需要 ECB
才能使 Java 工作,即使该模式并未在幕后使用(Brightwell,2015)。 OAEP 提供高水平的随机性和填充。让我们看一个例子。
清单 1 是 RsaTest.java 单元测试。这是对以下内容的完整演示:
清单 2 显示了 RsaKeyPairProducer.java。这是一个帮助程序类,负责生成一个新的 KeyPair
。 KeyPair
包含 PublicKey
和 PrivateKey
。清单 3 显示了 RsaPrivateKeyProducer.java。这是一个帮助程序类,负责从 PrivateKey
复制 byte[]
。
清单 4 显示了 RsaPublicKeyProducer.java。这是一个帮助程序类,负责从 PublicKey
复制 byte[]
。
清单 5 显示了 ByteArrayWriter.java,清单 6 显示了 ByteArrayReader.java。这些是负责将 byte[]
读取和写入文件的辅助类。由您决定如何存储密钥的 byte[]
,但它需要安全地存储在某个地方(文件、数据库、git 存储库等)。
清单 7 显示了 RsaEncrypter.java。这是一个负责加密的辅助类。
最后,清单 8 显示了 RsaDecrypter.java。这是一个负责解密的辅助类。
清单 1 – RsaTest.java 类
package org.thoth.crypto.asymmetric; import java.nio.file.Path; import java.nio.file.Paths; import java.security.KeyPair; import java.security.PrivateKey; import java.security.PublicKey; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; import org.thoth.crypto.io.ByteArrayReader; import org.thoth.crypto.io.ByteArrayWriter; /** * * @author Michael Remijan mjremijan@yahoo.com @mjremijan */ public class RsaTest { static Path privateKeyFile; static Path publicKeyFile; @BeforeClass public static void beforeClass() throws Exception { // Store the PrivateKey and PublicKey bytes in the ./target // diretory. Do this so it will be ignore by source control. // We don't want this file committed. privateKeyFile = Paths.get("./target/RsaPrivate.key").toAbsolutePath(); publicKeyFile = Paths.get("./target/RsaPublic.key").toAbsolutePath(); // Create KeyPair for RSA KeyPair keyPair = new RsaKeyPairProducer().produce(); // Store the PrivateKey bytes. This is what // you want to keep absolutely safe { ByteArrayWriter writer = new ByteArrayWriter(privateKeyFile); writer.write(keyPair.getPrivate().getEncoded()); } // Store the PublicKey bytes. This you // can freely distribute so others can // encrypt messages which you can then // decrypt with the PrivateKey you keep safe. { ByteArrayWriter writer = new ByteArrayWriter(publicKeyFile); writer.write(keyPair.getPublic().getEncoded()); } } @Test public void encrypt_and_decrypt() throws Exception { // setup PrivateKey privateKey = new RsaPrivateKeyProducer().produce( new ByteArrayReader(privateKeyFile).read() ); PublicKey publicKey = new RsaPublicKeyProducer().produce( new ByteArrayReader(publicKeyFile).read() ); RsaDecrypter decrypter = new RsaDecrypter(privateKey); RsaEncrypter encrypter = new RsaEncrypter(publicKey); String toEncrypt = "encrypt me"; // run byte[] encryptedBytes = encrypter.encrypt(toEncrypt); System.out.printf("Encrypted %s%n", new String(encryptedBytes,"UTF-8")); String decrypted = decrypter.decrypt(encryptedBytes); // assert Assert.assertEquals(toEncrypt, decrypted); } }
清单 2 – RsaKeyPairProducer.java 类
package org.thoth.crypto.asymmetric; import java.security.KeyPair; import java.security.KeyPairGenerator; /** * * @author Michael Remijan mjremijan@yahoo.com @mjremijan */ public class RsaKeyPairProducer { /** * Generates a new RSA-4096 bit {@code KeyPair}. * * @return {@code KeyPair}, never null * @throws RuntimeException All exceptions are caught * and re-thrown as {@code RuntimeException} */ public KeyPair produce() { KeyPairGenerator keyGen; try { keyGen = KeyPairGenerator.getInstance("RSA"); //keyGen.initialize(3072); keyGen.initialize(4096); return keyGen.generateKeyPair(); } catch (Exception ex) { throw new RuntimeException(ex); } } }
清单 3 – RsaPrivateKeyProducer.java 类
package org.thoth.crypto.asymmetric; import java.security.KeyFactory; import java.security.PrivateKey; import java.security.spec.PKCS8EncodedKeySpec; /** * * @author Michael Remijan mjremijan@yahoo.com @mjremijan */ public class RsaPrivateKeyProducer { /** * Regenerates a previous RSA {@code PrivateKey}. * * @param encodedByteArrayForPrivateKey The bytes this method * will use to regenerate a previously created {@code PrivateKey} * * @return {@code PrivateKey}, never null * @throws RuntimeException All exceptions are caught * and re-thrown as {@code RuntimeException} */ public PrivateKey produce(byte[] encodedByteArrayForPrivateKey) { try { PrivateKey privateKey = KeyFactory.getInstance("RSA") .generatePrivate(new PKCS8EncodedKeySpec(encodedByteArrayForPrivateKey)); return privateKey; } catch (Exception ex) { throw new RuntimeException(ex); } } }
清单 4 – RsaPublicKeyProducer.java 类
package org.thoth.crypto.asymmetric; import java.security.KeyFactory; import java.security.PublicKey; import java.security.spec.X509EncodedKeySpec; /** * * @author Michael Remijan mjremijan@yahoo.com @mjremijan */ public class RsaPublicKeyProducer { /** * Regenerates a previous RSA {@code PublicKey}. * * @param encodedByteArrayForPublicKey The bytes this method * will use to regenerate a previously created {@code PublicKey} * * @return {@code PublicKey}, never null * @throws RuntimeException All exceptions are caught * and re-thrown as {@code RuntimeException} */ public PublicKey produce(byte[] encodedByteArrayForPublicKey) { try { PublicKey publicKey = KeyFactory.getInstance("RSA") .generatePublic(new X509EncodedKeySpec(encodedByteArrayForPublicKey)); return publicKey; } catch (Exception ex) { throw new RuntimeException(ex); } } }
清单 5 – ByteArrayWriter.java 类
package org.thoth.crypto.io; import java.io.IOException; import java.io.OutputStream; import java.io.PrintWriter; import java.nio.file.Files; import java.nio.file.Path; /** * * @author Michael Remijan mjremijan@yahoo.com @mjremijan */ public class ByteArrayWriter { protected Path outputFile; private void initOutputFile(Path outputFile) { this.outputFile = outputFile; } private void initOutputDirectory() { Path outputDirectory = outputFile.getParent(); if (!Files.exists(outputDirectory)) { try { Files.createDirectories(outputDirectory); } catch (IOException e) { throw new RuntimeException(e); } } } public ByteArrayWriter(Path outputFile) { initOutputFile(outputFile); initOutputDirectory(); } public void write(byte[] bytesArrayToWrite) { try ( OutputStream os = Files.newOutputStream(outputFile); PrintWriter writer = new PrintWriter(os); ){ for (int i=0; i<bytesArrayToWrite.length; i++) { if (i>0) { writer.println(); } writer.print(bytesArrayToWrite[i]); } } catch (IOException ex) { throw new RuntimeException(ex); } } }
清单 6 – ByteArrayReader.java 类
package org.thoth.crypto.io; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.file.Path; import java.util.Scanner; /** * * @author Michael Remijan mjremijan@yahoo.com @mjremijan */ public class ByteArrayReader { protected Path inputFile; public ByteArrayReader(Path inputFile) { this.inputFile = inputFile; } public byte[] read() { try ( Scanner scanner = new Scanner(inputFile); ByteArrayOutputStream baos = new ByteArrayOutputStream(); ){ while (scanner.hasNext()) { baos.write(Byte.parseByte(scanner.nextLine())); } baos.flush(); return baos.toByteArray(); } catch (IOException ex) { throw new RuntimeException(ex); } } }
清单 7 – RsaEncrypter.java 类
package org.thoth.crypto.asymmetric; import java.security.PublicKey; import javax.crypto.Cipher; /** * * @author Michael Remijan mjremijan@yahoo.com @mjremijan */ public class RsaEncrypter { protected RsaCipher cipher; public RsaEncrypter(PublicKey key) { this.cipher = new RsaCipher(Cipher.ENCRYPT_MODE, key); } public byte[] encrypt(String message) { try { return cipher .update(message.getBytes("UTF-8")) .doFinal() ; } catch (Exception e) { throw new RuntimeException(e); } } }
清单 8 – RsaDecrypter.java 类
package org.thoth.crypto.asymmetric; import java.security.PrivateKey; import javax.crypto.Cipher; /** * * @author Michael Remijan mjremijan@yahoo.com @mjremijan */ public class RsaDecrypter { protected RsaCipher cipher; public RsaDecrypter(PrivateKey key) { this.cipher = new RsaCipher(Cipher.DECRYPT_MODE, key); } public String decrypt(byte[] message) { try { return new String( cipher.update(message).doFinal() , "UTF-8" ); } catch (Exception e) { throw new RuntimeException(e); } } }
加密并不容易。简单的示例将导致您的应用程序具有安全漏洞的实现。如果您需要公钥/私钥、非对称加密算法,请使用带有 4096 位密钥的 RSA/ECB/OAEPWithSHA–512AndMGF1Padding。
Sheth, M.(2017 年,4 月 18 日)。 Java密码学中的加密和解密。取自 https://www.veracode.com/blog/research/encryption-and-decryption-java-cryptography。
Brightwell, W.,斗篷。 (2015 年 5 月 4 日)。 ECB 模式与 RSA 加密一起使用是否安全?取自 https://crypto.stackexchange.com/questions/25420/is-ecb-mode-safe-to-use-with-rsa-encryption。
玛丽莲娜。 (2016 年,11 月 29 日)。 Java – 非对称加密示例。取自 https://www.mkyong.com/java/java-asymmetric-cryptography-example/。
密钥大小。 (2017 年,10 月 12 日)。维基百科,免费的百科全书。取自“https://en.wikipedia.org/wiki/Key_size。
用户4982。 (2013 年,11 月 4 日)。 IV 如何与 RSA 加密结合使用?。从 https://crypto.stackexchange.com/questions/11403/how-are-ivs-used-in-association-with-rsa-encryption 中检索。
标签2: Java教程地址:https://www.cundage.com/article/jcg-choosing-java-cryptographic-algorithms-part-3-public-private-key-asymmetric-encryption.html