Skip to main content

Security

All API requests must be made over HTTPS (TLS 1.2).

API authentication

Every request to the API must be authenticated using the JWS detached token

  • JSON Web Signature (RFC7515) - with an asymmetric signature. The algorithm used for the signature must be ES256 (EC NIST P-256 DSA key with SHA-256 hashing).

Elliptic-curve cryptography (ECC) is an approach to public-key cryptography based on the algebraic structure of elliptic curves over finite fields. ECC, an alternative technique to RSA, is a powerful cryptography approach.

As the JWS detached token is defined, it does not contain the payload and the format used is {header}..{signature}.

The signature is only about the request body in JSON format using UTF-8 encoding. Ensure that the JWT library used to sign the payload does not add any other field to it in the process.

The token header must contain:

  • alg: The cypher algorithm ID. Must be ES256.
  • aud: Target endpoint. It must be specified as {httpMethod} {urlPath}. The {urlPath} includes the path with the query string but not the domain. e.g., POST /v1/payments.
  • exp: Expiration timestamp in JSON format (5 minutes max). Make sure your system clock is in sync to avoid unexpected unauthorized responses.
  • kid: The ID to identify the private key used to sign the request. The kid is a KSUID ID provided by the support team when the public key is registered in the platform.

JWS detached

X-Signature header using the JSON Web Signature token: {header}..{signature}.

Each API credential can be assigned selected scopes to segregate permissions to different endpoints. In each endpoint specification, it is indicated the scope required.

Example: "X-Signature": "eyJhbGciOiJ..ICjcwYzA5Ny01ZjQzLT".

  • Security Scheme Type: API Key
  • Header parameter name: X-Signature

Generate a private EC key

Generate a private EC key, of size 256, and output it to a file named private-key.pem.

openssl ecparam -name prime256v1 -genkey -noout -out private-key.pem

Extract the public key from the key pair, which can be used in a certificate

openssl ec -in private-key.pem -pubout -out public-key.pem

After running these two commands you end up with two files: private-key.pem and public-key.pem. The private key private-key.pem must not be shared. Share the public key public-key.pem with developer support by emailing support@nomupay.com. NomuPay´s developer support team will provide the key identifier kid which must be included in every request, and the shared HMAC key to verify the response signature.

Revoke/rotate keys

If your private key has been stolen, anyone can impersonate you. If the shared HMAC key has been stolen, you can not ensure the origins of responses or webhook messages.

If you need to regenerate your keys or revoke the existing ones, generate new keys and contact support@nomupay.com to indicate the key identifier kid of the keys to revoke.

It is possible to have multiple keys at the same time. When you generate a new set of keys, the previous keys should be kept active for a period of several days. This ensures you're able to process with two signatures (as NomuPay's APIs operate using an asynchronous system we are not able to able to apply only the new signature to the following responses or sent webhooks notifications). During this time, your endpoint has multiple active secrets and it generates one signature for each secret.

When the old keys are revoked only the current X-Signature will exist in the header.

Code snippets

For this example, we use a commonly recommended package jose-jwt which will help you create and validate JWT tokens with ease. You can find it on GitHub.

using Jose;
using System.Security.Cryptography;

public async Task<string> GetJwsDetachedToken(string payload, string method, Uri path)
{
const string KeyId = "YOUR_KEY_ID";
const string PrivateKeyFilePath = "private-key.pem";
const int ExpirationInMinutes = 3;

string privateKeyContent = await GetPrivateKeyFromFile(PrivateKeyFilePath);
byte[] blocks = Convert.FromBase64String(privateKeyContent);
var privateKeyEcDsa = ECDsa.Create();
privateKeyEcDsa.ImportECPrivateKey(blocks, out _);

TimeSpan exp = DateTime.UtcNow.AddMinutes(ExpirationInMinutes) - new DateTime(1970, 1, 1);

Dictionary<string, object> headers = new()
{
["aud"] = $"{method} {path}",
["kid"] = KeyId,
["exp"] = (int)exp.TotalSeconds,
};

return JWT.Encode(payload, privateKeyEcDsa, JwsAlgorithm.ES256, extraHeaders: headers, options: new JwtOptions { DetachPayload = true});
}

private static async Task<string> GetPrivateKeyFromFile(string fileName)
{
string privateKey = await File.ReadAllTextAsync(fileName, System.Text.Encoding.UTF8);

return privateKey
.Replace("-----BEGIN PRIVATE KEY-----", string.Empty)
.Replace("-----BEGIN EC PRIVATE KEY-----", string.Empty)
.Replace("-----END PRIVATE KEY-----", string.Empty)
.Replace("-----END EC PRIVATE KEY-----", string.Empty);
}

Verifying signed responses

To allow our clients to verify the origin of public API responses and webhook messages, a JWS is included in each API response and webhook message as depicted below:

  • JWS token is passed in header X-Signature: {jwsToken}. There will be as many X-Signature headers as HMAC keys coexist. Several HMAC keys are only valid during a rotation period.
  • The algorithm used for signature is HS256 with a HMAC shared key provided by NomuPay´s support team.
  • Header parameters (strict validation):
    • alg: The cypher algorithm ID. Must be HS256.
    • aud: Only for webhook messages, containing the target URL.
    • exp: Expiration timestamp in JSON format (5 minutes max). It must be checked to avoid a timing attack.
    • kid: The ID to identify the shared key used to sign the token, provided by NomuPay´s support team. It is in KSUID format. The format must be checked to avoid attempts of injection.
  • Payload: Contains the response body or webhook message. This should be used as is received, without any transformation.
  • Format: {headers}..{signature}.

Code snippets

For this example, we use a commonly recommended package jose-jwt which will help you create and validate JWT tokens with ease. You can find it on GitHub.

using Jose;

public bool VerifyResponseSignature(string jwsDetached, string payload, string sharedKey)
{
byte[] secretKey = Encoding.UTF8.GetBytes(sharedKey);

try
{
JWT.Decode(jwsDetached, secretKey, JwsAlgorithm.HS256, payload: payload);

return true;
}
catch (IntegrityException)
{
return false;
}
}