注意:本節內容主要參考自java
14.一、數字簽名算法git
特色:算法
過程:apache
1)消息發送者產生一個密鑰對(私鑰+公鑰),而後將公鑰發送給消息接收者數組
2)消息發送者使用消息摘要算法對原文進行加密(加密後的密文稱做摘要)安全
3)消息發送者將上述的摘要使用私鑰加密獲得密文--這個過程就被稱做簽名處理,獲得的密文就被稱做簽名(注意,這個簽名是名詞)架構
4)消息發送者將原文與密文發給消息接收者分佈式
5)消息接收者使用公鑰對密文(即簽名)進行解密,獲得摘要值content1ide
6)消息接收者使用與消息發送者相同的消息摘要算法對原文進行加密,獲得摘要值content2測試
7)比較content1是否是與content2相等,若相等,則說明消息沒有被篡改(消息完整性),也說明消息倒是來源於上述的消息發送方(由於其餘人是沒法僞造簽名的,這就完成了「抗否定性」和「認證消息來源」)
類比:
手寫簽名:
常見的數字簽名算法:
14.二、RSA
常見算法:
注意:在《Java加密與解密(第二版)》一書中,說JDK6不支持SHA256WithRSA,可是通過我本身測試1.6.45是支持的。
實現方式:(參考上邊三行)
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 }
注意點:
疑問:在《Java加密與解密的藝術(第二版)》一書中,做者說:「RSA數字簽名算法的簽名值與密鑰長度相同」,可是在個人測試中,結果不一致:
1)在我配置非對稱密鑰爲512的時候,測出來的公鑰長度是96位,私鑰長度是345位,與512差很遠,那這裏的512究竟是怎麼計算的?
2)消息摘要的結果長度是64(摘要值的二進制本身數組長度),不知道究竟是怎麼與密鑰長度相同的?
以上兩個問題,有知道的朋友請指點一下,謝謝!