数字签名问题

安全 专栏收录该内容
1 篇文章 0 订阅

一、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));
	}
}
  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页

打赏作者

~fly_tian~

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值