cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
580
Views
0
Helpful
4
Replies

/admin/v1/users giving a 40103 in DUO Security

enamul-haque
Level 1
Level 1

Hello Sir,

I am trying to create using  /admin/v1/users it shows {
"code":40103,
"message":"Invalid signature in request credentials",
"stat":"FAIL"
}

The canonical string I’m generating for use in the HMAC :

Tue, 26 Sep 2023 21:52:23 +0600
POST
/admin/v1/users
email=abc%40example.com&username=newuser

HMAC-SHA1 hex String:
be16c29b840f0618bb947d7e2574f892b82889b7

And the endpoint I’m hitting ends up looking like this:
https://api-d221a358.duosecurity.com/admin/v1/users?email=abc%40example.com&username=newuser

Here is my java code:

public class DuoCreateUsers {

private static SortedMap<String, Object> params = new TreeMap<String, Object>();

public static void main(String[] args) throws IOException {
String ikey = "x";
String skey = "x";
String host = "x";
String httpMethod = "POST";
String requestPath = "/admin/v1/users";
String timestamp = OffsetDateTime.now().format(DateTimeFormatter.RFC_1123_DATE_TIME);

String username = "newuser";
String email = "abc@example.com";

params.put("username",username);
params.put("email",email);

String queryString = canonQueryString();

String canonicalRequest = timestamp + "\n" + httpMethod + "\n" + requestPath + "\n" + queryString;
System.out.println("canonicalRequest = " + canonicalRequest);

String signature = sign1(canonicalRequest, skey);
// System.out.println("signature = " + signature);

String url = "https://" + host + requestPath+"?"+queryString;
System.out.println("url = " + url);

HttpClient httpClient = HttpClients.createDefault();
HttpPost httpPost = new HttpPost(url);
httpPost.setHeader("Date", timestamp);
httpPost.setHeader("Authorization", "Basic " + Base64.getEncoder().encodeToString((ikey + ":" + signature).getBytes()));
httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded");


httpPost.setEntity(new StringEntity(params.toString()));
// Make the request
HttpResponse response = httpClient.execute(httpPost);
HttpEntity entity = response.getEntity();
String responseContent = entity != null ? EntityUtils.toString(entity) : "";

String rs = "Response Status Code: " + response.getStatusLine().getStatusCode() + "\nResponse Content:\n" + responseContent;
System.out.println("rs = " + rs);

}


public static String canonQueryString()
throws UnsupportedEncodingException {
ArrayList<String> args = new ArrayList<String>();

for (String key : params.keySet()) {
String name = URLEncoder
.encode(key, "UTF-8")
.replace("+", "%20")
.replace("*", "%2A")
.replace("%7E", "~");
String value = URLEncoder
.encode(params.get(key).toString(), "UTF-8")
.replace("+", "%20")
.replace("*", "%2A")
.replace("%7E", "~");
args.add(name + "=" + value);
}

return com.duosecurity.client.Util.join(args.toArray(), "&");
}

private static String sign1(String data, String secretKey) {
try {

// Create an HMAC-SHA1 key from the secret key
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "HmacSHA1");

// Initialize the HMAC-SHA1 algorithm
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(secretKeySpec);

// Calculate the HMAC-SHA1 hash
byte[] hmacSha1Bytes = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));

// Convert the result to a hexadecimal string
String hmacSha1Hex = bytesToHex(hmacSha1Bytes);

// Print the HMAC-SHA1 hash
System.out.println("HMAC-SHA1: " + hmacSha1Hex);

return hmacSha1Hex;
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
e.printStackTrace();
}

return null;
}

// Helper method to convert bytes to a hexadecimal string
private static String bytesToHex(byte[] bytes) {
StringBuilder hexStringBuilder = new StringBuilder();
for (byte b : bytes) {
hexStringBuilder.append(String.format("%02x", b));
}
return hexStringBuilder.toString();
}
}

Please help what is the wrong of my code?

 

 

4 Replies 4

DuoKristina
Cisco Employee
Cisco Employee

The canonical string I’m generating for use in the HMAC :

Tue, 26 Sep 2023 21:52:23 +0600
POST
/admin/v1/users
email=abc%40example.com&username=newuser

 


are you leaving out the third line of five which is your API hostname? From the Admin API doc example:DuoKristina_0-1695756232712.png

Duo, not DUO.

enamul-haque
Level 1
Level 1

Hi @DuoKristina 

Thanks you very much for your reply.

I have also added this third line same things happen.

Wed, 27 Sep 2023 01:39:43 +0600
POST
api-d221a358.duosecurity.com
/admin/v1/users
email=enamul.haque%40gmail.com&username=ehaque

Here is the code..

String canonicalRequest = timestamp + "\n" + httpMethod + "\n" + host+"\n"+ requestPath +  "\n" + queryString;

System.out.println("canonicalRequest = " + canonicalRequest);

String signature = sign1(canonicalRequest, skey);
System.out.println("signature = " + signature);

String url = "https://" + host + requestPath+"?"+queryString;
System.out.println("url = " + url);

String authString ="Basic " + Base64.getEncoder().encodeToString((ikey + ":" + signature).getBytes());
System.out.println("authString = " + authString);

HttpClient httpClient = HttpClients.createDefault();
HttpPost httpPost = new HttpPost(url);
httpPost.setHeader("Authorization", authString);
httpPost.setHeader("Date", timestamp);
httpPost.setHeader("Host",host);
httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded");

httpPost.setEntity(new StringEntity(canonJSONBody()));
// Make the request
HttpResponse response = httpClient.execute(httpPost);
HttpEntity entity = response.getEntity();
String responseContent = entity != null ? EntityUtils.toString(entity) : "";

 

enamul-haque
Level 1
Level 1

Hi @DuoKristina 

I have tried many ways. But every way shows Invalid signature.

Is it right way which I did below?

genetateSignature(canonicalString, skey):

HMAC-SHA1 Key (genetateSignature): afffa7f9cb019b4d9eceacd3d16d7afe4461d028
signature b4encode of HMAC-SHA1 Key = YWZmZmE3ZjljYjAxOWI0ZDllY2VhY2QzZDE2ZDdhZmU0NDYxZDAyOA==

Authorization heraders:
String authString ="Basic " + Base64.getEncoder().encodeToString((ikey + ":" + signature).getBytes());

Header Request:

httpPost.setHeader("Authorization", authString);
httpPost.setHeader("Date", timestamp);
httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded");

 

 
 

 

 

DuoKristina
Cisco Employee
Cisco Employee

Oh, I see you are the same person from the similar earlier post. Your last response to your separate, earlier post was removed for your protection because you accidentally posted what looked like a valid ikey and skey in your last response. Be sure to scrub any of your Duo account's details from your posts to the community!

I did get a chance to see in the other thread that even with our Java API client you still received the 40103. This is a weird thought, but I wonder if the issue is that you are already URL encoding the param and then our client also encodes them as well?

Duo, not DUO.
Quick Links