常见签名算法之SHA256withRSA
概述
在https://blog.csdn.net/chinoukin/article/details/100934995章节中,我介绍了用Hmac算法用于签名算法中的方法,本章节中将对常见的签名算法“SHA256withRSA”做一下分析与介绍。
此文由来
当我在使用springcloud的oauth2时,经常会用到jwt作为用户身份的确认和鉴权。而我们知道jwt是由三部分组成,其中第三部分就是数字签名了,而springcloud的oauth2中的默认jwt签名算法为SHA256withRSA。
下面通过调用OauthServer的{/oauth/token_key}接口来看一下返回情况:
HMAC与SHA256withRSA
hmac做签名时需要指定一个secret和指定hamc算法,常见的hmac算法有hamcsha1、hamcsha256等,通常用hmac生成信息摘要后会再用RSA算法对其进行加密
SHA256withRSA做签名则一步到位,需要先生成RSA**对,其中私钥用于签名,公钥用于验签。
为了方便理解:
hmac数字签名 = rsa_encrypt(hmac(信息) + RSA私钥)
SHA256withRSA数字签名 = SHA256withRSA_encrypt(信息 + RSA私钥)
RSA**之PKCS#1、PKCS#8
pkcs时**的语法标准,细心的同学应该会经常遇到PKCS#1,PKCS#8,PKCS#12等标准,具体所代表的含义这里不做赘述了。通常我们生成**对时,会对私钥进行加密,加密后的格式为PKCS#1或PKCS#8格式,而一般不会对公钥进行加密。简单例子: 用openssl生成PKCS#1格式的私钥:openssl genrsa -des3 -out test.key 1024
通过文件首尾部能判断:
JAVA示例
package com.cyq.secusign;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMWriter;
import org.bouncycastle.util.encoders.Base64;
import java.io.IOException;
import java.io.StringWriter;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.Signature;
import java.security.SignatureException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.HashMap;
import java.util.Map;
public class SignTest {
/**
* 生成没有加密的**对
* @param algorithm
* @param keysize
* @return
* @throws Exception
*/
public static Map<String, Object> createKey(String algorithm,int keysize) throws Exception {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(algorithm);
keyPairGen.initialize(keysize);
KeyPair keyPair = keyPairGen.generateKeyPair();
//通过对象 KeyPair 获取RSA公私钥对象RSAPublicKey RSAPrivateKey
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
//公私钥对象存入map中
Map<String, Object> keyMap = new HashMap<String, Object>(2);
keyMap.put("publicKey", publicKey);
keyMap.put("privateKey", privateKey);
return keyMap;
}
/**
* 生成加过密的**对 pkcs#1格式私钥 pkcs#8格式公钥
*
* @param algorithm
* @param keysize
* @param privateKeyPwd
* @return
* @throws Exception
*/
public static Map<String, Object> createEncryKeyStr(String algorithm, int keysize, String privateKeyPwd) throws Exception {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(algorithm);
keyPairGen.initialize(keysize);
KeyPair keyPair = keyPairGen.generateKeyPair();
//通过对象 KeyPair 获取RSA公私钥对象RSAPublicKey RSAPrivateKey
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
String pubKeyStr = new String(Base64.encode(publicKey.getEncoded())); //pkcs8格式
String priKeyStr = new String(Base64.encode(privateKey.getEncoded())); //pkcs8格式
System.out.println(priKeyStr);//从输出结果可以知道是PKCS#8格式的
//私钥加密
String privateKeyStr = privateKeyPwdToPKCS1(privateKey, privateKeyPwd);//使用BC加密私钥格式会被转为PKSC#1格式
System.out.println(privateKeyStr);
System.out.println(privateKeyNoPwdToPKCS1(privateKey));
//公私钥对象存入map中
Map<String, Object> keyMap = new HashMap<String, Object>(2);
keyMap.put("publicKeyStr", pubKeyStr);
keyMap.put("privateKeyStr", privateKeyStr);
return keyMap;
}
/**
* 将私钥转为PKCS#1格式私钥(加密)
*
* @param privateKey
* @param filePasswd
* @return
*/
private static String privateKeyPwdToPKCS1(PrivateKey privateKey, String filePasswd) {
Security.addProvider(new BouncyCastleProvider());
StringWriter sw = new StringWriter();
PEMWriter writer = new PEMWriter(sw);
try {
writer.writeObject(privateKey, "DESEDE", filePasswd.toCharArray(),
new SecureRandom());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (writer != null) {
writer.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return sw.toString();
}
/**
* 将私钥转为PKCS#1格式私钥(没有加密)
* @param privateKey
* @return
*/
private static String privateKeyNoPwdToPKCS1(PrivateKey privateKey){
Security.addProvider(new BouncyCastleProvider());
StringWriter sw = new StringWriter();
PEMWriter writer = new PEMWriter(sw);
try {
writer.writeObject(privateKey);
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
if(writer !=null){
writer.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return sw.toString();
}
public static final String SIGNATURE_ALGORITHM = "SHA256withRSA";
public static final String ENCODE_ALGORITHM = "SHA-256";
public static final String PLAIN_TEXT = "test string";
/**
* 签名
* @param privateKey 私钥
* @param plainText 明文
* @return
*/
public static byte[] sign(PrivateKey privateKey, String plainText) {
MessageDigest messageDigest;
byte[] signed = null;
try {
messageDigest = MessageDigest.getInstance(ENCODE_ALGORITHM);
messageDigest.update(plainText.getBytes());
byte[] outputDigest_sign = messageDigest.digest();
System.out.println("SHA-256编码后-----》"
+ org.apache.commons.codec.binary.Base64.encodeBase64String(outputDigest_sign));
Signature Sign = Signature.getInstance(SIGNATURE_ALGORITHM);
Sign.initSign(privateKey);
Sign.update(outputDigest_sign);
signed = Sign.sign();
System.out.println("SHA256withRSA签名后-----》"
+ org.apache.commons.codec.binary.Base64.encodeBase64String(signed));
} catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) {
e.printStackTrace();
}
return signed;
}
/**
* 验签
* @param publicKey 公钥
* @param plain_text 明文
* @param signed 签名
*/
public static boolean verifySign(PublicKey publicKey, String plain_text, byte[] signed) {
MessageDigest messageDigest;
boolean SignedSuccess=false;
try {
messageDigest = MessageDigest.getInstance(ENCODE_ALGORITHM);
messageDigest.update(plain_text.getBytes());
byte[] outputDigest_verify = messageDigest.digest();
//System.out.println("SHA-256加密后-----》" +bytesToHexString(outputDigest_verify));
Signature verifySign = Signature.getInstance(SIGNATURE_ALGORITHM);
verifySign.initVerify(publicKey);
verifySign.update(outputDigest_verify);
SignedSuccess = verifySign.verify(signed);
System.out.println("验证成功?---" + SignedSuccess);
} catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) {
e.printStackTrace();
}
return SignedSuccess;
}
/**
* bytes[]换成16进制字符串
*
* @param src
* @return
*/
public static String bytesToHexString(byte[] src) {
StringBuilder stringBuilder = new StringBuilder("");
if (src == null || src.length <= 0) {
return null;
}
for (int i = 0; i < src.length; i++) {
int v = src[i] & 0xFF;
String hv = Integer.toHexString(v);
if (hv.length() < 2) {
stringBuilder.append(0);
}
stringBuilder.append(hv);
}
return stringBuilder.toString();
}
public static void main(String[] args) throws Exception {
Map<String, Object> keyMap = createKey("RSA", 2048);
PublicKey publicKey = (PublicKey) keyMap.get("publicKey");
PrivateKey privateKey = (PrivateKey) keyMap.get("privateKey");
byte[] signBytes = sign(privateKey, PLAIN_TEXT);
System.out.println(verifySign(publicKey, PLAIN_TEXT, signBytes));
}
}
上一篇: 垂直化运营 让网站的发展柳暗花明又一村
下一篇: 论坛常用的几个推广方法分享
推荐阅读
-
Javascript之常见算法整理(持续更新)-柠檬味的前端-SegmentFault思否
-
Python爬虫系列之微信小程序逆向某优选爬虫签名算法
-
常见面试之机器学习算法思想简单梳理
-
Python数据结构与算法之常见的分配排序法示例【桶排序与基数排序】
-
Elasticsearch 之(22) 自定义相关度分数算法 和 常见的相关度分数优化方法
-
OPENSSL: ECDSA算法验签之签名数据的DER格式同一处理
-
常见查找算法之php, js,python版
-
Javascript之常见算法整理(持续更新)-柠檬味的前端-SegmentFault思否
-
《数据结构与算法之美》----常见的链表操作
-
常见签名算法之SHA256withRSA