Java环境下如何使用已有的访问密钥与密钥生成HMAC-SHA256签名访问REST API
Java Implementation for HMAC-SHA256 API Signature
Hey there, let's break down how to replicate that Python HTTP signature logic in Java. The core idea is generating an HMAC-SHA256 signature for a specific set of headers, then attaching it to your POST request's Authorization header. Here's a step-by-step solution with both standard library and Apache HttpClient examples.
Key Things to Replicate
First, let's align with what your Python code does:
- Sign a fixed list of headers:
(request-target),host,date,content-type,content-length - Use your base64-decoded secret key to generate the HMAC-SHA256 signature
- Format the signature into a valid
Signaturescheme Authorization header - Send the POST request with all required headers and JSON body
Example 1: Using Standard Java Libraries (No External Dependencies)
This uses HttpURLConnection and Java's built-in crypto tools—no extra libraries needed:
import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.Base64; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; public class ApiSignatureHandler { public static void main(String[] args) throws Exception { // Replace with your actual values String host = "api.your-service.com"; String urlPath = "/v1/your-endpoint"; String keyId = "your-access-key-id"; String base64EncodedSecret = "your-base64-secret-key"; // Same as key_secret_id in Python String jsonBody = "{\"data\": \"your-payload\"}"; // 1. Prepare required header values String requestMethod = "POST"; String requestTarget = requestMethod.toLowerCase() + " " + urlPath; String contentType = "application/json"; long contentLength = jsonBody.getBytes(StandardCharsets.UTF_8).length; String dateHeader = DateTimeFormatter.RFC_1123_DATE_TIME.format(ZonedDateTime.now()); // 2. Build the signing string (exact order matters!) StringBuilder signingString = new StringBuilder(); signingString.append("(request-target): ").append(requestTarget).append("\n"); signingString.append("host: ").append(host).append("\n"); signingString.append("date: ").append(dateHeader).append("\n"); signingString.append("content-type: ").append(contentType).append("\n"); signingString.append("content-length: ").append(contentLength); // 3. Generate HMAC-SHA256 signature byte[] secretKeyBytes = Base64.getDecoder().decode(base64EncodedSecret); Mac hmacSha256 = Mac.getInstance("HmacSHA256"); hmacSha256.init(new SecretKeySpec(secretKeyBytes, "HmacSHA256")); byte[] signatureBytes = hmacSha256.doFinal(signingString.toString().getBytes(StandardCharsets.UTF_8)); String base64Signature = Base64.getEncoder().encodeToString(signatureBytes); // 4. Build the Authorization header String authHeader = String.format( "Signature keyId=\"%s\",algorithm=\"hmac-sha256\",headers=\"(request-target) host date content-type content-length\",signature=\"%s\"", keyId, base64Signature ); // 5. Send the POST request URL apiUrl = new URL("https://" + host + urlPath); HttpURLConnection conn = (HttpURLConnection) apiUrl.openConnection(); conn.setRequestMethod("POST"); conn.setRequestProperty("Host", host); conn.setRequestProperty("Date", dateHeader); conn.setRequestProperty("Content-Type", contentType); conn.setRequestProperty("Content-Length", String.valueOf(contentLength)); conn.setRequestProperty("Authorization", authHeader); conn.setDoOutput(true); // Write the JSON body try (OutputStream os = conn.getOutputStream()) { byte[] payloadBytes = jsonBody.getBytes(StandardCharsets.UTF_8); os.write(payloadBytes, 0, payloadBytes.length); } // Handle the response int responseCode = conn.getResponseCode(); System.out.println("Response Code: " + responseCode); // Add code to read response body if needed } }
Example 2: Using Apache HttpClient 5.x
If you prefer using Apache HttpClient for cleaner request handling, here's how to do it:
First, add this Maven dependency (if using Maven):
<dependency> <groupId>org.apache.httpcomponents.client5</groupId> <artifactId>httpclient5</artifactId> <version>5.2.1</version> </dependency>
Then the code:
import org.apache.hc.client5.http.classic.methods.HttpPost; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; import org.apache.hc.client5.http.impl.classic.HttpClients; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.io.entity.StringEntity; import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.Base64; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; public class ApacheHttpClientSignatureExample { public static void main(String[] args) throws Exception { // Replace with your actual values String host = "api.your-service.com"; String urlPath = "/v1/your-endpoint"; String keyId = "your-access-key-id"; String base64EncodedSecret = "your-base64-secret-key"; String jsonBody = "{\"data\": \"your-payload\"}"; // Prepare header values String requestMethod = "POST"; String requestTarget = requestMethod.toLowerCase() + " " + urlPath; String contentType = ContentType.APPLICATION_JSON.getMimeType(); long contentLength = jsonBody.getBytes(StandardCharsets.UTF_8).length; String dateHeader = DateTimeFormatter.RFC_1123_DATE_TIME.format(ZonedDateTime.now()); // Build signing string StringBuilder signingString = new StringBuilder(); signingString.append("(request-target): ").append(requestTarget).append("\n"); signingString.append("host: ").append(host).append("\n"); signingString.append("date: ").append(dateHeader).append("\n"); signingString.append("content-type: ").append(contentType).append("\n"); signingString.append("content-length: ").append(contentLength); // Generate signature byte[] secretKeyBytes = Base64.getDecoder().decode(base64EncodedSecret); Mac hmacSha256 = Mac.getInstance("HmacSHA256"); hmacSha256.init(new SecretKeySpec(secretKeyBytes, "HmacSHA256")); byte[] signatureBytes = hmacSha256.doFinal(signingString.toString().getBytes(StandardCharsets.UTF_8)); String base64Signature = Base64.getEncoder().encodeToString(signatureBytes); // Build Authorization header String authHeader = String.format( "Signature keyId=\"%s\",algorithm=\"hmac-sha256\",headers=\"(request-target) host date content-type content-length\",signature=\"%s\"", keyId, base64Signature ); // Create and send request HttpPost postRequest = new HttpPost("https://" + host + urlPath); postRequest.setEntity(new StringEntity(jsonBody, ContentType.APPLICATION_JSON)); postRequest.setHeader("Host", host); postRequest.setHeader("Date", dateHeader); postRequest.setHeader("Content-Length", String.valueOf(contentLength)); postRequest.setHeader("Authorization", authHeader); try (CloseableHttpClient client = HttpClients.createDefault(); CloseableHttpResponse response = client.execute(postRequest)) { System.out.println("Response Code: " + response.getCode()); // Process response body here } } }
Critical Notes to Avoid Issues
- Date Header Format: Make sure the
Dateheader uses RFC 1123 format (e.g.,Wed, 20 Sep 2023 14:30:00 GMT). The examples use Java 8+ZonedDateTimeto generate this correctly. - Header Order: The order of headers in the signing string must exactly match the list provided—any deviation will cause the signature to be invalid.
- Secret Key Decoding: Your secret key is base64-encoded, so we decode it to bytes before generating the HMAC (just like the Python
b64decodecall). - Lowercase Header Names: The header names in the signing string are lowercase (e.g.,
content-typenotContent-Type)—this is required for the API to validate the signature correctly.
内容的提问来源于stack exchange,提问作者Zakaria Shahed
相关产品推荐
相关产品推荐

