Java加密解密之数字签名

转自https://blog.csdn.net/mn960mn/article/details/78177250

上一篇帖子,我们讲了MAC(消息认证码),它可以验证身份和防篡改。
它的机制是通过通信双方都持有相同的秘钥去实现,秘钥相同摘要才相同,没有秘钥就不能生成正确的摘要信息。


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


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

 

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

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

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

 
  1. #生成RSA私钥,默认是编码方式为PEM的PKCS#1格式

  2. #PKCS#1格式是传统的私钥格式

  3. openssl genrsa -out key.pem 1024

  4.  
  5. #从私钥中生成公钥,给OpenSSL验签用的

  6. openssl rsa -in key.pem -out pub.pem -pubout

  7.  
  8. #把PEM编码格式的私钥转换成DER编码的私钥,同时进行PKCS#1转换成PKCS#8(Java默认只能处理PKCS#8的格式)

  9. #-nocrypt 意思是不加密

  10. #给Java用

  11. openssl pkcs8 -topk8 -in key.pem -out pkcs8_prikey.der -inform PEM -outform DER -nocrypt

  12.  
  13. #从私钥中导出DER编码的公钥

  14. #给Java用

  15. 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文件的数字签名

 
  1. package com.security.sign;

  2.  
  3. import java.nio.file.Files;

  4. import java.nio.file.Paths;

  5. import java.security.KeyFactory;

  6. import java.security.KeyPair;

  7. import java.security.PrivateKey;

  8. import java.security.PublicKey;

  9. import java.security.Signature;

  10. import java.security.spec.PKCS8EncodedKeySpec;

  11. import java.security.spec.X509EncodedKeySpec;

  12.  
  13. import org.bouncycastle.util.encoders.Hex;

  14.  
  15. public class SignatureTest {

  16.  
  17. public static KeyPair getKeyPair() throws Exception {

  18. KeyFactory keyFactory = KeyFactory.getInstance("RSA");

  19.  
  20. byte[] publicKeyData = Files.readAllBytes(Paths.get("c:/tmp/pubkey.der"));

  21. byte[] privateKeyData = Files.readAllBytes(Paths.get("c:/tmp/pkcs8_prikey.der"));

  22.  
  23. X509EncodedKeySpec publicKeySpec= new X509EncodedKeySpec(publicKeyData);

  24. PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(privateKeyData);

  25.  
  26. PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);

  27. PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec);

  28.  
  29. return new KeyPair(publicKey, privateKey);

  30. }

  31.  
  32. /**

  33. * 用私钥生成签名

  34. *

  35. * Signature.getInstance(algorithm) 算法格式为 <digest>with<encryption>

  36. * 支持的算法有:MD5withRSA、SHA256withRSA、SHA256withDSA等等

  37. *

  38. * 全部支持的算法见官方文档:

  39. * https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Signature

  40. */

  41. public static byte[] sign(String signatureAlgorithm, PrivateKey privateKey, byte[] data) throws Exception {

  42. Signature sign = Signature.getInstance(signatureAlgorithm);

  43. sign.initSign(privateKey);

  44. sign.update(data);

  45. byte[] result = sign.sign();

  46. return result;

  47. }

  48.  
  49. /**

  50. * 用公钥验签

  51. */

  52. public static boolean verify(String signatureAlgorithm, PublicKey publicKey, byte[] data, byte[] signature) throws Exception {

  53. Signature sign = Signature.getInstance(signatureAlgorithm);

  54. sign.initVerify(publicKey);

  55. sign.update(data);

  56. return sign.verify(signature);

  57. }

  58.  
  59. public static void main(String[] args) throws Exception {

  60. KeyPair keyPair = getKeyPair();

  61.  
  62. PrivateKey privateKey = keyPair.getPrivate();

  63. PublicKey publicKey = keyPair.getPublic();

  64.  
  65. String signatureAlgorithm = "SHA256withRSA";

  66.  
  67. //需要签名的数据

  68. byte[] data = Files.readAllBytes(Paths.get("c:/tmp/apache-tomcat-8.5.23.zip"));

  69.  
  70. //数据+私钥签名

  71. byte[] signatureData = sign(signatureAlgorithm, privateKey, data);

  72.  
  73. //把签名转换成十六进制的文本

  74. System.out.println(Hex.toHexString(signatureData));

  75.  
  76. //数据+公钥+签名结果进行验证

  77. boolean result = verify(signatureAlgorithm, publicKey, data, signatureData);

  78. System.out.println(result);

  79. }

  80. }


执行之后,输出结果为:

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签名的结果是一致的

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

 
  1. #把签名结果输出到sign.sig

  2. openssl dgst -sign key.pem -sha256 -out sign.sig /tmp/apache-tomcat-8.5.23.zip

  3.  
  4. #验签

  5. 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中,输出结果如下:

NONEWITHDSA
SHA384WITHECDSA
SHA224WITHDSA
SHA256WITHRSA
MD5WITHRSA
SHA1WITHRSA
SHA512WITHRSA
MD2WITHRSA
SHA256WITHDSA
SHA1WITHECDSA
MD5ANDSHA1WITHRSA
SHA224WITHRSA
NONEWITHECDSA
NONEWITHRSA
SHA256WITHECDSA
SHA224WITHECDSA
SHA384WITHRSA
SHA512WITHECDSA
SHA1WITHDSA

猜你喜欢

转载自blog.csdn.net/sanyaoxu_2/article/details/81407295