为什么 SHA256withRSA 签名在逐字节计算和一次计算时不同?

Why is SHA256withRSA Signature Different when Computed Byte-by-Byte versus All at Once?

我有两段代码使用 Java 的“SHA256withRSA”Signature。一种方法是 InputStream 装饰器,它通过 read() 方法逐字节更新签名:

public class SigningInputStream extends InputStream {
    // Removed for brevity: declaration of useful objects

    @Override
    public int read() throws IOException {
        final int nextByte = source.read();
        try {
            sign.update((byte) nextByte);
        } catch (java.security.SignatureException e) {
            throw new IOException("Unknown exception while signing file", e);
        }

        return nextByte;
    }

    // Removed for brevity 
}

对方一次性生成签名:

Signature sign = Signature.getInstance("SHA256withRSA");
sign.initSign(privateKey);
        
sign.update(contents);
byte[] signature = sign.sign();
return Base64.getEncoder().encodeToString(signature);

这两种方法给了我不同的结果。我仍在通读 spec(我从另一个 SO 问题中找到链接),但我不认为我会完全理解它;为什么这两种签名方法(逐字节签名与一次全部签名)会产生不同的签名?

您没有说明您的 SigningInputStream 是如何使用的。因此,让我们假设它在没有任何重置的情况下被完全读取,例如像这样:

SigningInputStream sigIS = new SigningInputStream(...);
while (sigIS.read() != -1);

在这种情况下,上面的循环已经暗示了问题:如果因为已经到达流的末尾而没有可用字节,read returns 值 -1.

因此,如果您的 final int nextByte = source.read()-1,您必须忽略此值,因为它不是流内容的一部分:

public int read() throws IOException
{
    final int nextByte = source.read();
    if (nextByte != -1)
    [
        try {
            sign.update((byte) nextByte);
        } catch (java.security.SignatureException e) {
            throw new IOException("Unknown exception while signing file", e);
        }
    }

    return nextByte;
}