如何实现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

回到顶部