一、XX的数字签名规则
1、 各中心间的调用,签名不作强制要求。–内部服务调用无验证,加不加都不影响;但是和集团交互的必须有,不然服务无法通过
2、 如果要求签名,则签名规则如下:
调用方需要用请求报文中的transactionId(流水-28位) +”svcCont”开始到”svcCont”结束的业务信息json串(报文的正文–body体内容)+ SecretKey(秘钥),最后组合成字符串,采用MD5方式取哈希值(32位小写)生成的摘要用RSA算法进行签名,签名后的数据放在TcpCont/ Sign字段。
具体实现:经沟通,其实际实现,没有用RSA进行签名,只是通过MD5方式生产32位小写摘要。调用集团接口的时候,集团会通过同样的加密方式获取报文进行加密,然后和传过去的签名比较看两个签名是否一致,如果一致则放行,反之拒绝。
二、针对RSA数字签名的测试
java中数字签名MD5withRSA和SHA1withRSA
1.基于openssl生成RSA公私钥对
a)从网上下载openssl工具:下载地址
b)生成私钥
根据系统版本下载对应的安装版本;以下是在win64 的操作
进入到openssl的bin目录下,执行以下命令:
以管理员的身份运行openssl.exe
然后在命令窗口里执行 --生成秘钥文件
genrsa -out rsa_private_key.pem 1024
或者直接执行
openssl genrsa -out rsa_private_key.pem 1024
具体内容
-----BEGIN RSA PRIVATE KEY-----
MIICWwIBAAKBgQCsra0fcuFvvNGrQuyZ9jtR+x2b1hLaTHXd6OgHLQS1zSBtnILf
I6kxGrGmrB6ndR++OfUhx4yt9ErtTRtDLFRkTCFkCQdmvl4XF2jVaj1vWHAkCVT9
AHdTPvRBcDHsMrjC+Tvd96zglWNf69xM3+rDxwFDs4wqcWNSB+cz2IpcWQIDAQAB
AoGAXQBZSqPT+RtIDaZIyg9NGbVW2BSvKLWfkbocJalAbS4idgaikc2iFVlcLvlq
tSQmyYxrkxrnkNUO+cnakg4xEMUwU1F3LIrNeNBcTF2AeEoVM73Upg2ot92QDa9A
o/mVt3jEqeDEaQ98vNuyIzFWIjWJRa+/gMreIjGC/PFHBdECQQDaVCwjqHmOGH4n
NuUwWhX/g5Kq3kUUQ8T0Bj3KZFhbhnbtuWQ7XzQnGchLdi5g6b6y4oHAuywWx1aI
8ByM1NgtAkEAynkTW7IZ1FBzRqd4lhueBtzupXGImKbGvR4TjPtXNMB3AbXR+9mO
9Nd/Ne+aVymNK+JGf9vrZlv5/W4PyYekXQJABnPwbglftlxw9hXARmKTPd8Uqaoy
Hnw2SQQaNW4r0mCyCSqulLamovU3H0vZ6RFfiof5T8n4bp/rpdZ68Yqk7QJABhxl
Yy0a7neHIyjG0PEAcgCVC1VD5CeUc/zGZkjntuXgMaMFNW65i9Y9NqmGpP7SDfME
YbOHhU1dXDLIMzLu2QJASwVi+mDoKUf7PlmTl9vbtR9p2HOM2HhefSGSCMYXNsJj
2/fdlDEL8W3Vz42Oxmjps+JEdi1DX6n8wU2DJmuDXw==
-----END RSA PRIVATE KEY-----
c)生成公钥
执行
rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem
会在bin目录下看到新生成的公钥文件rsa_public_key.pem,文件内容如下:
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCsra0fcuFvvNGrQuyZ9jtR+x2b
1hLaTHXd6OgHLQS1zSBtnILfI6kxGrGmrB6ndR++OfUhx4yt9ErtTRtDLFRkTCFk
CQdmvl4XF2jVaj1vWHAkCVT9AHdTPvRBcDHsMrjC+Tvd96zglWNf69xM3+rDxwFD
s4wqcWNSB+cz2IpcWQIDAQAB
-----END PUBLIC KEY-----
2.编写java测试类
附件RsaSign.java
执行main方法
如果签名报以下错误:
java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException : algid parse error, not a sequence
则说明rsa私钥的格式不是pksc8格式,需要使用以下命令转换一下:
openssl pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -nocryp
或者下面的直接生成到一个新的文件里如下图
pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -nocryp -out rsa_private_keypkcs8.pem
注: -out rsa_private_keypkcs8.pem 将转换的pkc8的文件写入到rsa_private_keypkcs8.pem。
直接运行可测试。
测试代码
package com.sitech.resmng.demo;
import java.io.UnsupportedEncodingException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import org.apache.commons.lang.StringUtils;
import org.apache.tomcat.util.codec.binary.Base64;
public class RsaSign {
/**
* rsa签名
*
* @param content
* 待签名的字符串
* @param privateKey
* rsa私钥字符串
* @param charset
* 字符编码
* @return 签名结果
* @throws Exception
* 签名失败则抛出异常
*/
public static String rsaSign(String content, String privateKey, String charset) throws SignatureException {
try {
PrivateKey priKey = getPrivateKeyFromPKCS8("RSA", privateKey);
// 用指定算法产生签名对象(MD5withRSA/SHA1WithRSA……)
Signature signature = Signature.getInstance("MD5withRSA");
// 用私钥初始化签名对象
signature.initSign(priKey);
// 将待签名的数据传送给签名对象(须在初始化之后)
if (StringUtils.isEmpty(charset)) {
signature.update(content.getBytes());
} else {
signature.update(content.getBytes(charset));
}
// 返回签名结果字节数组
byte[] signed = signature.sign();
return new String(Base64.encodeBase64(signed));
} catch (Exception e) {
throw new SignatureException("RSAcontent = " + content + "; charset = " + charset, e);
}
}
public static PrivateKey getPrivateKeyFromPKCS8(String algorithm, String ins) throws Exception {
if (ins == null || StringUtils.isEmpty(algorithm)) {
return null;
}
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
byte[] encodedKey = Base64.decodeBase64(ins); /// 将字符串Base64解码
//创建私钥证书封装类
PKCS8EncodedKeySpec pk8Key= new PKCS8EncodedKeySpec(encodedKey);
return keyFactory.generatePrivate(pk8Key); //生成私钥
}
static boolean doCheck(String content, String sign, String publicKey, String charset) throws SignatureException {
try {
PublicKey pubKey = getPublicKeyFromX509("RSA", publicKey);
// 用指定算法产生签名对象
Signature signature = Signature.getInstance("MD5withRSA");
// 使用公钥初始化签名对象,用于验证签名
signature.initVerify(pubKey);
// 更新签名内容
signature.update(getContentBytes(content, charset));
// 得到验证结果
return signature.verify(Base64.decodeBase64(sign.getBytes()));
} catch (Exception e) {
throw new SignatureException(
"RSA验证签名[content = " + content + "; charset = " + charset + "; signature = " + sign + "]发生异常!", e);
}
}
private static PublicKey getPublicKeyFromX509(String algorithm, String publicKey) throws NoSuchAlgorithmException {
try {
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
// 先base64解码
byte[] encodedKey = Base64.decodeBase64(publicKey);
// 创建公钥证书封装类.
X509EncodedKeySpec x509key = new X509EncodedKeySpec(encodedKey);
//生成公钥
return keyFactory.generatePublic(x509key);
} catch (InvalidKeySpecException ex) {
// 不可能发生
}
return null;
}
private static byte[] getContentBytes(String content, String charset) throws UnsupportedEncodingException {
if (StringUtils.isEmpty(charset)) {
return content.getBytes();
}
return content.getBytes(charset);
}
public static void main(String[] args) throws Exception {
String content = "777777777xxxxxxxxxx";
String file_name = "C:/OpenSSL-Win64/bin/rsa_private_keypkcs8.pem";
String check_name = "C:/OpenSSL-Win64/bin/rsa_public_key.pem";
String check_key = TestUtils.getFileContent(check_name);
String privateKey = TestUtils.getFileContent(file_name);
String sign = rsaSign(content, privateKey, null);
System.out.println(sign);
System.out.println(doCheck(content, sign, check_key, null));
}
}