【问题标题】:Separate digest & signing using java security provider使用 java 安全提供程序分离摘要和签名
【发布时间】:2025-12-26 06:10:12
【问题描述】:

由于一些实现细节,我需要拆分散列和签名生成。我尝试使用“NONEwithRSA”签名算法来实现这一点。

这是一个基本的工作示例:

public void rsaSignatureIntegrityTest() {
    KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA");
    gen.initialize(2048, new SecureRandom());
    KeyPair pair = gen.generateKeyPair();

    byte[] digest = MessageDigest.getInstance("SHA-256").digest(MESSAGE);
    Signature signer = Signature.getInstance("NONEwithRSA");
    signer.initSign(pair.getPrivate());
    signer.update(digest);
    byte[] signed = signer.sign();

    Signature verifier = Signature.getInstance("SHA256withRSA");
    verifier.initVerify(pair.getPublic());
    verifier.update(MESSAGE);
    verifier.verify(signed);
}

运行这个,verifier.verify() 方法抛出一个 Signature 异常:

java.security.SignatureException: Signature encoding error
    at sun.security.rsa.RSASignature.engineVerify(RSASignature.java:204)
    at java.security.Signature$Delegate.engineVerify(Signature.java:1219)
    at java.security.Signature.verify(Signature.java:652)
    at testing.rsaSignatureIntegrityTest(testing.java:38)
    ...
Caused by: java.io.IOException: Sequence tag error
    at sun.security.util.DerInputStream.getSequence(DerInputStream.java:297)
    at sun.security.rsa.RSASignature.decodeSignature(RSASignature.java:229)
    at sun.security.rsa.RSASignature.engineVerify(RSASignature.java:195)
    ... 26 more

验证者对象似乎期望某种 DER 编码结构,它不是由签名者对象产生的。

关于如何让它工作的任何建议?

【问题讨论】:

    标签: java hash cryptography rsa digital-signature


    【解决方案1】:

    正如怀疑的那样,RSA 签名是使用包含哈希 oid 的包装摘要值生成的。使用 bouncycastle 这可以很轻松地完成。

    例子:

    public void rsaSignatureIntegrityTest() {
        KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA");
        gen.initialize(2048, new SecureRandom());
        KeyPair pair = gen.generateKeyPair();
    
        byte[] digest = MessageDigest.getInstance("SHA-256").digest(MESSAGE);
        Signature signer = Signature.getInstance("NONEwithRSA");
        signer.initSign(pair.getPrivate());
        signer.update(wrapForRsaSign(digest, "SHA-256"));
        byte[] signed = signer.sign();
        System.out.println(Base64.getEncoder().encodeToString(signed));
    
        Signature verifier = Signature.getInstance("SHA256withRSA");
        verifier.initVerify(pair.getPublic());
        verifier.update(MESSAGE);
        verifier.verify(signed);
    }
    
    private byte[] wrapForRsaSign(byte[] dig, String hashAlgo) {
        ASN1ObjectIdentifier oid = new DefaultDigestAlgorithmIdentifierFinder().find(hashAlgo).getAlgorithm();
        ASN1Sequence oidSeq = new DERSequence(new ASN1Encodable[] { oid, DERNull.INSTANCE });
        ASN1Sequence seq = new DERSequence(new ASN1Encodable[] { oidSeq, new DEROctetString(dig) });
        try {
            return seq.getEncoded();
        } catch (IOException e) {
            throw new DigestException(e);
        }
    }
    

    【讨论】:

    • 参考:EMSA-PKCS1-v1_5 的第 2 步。请注意,在这种情况下,DER 编码相当于添加一个固定前缀,如第 43 页注释 1 中所述。
    • @dave_thompson_085 感谢您的参考,我只是对其进行了逆向工程!