Information Security 28 min read

OpenAPI Security Practices: AppId/AppSecret, Signature Generation, and Implementation Guide

This article explains how to use OpenAPI standards to secure API interfaces by introducing AppId/AppSecret mechanisms, RSA‑based signatures, timestamp and nonce anti‑replay measures, and provides complete Java code examples for client‑side signing and server‑side verification along with common protection techniques such as rate limiting and data validation.

Code Ape Tech Column
Code Ape Tech Column
Code Ape Tech Column
OpenAPI Security Practices: AppId/AppSecret, Signature Generation, and Implementation Guide

To ensure standardized, reusable, and secure API interfaces, the OpenAPI specification defines a unified protocol for request parameters, signatures, and authentication, improving maintainability and extensibility.

1. AppId and AppSecret

AppId usage

AppId is a globally unique identifier used for user identification and data analysis. It is paired with AppSecret (a password‑like secret) to prevent malicious requests.

AppId generation

When generating an AppId , ensure global uniqueness; no additional algorithmic constraints are required.

AppSecret generation

AppSecret follows typical password‑strength rules and is treated as a secret key.

2. sign signature

RSASignature combines asymmetric encryption (RSA) with a hash algorithm (e.g., MD5 or SHA‑256) to produce a digital signature. The workflow includes:

Data hashing (digest) using MD5 or SHA‑256.

RSA private‑key signing of the hash.

RSA public‑key verification on the receiver side.

The signature protects against data tampering and identity spoofing.

Data anti‑tampering

Hashing guarantees that the same original data always yields the same digest; any modification changes the digest, allowing detection.

Identity anti‑spoofing

Using SHA256withRSA , the client hashes the request data, signs it with its private key, and the server verifies the signature with the corresponding public key.

3. Usage Example

Pre‑conditions

When no automated platform exists, the appId and appSecret are exchanged offline.

The provider generates a public‑private key pair for each appId and shares the public key with the client.

Client workflow

UserEntity userEntity = new UserEntity();
userEntity.setUserId("1");
userEntity.setPhone("13912345678");
String sign = getSHA256Str(JSONObject.toJSONString(userEntity));
Map
data = Maps.newHashMap();
data.put("appId", appId);
data.put("nonce", nonce);
data.put("sign", sign);
data.put("timestamp", timestamp);
// sort, concatenate, append appSecret, then sign with RSA private key
String appSign = sha256withRSASignature(privateKey, concatenatedParams);
Header header = Header.builder()
    .appId(appId).nonce(nonce).sign(sign).timestamp(timestamp).appSign(appSign).build();
APIRequestEntity request = new APIRequestEntity();
request.setHeader(header);
request.setBody(userEntity);
String requestParam = JSONObject.toJSONString(request);

Server workflow

Header header = apiRequestEntity.getHeader();
UserEntity user = JSONObject.parseObject(JSONObject.toJSONString(apiRequestEntity.getBody()), UserEntity.class);
String sign = getSHA256Str(JSONObject.toJSONString(user));
if (!sign.equals(header.getSign())) throw new Exception("Data signature error!");
String appSecret = getAppSecret(header.getAppId());
Map
data = new HashMap<>();
data.put("appId", header.getAppId());
data.put("nonce", header.getNonce());
data.put("sign", sign);
data.put("timestamp", header.getTimestamp());
String sb = buildSortedString(data) + "appSecret=" + appSecret;
if (!rsaVerifySignature(sb, publicKey, header.getAppSign())) throw new Exception("Public key verification error!");
System.out.println("Verification passed!");

4. Common Protection Measures

Timestamp

The timestamp parameter limits request validity (e.g., 5‑minute window) to prevent replay attacks.

long now = System.currentTimeMillis();
if ((now - Long.parseLong(timestamp)) / 1000 / 60 >= 5) throw new Exception("Request expired!");

Nonce

A random nonce ensures each request is used only once; the server stores used nonces (e.g., Guava cache or Redis) and rejects duplicates.

static Cache
cache = CacheBuilder.newBuilder()
    .expireAfterWrite(5, TimeUnit.MINUTES)
    .build();
String key = appId + "_" + nonce;
if (cache.getIfPresent(key) != null) throw new Exception("Request invalid!");
cache.put(key, "1");

Access Permission

APIs should enforce permission checks based on appId to restrict data access to authorized scopes.

Parameter Validation

All request parameters must be validated (length, type, format, required fields). SpringBoot Validation annotations (e.g., @NotBlank, @Email, @Size) are recommended.

@NotBlank(message = "Name cannot be empty")
private String name;
@Email
private String email;
@DecimalMin("5") @DecimalMax("10")
private BigDecimal amount;

Rate Limiting

Use Guava RateLimiter for single‑node limits or Redis/Sentinel for distributed throttling.

Sensitive Data Masking

Mask personal identifiers (ID, phone, bank card) before storage or transmission.

5. Additional Considerations

Clear API naming and description.

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

Define error codes and detailed messages.

Provide documentation and sample data.

Design for extensibility (versioning, backward compatibility).

6. Supplementary Topics

MD5 Usage

MD5 produces a fixed‑length, irreversible hash useful for password storage, but vulnerable to rainbow‑table attacks; adding a salt mitigates this risk.

String pwd = "123456";
String salt = "wylsalt";
String hashed = DigestUtils.md5Hex(salt + pwd);
System.out.println("MD5 with salt: " + hashed);

Symmetric Encryption

Examples of DES, 3DES, and AES encryption modes (ECB vs. CBC) are provided, highlighting security trade‑offs.

private static final String AES_ECB_PCK_ALG = "AES/ECB/NoPadding";
public static String encryptWithECB(String content, String aesKey, String charset) throws Exception {
    Cipher cipher = Cipher.getInstance(AES_ECB_PCK_ALG);
    cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(Base64.decodeBase64(aesKey.getBytes()), "AES"));
    return Hex.encodeHexString(cipher.doFinal(content.getBytes(charset)));
}

Overall, the guide combines theoretical security concepts with practical Java implementations to help developers build robust, tamper‑proof, and authenticated OpenAPI services.

JavaRate LimitingAPI 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.