无法解决HTTP基本身份验证的问题 - 邮件头中的非法字符(由HMAC编码产生)

时间:2014-05-05 20:03:05

标签: java rest base64 hmac http-basic-authentication

我不是REST API的专家,在尝试调用Duo 2 factor身份验证API时遇到了问题:https://www.duosecurity.com/docs/authapi#api-details

它看起来非常直接,但我认为我遗漏了一些东西,我已经为此工作了2天没有成功。

我正在使用Jersey,它给了我以下错误:

java.lang.IllegalArgumentException: Illegal character(s) in message header value: Basic RElGUE1MSVQyMU40OUhORURL[...]YTYzOQ==

(我已缩短上面一行中的键)

API使用HTTP基本身份验证来验证请求。

我确实按照文档中的说明进行了身份验证。我确实生成了HTTP密码作为请求的HMAC签名。我也按照文档中的说明构建了签名,首先从我的请求构建一个ASCII字符串,然后将这些组件与换行符连接起来并计算规范表示的HMAC-SHA1,然后在Base64中编码用户名:hmac。 / p>

我想我可能会误解编码部分的某些内容或者没有正确地做某事。

以下是我的代码的一部分:

public Enroll enroll(String username){
    HashMap<String, String> formData = new HashMap<String, String>();
    formData.put("username", username);

    String date = generateDate();
    String signature = constructSignature("POST", "/auth/v2/enroll", formData);

    String authValue = generateAuthValue(secretKey, signature);

    Enroll response = service.path("auth").path("v2").path("enroll").header("Date", date)
        .header("Content-Type", "application/x-www-form-urlencoded")
        .header("Authorization", authValue).type(MediaType.APPLICATION_FORM_URLENCODED_TYPE)
        .accept(MediaType.APPLICATION_JSON_TYPE).post(Enroll.class, formData);

    return response;
}

public String generateAuthValue(String secretKey, String signature){
    String hmacValue = calcShaHash(signature, integrationKey, secretKey);
    return hmacValue;
}

private String constructSignature(String method, String path, HashMap<String, String> params){
    String date = generateDate();
    String lineFeed = "\n";

    StringBuilder stringBuilder = new StringBuilder();
    stringBuilder.append(date);
    stringBuilder.append(lineFeed);
    stringBuilder.append(method);
    stringBuilder.append(lineFeed);
    stringBuilder.append(apiHostName);
    stringBuilder.append(lineFeed);
    stringBuilder.append(path);
    stringBuilder.append(lineFeed);
    stringBuilder = urlEncodeParameters(params, stringBuilder);
    return stringBuilder.toString();
}

private StringBuilder urlEncodeParameters(HashMap<String, String> params, StringBuilder stringBuilder){
    try{
        for (Map.Entry<String, String> entry : params.entrySet()){
            stringBuilder.append(URLEncoder.encode(entry.getKey().toString(), "UTF-8"));
            stringBuilder.append("=");
            stringBuilder.append(URLEncoder.encode(entry.getValue().toString(), "UTF-8"));
            stringBuilder.append("\n");
            //signature.concat(encoded);
        }
    }catch (UnsupportedEncodingException e){
        e.printStackTrace();
    }
    return stringBuilder;
}

public static String calcShaHash(String data, String integrationKey, String secretKey){
    String HMAC_SHA1_ALGORITHM = "HmacSHA1";
    StringBuilder result = new StringBuilder();

    try{
        Key signingKey = new SecretKeySpec(secretKey.getBytes("UTF-8"), HMAC_SHA1_ALGORITHM);
        Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM);
        mac.init(signingKey);
        byte[] rawHmac = mac.doFinal(data.getBytes("UTF-8"));

        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(integrationKey);
        stringBuilder.append(":");
        stringBuilder.append(Hex.encodeHexString(rawHmac).toString());

        byte[] byteAuthorizationValue = stringBuilder.toString().getBytes("UTF-8");
        result.append("Basic ");     
        result.append(Base64.encode(byteAuthorizationValue).toString());
    }catch (Exception e){
        e.printStackTrace();
    }
    return result.toString();
}


private String generateDate(){
    Date date = new Date();
    SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss ZZZZ");
    String formattedDate = sdf.format(date);
    return formattedDate;
}

2 个答案:

答案 0 :(得分:1)

确保您的requestXML或任何标头值没有任何非法字符,并将其替换为......

aXmlRequest=aXmlRequest.replaceAll("\n", "");

答案 1 :(得分:0)

我想这是我目前面临的同样问题。

非法字符是auth标头末尾的换行符。

请查看Java: fetch URL with HTTPBasic Authentication了解详情。

我使用commons-codec库中的org.apache.commons.codec.binary.Base64类来获取Base64加密。我不确定,但也许它可以解决你的问题。

祝你好运, 菲利克斯