How to generate/verify RSA-PSS with pre-computed content hash?
At RFC 8017 (PKCS#1 v2.2) chapter 9.1. "EMSA-PSS" the "Figure 2" diagram shows that input "M" is hashed producing "mHash", then it is padded and salted, and again run through the same hash algorithm as the initial input is done.
The PSSSigner.java class implements this diagram very faithfully.
How can I feed the algorithm with pre-computed mHash ? Maybe modifying the PSSSigner to enter two separate instances of the Hash algorithm, and instantiating first one as NullDigest ?
The Java Card has Signature API called: signPrecomputedHash() taking the mHash directly in from the caller.
Okay, this seem to be a documentation issue more than anything else. I modified the PSSSigner.java (to a separate instance) to create a case where the first content digest is (when needed) a NullDigest. However using this (below) public provider API produces results that validate successfully both ways.
Similar validator call agrees with pre-computed-hash test vector values. Both when given original data and when given the mHash of the original data.
Java 1.8 runtime RSASSA-PSS signer does not seem to know "NONEwithRSASSA-PSS", and thus can not be used to sign pre-computed hashes. But at least BC seem to work.
public static byte[] bcRSAPSSSign(final boolean input_is_mHash,
final byte[] input,
final KeyPair kp,
final String hashAlgName,
final int hashAlgSize)
throws SignatureException, NoSuchAlgorithmException,
InvalidAlgorithmParameterException, InvalidKeyException
{
// key length lookup is only for the debug printout, this can be omitted
PublicKey pubk = kp.getPublic();
if (!(pubk instanceof RSAPublicKey)) {
throw new SignatureException("The input key type is not an RSA key, it is: "+pubk.getAlgorithm());
}
RSAPublicKey rpk = (RSAPublicKey) pubk;
int keyLength = rpk.getModulus().bitLength();
// The DTBS size must match that of the hashAlgName when feeding mHash.
System.out.println("RSAPSS-Sign: input-is-mHash="+input_is_mHash+
" input.len="+input.length+" keyLength="+keyLength+
" hashAlg="+hashAlgName+" hashAlgSize="+hashAlgSize);
// Select the digest per hashAlgName
MGF1ParameterSpec mgfParam = new MGF1ParameterSpec(hashAlgName);
PSSParameterSpec pssParam = new PSSParameterSpec(hashAlgName,
"MGF1", mgfParam,
hashAlgSize, // SaltLen, bytes
PSSParameterSpec.TRAILER_FIELD_BC);
final Signature s;
if (input_is_mHash) {
s = Signature.getInstance("NONEwithRSASSA-PSS",BC);
System.out.println("Using signer NONEwithRSASSA-PSS,"+s.getProvider().getName());
} else {
s = Signature.getInstance(hashAlgName+"withRSASSA-PSS",BC);
System.out.println("Using signer "+hashAlgName+"withRSASSA-PSS,"+s.getProvider().getName());
}
s.initSign(kp.getPrivate());
// Set parameters _after_ init
s.setParameter(pssParam);
s.update(input);
return s.sign();
}