bc-java icon indicating copy to clipboard operation
bc-java copied to clipboard

How to generate/verify RSA-PSS with pre-computed content hash?

Open oh2mqk opened this issue 3 years ago • 1 comments

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.

oh2mqk avatar Apr 08 '22 13:04 oh2mqk

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();
    }

oh2mqk avatar Apr 11 '22 11:04 oh2mqk