第十四章 数字签名算法--RSA

数字签名详解
本文详细介绍了数字签名的概念及其工作原理,包括非对称加密算法与消息摘要算法的结合使用,以及如何确保数据完整性和来源认证。并通过类比手写签名帮助理解,并提供了RSA数字签名算法的具体实现。

注意:本节内容主要参考自

  • 《Java加密与解密的艺术(第2版)》第9章“带密钥的消息摘要算法--数字签名算法”
  • 《大型分布式网站架构(设计与实践)》第3章“互联网安全架构”

14.1、数字签名算法

特点:

  • 非对称加密算法+消息摘要算法的结合体
  • 抗否认性、认证数据来源、防止数据被篡改(具体意思与做法查看下边的过程与类比部分)
  • 私钥加密(签名)、公钥解密(验证)

过程:

1)消息发送者产生一个密钥对(私钥+公钥),然后将公钥发送给消息接收者

2)消息发送者使用消息摘要算法对原文进行加密(加密后的密文称作摘要)

3)消息发送者将上述的摘要使用私钥加密得到密文--这个过程就被称作签名处理,得到的密文就被称作签名(注意,这个签名是名词)

4)消息发送者将原文与密文发给消息接收者

5)消息接收者使用公钥对密文(即签名)进行解密,得到摘要值content1

6)消息接收者使用与消息发送者相同的消息摘要算法对原文进行加密,得到摘要值content2

7)比较content1是不是与content2相等,若相等,则说明消息没有被篡改(消息完整性),也说明消息却是来源于上述的消息发送方(因为其他人是无法伪造签名的,这就完成了“抗否认性”和“认证消息来源”)

类比:

手写签名:

  • 张三在合同上签了自己的名字,这样张三在后来想否认自己的这个合同内容时,就不行了(抗否认性)
  • 在张三签过名之后,若合同内容又发生了变化,这个时候签名就可以看做无效了
  • 当然,我们通过合同上张三的签名,我们就可以知道签名的来源是张三(认证数据来源)

常见的数字签名算法:

  • RSA(数字签名算法的经典,也是最常用的数字签名算法)
  • DSA(是后续数字签名算法的基础)
  • ECDSA(ECC+DSA的结合体,相较于其他数字签名算法,速度快,强度高,签名短,但是使用还没有RSA广泛)

14.2、RSA

常见算法:

  • MD5WithRSA(JDK6)
  • SHA1WithRSA(JDK6)
  • SHA256WithRSA(>=JDK1.6.45,Bouncy Castle-->简称BC)

注意:在《Java加密与解密(第二版)》一书中,说JDK6不支持SHA256WithRSA,但是经过我自己测试1.6.45是支持的。

实现方式:(参考上边三行)

  • JDK
  • BC

14.2.1、基于JDK6实现的MD5WithRSA或SHA1WithRSA或SHA256WithRSA算法

package com.uti.rsa.digital;

import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
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.codec.binary.Base64;

/**
 * 基于BC的RSA数字签名算法
 */
public class RSACoderBC {
    private static final String ENCODING = "UTF-8";
    private static final String KEY_ALGORITHM = "RSA";//非对称加密密钥算法
    private static final String SIGNATURE_ALGORITHM = "MD5withRSA";//指定数字签名算法(可以换成SHA1withRSA或SHA256withRSA)
    private static final int KEY_SIZE = 512;//非对称密钥长度(512~1024之间的64的整数倍)
    
    /**
     * 生成发送方密钥对
     */
    public static KeyPair initKey() throws NoSuchAlgorithmException{
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);//密钥对生成器
        keyPairGenerator.initialize(KEY_SIZE);//指定密钥长度
        KeyPair keyPair = keyPairGenerator.generateKeyPair();//生成密钥对
        return keyPair;
    }
    
    /**
     * 还原公钥
     * @param pubKey 二进制公钥
     */
    public static PublicKey toPublicKey(byte[] pubKey) throws NoSuchAlgorithmException, 
                                                              InvalidKeySpecException{
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);//密钥工厂
        return keyFactory.generatePublic(new X509EncodedKeySpec(pubKey));//还原公钥
    }
    
    /**
     * 还原私钥
     * @param priKey 二进制私钥
     */
    public static PrivateKey toPrivateKey(byte[] priKey) throws NoSuchAlgorithmException, 
                                                                InvalidKeySpecException{
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);//密钥工厂
        return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(priKey));//还原私钥
    }
    
    /**
     * 私钥加密(签名)
     * @param data     待加密数据
     * @param keyByte  私钥
     */
    public static byte[] encryptPriKey(String data, byte[] keyByte) throws NoSuchAlgorithmException, 
                                                                           InvalidKeySpecException, 
                                                                           InvalidKeyException, 
                                                                           SignatureException, 
                                                                           UnsupportedEncodingException {
        PrivateKey priKey = toPrivateKey(keyByte);//还原私钥

        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initSign(priKey);
        signature.update(data.getBytes(ENCODING));
        
        return signature.sign();
    }
    
    /**
     * 公钥解密(验证)
     * @param data        原文(待加密数据,也成为“待校验数据”)
     * @param keyByte    公钥
     * @param sign        密文(也称作“签名”)
     */
    public static boolean decryptPubKey(String data, byte[] keyByte, byte[] sign) throws NoSuchAlgorithmException, 
                                                                                         InvalidKeySpecException, 
                                                                                         InvalidKeyException, 
                                                                                         SignatureException, 
                                                                                         UnsupportedEncodingException {
        PublicKey pubKey = toPublicKey(keyByte);//还原公钥
        
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initVerify(pubKey);
        signature.update(data.getBytes(ENCODING));
        
        return signature.verify(sign);
    }
    
    /**
     * 获取公钥
     */
    public static byte[] getPublicKey(KeyPair keyPair){
        return keyPair.getPublic().getEncoded();
    }
    
    /**
     * 获取私钥
     */
    public static byte[] getPrivateKey(KeyPair keyPair){
        return keyPair.getPrivate().getEncoded();
    }
    
    /**
     * 测试
     */
    public static void main(String[] args) throws NoSuchAlgorithmException, 
                                                  InvalidKeyException, 
                                                  InvalidKeySpecException, 
                                                  SignatureException, 
                                                  UnsupportedEncodingException {
        byte[] pubKey1;//甲方公钥
        byte[] priKey1;//甲方私钥
        
        /*********************测试是否可以正确生成以上2个key*********************/
        KeyPair keyPair1 = RSACoderBC.initKey();//生成甲方密钥对
        pubKey1 = RSACoderBC.getPublicKey(keyPair1);
        priKey1 = RSACoderBC.getPrivateKey(keyPair1);
        
        System.out.println("甲方公钥pubKey1-->"+Base64.encodeBase64String(pubKey1)+"@@pubKey1.length-->"+pubKey1.length);
        System.out.println("甲方私钥priKey1-->"+Base64.encodeBase64String(priKey1)+"@@priKey1.length-->"+priKey1.length);
        
        /*********************测试甲方使用私钥加密数据向乙方发送,乙方使用公钥解密数据*********************/
        System.out.println("甲方-->乙方");
        String data = "找一个好姑娘啊!你好吗,孩子";
        byte[] encodeStr = RSACoderBC.encryptPriKey(data, priKey1);//加密(签名)
        System.out.println("甲方加密后的数据-->"+Base64.encodeBase64String(encodeStr)+"@@encodeStr.length-->"+encodeStr.length);
        boolean decodeStr = RSACoderBC.decryptPubKey(data, pubKey1, encodeStr);
        System.out.println("乙方检验结果-->"+decodeStr);
    }
}
View Code

注意点:

  • 代码与RSA非对称加密算法(查看第十二章)基本一样,只是将其中的私钥加密与公钥解密部分的加解密算法改为签名和验证算法,当然没有“公钥加密,私钥解密”

疑问:在《Java加密与解密的艺术(第二版)》一书中,作者说:“RSA数字签名算法的签名值与密钥长度相同”,但是在我的测试中,结果不一致:

1)在我配置非对称密钥为512的时候,测出来的公钥长度是96位,私钥长度是345位,与512差很远,那这里的512到底是怎么计算的?

2)消息摘要的结果长度是64(摘要值的二进制自己数组长度),不知道到底是怎么与密钥长度相同的?

以上两个问题,有知道的朋友请指点一下,谢谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值