调用Duo Security API出现Invalid signature错误,请求排查代码问题
排查Duo Security API "Invalid signature in request credentials" 错误
错误信息
{"code": 40103, "message": "Invalid signature in request credentials", "stat": "FAIL"}
问题代码
public class DuoAdminAPIClient { public static void main(String[] args) { // Replace with your Duo Admin API credentials String integrationKey = "DI7ABPU9TUJQO14RET9Q"; String secretKey = "YzDs7ZeQGMllravxDQxcn4jNAwyqF42P1XBDdGd2"; String apiHostname = "api-d221a358.duosecurity.com"; // Create an HttpClient instance HttpClient httpClient = HttpClients.createDefault(); try { // Define the user's attributes String username = "enamul_haque001"; String userFirstName = "Enamul"; String userLastName = "Haque"; // Construct the request body JSON String createUserRequestBody = String.format( "{\"username\": \"%s\", \"first_name\": \"%s\", \"last_name\": \"%s\"}", username, userFirstName, userLastName ); // Define the API endpoint String createUserUrl = "https://" + apiHostname + "/admin/v1/users"; // Generate the API signature // String timestamp = Long.toString(System.currentTimeMillis() / 1000); String timestamp = OffsetDateTime.now().format(DateTimeFormatter.RFC_1123_DATE_TIME); String sigPayload = timestamp + "\n" + createUserUrl + "\n" + createUserRequestBody; String signature = generateHmacSha1Signature(sigPayload, secretKey); // Create the HTTP POST request HttpPost createUserRequest = new HttpPost(createUserUrl); createUserRequest.addHeader("Authorization", "Basic " + Base64.encodeBase64String((integrationKey + ":" + signature).getBytes())); createUserRequest.addHeader("Content-Type", "application/json"); createUserRequest.addHeader("Date", timestamp); createUserRequest.setEntity(new StringEntity(createUserRequestBody)); // Send the request and get the response HttpResponse createUserResponse = httpClient.execute(createUserRequest); HttpEntity createUserEntity = createUserResponse.getEntity(); String createUserResponseString = EntityUtils.toString(createUserEntity); // Print the response (you can parse it to extract relevant information) System.out.println("Create User Response: " + createUserResponseString); } catch (Exception e) { e.printStackTrace(); } } private static String generateHmacSha1Signature(String payload, String secretKey) throws NoSuchAlgorithmException, InvalidKeyException { SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(), "HmacSHA1"); Mac mac = Mac.getInstance("HmacSHA1"); mac.init(keySpec); byte[] result = mac.doFinal(payload.getBytes()); return Base64.encodeBase64String(result); } }
代码问题及修复方案
1. Timestamp格式不符合要求
Duo API要求签名必须使用秒级UNIX时间戳(整数字符串),而非RFC_1123格式的日期字符串。恢复代码中被注释的正确写法:
String timestamp = Long.toString(System.currentTimeMillis() / 1000);
同时移除Date请求头,Duo API不依赖该头,签名用的timestamp才是校验依据。
2. 签名Payload构造错误
Duo API的签名Payload规则为:timestamp\nHTTP_METHOD\npath\ncanonicalized_parameters
HTTP_METHOD:大写的请求方法(此处为POST)path:仅取API的URI路径(如/admin/v1/users),不能用完整URLcanonicalized_parameters:POST请求的body内容,必须和实际发送的完全一致
错误的Payload写法:
String sigPayload = timestamp + "\n" + createUserUrl + "\n" + createUserRequestBody;
修正后:
String path = "/admin/v1/users"; String sigPayload = timestamp + "\nPOST\n" + path + "\n" + createUserRequestBody;
3. Authorization头前缀错误
Duo API的Authorization头前缀应为Duo,而非Basic。正确构造方式:
String authHeaderValue = Base64.encodeBase64String((integrationKey + ":" + signature).getBytes()); createUserRequest.addHeader("Authorization", "Duo " + authHeaderValue);
4. 字符编码一致性问题
生成签名和发送请求时,需指定统一的UTF-8编码,避免因编码差异导致签名不匹配:
- 签名生成时:
byte[] result = mac.doFinal(payload.getBytes(StandardCharsets.UTF_8));
- 请求实体:
createUserRequest.setEntity(new StringEntity(createUserRequestBody, StandardCharsets.UTF_8));
修复后的关键代码片段
// Generate the API signature String timestamp = Long.toString(System.currentTimeMillis() / 1000); String path = "/admin/v1/users"; String sigPayload = timestamp + "\nPOST\n" + path + "\n" + createUserRequestBody; String signature = generateHmacSha1Signature(sigPayload, secretKey); // Create the HTTP POST request HttpPost createUserRequest = new HttpPost(createUserUrl); String authHeaderValue = Base64.encodeBase64String((integrationKey + ":" + signature).getBytes(StandardCharsets.UTF_8)); createUserRequest.addHeader("Authorization", "Duo " + authHeaderValue); createUserRequest.addHeader("Content-Type", "application/json; charset=utf-8"); createUserRequest.setEntity(new StringEntity(createUserRequestBody, StandardCharsets.UTF_8));
内容的提问来源于stack exchange,提问作者Enamul Haque
相关产品推荐
相关产品推荐

