Android获取HERE地图OAuth Token遇401300签名不匹配错误求助
我正按照HERE地图身份与访问管理开发者教程获取OAuth Token,iOS应用可正常获取,但Android端始终失败,持续报错:
errorCode: '401300'. Signature mismatch. Authorization signature or client credential is wrong.
以下是我的实现代码:
HEREOAuthManager.java
public void fetchOAuthToken(final HereTokenFetchListener callback) { String timestamp = String.valueOf(String.valueOf(Instant.now().getEpochSecond())); String nonce = Util.randomStringGenerator(); String grant_type = "grant_type=client_credentials"; String oauth_consumer_key = "&oauth_consumer_key=" + clientID; String oauth_nonce = "&oauth_nonce=" + nonce; String oauth_signature_method = "&oauth_signature_method=HMAC-SHA256"; String oauth_timestamp = "&oauth_timestamp=" + timestamp; String oauth_version = "&oauth_version=1.0"; String paramsString = grant_type + oauth_consumer_key + oauth_nonce + oauth_signature_method + oauth_timestamp + oauth_version; String baseString = "POST&" + Util.urlEncode(tokenEndpoint) + "&" + Util.urlEncode(paramsString); // Generate signature String secret = Util.urlEncode(clientSecret) + "&"; String signature = Util.calculateHmacSha256(secret, baseString); // Construct Authorization header String authString = "OAuth oauth_consumer_key=\"" + clientID + "\",oauth_nonce=\"" + nonce + "\",oauth_signature=\"" + Util.urlEncode(signature) + "\",oauth_signature_method=\"HMAC-SHA256\"," + "oauth_timestamp=\"" + timestamp + "\",oauth_version=\"1.0\""; // Create HTTP client OkHttpClient client = new OkHttpClient(); // Create request body RequestBody requestBody = new FormBody.Builder() .add("grant_type", "client_credentials") .build(); // Create HTTP request Request request = new Request.Builder() .url(tokenEndpoint) .post(requestBody) .addHeader("Authorization", authString) .addHeader("Content-Type", "application/x-www-form-urlencoded") .build(); }
Utils.java
public static String calculateHmacSha256(String secret, String data) { try { String secretWithAmpersand = secret + "&"; SecretKeySpec secretKeySpec = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"); Mac mac = Mac.getInstance("HmacSHA256"); mac.init(secretKeySpec); byte[] hmacData = mac.doFinal(data.getBytes(StandardCharsets.UTF_8)); String baseEncodedSignature = Base64.getEncoder().encodeToString(hmacData); return urlEncode(baseEncodedSignature); } catch (NoSuchAlgorithmException | InvalidKeyException e) { e.printStackTrace(); return null; } } public static String urlEncode(String stringToEncode) { try { return java.net.URLEncoder.encode(stringToEncode, "UTF-8") .replace("+", "%20") // Replace '+' with '%20' .replace("=", "%3D") .replace("*", "%2A") // Replace '*' with '%2A' .replace("&", "%26") .replace( "~","%7E"); // Replace '~' with '%7E'; } catch (java.io.UnsupportedEncodingException e) { e.printStackTrace(); return null; } } public static String randomStringGenerator() { String ALLOWED_CHARACTERS ="0123456789qwertyuiopasdfghjklzxcvbnm"; final Random random=new Random(); final StringBuilder sb=new StringBuilder(8); for(int i=0;i<8;++i) sb.append(ALLOWED_CHARACTERS.charAt(random.nextInt(ALLOWED_CHARACTERS.length()))); return sb.toString(); }
排查出的核心问题及修复方案:
1. 签名密钥重复拼接&
在fetchOAuthToken中已将secret拼接为Util.urlEncode(clientSecret) + "&",但calculateHmacSha256里又执行了secretWithAmpersand = secret + "&",最终密钥变成urlEncode(clientSecret)&&,违反OAuth 1.0a规范(正确格式应为urlEncode(clientSecret)&)。
修复:删除calculateHmacSha256中的secretWithAmpersand变量,直接用传入的secret构建密钥:
SecretKeySpec secretKeySpec = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
2. 签名被双重URL编码
calculateHmacSha256已对base64签名执行urlEncode,但构造authString时又再次调用Util.urlEncode(signature),导致签名编码两次,直接引发不匹配。
修复:构造Authorization头时直接使用签名值:
String authString = "OAuth oauth_consumer_key=\"" + clientID + "\",oauth_nonce=\"" + nonce + "\",oauth_signature=\"" + signature + "\",oauth_signature_method=\"HMAC-SHA256\"," + "oauth_timestamp=\"" + timestamp + "\",oauth_version=\"1.0\"";
3. OAuth参数未按字典序排序
OAuth 1.0a要求baseString中的参数必须按字典序排序,当前直接拼接的方式存在潜在排序风险,可能因字符排序逻辑差异导致签名不匹配。
修复:用TreeMap自动排序参数后再拼接:
Map<String, String> paramsMap = new TreeMap<>(); paramsMap.put("grant_type", "client_credentials"); paramsMap.put("oauth_consumer_key", clientID); paramsMap.put("oauth_nonce", nonce); paramsMap.put("oauth_signature_method", "HMAC-SHA256"); paramsMap.put("oauth_timestamp", timestamp); paramsMap.put("oauth_version", "1.0"); StringBuilder paramsBuilder = new StringBuilder(); for (Map.Entry<String, String> entry : paramsMap.entrySet()) { if (paramsBuilder.length() > 0) { paramsBuilder.append("&"); } paramsBuilder.append(Util.urlEncode(entry.getKey())) .append("=") .append(Util.urlEncode(entry.getValue())); } String paramsString = paramsBuilder.toString();
修复以上三点后,应该可以正常获取OAuth Token。
内容的提问来源于stack exchange,提问作者user2574903

