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

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

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

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

After running these two commands you end up with two files: key.pem and public.pem. The private key key.pem must not be shared. Share the public key public.pem with developer support by emailing to 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-ecdsa.key";
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);
}