Search code examples
c#apiencryptionrsarsa-sha256

RSA-SHA256 Signature mismatches


I'm working on an implementation of EWP (Erasmus Without Papers), a set of API's for communication between universities world wide. Every call must be signed with RSA-SHA256. Every partner has a private and public key, the public keys are available in a registry with a keyId. A request has some headers: a digest of the body (sha256 hash), the host, date, x-request-id (random guid). These headers and their values are concatenated in a signing string, that is signed with the private key, the result being the signature that is also sent in the headers, along with the keyId and the other headers used for the signature. The server needs to check if the signature is valid, by creating the same signing string, looking up the public key in the registry and then checking if the signature in the request is indeed correct.

I have both the API as a client application that I use to test. I have access to 109 different partners to test, some of them are not ready yet (they show an error both in my client and the online validator, so I assume there is an error in that API server side, as it is a development environment), others are working fine. There is also a test API of EWP and there is an online validator you can use to send requests to any of these API's.

Strange thing is, my client works with almost all of the partner API's, while some of them give me an error my signature is wrong (when the online validator works fine). Using my client on my own API, validation works fine. Using the online validator, my validation does not work (invalid signature error). There are 2 partners where my client does not work and the online validator does, and 3 API's where my client works but the online validator doesn't (including my own API)

Can anyone think of anything that could cause these issues? Strange thing is, 2 weeks ago, the online validator did work on my API and the only thing that changed according to the EWP guy, is the key pair used by the online validator, but he claims I'm using the correct one now (I sent him my logs)

Could it be some kind of character set issue? An error only happening when some character is used in the public key or something like that?

Here is my code for the RSA-SHA256 part:

Create a signature in the client request, with the private key:

public string CreateSignature(string SigningString)
{
    byte[] message = Encoding.UTF8.GetBytes(SigningString);
    PemReader pr = new PemReader(new StringReader(privateKeyString));
    AsymmetricCipherKeyPair keyPair = (AsymmetricCipherKeyPair)pr.ReadObject();

    RSAParameters rsaParams = DotNetUtilities.ToRSAParameters((RsaPrivateCrtKeyParameters)keyPair.Private);
    RSACng rsaCng = new RSACng();
    rsaCng.ImportParameters(rsaParams);
    byte[] signatureBytes = rsaCng.SignData(message, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);

    return Convert.ToBase64String(signatureBytes);
}

Check if the received signature is correct:

public bool IsRsaHashCorrect(string originalMessage, string hash, string stringPublicKey)
{
    string x509Pem = @"-----BEGIN PUBLIC KEY-----" + stringPublicKey + "-----END PUBLIC KEY-----";

    byte[] message = Encoding.UTF8.GetBytes(originalMessage);
    byte[] signature = Convert.FromBase64String(hash);

    byte[] Sha256Message = SHA256.Create().ComputeHash(message);

    PemReader pr = new PemReader(new StringReader(x509Pem));
    AsymmetricKeyParameter publicKey = (AsymmetricKeyParameter)pr.ReadObject();

    RSAParameters rsaParams = DotNetUtilities.ToRSAParameters((RsaKeyParameters)publicKey);
    RSACng rsaCng = new RSACng();
    rsaCng.ImportParameters(rsaParams);

    var result = rsaCng.VerifyHash(Sha256Message, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);

    log.Info("check rsa-sha256 hash: " + result);

    return result;
}

So, long story short: above code is working fine sometimes, failing other times. Why oh why?


Solution

  • Problem is fixed! Turned out there was an issue with the format of the dates in the request headers. This date is also used in the signing string for the signature. Different date formats means invalid signature.

    The request sent by the online validator had the date Thu, 7 Jan 2021 14:01:58 GMT When fetching this date in my code, it was transformed into Thu, 07 Jan 2021 14:01:58 GMT (with a leading 0 in the day)

    requestContext.Date = request.Headers.GetValues("date").Last();
    

    Now when first validation fails, I try again with an altered date where the leading zero is deleted. Only if that fails too, the signature validation fails. Turns out some other partners are using leading zero's in their dates too, that's why my client did work with some API's and not with others...