EN - Decryption for Bulk Create Virtual Cards Number File
🔐 AES-256-GCM Encryption and Decryption Process Documentation
This scheme uses the AES-256-GCM algorithm with a key length of 32 bytes (256 bit).
All keys and data are transmitted using Base64 encoding.
aad=nil
(no additional authenticated data).
Core Concepts
API Key
Each user has a unique API Key
(an arbitrary-length string, e.g., 64 characters).
Purpose: Acts as the root key; the user can use it to encrypt/decrypt the “Batch Key” sent by the server.
Batch Key A random 32-byte binary key generated by the server for each batch of business. Purpose: Used to encrypt and decrypt actual business data.
Secret Key
Batch Key Generation and Distribution
-
The server generates a random BatchKey (32 bytes)
- Used to encrypt business data.
-
The server uses API Key with SHA-256 (32 bytes) as the key to encrypt BatchKey, and returns the SecretKey to the user:
secretKey = Base64(Nonce || Ciphertext || AuthTag)
- After receiving SecretKey, the user decrypts it using the same API Key to obtain the original BatchKey.
Business Data Encryption
Both the user and the server can use BatchKey to encrypt business data.
Encryption Steps:
-
Generate a random Nonce (12 bytes).
-
Call AES-256-GCM encryption:
Ciphertext, AuthTag = AES-GCM-Encrypt(BatchKey, Plaintext, Nonce, AAD=nil)
- Concatenate data:
Output = Nonce || Ciphertext || AuthTag
- Encode
Output
with Base64 to obtain the final ciphertext string.
Data Returned to the User:
EncData = Base64(Nonce || Ciphertext || AuthTag)
Business Data Decryption
When the user receives EncData
:
- Decode from Base64 to obtain the original byte sequence:
Raw = Nonce || Ciphertext || AuthTag
-
Split the fields:
-
Nonce
= first 12 bytes -
AuthTag
= last 16 bytes -
Ciphertext
= the middle part
-
-
Call AES-256-GCM decryption:
Plaintext = AES-GCM-Decrypt(BatchKey, Ciphertext, Nonce, AuthTag, AAD=nil)
- Obtain the original plaintext data.
Data Format Illustration
Nonce | Ciphertext | AuthTag |
---|---|---|
12 bytes | N bytes | 16 bytes |
Final transmission:
EncData = Base64(Nonce || Ciphertext || AuthTag)
User-Side Decryption Process
When the user receives SecretKey EncryptedBatchKey(Base64)
and Card Number EncryptedData(Base64)
, the decryption process is as follows:
Step 1. Derive AES Key from the API Key
APIKeyAESKey = SHA256(APIKey) // result is 32 bytes
Step 2. Decrypt the Batch Key
BatchKey = AES-GCM-Decrypt(APIKeyAESKey, SecretKey, Nonce, AuthTag, AAD=nil)
Step 3. Decrypt the Card Number
CardNumber = AES-GCM-Decrypt(BatchKey, EncryptedCardNumber, Nonce, AuthTag, AAD=nil)
Code Example
import hashlib, base64
from Crypto.Cipher import AES
def derive(api_key: str) -> bytes:
return hashlib.sha256(api_key.encode()).digest() # 32 bytes
def decrypt_gcm(key: bytes, enc_b64: str, aad: bytes = None) -> bytes:
raw = base64.b64decode(enc_b64)
nonce, ct_tag = raw[:12], raw[12:]
cipher = AES.new(key, AES.MODE_GCM, nonce=nonce, mac_len=16)
if aad:
cipher.update(aad)
return cipher.decrypt_and_verify(ct_tag[:-16], ct_tag[-16:])
# Unpack Batch Key
api_key = "your-api-key..."
api_key_ase = derive(api_key)
batch_key = decrypt_gcm(api_key_ase, secret_key) # []byte
# Decrypt business data
plain = decrypt_gcm(batch_key, enc_data_b64)
print(plain.decode())
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/sha256"
"encoding/base64"
"errors"
)
const (
gcmNonceSize = 12
gcmTagSize = 16
)
func deriveKeyFromAPIKey(apiKey string) []byte {
h := sha256.Sum256([]byte(apiKey))
return h[:] // 32 bytes
}
func DecryptGCM(key []byte, encBase64 string, aad []byte) ([]byte, error) {
raw, err := base64.StdEncoding.DecodeString(encBase64)
if err != nil {
return nil, err
}
if len(raw) < gcmNonceSize+gcmTagSize {
return nil, errors.New("ciphertext too short")
}
nonce := raw[:gcmNonceSize]
cipherAndTag := raw[gcmNonceSize:]
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
gcm, err := cipher.NewGCMWithTagSize(block, gcmTagSize)
if err != nil {
return nil, err
}
plain, err := gcm.Open(nil, nonce, cipherAndTag, aad)
if err != nil {
return nil, err
}
return plain, nil
}
func main() {
apiKey := "xxx"
secretKey := "1111"
encodeData := "qweqweqwe"
aesApiKey := deriveKeyFromAPIKey(apiKey)
batchKey,_ := DecryptGCM(aesApiKey,secretKey,nil)
cardNum,_ := DecryptGCM(batchKey,encodeData,nil)
}
Notes
-
Key length must be 32 bytes (AES-256).
-
Nonce must be unique; the same BatchKey must not reuse the same Nonce, otherwise it will cause serious security issues.
-
AuthTag
is automatically generated by AES-GCM and must be verified during decryption. -
In this scheme, AAD is not used (
nil
), so the user does not need to handle it.
Updated 2 days ago