虽然JDK最新版本已经有了JDK5和JDK6,但是在目前已经成熟的企业级应用中,JDK1.4所占的比例仍然是巨大的。JDK1.4在信息传递的安全性保证方面,主要有以下概念:
1,消息摘要:这是一种与消息认证码结合使用以确保消息完整性的技术;
2,私钥加密:被设计用来确保消息机密性的技术;
3,公钥加密:允许通信双方不必事先协商秘钥即可共享秘密消息的技术;
4,数字签名:证明另一方的消息确定来自正确通信方的位模式;
5,数字证书:通过让第三方认证机构认证消息,向数字签名添加另一级别安全性的技术;
6,代码签名:由可信的实体将签名嵌入被传递的代码中的概念;
7,SSL/TLS:在客户机和服务器之间建立安全通信通道的协议。传输层安全性(Transport Layer Security (TLS))是安全套接字层(Secure Sockets Layer (SSL))的替代品。
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 支持下列数字签名算法:
1,MD2/RSA
2,MD5/RSA
3,SHA1/DSA
4,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" );
}
}
数字签名代码示例:简单的方法
Signature 类使用由 KeyPairGenerator 类产生的密钥来操作数字签名。下面的示例中使用了下列方法:
(1)KeyPairGenerator.getInstance("RSA")、.initialize(1024) 和 .generateKeyPair():生成密钥。
(2)Cipher.getInstance("MD5WithRSA"):创建 Signature 对象。
(3).initSign(key.getPrivate()):初始化 Signature 对象。
(4).update(plainText) 和 .sign():用明文字符串计算签名。
(5).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" );
}
}
}





