China's SM2 and SM3: The Two Locks Every Gateway Request Must Pass
Two Locks in One Request: Understanding China's SM2 and SM3 with Diagrams
When integrating with government, financial, or enterprise open platforms, many developers first encounter "national cryptographic algorithms" (national commercial cryptography standards).
Interface documentation often opens with a requirement like: "To guarantee transmission security, all gateway requests use SM2 encryption and SM3 signing."
Yet during joint debugging, the most frequent console errors are these frustrating failures:
- "Signature verification failed" – the request body fields are intact, so why does it not match?
- "Decryption error" – the keys were copied from the backend, so why does the result come out as garbled text or throw an exception?
The root cause is often that many people treat "signing" and "encryption" as the same thing, leading to conceptual confusion, reversed call order, and using the wrong keys.
In fact, a single request contains two fundamentally different operations:
- SM2 solves "others cannot understand" (anti-eavesdropping);
- SM3 solves "others cannot alter" (anti-tampering).
This article will step by step break down how these two locks work, how they cooperate, and practical troubleshooting tips, so you can understand SM2 and SM3 at once.
1. Mental Model: Sending an Important Package
To fully grasp their division of labor, imagine an API request from the frontend to the gateway as sending a package containing an important contract:
┌─────────────────────────────────────────────────────────────────┐
│ 📄 Business Body (e.g., ID card, phone number, bank card) │
└────────────────────────────────┬────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 🔒 First Lock: SM2 = "Put into a password safe" (anti-eavesdropping, only needed on specific sensitive interfaces) │
│ • Use the platform's [public key] to lock plaintext into the safe, turning it into a garbled ciphertext │
│ • Even if a hacker captures the package, without the [private key] they cannot open or understand it │
└────────────────────────────────┬────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 🏷️ Second Lock: SM3 = "Attach an anti-tamper seal" (anti-tampering, required for all gateway requests) │
│ • Arrange the package label (including app ID, timestamp, safe ciphertext) │
│ • Use SM3 to compute a unique 64-character tamper-proof fingerprint (sign) and attach it on the package seal │
│ • Even if someone silently changes one punctuation mark, the gateway recomputes the fingerprint and it won't match, so the request is rejected │
└─────────────────────────────────────────────────────────────────┘
Compare the key dimensions of the two locks:
| Dimension | SM3 Signing (tamper-proof seal) | SM2 Encryption (password safe) |
|---|---|---|
| International counterpart | SHA-256, MD5 (hash/digest) | RSA, ECC (asymmetric public/private key encryption) |
| Can it reverse the plaintext? | Absolutely not (one-way fingerprint, irreversible) | Yes (public key encryption → private key decryption) |
| Core defense goal | Anti-tampering: ensure data is not subtly altered during transmission | Anti-eavesdropping: protect sensitive personal privacy and business data |
| Required per request? | Always (anything going through the gateway must include it) | On demand (only sensitive interfaces or when background has encryption enabled) |
| Key configuration needed? | No key (pure mathematical hash, public algorithm) | Must have key pair (public key encrypts, private key decrypts) |
After understanding this high-level mental model, we dig deeper into the technical details of both locks.
2. Deep Dive into the First Lock: SM3 "Tamper-Proof Seal"
2.1 What is SM3?
SM3 is a cryptographic hash algorithm published by the State Cryptography Administration of China (commercial cryptography GM/T 0004). Its role is equivalent to SHA-256 in the international cryptographic system.
SM3 has three core properties:
- One-way irreversible: You can easily compress a long text of millions of characters via SM3 into a 64-character hex string, but no one in the world can recover the original from that 64-character string.
- Fixed output length: Whether the input is empty
"", a few words, or a multi-gigabyte file, SM3 always outputs a fixed 256 bits (i.e., 32 bytes, or 64 hex characters). - Highly sensitive avalanche effect: If the plaintext changes by just 1 bit (for example, a comma becomes a period), the resulting 64-character hash changes drastically and unpredictably.
2.2 How the Gateway Verifies Signatures with SM3
In an open platform, the client and gateway agree on a standard verification flow:
sequenceDiagram
autonumber
participant Client as Client (frontend/App)
participant Hacker as Man-in-the-middle (capture/proxy)
participant Gateway as Business Gateway (Gateway)
Note over Client: 1. Extract request fields, sort by ASCII ascending<br/>2. Concatenate as key1=val1&key2=val2...<br/>3. Compute SM3: sign = sm3Hex(concatenated string)
Client->>Hacker: Send POST request (all params + sign)
Note over Hacker: Assume hacker tampers with a parameter:<br/>e.g., amount=100 becomes amount=99999
Hacker->>Gateway: Forward tampered request
Note over Gateway: 4. Gateway sorts and concatenates received fields by same rules<br/>5. Gateway recomputes an SM3 hash
alt Gateway recomputed SM3 === client's sign
Gateway-->>Client: ✅ Verification passed, continue business
else mismatch
Gateway-->>Client: ❌ Block request! Error: signature verification failed
end
Because of the avalanche effect, if a hacker alters even a single parameter value during transmission, the fingerprint the gateway recomputes with SM3 will inevitably differ from the sign sent by the client, exposing the tampering immediately.
2.3 Three High-Frequency Pitfalls in SM3 Signing
When writing signature code, these details can cause gateways to report "verification failed" if overlooked:
- Dictionary-order sorting (ascending ASCII) :
Parameter names involved in signing must be sorted by ASCII dictionary order. For example,
app_idcomes beforebiz_content, andbiz_contentcomes beforecharset. - Null-value filtering rules:
In most gateway specifications, parameters with values of
null,undefined, or empty string""do not participate in signature concatenation. If the client includesversion=&...while the gateway excludesversionwhen computing, the concatenated strings differ and verification fails. - Don't blindly put passthrough fields and headers into the signed string:
Many interfaces include HTTP headers (like
Authorization,certify_token) or external passthrough fields (likeheader={"userid":"..."}). Unless the interface docs explicitly say so, these transport-layer fields usually do not belong to the signature set; adding them is overreach.
3. Deep Dive into the Second Lock: SM2 "Password Safe"
3.1 What is SM2?
SM2 is an asymmetric cryptography algorithm based on elliptic curve cryptography (ECC) (commercial cryptography GM/T 0003), on the international stage it is mainly the counterpart of RSA.
Compared to traditional RSA-2048, SM2 (256-bit elliptic curve) offers equivalent or even higher security strength, but shorter keys, faster computation, and smaller ciphertext over the network.
The core of asymmetric encryption is a key pair:
- Public Key: used for encryption (locking). The public key is open; anyone can lock plaintext into the ciphertext safe.
- Private Key: used for decryption (unlocking). The private key must be kept strictly confidential; only the holder can open the safe and recover the plaintext.
3.2 Question: Why Does the Frontend Configuration Include Both Public and Private Keys?
Junior developers are often puzzled by a "security common sense": "Shouldn't the private key never be exposed to the frontend/client? Why does the gateway integration doc ask the frontend to configure both keys?"
This is because in many lightweight government and enterprise gateway scenarios, bidirectional encrypted communication is implemented:
- Outgoing (client → gateway): The client uses the platform-issued public key to lock sensitive content. Even if the ciphertext is captured during transmission, the man-in-the-middle has no platform private key and cannot see anything.
- Incoming (gateway → client): The gateway uses the application's public key to lock the response data, and the client unlocks it with the locally configured application private key.
The two key pairs each serve their own purpose, ensuring two-way anti-eavesdropping on the entire transmission loop.
3.3 Why Do Most SM2 Ciphertexts Start with 04?
If you look closely at SM2-encrypted hex strings, you'll notice almost all start with 04:
04c852914e34a6bc8910... (followed by a long hex string)
This is no coincidence; it is the standard indicator of uncompressed elliptic curve point format in cryptographic specifications:
- An elliptic curve public key point consists of coordinates $(x, y)$;
- International and national standards define the single-byte hex
04as the prefix for an uncompressed point, meaning that following bytes are the $x$ and $y$ coordinates.
Practical pitfalls:
- Some frontend crypto libraries (such as
sm-crypto) may output hex without the04prefix by default; you must manually add it according to gateway requirements. - When decrypting, if the ciphertext starts with
04, you need to strip the first two characters (04) before passing it to the underlying decryption function; otherwise, the decryptor may throwpoint not on curveor simply fail to decrypt.
3.4 Mode Pitfall: C1C3C2 or C1C2C3?
SM2 ciphertext consists of three parts:
- C1: a random elliptic curve point;
- C2: the actual ciphertext data;
- C3: a digest check value computed from the plaintext and curve point (anti-tampering).
Historically there are two arrangement modes:
- Old national standard:
C1C2C3 - New national standard and mainstream spec:
C1C3C2
Most mainstream government and financial gateways now uniformly use C1C3C2 (e.g., in sm-crypto, set cipherMode to 1). If the gateway uses C1C3C2 but your client is configured for C1C2C3, even with perfectly paired keys, decryption will fail!
4. The Final Combination: The Dressing Order of the Two Locks
When a request requires both SM2 encryption and SM3 signing, there is only one order, and it cannot be reversed!
Iron rule: lock the safe first, then attach the seal!
sequenceDiagram
autonumber
participant App as Business Code
participant GatewaySDK as Request Wrapper Layer
participant Network as Gateway (gateway.do)
App->>GatewaySDK: Pass plaintext business object { "phone": "138..." }
Note over GatewaySDK: Step 1: SM2 encryption<br/>Turn plaintext into ciphertext: "04a1b2..."
Note over GatewaySDK: Step 2: Dictionary-order concatenation<br/>Put ciphertext into biz_content!<br/>app_id=...&biz_content=04a1b2...×tamp=...
Note over GatewaySDK: Step 3: Compute SM3 seal<br/>sign = sm3Hex(string to be signed)
GatewaySDK->>Network: Send POST request (contains ciphertext biz_content and sign)
Note over Network: Step 4: Gateway first verifies signature (SM3)<br/>Recompute hash from ciphertext, compare sign<br/>(only after verification passes does it start SM2 private key decryption)
Why the Reverse Order Is Doomed to Fail?
Suppose you make the logical error: compute the signature on plaintext first, then encrypt the body.
- When the client computes the signature, it uses the plaintext string
biz_content={"phone":"138..."}; - After signing, you encrypt
biz_contentinto04a1b2...; - The gateway receives the request, sees only the ciphertext
04a1b2..., and recomputes SM3 on the ciphertext; - Result: the hash from the ciphertext can never equal the signature computed on the plaintext, so the gateway rejects it in the first step of verification as "illegal tampering" and never reaches decryption!
5. Joint Debugging Quick Reference Checklist
Next time you hit an error, run through this checklist item by item:
🛠️ When you encounter "Signature verification failed (SM3)"
- Is the order correct? Is the
biz_contentused in signing the SM2-encrypted ciphertext, not the plaintext? - Is the parameter sorting correct? Strictly sorted in ASCII ascending order?
- Have null values been filtered? Are
null,undefined,""excluded from the string to be signed? - Were extra fields mistakenly added? Check whether HTTP headers, cookies, or passthrough tokens were wrongly included in the signature list.
- Is the string concatenation format correct? Any extra spaces, newlines, or stray
&symbols? Is the encoding consistently UTF-8?
🔓 When you encounter "Decryption failure (SM2)"
- Is the
04prefix handled correctly? Check whether04should be stripped before decryption, or whether it was missed after encryption. - Are the arrangement modes consistent? Confirm both sides use
C1C3C2(recommended by the new national standard). - Are the public and private keys correctly paired? Confirm whether you confused the "platform public key" with the "application self-built public key"? Is the private key written correctly?
- Is the response data really ciphertext? Some gateways return plaintext error JSON (e.g.,
{"code":500,"msg":"system error"}) when reporting errors (like 404/500/Token expired). If you send that directly into the SM2 decryption function, it will definitely throw a syntax error.
6. Summary
Understanding the essence of SM2 and SM3 is just this diagram and a sixteen-character phrase:
┌──────────────────────┐
│ Business Request │
└──────────┬───────────┘
│
【First Lock】 ▼
SM2 Encryption (reversible) [ 04... ciphertext safe ] ---> solves "others cannot understand"
│
【Second Lock】 ▼
SM3 Signature (irreversible) [ 64-character tamper-proof seal sign ] ---> solves "others cannot alter"
Sixteen-character phrase for debugging: Public key locks the safe, ciphertext gets the seal; encrypt first, sign second; decrypt on return with private key.
Once you separate the roles and get the order right, connecting to a national-cryptography gateway is no mystery.