OSH
OSH

Reputation: 2937

RSACryptoServiceProvider.VerifyData(stream) to verify large files: overcoming assymentric class design

There is an odd design issue in RSACryptoServiceProvider:

While it is possible to sign very large files using RSACryptoServiceProvider.SignData:

public byte[] SignData(
    Stream inputStream,
    Object halg
)

, there is no way to actually validate the large file without reading the file entirely into a memory buffer. The only available method is:

public bool VerifyData(
    byte[] buffer,
    Object halg,
    byte[] signature
) 

which forces the caller to read the entire file into memory (possible yielding an OutOfMemoryException in the calling code).

I looked into extending the RSACryptoServiceProvider since internally the VerifyData method could be very easily overloaded to support streams. However the class is sealed and internally the method uses a bunch of internal methods that I can't access.

Did anyone run into this issue? Any easy solution to the problem?

BTW - for MS the fix would be a 6 line copy & paste with a single var type change...

Upvotes: 2

Views: 1960

Answers (1)

OSH
OSH

Reputation: 2937

This code does the trick:

    public byte[] SignData(RSACryptoServiceProvider rsaEncryptor, Stream stream, Object halg)
    {
        HashAlgorithm hash = (HashAlgorithm) CryptoConfig.CreateFromName((string) halg); /*Utils.ObjToHashAlgorithm(halg)*/
        byte[] hashVal = hash.ComputeHash(stream);
        return rsaEncryptor.SignHash(hashVal, (string) halg);
    }

    public bool VerifyData(RSACryptoServiceProvider rsaEncryptor, Stream stream, Object halg, byte[] signature)
    {
        HashAlgorithm hash = (HashAlgorithm) CryptoConfig.CreateFromName((string) halg); /*Utils.ObjToHashAlgorithm(halg)*/
        byte[] hashVal = hash.ComputeHash(stream);
        return rsaEncryptor.VerifyHash(hashVal, (string) halg, signature);
    }

and calling it is done by:

byte[] signature = SignData(rsa2, stream, "SHA256"); 
//here we write the signature to a file

and

if (VerifyData(rsaEncryptor, stream, "SHA256", File.ReadAllBytes(signatureFilePath)))
{
    Console.WriteLine("Verification completed successfully for file {0}", filePath);
    return true;
}
else
{
    throw new CryptographicException("verification failed for file {0}", filePath);
}

Upvotes: 2

Related Questions