Java 实现RSA非对称加密算法

网友投稿 223 2023-01-04

Java 实现RSA非对称加密算法

目录  公钥与私钥  java实现

公钥与私钥

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

公钥与私钥的生成有多种方式,可以通过程序生成(下文具体实现),可以通过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.bouncycastle

bcprov-jdk15on

1.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 < buffer.length; i++) {

result.append(String.format("%02x", buffer[i]));

}

return result.toString();

}

/**

* RSA私钥解密

*

* @param value

* 加密字符串

* @param privateKey

* 私钥

* @return 明文

* @throws Exception

* 解密过程中的异常信息

*/

public static String rsaDecrypt(String value, RSAPrivateKey privateKey) throws Exception {

if (value == null || value.length() == 0)

return "";

byte[] buffer = new byte[value.length() / 2];

for (int i = 0; i < buffer.length; i++) {

buffer[i] = (byte) Integer.parseInt(value.substring(i * 2, i * 2 + 2), 16);

}

// RSA解密

Cipher cipher = Cipher.getInstance("RSA");

cipher.init(Cipher.DECRYPT_MODE, privateKey);

buffer = cipher.doFinal(buffer);

return new String(buffer, "utf-8");

}

/**

* RSA签名

*

* @param value

* 加密字符串

* @param privateKey

* 私钥

* @param halg

* 加密算法,如MD5, SHA1, SHA256, SHA384, SHA512等

* @return 签名

* @throws Exception

* 签名过程中的异常信息

*/

public static String sign(String value, RSAPrivateKey privateKey, String halg) throws Exception {

Signature s = Signature.getInstance(halg.toUpperCase().endsWith("WithRSA") ? halg : (halg + "WithRSA"));

s.initSign(privateKey);

s.update(value.getBytes("utf-8"));

byte[] buffer = s.sign();

// 使用hex格式输出公钥

StringBuffer result = new StringBuffer();

for (int i = 0; i < buffer.length; i++) {

result.append(String.format("%02x", buffer[i]));

}

return result.toString();

}

/**

* RSA签名验证

*

* @param value

* 加密字符串

* @param publicKey

* 公钥

* @param halg

* 加密算法,如MD5, SHA1, SHA256, SHA384, SHA512等

* @return 签名合法则返回true,否则返回false

* @throws Exception

* 验证过程中的异常信息

*/

public static boolean verify(String value, RSAPublicKey publicKey, String signature, String halg) throws Exception {

Signature s = Signature.getInstance(halg.toUpperCase().endsWith("WithRSA") ? halg : (halg + "WithRSA"));

s.initVerify(publicKey);DMGYdXVq

s.update(value.getBytes("utf-8"));

byte[] buffer = new byte[signature.length() / 2];

for (int i = 0; i < buffer.length; i++) {

buffer[i] = (byte) Integer.parseInt(signature.substring(i * 2, i * 2 + 2), 16);

}

return s.verify(buffer);

}

}

生成公钥和私钥:

// 生成公私钥

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 = "上山打老虎";

DMGYdXVq 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非对称加密算法的详细内容,更多关于Java RSA非对称加密算法的资料请关注我们其它相关文章!

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:免费的音乐api接口网站(免费的音乐api接口网站推荐)
下一篇:api接口调用教程(API接口如何使用)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~