Java Signature & Verification Example
In API requests, to ensure data integrity and security, we use the RSA signature mechanism to encrypt and sign requests, and verify signatures on the receiving end.
This example provides a SignatureHandler class for generating signatures and verifying signatures
java
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.*;
import java.util.Base64;
/**
* RSA Signature & Verification Utility Class
* @author DianDianPay Team
* - Sign data using private key
* - Verify signature legality using public key
*/
public class SignatureHandler {
private final Key key; // Store private or public key object
private final boolean isPrivate; // Whether it's a private key
/**
* Constructor, initialize private or public key
*
* @param keyStr Base64 encoded private or public key (without headers and footers)
* @param keyType "private" for private key, "public" for public key
* @throws Exception if key format is incorrect or decoding fails
*/
public SignatureHandler(String keyStr, String keyType) throws Exception {
// Preprocessing: Remove PEM headers/footers and all newlines
keyStr = keyStr.replaceAll("\\n", "")
.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "")
.replace("-----BEGIN PUBLIC KEY-----", "")
.replace("-----END PUBLIC KEY-----", "");
// Base64 decode the key string
byte[] keyBytes = Base64.getDecoder().decode(keyStr);
// Parse private or public key based on keyType
if (keyType.equalsIgnoreCase("private")) {
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
this.key = keyFactory.generatePrivate(keySpec);
this.isPrivate = true;
} else if (keyType.equalsIgnoreCase("public")) {
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
this.key = keyFactory.generatePublic(keySpec);
this.isPrivate = false;
} else {
throw new IllegalArgumentException("Invalid keyType. Must be 'private' or 'public'.");
}
}
/**
* Generate RSA signature (using SHA256 digest algorithm)
*
* @param merchantId Merchant ID
* @param timestamp Timestamp
* @param timezone Timezone
* @param body JSON format request body string
* @return Generated Base64 encoded signature string
* @throws Exception if signing fails
*/
public String generateSignature(String merchantId, String timestamp, String timezone, String body) throws Exception {
if (!isPrivate) {
throw new IllegalStateException("Key must be private to generate signature.");
}
// Assemble signature content (note: must maintain consistent concatenation order)
String signContent = merchantId + "." + timestamp + "." + timezone + "." + body;
// Sign using RSA private key
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign((PrivateKey) this.key);
signature.update(signContent.getBytes(StandardCharsets.UTF_8));
// Return Base64 encoded signature
return Base64.getEncoder().encodeToString(signature.sign());
}
/**
* Verify RSA signature legality
*
* @param merchantId Merchant ID
* @param timestamp Timestamp
* @param timezone Timezone
* @param body JSON format request body string
* @param receivedSignature Received Base64 encoded signature string
* @return Verification result, true for success, false for failure
* @throws Exception if verification fails
*/
public boolean verifySignature(String merchantId, String timestamp, String timezone, String body,
String receivedSignature) throws Exception {
if (isPrivate) {
throw new IllegalStateException("Key must be public to verify signature.");
}
// Assemble signature content (consistent with `generateSignature` method)
String signContent = merchantId + "." + timestamp + "." + timezone + "." + body;
// Verify using RSA public key
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initVerify((PublicKey) this.key);
signature.update(signContent.getBytes(StandardCharsets.UTF_8));
// Base64 decode received signature
byte[] decodedSignature = Base64.getDecoder().decode(receivedSignature);
// Verify signature
return signature.verify(decodedSignature);
}
}