Java加密解密之数字签名

来源:互联网 发布:阿里云cname绑定 编辑:程序博客网 时间:2024/05/21 06:18
上一篇帖子,我们讲了MAC(消息认证码),它可以验证身份和防篡改。
它的机制是通过通信双方都持有相同的秘钥去实现,秘钥相同摘要才相同,没有秘钥就不能生成正确的摘要信息。


但是,它有个缺点,就是通信双方必须持有相同的秘钥,解决方法就是使用数字签名


数字签名(又称公钥数字签名、电子签章)是一种类似写在纸上的普通的物理签名,但是使用了非对称加密领域的技术实现,用于鉴别数字信息的方法。
一套数字签名通常定义两种互补的运算,私钥用于签名,公钥用于验证(验签)


数字签名是非对称密钥加密技术数字摘要技术的应用。


既然是非对称加密,就需要有一对秘钥,公钥和私钥

下面演示一下,用OpenSSL生成一对秘钥

#生成RSA私钥,默认是编码方式为PEM的PKCS#1格式#PKCS#1格式是传统的私钥格式openssl genrsa -out key.pem 1024#从私钥中生成公钥,给OpenSSL验签用的openssl rsa -in key.pem -out pub.pem -pubout#把PEM编码格式的私钥转换成DER编码的私钥,同时进行PKCS#1转换成PKCS#8(Java默认只能处理PKCS#8的格式)#-nocrypt 意思是不加密#给Java用openssl pkcs8 -topk8 -in key.pem -out pkcs8_prikey.der -inform PEM -outform DER -nocrypt#从私钥中导出DER编码的公钥#给Java用openssl rsa -in key.pem -pubout -outform DER -out pubkey.der

这样,就会生成四个文件,其中pkcs8_prikey.der、pubkey.der是给Java用的

有了秘钥对之后,就可以对文件进行签名了

下面使用Java(1.8.0_144)演示计算apache-tomcat-8.5.23.zip文件的数字签名

package com.security.sign;import java.nio.file.Files;import java.nio.file.Paths;import java.security.KeyFactory;import java.security.KeyPair;import java.security.PrivateKey;import java.security.PublicKey;import java.security.Signature;import java.security.spec.PKCS8EncodedKeySpec;import java.security.spec.X509EncodedKeySpec;import org.bouncycastle.util.encoders.Hex;public class SignatureTest {public static KeyPair getKeyPair() throws Exception {KeyFactory keyFactory = KeyFactory.getInstance("RSA");byte[] publicKeyData = Files.readAllBytes(Paths.get("c:/tmp/pubkey.der"));byte[] privateKeyData = Files.readAllBytes(Paths.get("c:/tmp/pkcs8_prikey.der"));X509EncodedKeySpec publicKeySpec= new X509EncodedKeySpec(publicKeyData);PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(privateKeyData);PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec);return new KeyPair(publicKey, privateKey);}/** * 用私钥生成签名 *  * Signature.getInstance(algorithm) 算法格式为 <digest>with<encryption> * 支持的算法有:MD5withRSA、SHA256withRSA、SHA256withDSA等等 *  * 全部支持的算法见官方文档: * https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Signature */public static byte[] sign(String signatureAlgorithm, PrivateKey privateKey, byte[] data) throws Exception {Signature sign = Signature.getInstance(signatureAlgorithm);sign.initSign(privateKey);sign.update(data);byte[] result = sign.sign();return result;}/** * 用公钥验签 */public static boolean verify(String signatureAlgorithm, PublicKey publicKey, byte[] data, byte[] signature) throws Exception {Signature sign = Signature.getInstance(signatureAlgorithm);sign.initVerify(publicKey);sign.update(data);return sign.verify(signature);}public static void main(String[] args) throws Exception {KeyPair keyPair = getKeyPair();PrivateKey privateKey = keyPair.getPrivate();PublicKey publicKey = keyPair.getPublic();String signatureAlgorithm = "SHA256withRSA";//需要签名的数据byte[] data = Files.readAllBytes(Paths.get("c:/tmp/apache-tomcat-8.5.23.zip"));//数据+公钥签名byte[] signatureData = sign(signatureAlgorithm, privateKey, data);//把签名转换成十六进制的文本System.out.println(Hex.toHexString(signatureData));//数据+私钥+签名结果进行验证boolean result = verify(signatureAlgorithm, publicKey, data, signatureData);System.out.println(result);}}

执行之后,输出结果为:

a4a68c93f811192fe96f5746c486fa37db6746a6f71b482d7c6a371078b99a567220b3eaf5a984fe

7626dd35eb806adf4cbf63b6e081631172babe8f1785d6f56ddeb9ce5c809f921ac10332cb02c8be

2de304ac20d5ef1c0d9cf7a0874615d27defff751a1fd8dc13849aeeb4ddd0f1ba5d7766e96e9be64

7294ff4a3224033


true

可以看到,签名很长,输出true表示验证通过。


同样的,我们来使用OpenSSL来进行对apache-tomcat-8.5.23.zip进行数字签名

签名的命令:

openssl dgst -sign key.pem -sha256 -hex /tmp/apache-tomcat-8.5.23.zip
结果:



可以看到,和Java签名的结果是一致的

上面这是把签名以十六进制文本输出,下面来同时进行签名和验证

#把签名结果输出到sign.sigopenssl dgst -sign key.pem -sha256 -out sign.sig /tmp/apache-tomcat-8.5.23.zip#验签openssl dgst -verify pub.pem -sha256 -signature sign.sig /tmp/apache-tomcat-8.5.23.zip

输出结果:


验签通过


上面的,Signature.getInstance(algorithm)  参数algorithm可以支持的值除了参考官方文档,还可以通过如下代码得出

Security.getAlgorithms("Signature").forEach(System.out::println);

在Java8中,输出结果如下:

NONEWITHDSASHA384WITHECDSASHA224WITHDSASHA256WITHRSAMD5WITHRSASHA1WITHRSASHA512WITHRSAMD2WITHRSASHA256WITHDSASHA1WITHECDSAMD5ANDSHA1WITHRSASHA224WITHRSANONEWITHECDSANONEWITHRSASHA256WITHECDSASHA224WITHECDSASHA384WITHRSASHA512WITHECDSASHA1WITHDSA



原创粉丝点击