【问题标题】:Is it possible to verify a SHA256withRSA signature with a SHA256 hash of the original data?是否可以使用原始数据的 SHA256 哈希验证 SHA256withRSA 签名?
【发布时间】:2021-01-22 20:54:49
【问题描述】:

长期以来,我一直在使用 X509 证书处理签名。

PrivateKey privateKey = ...
String document = ...

Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(privateKey);
signature.update(document.getBytes());
return signature.sign();

并验证...

PublicKey publicKey = ...
String document = ...
byte[] signature = ...

Signature signature = Signature.getInstance("SHA256withRSA");
signature.initVerify(publicKey);
signature.update(document.getBytes());
return signature.verify(signature);

很简单。

但是最近听说可以只用文档的SHA256哈希来验证签名,而不是整个文档...

PublicKey publicKey = ...
byte[] documentHash = MessageDigest.getInstance("SHA-256").digest(document.getBytes());
byte[] signature = ...

Signature signature = Signature.getInstance("SHA256withRSA");
signature.initVerify(publicKey);
// ???
return signature.verify(signature);

有可能吗?在 Java 中会怎样?

我是从另一家公司听说的,所以我无法访问源代码... =(

【问题讨论】:

标签: java certificate rsa x509certificate sha256


【解决方案1】:

简短的回答是YES

长答案与将签名与算法标识符一起包装的编码以及存档“仅在哈希上验证”功能的编码有关,您必须进行 2 次更改。

首先 - 将算法标识符添加到签名中: 正如@President James K. Polk 所写,您必须添加一些额外的字节才能正确编码验证函数的输入。根据您的需要 “EMSA-PKCS1-v1_5”-Padding(在此处描述:https://www.rfc-editor.org/rfc/rfc3447#page-41)您必须在前面添加一些代表 用于计算哈希的算法。

我有点懒,将必要的字节添加为硬编码字节数组,所以这个版本确实仅适用于SHA-256算法 - 如果 您曾经使用过不同的散列算法来更改前置字节:

String prependSha256String = "3031300D060960864801650304020105000420";
byte[] prependSha256 = hexStringToByteArray(prependSha256String);
int combinedLength = prependSha256.length + documentHash.length;
byte[] documentHashFull = new byte[combinedLength];
System.arraycopy(prependSha256, 0, documentHashFull, 0, prependSha256.length);
System.arraycopy(documentHash, 0, documentHashFull, prependSha256.length, documentHash.length);

第二 - 使用另一个 RSA 签名方案: 由于我们已经完成了 SHA-256 部分,我们需要一个名为“NonewithRSA”的“裸”RSA 方案,因此您需要更改实例化,例如:

Signature signatureVerifyHash = Signature.getInstance("NonewithRSA");

这些是两个 RSA 签名验证的结果(旧的和“新的”一个):

verify the signature with the full document
sigVerified: true

verify the signature with the SHA256 of the document only
sigVerifiedHash: true

这是完整的工作代码:

import java.security.*;

public class MainSo2 {
    public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
        System.out.println("Is it possible to verify a SHA256withRSA signature with a SHA256 hash of the original data?");

        // create a rsa keypair of 2048 bit keylength
        KeyPairGenerator rsaGenerator = KeyPairGenerator.getInstance("RSA");
        SecureRandom random = new SecureRandom();
        rsaGenerator.initialize(2048, random);
        KeyPair rsaKeyPair = rsaGenerator.generateKeyPair();
        PublicKey publicKey = rsaKeyPair.getPublic();
        PrivateKey privateKey = rsaKeyPair.getPrivate();

        String document = "The quick brown fox jumps over the lazy dog";
        // sign
        Signature signature = Signature.getInstance("SHA256withRSA");
        signature.initSign(privateKey);
        signature.update(document.getBytes());
        byte[] sig = signature.sign();

        // verify with full message
        System.out.println("\nverify the signature with the full document");
        Signature signatureVerify = Signature.getInstance("SHA256withRSA");
        signatureVerify.initVerify(publicKey);
        signatureVerify.update(document.getBytes());
        boolean sigVerified =  signatureVerify.verify(sig);
        System.out.println("sigVerified: " + sigVerified);

        // verify just the sha256 hash of the document
        System.out.println("\nverify the signature with the SHA256 of the document only");
        byte[] documentHash = MessageDigest.getInstance("SHA-256").digest(document.getBytes());
        // you need to prepend some bytes: 30 31 30 0D 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20
        // see https://www.rfc-editor.org/rfc/rfc3447#page-41
        // warning: this string is only for SHA-256 algorithm !!
        String prependSha256String = "3031300D060960864801650304020105000420";
        byte[] prependSha256 = hexStringToByteArray(prependSha256String);
        int combinedLength = prependSha256.length + documentHash.length;
        byte[] documentHashFull = new byte[combinedLength];
        System.arraycopy(prependSha256, 0, documentHashFull, 0, prependSha256.length);
        System.arraycopy(documentHash, 0, documentHashFull, prependSha256.length, documentHash.length);
        // lets verify
        Signature signatureVerifyHash = Signature.getInstance("NonewithRSA");
        signatureVerifyHash.initVerify(publicKey);
        // signatureVerifyHash.update(document.getBytes());
        signatureVerifyHash.update(documentHashFull);
        boolean sigVerifiedHash =  signatureVerifyHash.verify(sig);
        System.out.println("sigVerifiedHash: " + sigVerifiedHash);
    }

    public static byte[] hexStringToByteArray(String s) {
        int len = s.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                    + Character.digit(s.charAt(i + 1), 16));
        }
        return data;
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-12-10
    • 1970-01-01
    • 2011-06-20
    • 1970-01-01
    • 2015-02-06
    • 1970-01-01
    • 1970-01-01
    • 2017-03-24
    相关资源
    最近更新 更多