Request signing adds an extra layer of security to your API calls, ensuring that requests haven't been tampered with in transit. Request signing is currently optional for all endpoints.
Banked supports request signing using RSA private keys. Here's how to implement it step by step.
Overview
The process involves:
- Generate an RSA key pair
- Share your public key with Banked
- Sign each request using your private key
- Send the signature in your request headers
- Troubleshoot any signature validation errors
Prerequisites:
- Valid Banked account with API access
- OpenSSL or SSH keygen available for key generation
- Email access to contact Banked support
- Development environment with cryptographic libraries
- Understanding of RSA signatures and JWS (JSON Web Signatures)
Let's walk through each step.
Implementation Steps
Step 1. Generating RSA Key Pair
You can generate the key pair using any method that suits your environment. Banked supports any RSA key length, though we recommend following NIST guidelines with a minimum key length of 2048 bits.
Here is an example approach:
Using OpenSSL (recommended):
# Generate private key (2048-bit minimum, 4096-bit recommended) openssl genrsa -out private_key.pem 4096 # Extract public key openssl rsa -in private_key.pem -RSAPublicKey_out -out public_key.pem
Using SSH keygen:
ssh-keygen -t rsa -b 4096 -m PEM -f banked_key
Store your private key securely and never share it. Only the public key gets shared with Banked.
Step 2. Share Your Public Key with Banked
Email your public key to support@banked.com with the following details:
Email template:
Subject: Public Key for Request Signing - [Your Company Name] Hi Banked Team, Please add the attached public key for request signing on our account. Account details: - Company: [Your Company Name] - Account ID: [Your Account ID if known] The public key is in PKCS#1 PEM format as required. Thanks, [Your Name]
Requirement: The public key must be in PKCS#1 PEM format.
What PKCS#1 PEM format looks like:
-----BEGIN RSA PUBLIC KEY----- MIICCgKCAgEA... [base64 encoded key data] ... -----END RSA PUBLIC KEY-----
Step 3. Generate Request Signatures
Each request must be signed using JWS with PS512.
What Gets Signed
The signed string is a concatenation of these components in this exact order:
request_url_with_query_params + request_method + request_body + idempotency_key_value
Important: No separators between components - they are concatenated directly.
Signature Formula
base64_encode(JWS_PS512_Sign(request_url_with_query_params + request_method + request_body + idempotency_header))
The resulting JWS is then base64-encoded. We expect the compact form JWS to be base64 encoded.
Examples
Example: GET Request
For the following request:
curl --request GET \ --url https://api.banked.com/v2/payments/?mode=live \ --header 'X-Banked-Idempotency-Key: 1'
Signed string is:
"https://api.banked.com/v2/payments/?mode=liveGET1"
Example: POST Request
For the following request:
curl --request POST \ --url https://api.banked.com/v2/payments/?mode=live \ --header 'X-Banked-Idempotency-Key: 1' \ --data-raw '{"k":"v"}'
Signed string is:
"https://api.banked.com/v2/payments/?mode=livePOST{\"k\":\"v\"}1"
Step 4. Send the Request with Signature
Include the base64-encoded signature in the Signature
HTTP header:
Signature: <base64_signature>
Complete example using the GET request from Step 3:
curl --request GET \ --url https://api.banked.com/v2/payments/?mode=live \ --header 'X-Banked-Idempotency-Key: 1' \ --header 'Signature: eyJhbGciOiJQUzUxMiIsInR5cCI6IkpXVCJ9...'
Complete example using the POST request from Step 3:
curl --request POST \ --url https://api.banked.com/v2/payments/?mode=live \ --header 'X-Banked-Idempotency-Key: 1' \ --header 'Signature: eyJhbGciOiJQUzUxMiIsInR5cCI6IkpXVCJ9...' \ --data-raw '{"k":"v"}'
The signature value shown is truncated for readability. Your actual signature will be much longer.
Step 5. Troubleshooting Signature Issues
When implementing request signing, you might encounter validation errors. Here's how to handle and debug them:
Common Error Response
If signature validation fails, the Banked API responds with:
- HTTP Status:
401 Unauthorized
- Response Body:
{ "errors": [ { "code": "unauthorized", "source": { "parameter": "invalid" }, "title": "Request Verification Failed" } ] }
Debugging Checklist
If you receive this error, check the following:
1. String Construction
- [ ] URL includes query parameters exactly as sent
- [ ] HTTP method is uppercase (GET, POST, etc.)
- [ ] Request body matches exactly (including whitespace)
- [ ] Idempotency key value is correct
- [ ] No separators between concatenated components
2. Signature Generation
- [ ] Using JWS with PS512 algorithm
- [ ] Private key is valid RSA format
- [ ] JWS is in compact form
- [ ] Final result is base64 encoded
3. Request Headers
- [ ]
Signature
header contains the base64-encoded JWS - [ ]
X-Banked-Idempotency-Key
header matches the value used in signing - [ ] No extra whitespace in header values
Testing Your Implementation
Start with a simple GET request to verify your signing logic:
# Test with minimal GET request curl --request GET \ --url https://api.banked.com/v2/payments/ \ --header 'X-Banked-Idempotency-Key: test123' \ --header 'Signature: [your_generated_signature]' \ --verbose
Use --verbose
flag to see the exact request headers being sent.
Code Examples
Go Implementation
Here's a complete Go example that demonstrates the full signing process:
package main import ( "crypto/rand" "crypto/rsa" "encoding/base64" "fmt" "github.com/lestrrat-go/jwx/v2/jwa" "github.com/lestrrat-go/jwx/v2/jws" ) func main() { privateKey, err := rsa.GenerateKey(rand.Reader, 4096) if err != nil { panic(err) } msg := []byte("string to sign") jws, err := jws.Sign([]byte(msg), jws.WithKey(jwa.PS512, privateKey)) if err != nil { panic(err) } fmt.Println(base64.StdEncoding.EncodeToString(jws)) }
Signing Key Rotation
API Clients are responsible for managing their key lifecycles. To rotate keys:
- Generate a new RSA key pair
- Share the new public key with Banked via support@banked.com
- Wait for confirmation that both keys are active
- Switch your application to use the new private key
- Notify Banked to deprecate the old key
Banked temporarily supports both old and new keys during the transition period to ensure zero downtime.