RSA签名与加密原理

| 分类 网络安全之web安全  | 标签 RSA  加密  签名  验签  解密  密码  安全  网络安全  数据安全  通信  对称加密  非对称加密  公钥  私钥  国密  SM2  SM4 

RSA 加密是一种非对称加密。可以在不直接传递密钥的情况下完成加密。这能够确保信息的安全性,避免直接传递密钥所造成的被破解的风险

是由一对密钥来进行加解密的过程,分别称为公钥和私钥。两者之间有数学相关,该加密算法的原理就是对以极大整数做因数分解的困难性来保证安全性的

通常个人保存私钥、公钥匙公开的(也可以同时多人持有)

加密是为了防止信息被泄漏,而签名是为了防止信息被篡改

加密

比如A要传递一条信息给B

RSA 加密的过程如下:

  1. B生成一对密钥(公钥和私钥),私钥不公开,B自己保留;公钥匙公开的,任何人都可以获取
  2. B传递自己的公钥给A,A用B的公钥对消息进行加密
  3. B收到A加密的消息,利用B自己的私钥对消息进行解密

在这个过程中,只有2次传递过程,第一次是B传递公钥给A,第二次是A传递加密消息给A。即使都被别人截获,也没有危险性,因为只有B的私钥才能对消息进行解密,防止消息内容被泄漏

但是存在这样的风险,C截获了B给A的公钥,C用公钥加密消息后发送消息给B,这样B用私钥可以解析出消息的内容,这样就存在C骗B的风险

签名

B收到A的消息后,需要回复“收到”

RSA签名的过程如下

  1. B生成一对密钥(公钥和私钥),私钥不公开,B自己保留;公钥匙公开的,任何人都可以获取
  2. B用自己的私钥对消息加签,形成签名,并将加签的消息和消息本身一起传递给A
  3. A收到消息后,使用B的公钥进行验签,如果验签出来的内容与消息本身一致,证明消息是B回复的

在这个过程中,只有2次传递过程,第一次是B传递加签的消息和消息本身给B,第二次是A获取B的公钥。即使都被截获,也没有危险,因为只有B的私钥才能对消息进行签名,即使知道消息的内容,也没有办法伪造带签名的回复给A,防止了消息内容的篡改

但是存在这样的风险,虽然截获的消息不能被篡改,但是消息的内容可以利用公钥验签来获得,并不能方式泄漏

加密与签名

在实际的应用场景中,一般会同时使用加密和签名。比如A和B分别生成一套自己的公钥和私钥,A将自己的公钥给B,B将自己的公钥给A

当A要发消息给B时,先使用A的私钥对消息加签名(一般是信息的摘要),再用B的公钥对消息内容和签名信息进行加密

B收到消息后,用自己的私钥解密加密后的内容,得到明文后用A的公钥解签A用A自己的私钥的签名

这样既保证不被泄漏,也保证不被篡改

公钥加密、私钥解密

私钥签名、公钥验签

这里的例子是加密和加签都是使用RSA算法,实际中可能用RSA做签名,用SM2国密算法加密

思考题1:签名和加密的顺序要求?

思考题2:非对称加密算法的性能考量!

加签示例代码

比如,签名逻辑是这样的,对待签名字符串使用SHA-256算法做摘要,再使用私钥对摘要做签名(RSA2),最后对签名做BASE64 编码

使用Java 实现的代码如下

import java.security.PrivateKey;
import java.security.Signature;
import java.security.KeyFactory;

import org.apache.tomcat.util.codec.binary.Base64;


/**
 * content,需要加签的内容
 * privateKeyString,私钥字符串
 */
public String AddSign(String content, String privateKeyString)
{
	// 根据私钥字符串获取私钥
    PrivateKey priKey = getPrivateKeyFromPKCS8("RSA", new ByteArrayInputStream(privateKeyString.getBytes()));
    // 使用SHA-256做摘要
    Signature signature = Signature.getInstance("SHA256WithRSA");
    // 使用私钥
    signature.initSign(priKey);
    // UTF-8格式
    signature.update(content.getBytes("UTF-8"));
    // 签名
    byte[] signed = signature.sign();
    // 获取字符串格式的签名
    String signString = new String(Base64.encodeBase64(signed));

    return signString;
}


/**
 * 生成私钥
 * 
 * algorithm,比如"RSA"
 * ins,私钥字符串对应的输入流
 */
private PrivateKey getPrivateKeyFromPKCS8(String algorithm, InputStream ins) throws Exception 
{
    if (ins!=null && null!=algorithm && algorithm.length()!=0) {
        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
        byte[] encodedKey= Base64.decodeBase64(copyToString(ins, Charset.forName("UTF-8")));
        return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encodedKey));
    } else {
        return null;
    }
}

验签示例代码

上面签名算法对应的验签程序如下

import java.security.PublicKey;
import java.security.Signature;


/**
 * content,加签方使用上面的逻辑对内容进行加签,Content就是加签前的内容
 * signString,上面的加签方法获取的签名
 * publicKeyString,验签需要使用的公钥
 */
public boolean CheckSign(String content, String signString, String publicKeyString)
{
	// 获取公钥
    PublicKey pubKey = getPublicKeyFromX509("RSA", new ByteArrayInputStream(PublicKeyString.getBytes()));
    // SHA-256
    Signature signature = Signature.getInstance("SHA256WithRSA");
    // 使用公钥进行验签
    signature.initVerify(pubKey);
    // 对content内容进行验签
    signature.update(content.getBytes("UTF-8"));

    // 验签,检查签名值signString是否正确
    return signature.verify(Base64.decodeBase64(signString.getBytes()));
}

/**
 * 生成公钥
 */
public static PublicKey getPublicKeyFromX509(String algorithm, InputStream ins) throws Exception 
{
    KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
    byte[] encodedKey =  Base64.decodeBase64(copyToString(ins, Charset.forName("UTF-8")));
    return keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
}

在真实的应用中,加签和验签一般是两对非对称秘钥,比如支付系统,商户发起支付、微信端接受支付请求,那么

  • 商户侧私钥A,对应微信侧记录该商户的公钥B
    • 商户侧生成秘钥对,自己保留私钥,调用微信的接口,设置自己对应的公钥
    • 商户在调用支付请求接口之前,使用私钥A进行加签
    • 微信侧收到请求,使用该商户对应的公钥B进行验签
  • 以上是商户请求微信的过程,对应还是微信应答商户的流程呢
  • 微信侧私钥a,对应商户侧为公钥b
    • 微信侧生成秘钥对,自己保留私钥,告诉商户对应的公钥
    • 微信在应答商户之前,先对应答内容,使用私钥a进行加签
    • 商户侧收到应答后,使用对应的公钥b进行验签!

参考资料




如果本篇文章对您有所帮助,您可以通过微信(左)或支付宝(右)对作者进行打赏!


上一篇     下一篇