将 SHA1 和 RSA 与 java.security.Signature 与 MessageDigest 和 Cipher 结合使用

我试图理解Java java.security.Signature类的作用。如果我计算一个 SHA1 消息摘要,然后使用 RSA 加密该摘要,则得到的结果与要求 Signature 类对同一内容进行签名的结果不同:

// Generate new key
KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
String plaintext = "This is the message being signed";

// Compute signature
Signature instance = Signature.getInstance("SHA1withRSA");
instance.initSign(privateKey);
instance.update((plaintext).getBytes());
byte[] signature = instance.sign();

// Compute digest
MessageDigest sha1 = MessageDigest.getInstance("SHA1");
byte[] digest = sha1.digest((plaintext).getBytes());

// Encrypt digest
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
byte[] cipherText = cipher.doFinal(digest);

// Display results
System.out.println("Input data: " + plaintext);
System.out.println("Digest: " + bytes2String(digest));
System.out.println("Cipher text: " + bytes2String(cipherText));
System.out.println("Signature: " + bytes2String(signature));

结果(例如):

输入数据:这是正在签名
的消息 摘要: 62b0a9ef15461c82766fb5bdaae9edbe4ac2e067
密码文本: 057dc0d2f7f54acc95d3cf5cba9f944619394711003bdd12...
签名: 7177c74bbbb871cc0af92e30d2808ebae146f25d3fd8ba1622...

我必须从根本上误解Signature正在做什么 - 我已经通过它进行了跟踪,它似乎正在调用MessageDigest对象的更新,算法设置为SHA1,正如我所期望的那样,然后获取摘要,然后进行加密。是什么让结果不同?

编辑:

列奥尼达斯让我检查签名计划是否应该做我认为它所做的事情。RFC 中定义了两种类型的签名:

其中第一个(PKCS1)是我上面描述的那个。它使用哈希函数创建摘要,然后使用私钥对结果进行加密。

二种算法使用随机盐值,更安全但非确定性。如果重复使用相同的密钥,则从上面的代码生成的签名不会更改,因此我认为它不能是PSS。

编辑:

这是我使用的方法:bytes2string

private static String bytes2String(byte[] bytes) {
    StringBuilder string = new StringBuilder();
    for (byte b : bytes) {
        String hexString = Integer.toHexString(0x00FF & b);
        string.append(hexString.length() == 1 ? "0" + hexString : hexString);
    }
    return string.toString();
}

答案 1

好吧,我已经弄清楚发生了什么。Leonidas是对的,不仅仅是哈希被加密(在Cipher类方法的情况下),它是与摘要连接的哈希算法的ID:

  DigestInfo ::= SEQUENCE {
      digestAlgorithm AlgorithmIdentifier,
      digest OCTET STRING
  }

这就是为什么密码和签名的加密是不同的。


答案 2

要产生相同的结果,请执行以下操作:

MessageDigest sha1 = MessageDigest.getInstance("SHA1", BOUNCY_CASTLE_PROVIDER);
byte[] digest = sha1.digest(content);
DERObjectIdentifier sha1oid_ = new DERObjectIdentifier("1.3.14.3.2.26");

AlgorithmIdentifier sha1aid_ = new AlgorithmIdentifier(sha1oid_, null);
DigestInfo di = new DigestInfo(sha1aid_, digest);

byte[] plainSig = di.getDEREncoded();
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", BOUNCY_CASTLE_PROVIDER);
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
byte[] signature = cipher.doFinal(plainSig);

推荐