java rsa 验签_如何实现RSA签名与验签

md5,sha1,sha256等签名方式相信大家已经都非常熟悉了,今天介绍如何使用RSA进行签名。

RSA签名使用起来其实也是非常的简单,无非就是使用私钥进行签名,使用公钥进行验签。使用方法如下。

public classMain {

private static finalString private_key= "私钥";// 私钥为PKCS8格式

private static finalString public_key= "公钥";// 公钥为X509格式,如果提供的是公钥证书,则需要从公钥证书中提取该串

public static voidmain(String[] args) throwsException {

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);

// 使用公钥进行验签booleanresult = rsaSign.verify(content,"UTF-8",public_key,sign);System.out.println(result);}

}

以下是实现过程:

定义接口:即最终能够对外提供的服务。

public interfaceIAsymmetricSign {

String sign(String content,String charset,String privateKey) throwsException;

booleanverify(String content,String charset,String publicKey,String sign) throwsException;

}

定义抽象基类,约定子类必须实现doSign方法,doVerify方法,以及getSignAlgorithm方法供基类调用,即子类必须要实现的具体业务方法。

public abstract classBaseAsymmetricSign implementsIAsymmetricSign {

private staticString DEFAULT_CHARSET= "UTF-8";

@OverridepublicString sign(String content,String charset,String privateKey) throwsException {

if(content == null) {

throw newException("content不能为空");}

if(charset == null|| charset.isEmpty()) {

charset = DEFAULT_CHARSET;}

if(privateKey == null) {

throw newException("私钥不能为空");}

try{

returndoSign(content,charset,privateKey);} catch(Exception e) {

// 抛出异常时应该尽可能多的提供现场信息以供判断错误的原因throw newException("使用"+ getSignAlgorithm() + "进行签名时遭遇异常",e);}

}

@Overridepublic booleanverify(String content,String charset,String publicKey,String sign) throwsException {

if(content == null) {

throw newException("content不能为空");}

if(charset == null|| charset.isEmpty()) {

charset = DEFAULT_CHARSET;}

if(publicKey == null) {

throw newException("公钥不能为空");}

try{

returndoVerify(content,charset,publicKey,sign) ;} catch(Exception e) {

// 抛出异常时应该尽可能多的提供现场信息以供判断错误的原因throw newException("使用"+ getSignAlgorithm() + "进行验签时遭遇异常",e);}

}

protected abstractString doSign(String content,String charset,String privateKey) throwsException;

protected abstract booleandoVerify(String content,String charset,String publicKey,String sign) throwsException;

protected abstractString getSignAlgorithm() ;}

具体实现业务的类1

importjava.security.KeyFactory;importjava.security.PrivateKey;importjava.security.PublicKey;importjava.security.Signature;importjava.security.spec.PKCS8EncodedKeySpec;importjava.security.spec.X509EncodedKeySpec;

public classRSA2Sign extendsBaseAsymmetricSign {

private static finalString sign_algorithm= "SHA256WithRSA";private static finalString sign_type= "RSA";

@OverrideprotectedString doSign(String content,String charset,String privateKey) throwsException {

byte[] encodedKey = Base64.decode(privateKey,Base64.NO_WRAP);KeyFactory keyFactory = KeyFactory.getInstance(sign_type);PrivateKey priKey = keyFactory.generatePrivate(newPKCS8EncodedKeySpec(encodedKey));

Signature signature = Signature.getInstance(getSignAlgorithm());signature.initSign(priKey);signature.update(content.getBytes(charset));byte[] signed = signature.sign();returnBase64.encodeToString(signed,Base64.NO_WRAP);}

@Overrideprotected booleandoVerify(String content,String charset,String publicKey,String sign) throwsException {

byte[] encodedKey = Base64.decode(publicKey.getBytes(),Base64.NO_WRAP);KeyFactory keyFactory = KeyFactory.getInstance(sign_type);PublicKey pubKey = keyFactory.generatePublic(newX509EncodedKeySpec(encodedKey));

Signature signature = Signature.getInstance(getSignAlgorithm());signature.initVerify(pubKey);signature.update(content.getBytes(charset));

returnsignature.verify(Base64.decode(sign,Base64.NO_WRAP));}

@OverrideprotectedString getSignAlgorithm() {

returnsign_algorithm;}

}

具体实现业务的类2

public classRSASign extendsRSA2Sign{

// 和RSA2签名相比只有 除签名算法名不同以外,签名和验签的使用方法都是一样的private static finalString sign_algorithm= "SHA1WithRSA";

@OverrideprotectedString getSignAlgorithm() {

returnsign_algorithm;}

}

具体实现业务的类3, 如何使用国密进行签名可以参考 alipay-sdk-java 中的实现。

public classSMSign extendsBaseAsymmetricSign{

// 国密签名未实现

@OverrideprotectedString doSign(String content,String charset,String privateKey) throwsException {

throw newException("暂未实现该签名");}

@Overrideprotected booleandoVerify(String content,String charset,String publicKey,String sign) throwsException {

throw newException("暂未实现该签名");}

@OverrideprotectedString getSignAlgorithm() {

return"SM";}

}

工厂类,对外提供签名实例。  其实这里我一直是挺疑惑的,为啥是每个线程new一个实例进行签名和验签,难道上面的实现是非线程安全的么。

public classSignManage {

privateSignManage(){}

public staticIAsymmetricSign getSigner(String type) throwsException {

if(type.equals("RSA2")) {

return newRSA2Sign();}

if(type.equals("RSA")) {

return newRSASign();}

if(type.equals("sm")) {

return newSMSign();}

throw newException("暂不支持该签名方式");}

}

参考实现:

62ef645b4c75a9a48e32aa0566e07de2.png

实现还依赖base64编码和解码,网上实现很多,也可以直接copy alipay-sdk-java中的实现。

具体的业务实现中需要通信的两方交换公钥。

假设A和B通信 , A需要生成一对钥匙,然后把公钥提供供B。 B也需要生成一对钥匙,把公钥提供给A。

A给B发消息 , A使用A的私钥进行签名或者加密,B收到消息后使用A提供的公钥进行验签或者解密。

B给A发消息 , B使用B的私钥进行签名或者加密,A收到消息后使用B提供的公钥进行验签或者解密。

之所以这么实现是因为私钥是万万不能泄露的。

同时应该告知对方使用的具体的签名算法。

更安全的做法是交换公钥证书,这就需要把公钥转换成证书,具体如何操作下一篇博客进行整理。

生成私钥和公钥,直接使用支付宝开放平台的开发助手工具。

fd6e761a17d3afe43815cd5ff64a837d.png


版权声明:本文为weixin_35792468原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。