0

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?

xanatos
  • 109,618
  • 12
  • 197
  • 280
Joep Demey
  • 93
  • 2
  • 8
  • Such behavior could occur if some of the public keys of the problematic APIs are wrong (at least your key or that of the online validator). However, the online validator should work for your API, since the public key you used _supposedly_ matches. On the other hand, why would the problem occur only after the key pair has been changed? Can you get the private key and the keyId of the online validator (since they are presumably just test data)? If so, you could run your client with these values. If your client then behaves like the online validator, this would substantiate a key problem. – Topaco Jan 07 '21 at 12:12
  • Thank you for your response Topaco. When the online validator sends a request to my API, it uses the EWP private key to sign the request, and I need to look up the EWP public key in the registry. My private/public keys are only used when I send a request to another API, or when I sign the response. When a request is sent to me, I need to look up the public key of the sender in the registry. EWP changes it keys whenever they publish any modifications on their side, so according to them, that's the only thing that could have changed since the online validator was working for my code... – Joep Demey Jan 07 '21 at 12:46
  • It is a good idea to test with the private/public key used by the online validator, I hope they can provide it to me... – Joep Demey Jan 07 '21 at 12:47
  • Just to clarify: _Such behavior could occur if some of the public keys of the problematic APIs are wrong_ was misleadingly worded by me. By _public keys_ I meant those public keys _in the registry_ of an API x. E.g. if the registry of API x contains the wrong public key of you, but the correct one of the online validator, the verification of the message sent by your client to API x would fail, while the verification of the message sent by the online validator to API x would succeed, etc. This way, the success/failure combinations found could be easily explained according to my understanding. – Topaco Jan 07 '21 at 14:00

1 Answers1

0

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...

Joep Demey
  • 93
  • 2
  • 8