RSA 数字签名—— Java 实现
程序员文章站
2022-03-14 20:29:44
...
依赖
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.6</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.9</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
代码
package awesome.data.structure.algorithm.encrypt.rsa;
import awesome.data.structure.algorithm.encrypt.md5.Md5Util;
import org.apache.tomcat.util.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
/**
* RSA 工具类
*
* @author: Andy
* @time: 2019/5/10 16:50
* @since
*/
public class RSAUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(RSAUtil.class);
private static final int RSA_SIZE_1024 = 1024;
private static final String ALGORITHM = "SHA1WithRSA";
/**
* 生成 RSA **对
*
* @param keySize
* @return: {@link Map<String,Object> }
* @author: Andy
* @time: 2019/5/10 16:59
*/
public static Map<String, Object> createKeyPair(int keySize) {
KeyPairGenerator ****** = null;
try {
****** = KeyPairGenerator.getInstance("RSA");
} catch (NoSuchAlgorithmException e) {
LOGGER.error("初始化**工具异常", e);
return null;
}
******.initialize(keySize, new SecureRandom());
KeyPair key = ******.generateKeyPair();
PublicKey publicKey = key.getPublic();
PrivateKey privateKey = key.getPrivate();
Map map = new HashMap();
map.put("publicKey", publicKey);
map.put("privateKey", privateKey);
map.put("publicKeyBase64", Base64.encodeBase64String(publicKey.getEncoded()));
map.put("privateKeyBase64", Base64.encodeBase64String(privateKey.getEncoded()));
return map;
}
/**
* 获得公钥的 Base64 字符串
*
* @param publicKey 公钥
* @return: {@link String }
* @author: Andy
* @time: 2019/5/10 17:11
*/
public static String getBase64PublicKeyString(PublicKey publicKey) {
return Base64.encodeBase64URLSafeString(publicKey.getEncoded()).trim();
}
/**
* 获得私钥的 Base64 字符串
*
* @param privateKey 公钥
* @return: {@link String }
* @author: Andy
* @time: 2019/5/10 17:11
*/
public static String getBase64PrivateKeyString(PrivateKey privateKey) {
return Base64.encodeBase64URLSafeString(privateKey.getEncoded()).trim();
}
/**
* 获取公钥
*
*
* @param publicKeyBase64 公钥的 Base64 字符串
* @return: {@link PublicKey }
* @author: Andy
* @time: 2019/5/10 18:05
*/
public static PublicKey getPublicKey(String publicKeyBase64)
throws NoSuchAlgorithmException, InvalidKeySpecException {
X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(org.apache.commons.net.util.Base64.decodeBase64(publicKeyBase64));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(pubKeySpec);
return publicKey;
}
/**
* 获取私钥
*
*
* @param privateKeyBase64 私钥的 Base64 字符串
* @return: {@link PrivateKey }
* @author: Andy
* @time: 2019/5/10 18:05
*/
public static PrivateKey getPrivateKey(String privateKeyBase64)
throws NoSuchAlgorithmException, InvalidKeySpecException {
PKCS8EncodedKeySpec priKeySpec = new PKCS8EncodedKeySpec(org.apache.commons.net.util.Base64.decodeBase64(privateKeyBase64));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey priKey = keyFactory.generatePrivate(priKeySpec);
return priKey;
}
/**
* 使用私钥对数据进行数字签名
*
* @param data 需要签名的数据
* @param privateKey 私钥
* @return: {@link byte[] }
* @author: Andy
* @time: 2019/5/10 17:15
*/
public static byte[] sign(byte[] data, PrivateKey privateKey)
throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
Signature signature = Signature.getInstance(ALGORITHM);
signature.initSign(privateKey);
signature.update(data);
return signature.sign();
}
/**
* 使用私钥对数据进行数字签名
*
* @param data 需要签名的字符串
* @param privateKey 私钥
* @return: {@link String }
* @author: Andy
* @time: 2019/5/10 17:15
*/
public static String sign(String data, PrivateKey privateKey)
throws InvalidKeyException, NoSuchAlgorithmException, SignatureException {
return Base64.encodeBase64URLSafeString(sign(data.getBytes(), privateKey)).trim();
}
/**
* 签名校验
*
* @param data 参与签名的数据
* @param sign 数字签名
* @param publicKey 公钥
* @return: {@link boolean }
* @author: Andy
* @time: 2019/5/10 17:22
*/
public static boolean verify(byte[] data, byte[] sign, PublicKey publicKey)
throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
Signature signature = Signature.getInstance(ALGORITHM);
signature.initVerify(publicKey);
signature.update(data);
return signature.verify(sign);
}
/**
* 签名校验
*
* @param data 参与签名的数据
* @param sign 数字签名
* @param publicKey 公钥
* @return: {@link boolean }
* @author: Andy
* @time: 2019/5/10 17:22
*/
public static boolean verify(String data, String sign, PublicKey publicKey)
throws InvalidKeyException, NoSuchAlgorithmException, SignatureException {
return verify(data.getBytes(), Base64.decodeBase64(sign), publicKey);
}
/**
* 获取参与签名的参数的字符串。参数拼接的顺序由 TreeMap 决定。
*
* @param paramsMap 参与签名的参数名和参数值的映射
* @return: {@link String }
* @author: Andy
* @time: 2019/5/10 17:43
*/
public static String getSourceSignData(TreeMap<String, String> paramsMap) {
StringBuilder paramsBuilder = new StringBuilder();
boolean first = true;
for (Map.Entry<String, String> paramEntry : paramsMap.entrySet()) {
if(!first){
paramsBuilder.append("&");
}else {
first = false;
}
paramsBuilder.append(paramEntry.getKey()).append("=").append(paramEntry.getValue());
}
return paramsBuilder.toString();
}
/**
* 设计流程:
* <p>
* 1、创建**对
* 2、获取参与签名的数据
* 3、获取参与签名的数据的摘要(MD5值)
* 4、使用私钥对摘要进行数字签名
* 5、使用公钥验证签名
*
* @author: Andy
* @time: 2019/5/10 17:32
*/
public static void main(String[] args) throws InvalidKeySpecException, NoSuchAlgorithmException, SignatureException, InvalidKeyException {
//1、创建**对
Map<String, Object> keyPairMap = createKeyPair(RSA_SIZE_1024);
String publicKeyBase64 = keyPairMap.get("publicKeyBase64").toString();
String privateKeyBase64 = keyPairMap.get("privateKeyBase64").toString();
System.out.println(String.format("publicKeyBase64: %s", publicKeyBase64));
System.out.println(String.format("privateKeyBase64: %s", privateKeyBase64));
//2、获取参与签名的数据
//创建 TreeMap, 排序方式为字符串的自然顺序
TreeMap<String, String> paramsMap = new TreeMap<>();
paramsMap.put("token", "token");
paramsMap.put("batchNo", "批次号");
paramsMap.put("totalBillCount", "1");
paramsMap.put("sumMoney", "1");
paramsMap.put("idCard", "452626199212070014");
paramsMap.put("name", "张三");
paramsMap.put("afterTaxMoney", "1");
paramsMap.put("remark", "备注");
String sourceSignData = getSourceSignData(paramsMap);
System.out.println(String.format("参与签名的数据: %s", sourceSignData));
//3、获取参与签名的数据的摘要(MD5值)
String md5Str = Md5Util.encrypt(sourceSignData);
System.out.println(String.format("参与签名的数据的摘要(MD5值): %s", md5Str));
//4、使用私钥对摘要进行数字签名
String signature = sign(md5Str, getPrivateKey(privateKeyBase64));
System.out.println(String.format("数字签名: %s", signature));
//5、使用公钥验证签名
boolean success = verify(md5Str, signature, getPublicKey(publicKeyBase64));
System.out.println(String.format("签名验证结果:%s", success));
}
}
package awesome.data.structure.algorithm.encrypt.md5;
import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Md5 工具类
*
* @author: Andy
* @time: 2019/5/10 17:27
* @since
*/
public class Md5Util {
private static final Logger LOGGER = LoggerFactory.getLogger(Md5Util.class);
/**
* 简单 MD5 加密
*
* @param str
* @return: {@link String }
* @author: Andy
* @time: 2019/4/29 15:28
* @since
*/
public static String encrypt(String str) {
try {
return DigestUtils.md5Hex(str).toUpperCase();
} catch (Exception e) {
LOGGER.error("MD5加密失败!", e);
}
return null;
}
}
运行结果
publicKeyBase64: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCMcizy+1sAprsTLKyk8DLU8yR29ZMuol7T50LeyG4JMQxewXm05g56UlVg1cx8FAJCrMUg6ZU8KyjJycP+au9mkGsRUZv2k5Pwahhmt9Y2yviuIDUuGJ3/4ojgvaqsxSE+yQi+rLconS5DxwOOTJ0Dp5nG7eo5auVCkk8+uy7JlwIDAQAB
privateKeyBase64: MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAIxyLPL7WwCmuxMsrKTwMtTzJHb1ky6iXtPnQt7IbgkxDF7BebTmDnpSVWDVzHwUAkKsxSDplTwrKMnJw/5q72aQaxFRm/aTk/BqGGa31jbK+K4gNS4Ynf/iiOC9qqzFIT7JCL6styidLkPHA45MnQOnmcbt6jlq5UKSTz67LsmXAgMBAAECgYB+KbKnM5S0KRK0TtVn9T40fZasJj5pDgMRaBVx+6qdJyptlG+4SVGIIJ4Btw1SCMdfDcSnpC0jN8IUQuOFkJosu7N6iB4IxEIe7cDOWppO5fOAR51c9tU+Z6544RdEcL9+X4S2GfdT9IObriMrcg0q0PSnj73MpkKJ7jV0ybWNQQJBAL3AnHjgxFMYFInoagQ0yX1KMSQlv+OrbQbQoZYGsvaacRcVKa0wngeeF3lzFN2XxoMqUR1e5gbMiHJMTdjPKw0CQQC9erxjde5NYJtGtPYTx6bXL3zDKI/T82uqlCt3lMjTe+s73q9oUPFvBU0Zj/IWlqD0kgB1NETMJid1WPT+KI4zAkAdmKNPP0+f3kulzvhqO4mJ3z6W8sRhUGWrAHOToOvdBu3IueZMOx8K9R+YM9j3TysJXlpUiG68dL57hWdG/9kpAkBVXLcEg/uw5mXt69a0wIx8g0tMzLhHP3Hw7kHK5L+47ynh1gfmEhlC7/t1GbFx1bh7lk8YfkzEQCgkqMfuvKybAkAnkLilyXl60wLfeinuynte1IQGk5kNuH0/DaP89UkWnXIEfH4FBIXETIhSRXHYnDMEGfojuE3J25QZw2eKY8rq
参与签名的数据: afterTaxMoney=1&batchNo=批次号&idCard=452626199212070014&name=张三&remark=备注&sumMoney=1&token=token&totalBillCount=1
参与签名的数据的摘要(MD5值): 553218F911324C5F45282142C036778F
数字签名: inrW4yFyEZrq1RNMVVaca5ImPiudcRsoCtlJCu8YADZ6mBLI5pHd9MkCElxNJCxFMbraEbXA8blhMbvdt3fqcgnJADIcy0xPVLLNXubCDes-brgmMBcmRLoEAFR_KZrDxj_5QzGZUekkiQxNh8_2emBxqvXjhUCJr-uwAzuwP9A
签名验证结果:true