使用 java 安全提供商分离摘要和签名

Separate digest & signing using java security provider

由于一些实现细节,我需要拆分散列和签名生成。我尝试使用 '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 编码结构,这不是由签名者对象产生的。

关于如何让它工作有什么建议吗?

正如所怀疑的那样,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);
    }
}