Information Security 28 min read

Securing OpenAPI Interfaces with AppId, AppSecret, and RSA Signatures

This article explains how to standardize and protect OpenAPI interfaces by using globally unique AppId/AppSecret pairs, generating RSA‑based signatures, implementing timestamp and nonce checks, and applying common security measures such as rate limiting, whitelist/blacklist controls, and sensitive data handling, all illustrated with complete Java code examples.

Code Ape Tech Column
Code Ape Tech Column
Code Ape Tech Column
Securing OpenAPI Interfaces with AppId, AppSecret, and RSA Signatures

Introduction

To ensure standardized, reusable, and secure software interfaces, the OpenAPI specification defines a unified protocol for request formats, parameters, responses, and usage, improving maintainability, extensibility, and safety.

1. AppId and AppSecret

AppId Usage

AppId is a globally unique identifier that helps identify users and support data analysis. It is paired with an AppSecret (acting like a password) to prevent malicious use.

AppId and AppSecret are combined to create a signature that is encrypted according to a specific rule. The client includes this signature in the request; the server verifies it before allowing data exchange.

AppId Generation

AppId must be globally unique; any method that guarantees uniqueness is acceptable.

AppSecret Generation

AppSecret is treated as a password and should be generated following standard password‑security guidelines.

2. sign Signature

RSASignature

Before discussing signatures, understand two concepts: asymmetric encryption (e.g., RSA) and hash algorithms (e.g., MD5). Asymmetric encryption can be used for public‑key encryption/decryption or, combined with a hash, for digital signatures. RSA signatures typically use SHA256withRSA .

Signature Purpose

Signatures serve two main scenarios: data tampering prevention and identity impersonation protection.

Data Tampering Prevention

By hashing the original data, the server can recompute the hash and compare it with the client’s value; a match confirms the data has not been altered during transmission.

Identity Impersonation Prevention

Using SHA256withRSA , the client hashes the data, encrypts the hash with its private RSA key, and the server verifies the signature with the corresponding public key.

3. Usage Example

Pre‑conditions

When no automated platform exists, AppId and AppSecret are distributed offline; the secret must be stored securely by the client. Public‑private key pairs are also generated offline and shared securely.

Interaction Flow

Client Preparation

1. Compute a SHA‑256 hash of the business parameters to obtain sign :

<code>// Business request parameters
UserEntity userEntity = new UserEntity();
userEntity.setUserId("1");
userEntity.setPhone("13912345678");

// Generate signature using SHA‑256
String sign = getSHA256Str(JSONObject.toJSONString(userEntity));
</code>

2. Assemble header parameters (appId, nonce, sign, timestamp) in natural order, filter out empty values, and sign them with the private key to obtain appSign :

<code>Map<String, String> data = Maps.newHashMap();
data.put("appId", appId);
data.put("nonce", nonce);
data.put("sign", sign);
data.put("timestamp", timestamp);
Set<String> keySet = data.keySet();
String[] keyArray = keySet.toArray(new String[keySet.size()]);
Arrays.sort(keyArray);
StringBuilder sb = new StringBuilder();
for (String k : keyArray) {
    if (data.get(k).trim().length() > 0) {
        sb.append(k).append("=").append(data.get(k).trim()).append("&");
    }
}
sb.append("appSecret=").append(appSecret);
System.out.println("[Client] Concatenated parameters: " + sb.toString());
String appSign = sha256withRSASignature(appKeyPair.get(appId).get("privateKey"), sb.toString());
System.out.println("[Client] appSign: " + appSign);
</code>

3. Build the request object and send it to the server:

<code>Header header = Header.builder()
        .appId(appId)
        .nonce(nonce)
        .sign(sign)
        .timestamp(timestamp)
        .appSign(appSign)
        .build();
APIRequestEntity apiRequestEntity = new APIRequestEntity();
apiRequestEntity.setHeader(header);
apiRequestEntity.setBody(userEntity);
String requestParam = JSONObject.toJSONString(apiRequestEntity);
System.out.println("[Client] Request parameters: " + requestParam);
</code>

Server Preparation

1. Parse the request, recompute the sign , and compare it with the header value:

<code>Header header = apiRequestEntity.getHeader();
UserEntity userEntity = JSONObject.parseObject(JSONObject.toJSONString(apiRequestEntity.getBody()), UserEntity.class);
String sign = getSHA256Str(JSONObject.toJSONString(userEntity));
if (!sign.equals(header.getSign())) {
    throw new Exception("Data signature error!");
}
</code>

2. Retrieve appSecret using appId , rebuild the signature string, and verify it with the public key:

<code>String appId = header.getAppId();
String appSecret = getAppSecret(appId);
String nonce = header.getNonce();
String timestamp = header.getTimestamp();
Map<String, String> data = Maps.newHashMap();
data.put("appId", appId);
data.put("nonce", nonce);
data.put("sign", sign);
data.put("timestamp", timestamp);
Set<String> keySet = data.keySet();
String[] keyArray = keySet.toArray(new String[0]);
Arrays.sort(keyArray);
StringBuilder sb = new StringBuilder();
for (String k : keyArray) {
    if (data.get(k).trim().length() > 0) {
        sb.append(k).append("=").append(data.get(k).trim()).append("&");
    }
}
sb.append("appSecret=").append(appSecret);
if (!rsaVerifySignature(sb.toString(), appKeyPair.get(appId).get("publicKey"), header.getAppSign())) {
    throw new Exception("Public key verification error!");
}
System.out.println("[Server] Verification passed!");
</code>

4. Common Protection Measures

Timestamp

Timestamp limits request validity to a configurable window (e.g., 5 minutes). The server checks the difference between the current time and the timestamp; requests older than the window are rejected.

<code>long now = LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
if ((now - Long.parseLong(timestamp)) / 1000 / 60 >= 5) {
    throw new Exception("Request expired!");
}
</code>

Nonce

Nonce is a random value generated per request. The server stores each nonce (e.g., in a Guava cache with a 5‑minute TTL). If a nonce is seen again within the TTL, the request is considered a replay and rejected.

<code>String cacheKey = appId + "_" + nonce;
if (cache.getIfPresent(cacheKey) != null) {
    throw new Exception("Request invalid – replay detected!");
}
cache.put(cacheKey, "1");
</code>

Access Permissions

Data access should be scoped to the AppId’s authorized resources, ensuring each AppId can only retrieve data within its permission set.

Parameter Validation

All incoming parameters must be validated for length, type, format, and business rules. Spring Boot Validation annotations (e.g., @NotBlank , @DecimalMin , @Email ) provide a concise way to enforce these checks.

<code>@NotBlank(message = "Name cannot be empty")
private String name;

@DecimalMin(value = "5", message = "Amount must be >= 5")
@DecimalMax(value = "10", message = "Amount must be <= 10")
private BigDecimal amount;
</code>

Rate Limiting

To protect services from overload or abuse, apply rate limiting (e.g., Guava RateLimiter for single‑node, Redis or Sentinel for distributed environments).

Sensitive Data Handling

Sensitive fields such as ID numbers, phone numbers, or bank cards must be masked or encrypted before storage or transmission.

Whitelist / Blacklist

Whitelist IPs for trusted service‑to‑service calls; blacklist IPs to block known malicious sources.

5. Additional Considerations

API name and description should be concise and clearly convey purpose.

Support standard HTTP methods (GET, POST, PUT, DELETE) with well‑defined request/response schemas.

Define comprehensive error codes and messages.

Provide thorough documentation and sample data for developers.

Design APIs to be extensible (e.g., versioning) while maintaining backward compatibility.

JavaEncryptionAPI securityOpenAPIAppIdAppSecretRSA signature
Code Ape Tech Column
Written by

Code Ape Tech Column

Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.