無紙簽名
概述
在本章中,我們將研究數位簽章,它是確定交換訊息的通訊方身份的第一個層級。我們將通過代碼樣本說明標識訊息源的兩種方法(一種比較難,另一種比較容易)。我們還將列出 JDK 1.4 支援的數位簽章演算法,並研究所涉及的類和方法。
回頁首
什麼是數位簽章?
您注意到什麼是公開金鑰密碼術?中描述的公開金鑰訊息交換的缺陷了嗎?Bob 怎麼能夠證實該訊息確實是來自於 Alice 呢?Eve 可以用她的公開金鑰替代 Alice 的公開金鑰,然後 Bob 就會與 Eve 交換訊息,並以為她就是 Alice。這被稱為中間人(Man-in-the-Middle)攻擊。
我們可以通過使用數位簽章解決該問題 ― 數位簽章是證實訊息來自特定通訊方的位元模式。
實現數位簽章的方法之一是逆用什麼是公開金鑰密碼術?中描述的公開金鑰過程。不是用公開金鑰加密和用私密金鑰解密,而是由發送方用私密金鑰來對訊息簽名,然後接收方用發送方的公開金鑰來解密訊息。因為只有發送方才知道私密金鑰,所以接收方可以確保訊息確實是來自接收方。
實際上,訊息摘要(什麼是訊息摘要?)(但並非整個訊息)是用私密金鑰簽名的位流。因此,如果 Alice 想發送一條簽名的訊息給 Bob,她就產生該訊息的訊息摘要,然後用私密金鑰對它簽名。她將訊息(以明文形式)和簽名的訊息摘要發送給 Bob。Bob 用 Alice 的公開金鑰解密簽名的訊息摘要,然後計算明文訊息的訊息摘要並檢查兩個摘要是否匹配。如果它們匹配,則
Bob 可以確認訊息來自於 Alice。
註:數位簽章不提供訊息加密,所以如果您還需要機密性,則必須將加密技術與簽名結合使用。
您可以將 RSA 演算法用於數位簽章和加密。名為 DSA(數位簽章演算法 (Digital Signature Algorithm))的美國標準可以用於數位簽章,但不可以用於加密。
回頁首
演算法
JDK 1.4 支援下列數位簽章演算法:
- MD2/RSA
- MD5/RSA
- SHA1/DSA
- SHA1/RSA
我們將在本章中研究兩個樣本。首先研究困難的方法(請參閱數位簽章程式碼範例:困難的方法 ),它使用我們已經討論過的用於訊息摘要和公開金鑰密碼術的原語來實現數位簽章。然後研究簡單的方法(請參閱數位簽章程式碼範例:簡單的方法),它使用
Java 語言對簽名的直接支援。
回頁首
數位簽章程式碼範例:困難的方法
import java.security.*;import javax.crypto.*;//// This program demonstrates the digital signature technique at the// primative level by generating a message digest of the plaintext// and signing it with an RSA private key, to create the signature.// To verify the signature, the message digest is again generated from// the plaintext and compared with the decryption of the signature// using the public key. If they match, the signature is verified.public class DigitalSignature1Example { public static void main (String[] args) throws Exception { // // check args and get plaintext if (args.length !=1) { System.err.println("Usage: java DigitalSignature1Example text"); System.exit(1); } byte[] plainText = args[0].getBytes("UTF8"); // // get an MD5 message digest object and compute the plaintext digest MessageDigest messageDigest = MessageDigest.getInstance("MD5"); System.out.println( "\n" + messageDigest.getProvider().getInfo() ); messageDigest.update( plainText ); byte[] md = messageDigest.digest(); System.out.println( "\nDigest: " ); System.out.println( new String( md, "UTF8") ); // // generate an RSA keypair System.out.println( "\nStart generating RSA key" ); KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); keyGen.initialize(1024); KeyPair key = keyGen.generateKeyPair(); System.out.println( "Finish generating RSA key" ); // // get an RSA cipher and list the provider Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); System.out.println( "\n" + cipher.getProvider().getInfo() ); // // encrypt the message digest with the RSA private key // to create the signature System.out.println( "\nStart encryption" ); cipher.init(Cipher.ENCRYPT_MODE, key.getPrivate()); byte[] cipherText = cipher.doFinal(md); System.out.println( "Finish encryption: " ); System.out.println( new String(cipherText, "UTF8") ); // // to verify, start by decrypting the signature with the // RSA private key System.out.println( "\nStart decryption" ); cipher.init(Cipher.DECRYPT_MODE, key.getPublic()); byte[] newMD = cipher.doFinal(cipherText); System.out.println( "Finish decryption: " ); System.out.println( new String(newMD, "UTF8") ); // // then, recreate the message digest from the plaintext // to simulate what a recipient must do System.out.println( "\nStart signature verification" ); messageDigest.reset(); messageDigest.update(plainText); byte[] oldMD = messageDigest.digest(); // // verify that the two message digests match int len = newMD.length; if (len > oldMD.length) { System.out.println( "Signature failed, length error"); System.exit(1); } for (int i = 0; i < len; ++i) if (oldMD[i] != newMD[i]) { System.out.println( "Signature failed, element error" ); System.exit(1); } System.out.println( "Signature verified" ); }} |
回頁首
樣本執行
D:\IBM>java DigitalSignature1Example "This is a test!"SUN (DSA key/parameter generation; DSA signing; SHA-1, MD5 digests; SecureRandom; X.509 certificates; JKS keystore; PKIX CertPathValidator; PKIX CertPathBuilder; LDAP, Collection CertStores)Digest:D647dbdek12*e,ad.?eStart generating RSA keyFinish generating RSA keyBouncyCastle Security Provider v1.12Start encryptionFinish encryption:Akjsdfp-9q8237nrcas-9de8fn239-4rb[*[OPOsjkdfJDL:JF;lkjs;ldjStart decryptionFinish decryption:iNdf6D213$dcd(ndz!0)Start signature verificationSignature verified |
回頁首
數位簽章程式碼範例:簡單的方法
Signature
類使用由 KeyPairGenerator
類產生的密鑰來運算元字簽名。下面的樣本中使用了下列方法:
KeyPairGenerator.getInstance("RSA")
、.initialize(1024)
和 .generateKeyPair()
:產生密鑰。
Cipher.getInstance("MD5WithRSA")
:建立 Signature
對象。
.initSign(key.getPrivate())
:初始化 Signature
對象。
.update(plainText)
和 .sign()
:用明文字串計算簽名。
.initVerify(key.getPublic())
和 .verify(signature)
:驗證簽名。
import java.security.*;import javax.crypto.*;//// This example uses the digital signature features to generate and// verify a signature much more easily than the previous examplepublic class DigitalSignature2Example { public static void main (String[] args) throws Exception { // // check args and get plaintext if (args.length !=1) { System.err.println("Usage: java DigitalSignature1Example text"); System.exit(1); } byte[] plainText = args[0].getBytes("UTF8"); // // generate an RSA keypair System.out.println( "\nStart generating RSA key" ); KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); keyGen.initialize(1024); KeyPair key = keyGen.generateKeyPair(); System.out.println( "Finish generating RSA key" ); // // get a signature object using the MD5 and RSA combo // and sign the plaintext with the private key, // listing the provider along the way Signature sig = Signature.getInstance("MD5WithRSA"); sig.initSign(key.getPrivate()); sig.update(plainText); byte[] signature = sig.sign(); System.out.println( sig.getProvider().getInfo() ); System.out.println( "\nSignature:" ); System.out.println( new String(signature, "UTF8") ); // // verify the signature with the public key System.out.println( "\nStart signature verification" ); sig.initVerify(key.getPublic()); sig.update(plainText); try { if (sig.verify(signature)) { System.out.println( "Signature verified" ); } else System.out.println( "Signature failed" ); } catch (SignatureException se) { System.out.println( "Signature failed" ); } }} |
回頁首
樣本執行
Start generating RSA keyFinish generating RSA keySun JSSE provider(implements RSA Signatures, PKCS12, SunX509 key/trust factories, SSLv3, TLSv1)Signature:Ldkjahasdlkjfq[?owc42093nhasdk1a;sn;a#a;lksjd;fl@#kjas;ldjf78qwe09r7Start signature verificationSignature verified |
FROM: http://www.ibm.com/developerworks/cn/education/java/j-sec1/section6.html