Java 安全性,第 1 部分: 密码术基础(数字签名)

无纸签名

概述

在本章中,我们将研究数字签名,它是确定交换消息的通信方身份的第一个级别。我们将通过代码样本说明标识消息源的两种方法(一种比较难,另一种比较容易)。我们还将列出 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 verification
Signature verified





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值