Java 实现RSA非对称加密算法

RSA解决了对称加密的一个不足,比如AES算法加密和解密时使用的是同一个秘钥,因此这个秘钥不能公开,因此对于需要公开秘钥的场合,我们需要在加密和解密过程中使用不同的秘钥,加密使用的公钥可以公开,解密使用的私钥要保密,这就是非对称加密的好处。 

  公钥与私钥

  公钥与私钥是成对的,一般的,我们认为的是公钥加密、私钥解密、私钥签名、公钥验证,有人说成私钥加密,公钥解密时不对的。

  公钥与私钥的生成有多种方式,可以通过程序生成(下文具体实现),可以通过openssl工具:  

 # 生成一个私钥,推荐使用1024位的秘钥,秘钥以pem格式保存到-out参数指定的文件中,采用PKCS1格式 openssl genrsa -out rsa.pem 1024 # 生成与私钥对应的公钥,生成的是Subject Public Key,一般配合PKCS8格式私钥使用 openssl rsa -in rsa.pem -pubout -out rsa.pub  

  RSA生成公钥与私钥一般有两种格式:PKCS1和PKCS8,上面的命令生成的秘钥是PKCS1格式的,而公钥是Subject Public Key,一般配合PKCS8格式私钥使用,所以就可能会涉及到PKCS1和PKCS8之间的转换:

 # PKCS1格式私钥转换为PKCS8格式私钥,私钥直接输出到-out参数指定的文件中 openssl pkcs8 -topk8 -inform PEM -in rsa.pem -outform pem -nocrypt -out rsa_pkcs8.pem # PKCS8格式私钥转换为PKCS1格式私钥,私钥直接输出到-out参数指定的文件中 openssl rsa -in rsa_pkcs8.pem -out rsa_pkcs1.pem # PKCS1格式公钥转换为PKCS8格式公钥,转换后的内容直接输出 openssl rsa -pubin -in rsa.pub -RSAPublicKey_out # PKCS8格式公钥转换为PKCS1格式公钥,转换后的内容直接输出 openssl rsa -RSAPublicKey_in -pubout -in rsa.pub

  现实中,我们往往从pem、crt、pfx文件获取公私和私钥,crt、pfx的制作可以参考:简单的制作ssl证书,并在nginx和IIS中使用,或者使用现成的:https://pan.baidu.com/s/1MJ5YmuZiLBnf-DfNR_6D7A (提取码:c6tj),密码都是:123456

  Java实现

  为简化说明介绍,这里我直接封装了一个工具类,因为要从pem、crt、pfx文件获取公私和私钥,因此引用了一个第三方包:BouncyCastle,可以直接在pom.xml中添加依赖:  

  org.bouncycastlebcprov-jdk15on1.68

  或者去mvn上下载:跳转

  简单封装的RsaUtil.java:  

 import java.io.FileInputStream; import java.io.FileReader; import java.io.FileWriter; import java.math.BigInteger; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.KeyStore; import java.security.PrivateKey; import java.security.PublicKey; import java.security.SecureRandom; import java.security.Security; import java.security.Signature; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.KeySpec; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.RSAPrivateCrtKeySpec; import java.security.spec.RSAPublicKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.Enumeration; import javax.crypto.Cipher; import org.bouncycastle.asn1.ASN1Integer; import org.bouncycastle.asn1.ASN1Primitive; import org.bouncycastle.asn1.DLSequence; import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.util.io.pem.PemObject; import org.bouncycastle.util.io.pem.PemReader; import org.bouncycastle.util.io.pem.PemWriter; public class RsaUtil { static { Security.addProvider(new BouncyCastleProvider()); } /** * 随机生成密钥对 * * @param usePKCS8 *            是否采用PKCS8填充模式 */ public static Object[] generateRsaKey(boolean usePKCS8) throws Exception { KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA", BouncyCastleProvider.PROVIDER_NAME); // 初始化 keyPairGen.initialize(1024, new SecureRandom()); // 生成一个密钥对,保存在keyPair中 KeyPair keyPair = keyPairGen.generateKeyPair(); RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); // 得到私钥 RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); // 得到公钥 // 这两个公私钥是PKCS8格式的 byte[] publicKeyBytes = publicKey.getEncoded(); byte[] privateKeyBytes = privateKey.getEncoded(); if (!usePKCS8) { // 将PSCK8格式公私钥转换为PKCS1格式 publicKeyBytes = pkcs8ToPkcs1(false, publicKeyBytes); privateKeyBytes = pkcs8ToPkcs1(true, privateKeyBytes); } return new Object[] { publicKeyBytes, privateKeyBytes }; } /** * 从Pem文件读取密钥对 * * @param reader *            输入流 * @param pemFileName *            pem文件 */ public static byte[] readFromPem(String pemFileName) throws Exception { PemReader pemReader = new PemReader(new FileReader(pemFileName)); PemObject pemObject = pemReader.readPemObject(); byte[] publicKey = pemObject.getContent(); pemReader.close(); return publicKey; } /** * 从Pem文件读取密钥 * * @param isPrivateKey *            是否是私钥 * @param buffer *            字节 * @param pemFileName *            pem文件 */ public static void writeToPem(byte[] buffer, boolean isPrivateKey, String pemFileName) throws Exception { PemObject pemObject = new PemObject(isPrivateKey ? "RSA PRIVATE KEY" : "RSA PUBLIC KEY", buffer); FileWriter fileWriter = new FileWriter(pemFileName); PemWriter pemWriter = new PemWriter(fileWriter); pemWriter.writeObject(pemObject); pemWriter.close(); } /** * 从crt文件读取公钥(pkcs8) * * @param crtFileName *            crt文件 * @return 公钥 */ public static byte[] readPublicKeyFromCrt(String crtFileName) throws Exception { CertificateFactory cf = CertificateFactory.getInstance("X.509"); X509Certificate cert = (X509Certificate) cf.generateCertificate(new FileInputStream(crtFileName)); PublicKey publicKey = cert.getPublicKey(); return publicKey.getEncoded(); } /** * 从pfx文件读取秘钥对(pkcs8) * * @param pfxFileName *            pfx文件 * @return 秘钥对 */ public static Object[] readFromPfx(String pfxFileName, String password) throws Exception { KeyStore keystore = KeyStore.getInstance("PKCS12"); char[] passwordChars = null; if (password == null || password.equals("")) { passwordChars = null; } else { passwordChars = password.toCharArray(); } keystore.load(new FileInputStream(pfxFileName), passwordChars); Enumeration enums = keystore.aliases(); PrivateKey privateKey = null; Certificate certificate = null; while (enums.hasMoreElements()) { String alias = enums.nextElement(); System.out.println(alias); if (keystore.isKeyEntry(alias)) { privateKey = (PrivateKey) keystore.getKey(alias, passwordChars); certificate = keystore.getCertificate(alias); } if (privateKey != null && certificate != null) break; } if (privateKey == null || certificate == null) { throw new Exception("fail to read key from pfx"); } PublicKey publicKey = certificate.getPublicKey(); return new Object[] { publicKey.getEncoded(), privateKey.getEncoded() }; } /** * Pkcs8转Pkcs1 * * @param isPrivateKey *            是否是私钥转换 * @param buffer *            Pkcs1秘钥 * @return Pkcs8秘钥 * @throws Exception *             加密过程中的异常信息 */ public static byte[] pkcs8ToPkcs1(boolean isPrivateKey, byte[] buffer) throws Exception { if (isPrivateKey) { PrivateKeyInfo privateKeyInfo = PrivateKeyInfo.getInstance(buffer); return privateKeyInfo.parsePrivateKey().toASN1Primitive().getEncoded(); } else { SubjectPublicKeyInfo subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(buffer); return subjectPublicKeyInfo.parsePublicKey().toASN1Primitive().getEncoded(); } } /** * Pkcs1转Pkcs8 * * @param isPrivateKey *            是否是私钥转换 * @param buffer *            Pkcs1秘钥 * @return Pkcs8秘钥 * @throws Exception *             加密过程中的异常信息 */ public static byte[] pkcs1ToPkcs8(boolean isPrivateKey, byte[] buffer) throws Exception { AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption); ASN1Primitive asn1Primitive = ASN1Primitive.fromByteArray(buffer); if (isPrivateKey) { PrivateKeyInfo privateKeyInfo = new PrivateKeyInfo(algorithmIdentifier, asn1Primitive); return privateKeyInfo.getEncoded(); } else { SubjectPublicKeyInfo subjectPublicKeyInfo = new SubjectPublicKeyInfo(algorithmIdentifier, asn1Primitive); return subjectPublicKeyInfo.getEncoded(); } } /** * RSA公钥 * * @param usePKCS8 *            是否采用PKCS8填充模式 * @param publicKey *            公钥 * @return 公钥 * @throws Exception *             加密过程中的异常信息 */ public static RSAPublicKey generatePublicKey(boolean usePKCS8, byte[] publicKey) throws Exception { KeySpec keySpec; if (usePKCS8) { // PKCS8填充 keySpec = new X509EncodedKeySpec(publicKey); } else { // PKCS1填充 DLSequence sequence = (DLSequence) ASN1Primitive.fromByteArray(publicKey); BigInteger v1 = ((ASN1Integer) sequence.getObjectAt(0)).getValue(); BigInteger v2 = ((ASN1Integer) sequence.getObjectAt(1)).getValue(); keySpec = new RSAPublicKeySpec(v1, v2); } RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA", BouncyCastleProvider.PROVIDER_NAME).generatePublic(keySpec); return pubKey; } /** * RSA私钥 * * @param usePKCS8 *            是否采用PKCS8填充模式 * @param privateKey *            私钥 * @return 私钥 * @throws Exception *             解密过程中的异常信息 */ public static RSAPrivateKey generatePrivateKey(boolean usePKCS8, byte[] privateKey) throws Exception { KeySpec keySpec; if (usePKCS8) { // PKCS8填充 keySpec = new PKCS8EncodedKeySpec(privateKey); } else { // PKCS1填充 DLSequence sequence = (DLSequence) ASN1Primitive.fromByteArray(privateKey); // BigInteger v1= ((ASN1Integer)sequence.getObjectAt(0)).getValue(); BigInteger v2 = ((ASN1Integer) sequence.getObjectAt(1)).getValue(); BigInteger v3 = ((ASN1Integer) sequence.getObjectAt(2)).getValue(); BigInteger v4 = ((ASN1Integer) sequence.getObjectAt(3)).getValue(); BigInteger v5 = ((ASN1Integer) sequence.getObjectAt(4)).getValue(); BigInteger v6 = ((ASN1Integer) sequence.getObjectAt(5)).getValue(); BigInteger v7 = ((ASN1Integer) sequence.getObjectAt(6)).getValue(); BigInteger v8 = ((ASN1Integer) sequence.getObjectAt(7)).getValue(); BigInteger v9 = ((ASN1Integer) sequence.getObjectAt(8)).getValue(); keySpec = new RSAPrivateCrtKeySpec(v2, v3, v4, v5, v6, v7, v8, v9); } RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA", BouncyCastleProvider.PROVIDER_NAME).generatePrivate(keySpec); return priKey; } /** * RSA公钥加密 * * @param value *            加密字符串 * @param publicKey *            公钥 * @return 密文 * @throws Exception *             加密过程中的异常信息 */ public static String rsaEncrypt(String value, RSAPublicKey publicKey) throws Exception { if (value == null || value.length() == 0) return ""; // RSA加密 Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.ENCRYPT_MODE, publicKey); byte[] buffer = cipher.doFinal(value.getBytes("utf-8")); // 使用hex格式输出公钥 StringBuffer result = new StringBuffer(); for (int i = 0; i 

   生成公钥和私钥:  

 // 生成公私钥 Object[] rsaKey = RsaUtil.generateRsaKey(usePKCS8); //usePKCS8=true表示是否成PKCS8格式的公私秘钥,否则乘车PKCS1格式的公私秘钥 byte[] publicKey = (byte[]) rsaKey[0]; byte[] privateKey = (byte[]) rsaKey[1];

  生成秘钥后,需要保存,一般保存到pem文件中: 

 // 保存到pem文件,filePath是保存目录 RsaUtil.writeToPem(publicKey, false, filePath + "rsa.pub"); RsaUtil.writeToPem(privateKey, true, filePath + "rsa.pem");

   可以保存到pem文件中,当然也可以从pem文件中读取了:  

 // 从Pem文件读取公私钥,filePath是文件目录 byte[] publicKey = RsaUtil.readFromPem(filePath + "rsa.pub"); byte[] privateKey = RsaUtil.readFromPem(filePath + "rsa.pem");

  还可以从crt证书中读取公钥,而crt文件不包含私钥,因此需要单独获取私钥: 

 // 从crt文件读取公钥(crt文件中不包含私钥),filePath是文件目录 byte[] publicKey = RsaUtil.readPublicKeyFromCrt(filePath + "demo.crt"); byte[] privateKey = RsaUtil.readFromPem(filePath + "demo.key");

   pfx文件中包含了公钥和私钥,可以很方便就读取到:  

 // 从pfx文件读取公私钥,filePath是文件目录 Object[] rsaKey = RsaUtil.readFromPfx(filePath + "demo.pfx", "123456"); byte[] publicKey = (byte[]) rsaKey[0]; byte[] privateKey = (byte[]) rsaKey[1];

  有时候我们还可能需要进行秘钥的转换:  

 // Pkcs8格式公钥转换为Pkcs1格式公钥 publicKey = RsaUtil.pkcs8ToPkcs1(false, publicKey); // Pkcs8格式私钥转换为Pkcs1格式私钥 privateKey = RsaUtil.pkcs8ToPkcs1(true, privateKey); // Pkcs1格式公钥转换为Pkcs8格式公钥 publicKey = RsaUtil.pkcs1ToPkcs8(false, publicKey); // Pkcs1格式私钥转换为Pkcs8格式私钥 privateKey = RsaUtil.pkcs1ToPkcs8(true, privateKey);

  有了公钥和私钥,接下就就能实现加密、解密、签名、验证签名等操作了:  

 RSAPublicKey rsaPublicKey = RsaUtil.generatePublicKey(usePKCS8, publicKey); RSAPrivateKey rsaPrivateKey = RsaUtil.generatePrivateKey(usePKCS8, privateKey); String encryptText = RsaUtil.rsaEncrypt(text, rsaPublicKey); System.out.printf("【%s】经过【RSA】加密后:%s\n", text, encryptText); String decryptText = RsaUtil.rsaDecrypt(encryptText, rsaPrivateKey); System.out.printf("【%s】经过【RSA】解密后:%s\n", encryptText, decryptText); String signature = RsaUtil.sign(text, rsaPrivateKey, "MD5"); System.out.printf("【%s】经过【RSA】签名后:%s\n", text, signature); boolean result = RsaUtil.verify(text, rsaPublicKey, signature, "MD5"); System.out.printf("【%s】的签名【%s】经过【RSA】验证后结果是:" + result, text, signature);

  这里完整的demo代码:

 import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; public class RsaMain { public static void main(String[] args) { try { String text = "上山打老虎"; boolean usePKCS8 = true; // usePKCS8=true表示是否成PKCS8格式的公私秘钥,否则乘车PKCS1格式的公私秘钥 String filePath = RsaUtil.class.getClassLoader().getResource("").getPath(); System.out.printf("文件路径:%s\n", filePath);// 存放pem,crt,pfx等文件的目录 byte[] publicKey, privateKey;// 公钥和私钥 // 生成公私钥 Object[] rsaKey = RsaUtil.generateRsaKey(usePKCS8); // usePKCS8=true表示是否成PKCS8格式的公私秘钥,否则乘车PKCS1格式的公私秘钥 publicKey = (byte[]) rsaKey[0]; privateKey = (byte[]) rsaKey[1]; // 从Pem文件读取公私钥,filePath是文件目录 // publicKey = RsaUtil.readFromPem(filePath + "rsa.pub"); // privateKey = RsaUtil.readFromPem(filePath + "rsa.pem"); // 从pfx文件读取公私钥,filePath是文件目录 // Object[] rsaKey = RsaUtil.readFromPfx(filePath + "demo.pfx", // "123456"); // publicKey = (byte[]) rsaKey[0]; // privateKey = (byte[]) rsaKey[1]; // 从crt文件读取公钥(crt文件中不包含私钥),filePath是文件目录 // publicKey = RsaUtil.readPublicKeyFromCrt(filePath + "demo.crt"); // privateKey = RsaUtil.readFromPem(filePath + "demo.key"); // 保存到pem文件,filePath是保存目录 RsaUtil.writeToPem(publicKey, false, filePath + "rsa.pub"); RsaUtil.writeToPem(privateKey, true, filePath + "rsa.pem"); // Pkcs8格式公钥转换为Pkcs1格式公钥 publicKey = RsaUtil.pkcs8ToPkcs1(false, publicKey); // Pkcs8格式私钥转换为Pkcs1格式私钥 privateKey = RsaUtil.pkcs8ToPkcs1(true, privateKey); // Pkcs1格式公钥转换为Pkcs8格式公钥 publicKey = RsaUtil.pkcs1ToPkcs8(false, publicKey); // Pkcs1格式私钥转换为Pkcs8格式私钥 privateKey = RsaUtil.pkcs1ToPkcs8(true, privateKey); RSAPublicKey rsaPublicKey = RsaUtil.generatePublicKey(usePKCS8, publicKey); RSAPrivateKey rsaPrivateKey = RsaUtil.generatePrivateKey(usePKCS8, privateKey); String encryptText = RsaUtil.rsaEncrypt(text, rsaPublicKey); System.out.printf("【%s】经过【RSA】加密后:%s\n", text, encryptText); String decryptText = RsaUtil.rsaDecrypt(encryptText, rsaPrivateKey); System.out.printf("【%s】经过【RSA】解密后:%s\n", encryptText, decryptText); String signature = RsaUtil.sign(text, rsaPrivateKey, "MD5"); System.out.printf("【%s】经过【RSA】签名后:%s\n", text, signature); boolean result = RsaUtil.verify(text, rsaPublicKey, signature, "MD5"); System.out.printf("【%s】的签名【%s】经过【RSA】验证后结果是:" + result, text, signature); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }

以上就是Java 实现RSA非对称加密算法的详细内容,更多请关注0133技术站其它相关文章!

赞(0) 打赏
未经允许不得转载:0133技术站首页 » Java