如何实现RSA签名与验签
RSA签名使用起来其实也是非常的简单,无非就是使用私钥进行签名,使用公钥进行验签。使用方法如下。
public class Main {private static final String private_key = "私钥";
// 私钥为PKCS8格式
private static final String public_key = "公钥";
// 公钥为X509格式,如果提供的是公钥证书,则需要从公钥证书中提取该串
public static void main(String[] args) throws Exception {
String content = "待签名内容";
IAsymmetricSign rsaSign = SignManage.getSigner("RSA2");
// IAsymmetricSign rsaSign = SignManage.getSigner("RSA");
// 使用私钥进行签名
String sign = rsaSign.sign(content, "UTF-8", private_key);
System.out.println(sign);
// 使用公钥进行验签
boolean result = rsaSign.verify(content, "UTF-8", public_key, sign);
System.out.println(result);
}
}
以下是实现过程:
定义接口:即最终能够对外提供的服务。
public interface IAsymmetricSign {String sign(String content, String charset, String privateKey) throws Exception;
boolean verify(String content, String charset, String publicKey, String sign) throws Exception;
}
定义抽象基类,约定子类必须实现doSign方法,doVerify方法,以及getSignAlgorithm方法供基类调用,即子类必须要实现的具体业务方法。
public abstract class BaseAsymmetricSign implements IAsymmetricSign {private static String DEFAULT_CHARSET = "UTF-8";
@Override
public String sign(String content, String charset, String privateKey) throws Exception {
if (content == null) {
throw new Exception("content不能为空");
}
if (charset == null || charset.isEmpty()) {
charset = DEFAULT_CHARSET;
}
if (privateKey == null) {
throw new Exception("私钥不能为空");
}
try {
return doSign(content, charset, privateKey);
} catch (Exception e) {
// 抛出异常时应该尽可能多的提供现场信息以供判断错误的原因
throw new Exception("使用" + getSignAlgorithm() + "进行签名时遭遇异常", e);
}
}
@Override
public boolean verify(String content, String charset, String publicKey, String sign) throws Exception {
if (content == null) {
throw new Exception("content不能为空");
}
if (charset == null || charset.isEmpty()) {
charset = DEFAULT_CHARSET;
}
if (publicKey == null) {
throw new Exception("公钥不能为空");
}
try {
return doVerify(content,charset,publicKey,sign) ;
} catch (Exception e) {
// 抛出异常时应该尽可能多的提供现场信息以供判断错误的原因
throw new Exception("使用" + getSignAlgorithm() + "进行验签时遭遇异常", e);
}
}
protected abstract String doSign(String content, String charset, String privateKey) throws Exception;
protected abstract boolean doVerify(String content, String charset, String publicKey, String sign) throws Exception;
protected abstract String getSignAlgorithm() ;
}
具体实现业务的类1
import java.security.KeyFactory;import java.security.PrivateKey;import java.security.PublicKey;import java.security.Signature;import java.security.spec.PKCS8EncodedKeySpec;import java.security.spec.X509EncodedKeySpec;public class RSA2Sign extends BaseAsymmetricSign {
private static final String sign_algorithm = "SHA256WithRSA";
private static final String sign_type = "RSA";
@Override
protected String doSign(String content, String charset, String privateKey) throws Exception {
byte[] encodedKey = Base64.decode(privateKey, Base64.NO_WRAP);
KeyFactory keyFactory = KeyFactory.getInstance(sign_type);
PrivateKey priKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encodedKey));
Signature signature = Signature.getInstance(getSignAlgorithm());
signature.initSign(priKey);
signature.update(content.getBytes(charset));
byte[] signed = signature.sign();
return Base64.encodeToString(signed, Base64.NO_WRAP);
}
@Override
protected boolean doVerify(String content, String charset, String publicKey, String sign) throws Exception {
byte[] encodedKey = Base64.decode(publicKey.getBytes(), Base64.NO_WRAP);
KeyFactory keyFactory = KeyFactory.getInstance(sign_type);
PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
Signature signature = Signature.getInstance(getSignAlgorithm());
signature.initVerify(pubKey);
signature.update(content.getBytes(charset));
return signature.verify(Base64.decode(sign, Base64.NO_WRAP));
}
@Override
protected String getSignAlgorithm() {
return sign_algorithm;
}
}
具体实现业务的类2
public class RSASign extends RSA2Sign{// 和RSA2签名相比只有 除签名算法名不同以外,签名和验签的使用方法都是一样的
private static final String sign_algorithm = "SHA1WithRSA";
@Override
protected String getSignAlgorithm() {
return sign_algorithm;
}
}
具体实现业务的类3, 如何使用国密进行签名可以参考 alipay-sdk-java 中的实现。
public class SMSign extends BaseAsymmetricSign{// 国密签名未实现
@Override
protected String doSign(String content, String charset, String privateKey) throws Exception {
throw new Exception("暂未实现该签名");
}
@Override
protected boolean doVerify(String content, String charset, String publicKey, String sign) throws Exception {
throw new Exception("暂未实现该签名");
}
@Override
protected String getSignAlgorithm() {
return "SM" ;
}
}
工厂类,对外提供签名实例。 其实这里我一直是挺疑惑的,为啥是每个线程new一个实例进行签名和验签,难道上面的实现是非线程安全的么。
public class SignManage {private SignManage(){}
public static IAsymmetricSign getSigner(String type) throws Exception {
if (type.equals("RSA2")) {
return new RSA2Sign();
}
if (type.equals("RSA")) {
return new RSASign();
}
if (type.equals("sm")) {
return new SMSign();
}
throw new Exception("暂不支持该签名方式");
}
}
参考实现:
实现还依赖base64编码和解码,网上实现很多,也可以直接copy alipay-sdk-java中的实现。
具体的业务实现中需要通信的两方交换公钥。
假设A和B通信 , A需要生成一对钥匙,然后把公钥提供供B。 B也需要生成一对钥匙,把公钥提供给A。
A给B发消息 , A使用A的私钥进行签名或者加密,B收到消息后使用A提供的公钥进行验签或者解密。
B给A发消息 , B使用B的私钥进行签名或者加密,A收到消息后使用B提供的公钥进行验签或者解密。
之所以这么实现是因为私钥是万万不能泄露的。
同时应该告知对方使用的具体的签名算法。
更安全的做法是交换公钥证书,如何生成公钥证书,如何验证公钥证书及从证书中提取公钥等问题下一篇博客进行整理。
生成私钥和公钥,直接使用支付宝开放平台的开发助手工具。
以上是 如何实现RSA签名与验签 的全部内容, 来源链接: utcz.com/z/515049.html