fdomig
fdomig

Reputation: 4457

How to extend/add a Bouncycastle digest algorithm for CMS?

With Java I want to cryptographically sign given byte[] hash with RSA and return byte[] signature in the Cryptographic Message Syntax (CMS) format.

I am using the Bouncycastle Java API (BC) for this purpose and face the problem of the not existing NONEwithRSA signature type. (java.lang.IllegalArgumentException: Unknown signature type requested: NONEWITHRSA).

I, unfortunately, cannot change the input nor the required output format so I am required to extend/add to the existing BC algorithm so that I can use NONEwithRSA instead of all the other existing (e.g. SHA256withRSA). How do I do this? I have not found any example in the BC documentation.

My desired usage would be similar to this

byte[] doSigningCMS(byte[] data, X509Certificate cert, PrivateKey key) throws Exception {

    CMSSignedDataGenerator signGen = new CMSSignedDataGenerator();
    CMSTypedData content = new CMSProcessableByteArray(data);

    ContentSigner signer = new JcaContentSignerBuilder("NONEwithRSA").build(key);

    DigestCalculatorProvider dcp = new JcaDigestCalculatorProviderBuilder().build();

    signGen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(dcp).build(signer, cert));

    return signGen.generate(content, false).getEncoded();
}

What I have tried so far

I hope for some advice and guidance to do this (if even possible) "the right way". Thanks.

Upvotes: 1

Views: 2665

Answers (1)

pedrofb
pedrofb

Reputation: 39241

"NoneWithRSA" is a RSA digital signature without adding the OID of the digest algorithm. The cryptographic provider does not make the digest either. Basically it is a PKCS#1_v15 encryption with the private key.

You can see how it works in this OpenJDK test https://github.com/ddopson/openjdk-test/blob/master/java/security/Signature/NONEwithRSA.java

Since Bouncycastle seems does not support it, I think you can supply your own ContentSigner implementation using the default provider instead of using JcaContentSignerBuilder

It is supposed that the input data is already hashed, so if you are doing a PKCS#1 signature, I think you need to provide the signature algorithm. Looking at the RFC3477 it depends on the hash algorithm used.

A.2.4 RSASSA-PKCS1-v1_5

The object identifier for RSASSA-PKCS1-v1_5 shall be one of the following. The choice of OID depends on the choice of hash algorithm: MD2, MD5, SHA-1, SHA-256, SHA-384, or SHA-512.


String sigAlgo = "SHA256WithRSAEncryption"; // "SHA256WithRSAEncryption" for SHA256, "SHA1WithRSAEncryption" for SHA1, etc.
ContentSigner signer = new CustomContentSigner(key, sigAlgo );

public class CustomContentSigner implements ContentSigner {

    private AlgorithmIdentifier algorithmIdentifier;
    private Signature signature;
    private ByteArrayOutputStream outputStream;

    public CustomContentSigner(PrivateKey privateKey, String sigAlgo) {
        //Utils.throwIfNull(privateKey, sigAlgo);
       this.algorithmIdentifier = new DefaultSignatureAlgorithmIdentifierFinder().find(sigAlgo);

        try {
            this.outputStream = new ByteArrayOutputStream();
            this.signature = Signature.getInstance("NONEwithRSA");
            this.signature.initSign(privateKey);
        } catch (GeneralSecurityException gse) {
            throw new IllegalArgumentException(gse.getMessage());
        }
    }

    @Override
    public AlgorithmIdentifier getAlgorithmIdentifier() {
        return algorithmIdentifier;
    }

    @Override
    public OutputStream getOutputStream() {
        return outputStream;
    }

    @Override
    public byte[] getSignature() {
        try {
            signature.update(outputStream.toByteArray());
            return signature.sign();
        } catch (GeneralSecurityException gse) {
            gse.printStackTrace();
            return null;
        }
    }
}

Disclaimer: I do not know if it will work but you can try it

Upvotes: 3

Related Questions