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

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

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

14.一、数字签名算法git

特色:算法

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

过程:apache

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

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

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

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

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

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

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

类比:

手写签名:

  • 张三在合同上签了本身的名字,这样张三在后来想否定本身的这个合同内容时,就不行了(抗否定性)
  • 在张三签过名以后,若合同内容又发生了变化,这个时候签名就能够看作无效了
  • 固然,咱们经过合同上张三的签名,咱们就能够知道签名的来源是张三(认证数据来源)

常见的数字签名算法:

  • RSA(数字签名算法的经典,也是最经常使用的数字签名算法)
  • DSA(是后续数字签名算法的基础)
  • ECDSA(ECC+DSA的结合体,相较于其余数字签名算法,速度快,强度高,签名短,可是使用尚未RSA普遍)

14.二、RSA

常见算法:

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

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

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

  • JDK
  • BC

14.2.一、基于JDK6实现的MD5WithRSA或SHA1WithRSA或SHA256WithRSA算法

  1 package com.uti.rsa.digital;
  2 
  3 import java.io.UnsupportedEncodingException;
  4 import java.security.InvalidKeyException;
  5 import java.security.KeyFactory;
  6 import java.security.KeyPair;
  7 import java.security.KeyPairGenerator;
  8 import java.security.NoSuchAlgorithmException;
  9 import java.security.PrivateKey;
 10 import java.security.PublicKey;
 11 import java.security.Signature;
 12 import java.security.SignatureException;
 13 import java.security.spec.InvalidKeySpecException;
 14 import java.security.spec.PKCS8EncodedKeySpec;
 15 import java.security.spec.X509EncodedKeySpec;
 16 
 17 import org.apache.commons.codec.binary.Base64;
 18 
 19 /**
 20  * 基于BC的RSA数字签名算法
 21  */
 22 public class RSACoderBC {
 23     private static final String ENCODING = "UTF-8";
 24     private static final String KEY_ALGORITHM = "RSA";//非对称加密密钥算法
 25     private static final String SIGNATURE_ALGORITHM = "MD5withRSA";//指定数字签名算法(能够换成SHA1withRSA或SHA256withRSA)
 26     private static final int KEY_SIZE = 512;//非对称密钥长度(512~1024之间的64的整数倍)
 27     
 28     /**
 29      * 生成发送方密钥对
 30      */
 31     public static KeyPair initKey() throws NoSuchAlgorithmException{
 32         KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);//密钥对生成器
 33         keyPairGenerator.initialize(KEY_SIZE);//指定密钥长度
 34         KeyPair keyPair = keyPairGenerator.generateKeyPair();//生成密钥对
 35         return keyPair;
 36     }
 37     
 38     /**
 39      * 还原公钥
 40      * @param pubKey 二进制公钥
 41      */
 42     public static PublicKey toPublicKey(byte[] pubKey) throws NoSuchAlgorithmException, 
 43                                                               InvalidKeySpecException{
 44         KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);//密钥工厂
 45         return keyFactory.generatePublic(new X509EncodedKeySpec(pubKey));//还原公钥
 46     }
 47     
 48     /**
 49      * 还原私钥
 50      * @param priKey 二进制私钥
 51      */
 52     public static PrivateKey toPrivateKey(byte[] priKey) throws NoSuchAlgorithmException, 
 53                                                                 InvalidKeySpecException{
 54         KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);//密钥工厂
 55         return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(priKey));//还原私钥
 56     }
 57     
 58     /**
 59      * 私钥加密(签名)
 60      * @param data     待加密数据
 61      * @param keyByte  私钥
 62      */
 63     public static byte[] encryptPriKey(String data, byte[] keyByte) throws NoSuchAlgorithmException, 
 64                                                                            InvalidKeySpecException, 
 65                                                                            InvalidKeyException, 
 66                                                                            SignatureException, 
 67                                                                            UnsupportedEncodingException {
 68         PrivateKey priKey = toPrivateKey(keyByte);//还原私钥
 69 
 70         Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
 71         signature.initSign(priKey);
 72         signature.update(data.getBytes(ENCODING));
 73         
 74         return signature.sign();
 75     }
 76     
 77     /**
 78      * 公钥解密(验证)
 79      * @param data        原文(待加密数据,也成为“待校验数据”)
 80      * @param keyByte    公钥
 81      * @param sign        密文(也称做“签名”)
 82      */
 83     public static boolean decryptPubKey(String data, byte[] keyByte, byte[] sign) throws NoSuchAlgorithmException, 
 84                                                                                          InvalidKeySpecException, 
 85                                                                                          InvalidKeyException, 
 86                                                                                          SignatureException, 
 87                                                                                          UnsupportedEncodingException {
 88         PublicKey pubKey = toPublicKey(keyByte);//还原公钥
 89         
 90         Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
 91         signature.initVerify(pubKey);
 92         signature.update(data.getBytes(ENCODING));
 93         
 94         return signature.verify(sign);
 95     }
 96     
 97     /**
 98      * 获取公钥
 99      */
100     public static byte[] getPublicKey(KeyPair keyPair){
101         return keyPair.getPublic().getEncoded();
102     }
103     
104     /**
105      * 获取私钥
106      */
107     public static byte[] getPrivateKey(KeyPair keyPair){
108         return keyPair.getPrivate().getEncoded();
109     }
110     
111     /**
112      * 测试
113      */
114     public static void main(String[] args) throws NoSuchAlgorithmException, 
115                                                   InvalidKeyException, 
116                                                   InvalidKeySpecException, 
117                                                   SignatureException, 
118                                                   UnsupportedEncodingException {
119         byte[] pubKey1;//甲方公钥
120         byte[] priKey1;//甲方私钥
121         
122         /*********************测试是否能够正确生成以上2个key*********************/
123         KeyPair keyPair1 = RSACoderBC.initKey();//生成甲方密钥对
124         pubKey1 = RSACoderBC.getPublicKey(keyPair1);
125         priKey1 = RSACoderBC.getPrivateKey(keyPair1);
126         
127         System.out.println("甲方公钥pubKey1-->"+Base64.encodeBase64String(pubKey1)+"@@pubKey1.length-->"+pubKey1.length);
128         System.out.println("甲方私钥priKey1-->"+Base64.encodeBase64String(priKey1)+"@@priKey1.length-->"+priKey1.length);
129         
130         /*********************测试甲方使用私钥加密数据向乙方发送,乙方使用公钥解密数据*********************/
131         System.out.println("甲方-->乙方");
132         String data = "找一个好姑娘啊!你好吗,孩子";
133         byte[] encodeStr = RSACoderBC.encryptPriKey(data, priKey1);//加密(签名)
134         System.out.println("甲方加密后的数据-->"+Base64.encodeBase64String(encodeStr)+"@@encodeStr.length-->"+encodeStr.length);
135         boolean decodeStr = RSACoderBC.decryptPubKey(data, pubKey1, encodeStr);
136         System.out.println("乙方检验结果-->"+decodeStr);
137     }
138 }
View Code

注意点:

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

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

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

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

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

相关文章
相关标签/搜索