Invalid signature in request credentials in duosecurity

407 Views Asked by At

I am using duo security. But When I want to call using duo api, it shows Invalid signature in request credentials. Here is the error:

{"code": 40103, "message": "Invalid signature in request credentials", "stat": "FAIL"}

Here is my code..

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);
    }
}

What is the wrong of the code? Please help me

2

There are 2 best solutions below

1
On
final Working example!!!!!!!!!!
  
package com.technohunk.kafka;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TreeSet;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Hex;
public class DuoSecuritySign {
    static final String DOMAIN = "api-xxxxxx.duosecurity.com";
private static final String INTEGRATION_KEY = "DIU6E0ZIID76LWKWIBHZ";
    private static final String SECRET_KEY = "xxxxxxxxxxxxxx";
    private static final String HOST = "https://api-xxxxxxxxxxxxxx.duosecurity.com";
    
    private static final String PATH = "/admin/v1/users";
        public static void main(String[] args) throws UnsupportedEncodingException {
        //public static void main(String[] args) {
            try {
                // Create canonical string
                String now = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z", Locale.US).format(new Date());
                List<String> canon = new ArrayList<>();
                canon.add(now);
                canon.add("GET");
                canon.add(DOMAIN);
                canon.add(PATH);
                // Assuming 'params' is a Map<String, String> containing query parameters
                Map<String, String> params = new HashMap<>();
                List<String> argsList = new ArrayList<>();
                for (String key : new TreeSet<>(params.keySet())) {
                    String val = URLEncoder.encode(params.get(key), StandardCharsets.UTF_8.toString());
                    argsList.add(String.format("%s=%s", URLEncoder.encode(key, StandardCharsets.UTF_8.toString()), val));
                }
                canon.add(String.join("&", argsList));
                String canonicalString = String.join("\n", canon);
                System.out.println("canonicalString = "+canonicalString);
                // Sign the canonical string
                SecretKeySpec keySpec = new SecretKeySpec(SECRET_KEY.getBytes(StandardCharsets.UTF_8), "HmacSHA1");
                Mac mac = Mac.getInstance("HmacSHA1");
                mac.init(keySpec);
                byte[] rawHmac = mac.doFinal(canonicalString.getBytes(StandardCharsets.UTF_8));
                String signature = Hex.encodeHexString(rawHmac);
               ///String signature = Base64.getEncoder().encodeToString(rawHmac);

                // Construct Authorization header
                String authorizationHeader = "Basic " + Base64.getEncoder().
                       encodeToString((INTEGRATION_KEY + ":" + signature).getBytes(StandardCharsets.UTF_8));
               // Construct URI
                URI uri = new URI(HOST + PATH);
                // Create HTTP request with headers
                HttpRequest request = HttpRequest.newBuilder(uri)
                        .header("Authorization", authorizationHeader)
                        .header("Content-Type", "application/x-www-form-urlencoded")
                        .header("X-Duo-Date", now)
                        .GET()
                        .build();

                // Send the request and get the response
                HttpClient client = HttpClient.newHttpClient();
                HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
                // Print the response
                System.out.println("Response Code: " + response.statusCode());
                System.out.println("Response Header: " + response.headers());
                System.out.println("Response: " + response.body());

            } catch (NoSuchAlgorithmException | InvalidKeyException | InterruptedException 
                  | java.io.IOException | java.net.URISyntaxException e) {
                e.printStackTrace();
            }
        }
}
0
On

Your sign Payload is wrongly formatted

      String sigPayload = timestamp + "\n" + createUserUrl + "\n" + createUserRequestBody;

refer below link

https://github.com/duosecurity/duo_client_java/blob/ec8d0e8070e64e8f015e54a38e555818136e0bc6/duo-client/src/main/java/com/duosecurity/client/Http.java

where you can find below method

String canonRequest(String date, int sigVersion)

this method will help you to construct right signPayload /CanonRequest