You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

调用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),不能用完整URL
  • canonicalized_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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 14:55:01