知方号

知方号

第十四章 数字签名算法

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是支持的。

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

JDKBC

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); }}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); }}

注意点:

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

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

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

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

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至lizi9903@foxmail.com举报,一经查实,本站将立刻删除。