| « | January 2026 | » | | 日 | 一 | 二 | 三 | 四 | 五 | 六 | | | | | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | |
| 公告 |
| 暂无公告... |
| Blog信息 |
|
blog名称:天地无用 日志总数:55 评论数量:43 留言数量:1 访问次数:195777 建立时间:2008年4月17日 |

| |
|
密码术基础(续)(转) 软件技术
kkk888929 发表于 2008/4/21 13:47:39 |
| 第四章 保持消息的机密性
■概述 在本节中,我们将研究私钥加密的使用并主要讨论诸如密码块、填充、序列密码和密码方式之类的概念。我们将迅速地讨论密码算法、类和方法的细节并用代码示例和样本执行来说明这个概念。
■什么是私钥密码术? 消息摘要可以确保消息的完整性,但不能用来确保消息的机密性。要确保机密性,我们需要使用私钥密码术来交换私有消息。 Alice 和Bob 各有一个只有他们两人知道的共享密钥,并且约定使用一种公用密码算法或密码。也就是说,他们保持密钥的私有性。当Alice 想给Bob 发送消息时,她加密原始消息(称为明文)以创建密文,然后将密文发送给Bob。Bob 接收了来自Alice 的密文并用自己的私钥对它解密,以重新创建原始明文消息。如果窃听者Eve 侦听该通信,她仅得到密文,因此消息的机密性得以保持。 您可以加密单一位或位块(称为块)。块(称为密码块)通常是64 位大小。如果消息大小不是64 位的整数倍,那么必须填充短块(关于填充的更多信息请参阅什么是填充?)。单一位加密在硬件实现中更常见。单一位的密码被称为序列密码。 私钥加密的强度取决于密码算法和密钥的长度。如果算法比较好,那么攻击它的唯一方法就是使用尝试每个可能密钥的蛮力攻击,它平均要尝试(1/2)*2*n 次,其中n 是密钥的位数。 在美国出口法规很严时,只允许出口40 位密钥。这种密钥长度相当弱。美国官方标准(DES 算法)使用56 位密钥,但随着处理器速度的增加,它变得越来越弱。现在,通常首选128 位密钥。如果每秒可以尝试一百万个密钥,那么,使用128 位密钥,找到密钥所需的平均时间是宇宙年龄的许多倍!
■什么是填充? 如前所述,如果使用分组密码而消息长度不是块长度的整数倍,那么,必须用字节填充最后一个块以凑成完整的块大小。有许多方法填充块,譬如全用零或一。在本教程中,我们将对私钥加密使用PKCS5 填充,而对公钥加密使用PKCS1。 使用PKCS5 时,由一个其值表示剩余字节数的重复字节来填充短块。我们不会在本教程中更深入地讨论填充算法,但您需要了解的信息是,JDK 1.4 支持下列填充技术: 无填充 ·PKCS5 ·OAEP ·SSL3 BouncyCastle 库(请参阅第三方库充实了安全性和参考资料)支持其它填充技术。
■方式:确定加密是如何工作的 可以以各种方式使用给定密码。方式允许您确定加密是如何工作的。 例如,您可以允许一个块的加密依赖于前一个块的加密,也可以使块的加密独立于任何其它块的加密。 根据您的需求选择方式,并且您必须考虑各方面的权衡(安全性、并行处理能力以及明文和密文的容错等)。方式的选择超出了本教程的讨论范围(请参阅参考资料以进行更深入的阅读),但再次指出,您要了解的信息是,Java 平台支持下列方法: ·ECB(电子密码本(Electronic Code Book)) ·CBC(密码块链接(Cipher Block Chaining)) ·CFB(密码反馈方式(Cipher Feedback Mode)) ·OFB(输出反馈方式(Output Feedback Mode)) ·PCBC(填充密码块链接(Propagating Cipher Block Chaining))
■算法、类和方法 JDK 1.4 支持下列私钥算法: ·DES。DES(数据加密标准)是由IBM 于上世纪70 年代发明的,美国政府将其采纳为标准。它是一种56 位分组密码。 ·TripleDES。该算法被用来解决使用DES 技术的56 位时密钥日益减弱的强度,其方法是:使用两个密钥对明文运行DES 算法三次,从而得到112 位有效密钥强度。TripleDES 有时称为DESede(表示加密、解密和加密这三个阶段)。 ·AES。AES(高级加密标准)取代DES 成为美国标准。它是由Joan Daemen 和Vincent Rijmen 发明的,也被称为Rinjdael 算法。它是128 位分组密码,密钥长度为128 位、192 位或256 位。 ·RC2、RC4、和RC5。这些算法来自领先的加密安全性公司RSA Security。 ·Blowfish。这种算法是由Bruce Schneier 开发的,它是一种具有从32 位到448 位(都是8 的整数倍)可变密钥长度的分组密码,被设计用于在软件中有效实现微处理器。 ·PBE。PBE(基于密码的加密)可以与多种消息摘要和私钥算法结合使用。 Cipher 类使用由KeyGenerator 类产生的密钥操作私钥算法。私钥密码术代码示例中使用了下列方法: ·KeyGenerator.getInstance("DES")、.init(56) 和.generateKey():生成密钥。 ·Cipher.getInstance("DES/ECB/PKCS5Padding"):创建Cipher 对象(确定算法、方式和填充)。 ·.init(Cipher.ENCRYPT_MODE, key):初始化Cipher 对象。 ·.doFinal(plainText):用明文字符串计算密文。 ·.init(Cipher.DECRYPT_MODE, key):解密密文。 ·.doFinal(cipherText):计算密文。
■私钥密码术代码示例 import java.security.*; import javax.crypto.*; // // encrypt and decrypt using the DES private key algorithm public class PrivateExample { public static void main (String[] args) throws Exception { // // check args and get plaintext if (args.length !=1) { System.err.println("Usage: java PrivateExample text"); System.exit(1); } byte[] plainText = args[0].getBytes("UTF8"); // // get a DES private key System.out.println( "\nStart generating DES key" ); KeyGenerator keyGen = KeyGenerator.getInstance("DES"); keyGen.init(56); Key key = keyGen.generateKey(); System.out.println( "Finish generating DES key" ); // // get a DES cipher object and print the provider Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding"); System.out.println( "\n" + cipher.getProvider().getInfo() ); // // encrypt using the key and the plaintext System.out.println( "\nStart encryption" ); cipher.init(Cipher.ENCRYPT_MODE, key); byte[] cipherText = cipher.doFinal(plainText); System.out.println( "Finish encryption: " ); System.out.println( new String(cipherText, "UTF8") ); // // decrypt the ciphertext using the same key System.out.println( "\nStart decryption" ); cipher.init(Cipher.DECRYPT_MODE, key); byte[] newPlainText = cipher.doFinal(cipherText); System.out.println( "Finish decryption: " ); System.out.println( new String(newPlainText, "UTF8") ); } }
■私钥密码术样本执行 D:\IBM>java PrivateExample "This is a test!"
Start generating DES key Finish generating DES key
SunJCE Provider (implements DES, Triple DES, Blowfish, PBE, Diffie-Hellman, HMAC-MD5, HMAC-SHA1)
Start encryption Finish encryption: Kdkj4338*3n1#kxkgtixo4
Start decryption Finish decryption: This is a test!
第五章 用公钥保密消息
■概述 在本章中,我们将研究公钥密码术,该特性解决在事先没有约定密钥的通信双方之间加密消息的问题。我们将简单地浏览一下支持公钥功能的算法、类和方法,并提供代码样本和执行来说明该概念。
■什么是公钥密码术? 私钥密码术受到一个主要缺点的困扰:一开始怎样将私钥交给Alice 和Bob 呢?如果Alice 生成了私钥,则必须将它发送给Bob,但私钥是敏感信息,所以应该加密。但是,还没有交换密钥来执行加密。 公钥密码术是上世纪70 年代发明的,它解决在没有事先约定密钥的通信双方之间加密消息的问题。 在公钥密码术中,Alice 和Bob 不仅有不同的密钥,而且每人有两个密钥。一个是私有的,不应与任何人共享。另一个是公共的,可以与任何人共享。 当Alice 想给Bob 发送安全消息时,她用Bob 的公钥加密消息并将结果发送给Bob。Bob 使用他的私钥解密消息。当Bob 想给Alice 发送安全消息时,他用Alice 的公钥加密消息并将结果发送给Alice。Alice 使用她的私钥解密消息。Eve 可以窃听公钥和已加密的消息,但她不能解密消息,因为她没有任何一个私钥。 公钥和私钥是成对生成的,并需要比同等强度的私钥加密密钥更长。RSA 算法的典型密钥长度是1,024 位。从该密钥对的一个成员派生出另一个是不可行的。 公钥加密比较慢(比私钥加密慢100 到1,000 倍),因此实际上通常使用混合技术。公钥加密被用于向对方分发称为会话密钥的私钥,使用该私有会话密钥的私钥加密被用于进行大量的消息加密。
■算法、类和方法 公钥加密中使用下列两种算法: ·RSA。这个算法是最流行的公钥密码,但JDK 1.4 中不支持它。您必须使用类似于BouncyCastle 的第三方库来获得这种支持。 ·Diffie-Hellman。技术上将这种算法称为密钥协定算法。它不能用于加密,但可以用来允许双方通过在公用通道上共享信息来派生出秘钥。然后这个密钥可以用于私钥加密。 Cipher 类使用由KeyPairGenerator 类产生的密钥来操作公钥算法。公钥密码术代码示例示例中使用了下列方法: ·KeyPairGenerator.getInstance("RSA")、.initialize(1024) 和.generateKeyPair():生成密钥对。 ·Cipher.getInstance("RSA/ECB/PKCS1Padding") 创建Cipher 对象(确定算法、方式和填充)。 ·.init(Cipher.ENCRYPT_MODE, key.getPublic()):初始化Cipher 对象。 ·.doFinal(plainText):用明文字符串计算密文。 ·.init(Cipher.DECRYPT_MODE, key.getPrivate()) 和.doFinal(cipherText):解密密文。
■公钥密码术代码示例 import java.security.*; import javax.crypto.*; // // Public Key cryptography using the RSA algorithm. public class PublicExample { public static void main (String[] args) throws Exception { // // check args and get plaintext if (args.length !=1) { System.err.println("Usage: java PublicExample text"); System.exit(1); } byte[] plainText = args[0].getBytes("UTF8"); // // generate an RSA key 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 object and print the provider Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); System.out.println( "\n" + cipher.getProvider().getInfo() ); // // encrypt the plaintext using the public key System.out.println( "\nStart encryption" ); cipher.init(Cipher.ENCRYPT_MODE, key.getPublic()); byte[] cipherText = cipher.doFinal(plainText); System.out.println( "Finish encryption: " ); System.out.println( new String(cipherText, "UTF8") ); // // decrypt the ciphertext using the private key System.out.println( "\nStart decryption" ); cipher.init(Cipher.DECRYPT_MODE, key.getPrivate()); byte[] newPlainText = cipher.doFinal(cipherText); System.out.println( "Finish decryption: " ); System.out.println( new String(newPlainText, "UTF8") ); } }
公钥密码术样本执行第5 页(共5 页) D:\IBM>java PublicExample "This is a test!"
Start generating RSA key Finish generating RSA key
BouncyCastle Security Provider v1.12
Start encryption Finish encryption: Ajsd843*342l,AD;LKJL;1!*AD(XLKASD498asdjlkkKSFJHDuhpja;d(kawe#kjalfcas, .asd+,1LKSDJf;khaouiwheyahdsl87458q9734hjfc*nuywe
Start decryption Finish decryption: This is a test!
第六章 无纸签名
■概述 在本章中,我们将研究数字签名,它是确定交换消息的通信方身份的第一个级别。我们将通过代码样本说明标识消息源的两种方法(一种比较难,另一种比较容易)。我们还将列出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.?e
Start generating RSA key Finish generating RSA key
BouncyCastle Security Provider v1.12
Start encryption Finish encryption: Akjsdfp-9q8237nrcas-9de8fn239-4rb[*[OPOsjkdfJDL:JF;lkjs;ldj
Start decryption Finish decryption: iNdf6D213$dcd(ndz!0)
Start signature verification Signature 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 example public 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 key Finish generating RSA key Sun JSSE provider(implements RSA Signatures, PKCS12, SunX509 key/trust factories, SSLv3, TLSv1) Signature: Ldkjahasdlkjfq[?owc42093nhasdk1a;sn;a#a;lksjd;fl@#kjas;ldjf78qwe09r7 Start signature verificationSignature verified |
|
|