Mobile Gateway Service signs each API request and sends the signature to your backend server, where you verify it to confirm the request came from the gateway and was not tampered with.
When you enable signature verification for an API group in the gateway console, Mobile Gateway Service signs each request in that group. You create the public and private keys used for signing in the gateway console.
Your backend server reads the signature from the request header, reconstructs the string to sign locally, applies the same algorithm, and compares the result against the received signature to determine whether the request is valid.
How it works
Verification has three stages:
Read the headers. Mobile Gateway Service attaches the signature and key identifier to every request in two specific headers.
Reconstruct the string to sign. Build the same string the gateway used as signing input: the HTTP method, the Content-MD5 value, and the constructed URL.
Verify the signature. Apply your chosen algorithm (MD5+salt, RSA, SM3, or SM2) to the reconstructed string and compare the output to the received signature.
The gateway stores the computed signature in the request header X-Mgs-Proxy-Signature.
The secret key identifier — used to look up the corresponding secret value — is sent in the request header X-Mgs-Proxy-Signature-Secret-Key.
Construct the string to sign
Build the string to sign
String stringToSign =
HTTPMethod + "\n" +
Content-MD5 + "\n" +
Url
HTTPMethod: The HTTP method in uppercase, such asPUTorPOST.-
Content-MD5: The MD5 value of the request body, calculated as follows:If
HTTPMethodis notPUTorPOST, the MD5 value is an empty string ("").If the request body is a form, the MD5 is an empty string
"". Otherwise, proceed to the next step.-
Calculate the MD5 value. If the request has no body,
bodyStreamis the string"null".String content-MD5 = Base64.encodeBase64(MD5(bodyStream.getBytes("UTF-8")));ImportantEven if the
Content-MD5value is an empty string (""), the subsequent line feed (\n) in the string to sign must be included. This results in two consecutive\ncharacters in the string.
-
Url: Constructed from the path, query, and form parameters. For example, given the requesthttp://ip:port/test/testSign?c=3&a=1with form parametersb=2&d=4, build theUrlas follows:Extract the path — the portion after
ip:portand before?. In this example, the path is/test/testSign.If the request has no query or form parameters, the
Urlequals the path.-
Sort the query and form parameters by key in lexicographic order, then concatenate them as
Key1=Value1&Key2=Value2&...&KeyN=ValueN. In this example, the result isa=1&b=2&c=3&d=4.NoteIf a query or form parameter has multiple values, use only the first
value. Construct the
Urlin the formatPath?Key1=Value1&Key2=Value2&...&KeyN=ValueN. In this example, theUrlis/test/testSign?a=1&b=2&c=3&d=4.
Verify the signature
Use the same algorithm configured when you set up the API group in the gateway console.
-
Verify the signature using the MD5 algorithm
String sign = "xxxxxxx"; // The signature from Mobile Gateway Service String salt ="xxx"; // The MD5 salt MessageDigest digest = MessageDigest.getInstance("MD5"); String toSignedContent = stringToSign + salt; byte[] content = digest.digest(toSignedContent.getBytes("UTF-8")); String computedSign = new String(Hex.encodeHexString(content)); boolean isSignLegal = sign.equals(computedSign) ? true : false; -
Verify the signature using the RSA algorithm
String sign = "xxxxxxx"; // The signature from Mobile Gateway Service String publicKey ="xxx"; // The RSA public key from Mobile Gateway Service PublicKey pubKey = KeyReader.getPublicKeyFromX509("RSA", new ByteArrayInputStream(publicKey.getBytes())); java.security.Signature signature = java.security.Signature.getInstance("SHA1WithRSA"); signature.initVerify(pubKey); signature.update(stringToSign.getBytes("UTF-8")); boolean isSignLegal = signature.verify(Base64.decodeBase64(sign.getBytes("UTF-8"))); -
Verify the signature using a Chinese cryptographic algorithm
SM3 algorithm:
String sign = xxxx; String salt = xxxxxx; String toSignedContent = stringToSign + salt; byte[] srcData = toSignedContent.getBytes("UTF-8"); SM3Digest digest = new SM3Digest(); digest.update(srcData, 0, srcData.length); byte[] resultHash = new byte[digest.getDigestSize()]; digest.doFinal(resultHash, 0); String computedSign = Hex.encodeHexString(resultHash); boolean isSignLegal = sign.equals(computedSign);SM2 algorithm:
String sign = xxxx; String pubKey = xxxx; Signature signature = Signature.getInstance("SM3withSM2", "LOCCSBC"); KeyPair keyPair = getSmKeyPair(pubKey); PublicKey publicKey = keyPair.getPublic(); signature.initVerify(publicKey); signature.update(stringToSign.getBytes("UTF-8")); return signature.verify(Hex.decodeHex(sign));public static KeyPair getSmKeyPair(String keyPairContent) throws IOException, InvalidKeySpecException, NoSuchProviderException, NoSuchAlgorithmException { KeyFactory keyFactory = KeyFactory.getInstance("SM2", "LOCCSBC"); byte[] keyBytes = getPKCS1fromPEMString(keyPairContent); SM2ParameterSpec spec = SM2NamedCurveTable.getParameterSpec("sm2p256v1", "1234567812345678"); SM2PrivateKeySpec sm2PrivateKeySpec = new SM2PrivateKeySpec(BigIntegers.fromUnsignedByteArray(keyBytes), spec); SM2PrivateKey prvkey = (SM2PrivateKey) keyFactory.generatePrivate(sm2PrivateKeySpec); SM2PublicKeySpec sm2PublicKeySpec = new SM2PublicKeySpec(prvkey.getQ(), spec); SM2PublicKey pubKey = (SM2PublicKey) keyFactory.generatePublic(sm2PublicKeySpec); return new KeyPair(pubKey, prvkey); }public static byte[] getPKCS1fromPEMString(String pemStr) { // pkcs1 try { PEMParser reader = new PEMParser(new StringReader(pemStr)); PemObject pemObject = reader.readPemObject(); reader.close(); ASN1InputStream asn1In = new ASN1InputStream(pemObject.getContent()); ASN1Sequence derSequence = (ASN1Sequence) asn1In.readObject(); asn1In.close(); // openssl format Object obj = derSequence.getObjectAt(1); if (obj instanceof ASN1OctetString){ return ((ASN1OctetString) obj).getOctets(); } // gmssl format DEROctetString derOctetString = (DEROctetString)derSequence.getObjectAt(2); ASN1Sequence sequence = (ASN1Sequence)ASN1Sequence.fromByteArray(derOctetString.getOctets()); return ((ASN1OctetString)sequence.getObjectAt(1)).getOctets(); } catch (Exception e) { throw new RuntimeException(e); } }
Code examples
For a complete implementation, see HttpSignUtil.java.
Troubleshooting
Signature mismatch
If verification fails, the most common cause is a mismatch between the string to sign your backend constructed and the string the gateway used.
Log the exact value of stringToSign your backend constructs and compare it character by character against what the gateway computed. Check for:
Trailing
\nafter an emptyContent-MD5. Even whenContent-MD5is an empty string, the\nseparator must still be present in the string to sign. This produces two consecutive\ncharacters — a common source of mismatch.Parameter sort order. Query and form parameters must be sorted in lexicographic order by key before concatenation. Confirm your sort is case-sensitive and matches the gateway's ordering.
Form body treated as content. If the request body is a form,
Content-MD5must be an empty string (""), not the MD5 of the form body bytes.Wrong secret value. Confirm the salt or public key your backend uses matches the value configured in the gateway console for the secret key identifier in
X-Mgs-Proxy-Signature-Secret-Key.