抽象
这是涵盖Java加密算法的三部分博客系列的第3部分。 本系列介绍如何实现以下目标:
- 使用SHA–512散列
- 使用AES–256的单密钥对称加密
- RSA–4096
这第三篇文章详细介绍了如何实现非对称的RSA-4096公/私钥加密。 让我们开始吧。
免责声明
这篇文章仅供参考。 在使用所提供的任何信息之前,请认真思考。 从中学到东西,但最终自己做出决定,风险自负。
要求
我使用以下主要技术完成了本文的所有工作。 您可能可以使用不同的技术或版本来做相同的事情,但不能保证。
- Java 1.8.0_152_x64
- NetBeans 8.2(内部版本201609300101)
- Maven 3.0.5(与NetBeans捆绑在一起)
下载
访问我的GitHub页面以查看我所有的开源项目。 这篇文章的代码位于项目中: thoth-cryptography
非对称加密
关于
非对称算法基于两个密钥:公钥和私钥。 公钥负责加密,私钥负责解密。 公用密钥可以自由分发。 使用公共密钥,任何客户端都可以加密一条消息,只有您(使用私有密钥)可以解密该消息(非对称算法,第3段)。
非对称算法是Internet的主力军。 像SSH,OpenPGP,SSL和TLS之类的协议依赖于非对称算法(Rouse,2016,第2段)。 使用网络浏览器进行在线银行之类的工作的人都固有地知道非对称算法的重要性。
截至目前,已完成的研究似乎表明,以下是最佳和最安全的公钥/私钥,非对称加密算法(Sheth,2017年,“选择正确的算法”,第2段):
- 算法: RSA
- 模式: ECB //确实没有,但是需要ECB才能使Java工作。
- 填充: OAEPWithSHA–512AndMGF1Padding
- 密钥大小: 4096位
RSA不是分组密码,因此ECB模式没有多大意义,但是,即使未在内部使用该模式,也需要ECB
来使Java工作(Brightwell,2015年)。 OAEP提供了高度的随机性和填充性。 让我们看一个例子。
例
清单1是RsaTest.java单元测试。 这是以下内容的完整演示:
- 生成并存储RSA 4096位密钥
- RSA加密
- RSA解密
清单2显示了RsaKeyPairProducer.java 。 这是一个帮助程序类,负责产生一个新的KeyPair
。 KeyPair
对同时包含PublicKey
和PrivateKey
。
清单3显示了RsaPrivateKeyProducer.java 。 这是一个帮助程序类,负责从byte[]
再现PrivateKey
。
清单4显示了RsaPublicKeyProducer.java 。 这是一个帮助程序类,负责从byte[]
复制PublicKey
。
清单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;@BeforeClasspublic 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 RSAKeyPair 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());}}@Testpublic void encrypt_and_decrypt() throws Exception {// setupPrivateKey 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";// runbyte[] encryptedBytes= encrypter.encrypt(toEncrypt);System.out.printf("Encrypted %s%n", new String(encryptedBytes,"UTF-8"));String decrypted= decrypter.decrypt(encryptedBytes);// assertAssert.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检索。
- 最佳算法,模式和填充
- 使用4096位密钥
华盛顿州布莱特韦尔,雨披。 (2015年5月4日)。 ECB模式与RSA加密一起使用是否安全? 从https://crypto.stackexchange.com/questions/25420/is-ecb-mode-safe-to-use-with-rsa-encryption检索。
- 欧洲央行不适用; Java不会在幕后使用它。
- RSA使用随机性和填充来避免相同明文的ECB问题生成相同的密文
玛丽莲娜 (2016年11月29日)。 Java –非对称密码术示例。 从https://www.mkyong.com/java/java-asymmetric-cryptography-example/检索。
- 将公用/专用密钥写入文件
- 从文件中读取公钥/私钥
- 加密与解密
密钥大小。 (2017年10月12日)。 维基百科。 取自https://en.wikipedia.org/wiki/Key_size 。
- 到2030年,2048位密钥就足够了
- 如果2030年以后仍需要安全性,则应使用3072位的RSA密钥长度
用户4982。 (2013年11月4日)。 IV如何与RSA加密关联使用? 取自https://crypto.stackexchange.com/questions/11403/how-are-ivs-used-in-association-with-rsa-encryption 。
- IV值不适用于RSA
翻译自: https://www.javacodegeeks.com/2017/12/choosing-java-cryptographic-algorithms-part-3-public-private-key-asymmetric-encryption.html