Java加密解密之數字簽名
阿新 • • 發佈:2018-12-30
上一篇帖子,我們講了MAC(訊息認證碼),它可以驗證身份和防篡改。
它的機制是通過通訊雙方都持有相同的祕鑰去實現,祕鑰相同摘要才相同,沒有祕鑰就不能生成正確的摘要資訊。
但是,它有個缺點,就是通訊雙方必須持有相同的祕鑰,解決方法就是使用數字簽名
數字簽名(又稱公鑰數字簽名、電子簽章)是一種類似寫在紙上的普通的物理簽名,但是使用了非對稱加密領域的技術實現,用於鑑別數字資訊的方法。
一套數字簽名通常定義兩種互補的運算,私鑰用於簽名,公鑰用於驗證(驗籤)。
這樣,就會生成四個檔案,其中pkcs8_prikey.der、pubkey.der是給Java用的
執行之後,輸出結果為:
輸出結果:
在Java8中,輸出結果如下:
它的機制是通過通訊雙方都持有相同的祕鑰去實現,祕鑰相同摘要才相同,沒有祕鑰就不能生成正確的摘要資訊。
但是,它有個缺點,就是通訊雙方必須持有相同的祕鑰,解決方法就是使用數字簽名
數字簽名(又稱公鑰數字簽名、電子簽章)是一種類似寫在紙上的普通的物理簽名,但是使用了非對稱加密領域的技術實現,用於鑑別數字資訊的方法。
一套數字簽名通常定義兩種互補的運算,私鑰用於簽名,公鑰用於驗證(驗籤)。
數字簽名是非對稱金鑰加密技術與數字摘要技術的應用。
既然是非對稱加密,就需要有一對祕鑰,公鑰和私鑰
下面演示一下,用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.sig
openssl 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中,輸出結果如下:
NONEWITHDSA
SHA384WITHECDSA
SHA224WITHDSA
SHA256WITHRSA
MD5WITHRSA
SHA1WITHRSA
SHA512WITHRSA
MD2WITHRSA
SHA256WITHDSA
SHA1WITHECDSA
MD5ANDSHA1WITHRSA
SHA224WITHRSA
NONEWITHECDSA
NONEWITHRSA
SHA256WITHECDSA
SHA224WITHECDSA
SHA384WITHRSA
SHA512WITHECDSA
SHA1WITHDSA