GDAX API请求签名失败返回400错误,求排查修正方案
Let's walk through the issues in your code that are triggering the 400 Bad Request error. These are the critical fixes you need to implement:
Key Issues & Fixes
1. Mismatched Timestamps
You're generating two different timestamps: one for the signature, and a separate one for the CB-ACCESS-TIMESTAMP header. GDAX requires these values to be identical—if they don't match, the signature validation fails immediately.
2. Wrong Timestamp Precision
GDAX expects timestamps in seconds since epoch, but System.currentTimeMillis() returns milliseconds. Using a millisecond timestamp will cause the API to reject your request outright.
3. Base64 Encoding Includes Newlines
The Base64.DEFAULT flag adds newline characters to the encoded string, which breaks the signature format GDAX expects. Use Base64.NO_WRAP to produce a clean, single-line Base64 string.
4. Unsafe Shared Mac Instance
If GDAXConstants.SHARED_MAC is a global, reused Mac object, it's not thread-safe. AsyncTask runs in a background thread, and sharing Mac instances across threads can cause corrupted signatures. Always create a new Mac instance for each signature generation.
Modified Code
Updated doInBackground Method
protected Void doInBackground(String... params) { try { URL url = new URL(baseUrl + params[0]); HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); // Generate a single, second-precision timestamp (critical for GDAX) String timestamp = String.valueOf(System.currentTimeMillis() / 1000); urlConnection.setDoInput(true); urlConnection.setDoOutput(true); urlConnection.setRequestMethod("GET"); // Set method first (good practice for HttpURLConnection) urlConnection.setRequestProperty("Content-Type", "application/json"); urlConnection.setRequestProperty("CB-ACCESS-KEY", key); // Use the SAME timestamp for both signature generation and request header urlConnection.setRequestProperty("CB-ACCESS-SIGN", generate(params[0], "GET", "", timestamp)); urlConnection.setRequestProperty("CB-ACCESS-TIMESTAMP", timestamp); urlConnection.setRequestProperty("CB-ACCESS-PASSPHRASE", passphrase); int statusCode = urlConnection.getResponseCode(); if (statusCode == 200) { InputStream inputStream = new BufferedInputStream(urlConnection.getInputStream()); String response = convertInputStreamToString(inputStream); Log.d("TAG", response); } else { Log.d("TAG", "Status code: " + statusCode); } } catch (Exception e) { Log.d("TAG", "Error: " + e.getLocalizedMessage()); e.printStackTrace(); } return null; }
Updated generate Method
public String generate(String requestPath, String method, String body, String timestamp) { try { String prehash = timestamp + method.toUpperCase() + requestPath + body; byte[] secretDecoded = Base64.decode(secretKey, Base64.DEFAULT); SecretKeySpec keyspec = new SecretKeySpec(secretDecoded, "HmacSHA256"); // Create a new Mac instance each time to avoid thread-safety issues Mac sha256 = Mac.getInstance("HmacSHA256"); sha256.init(keyspec); // Use NO_WRAP to ensure no newlines in the Base64 output String shadone = Base64.encodeToString(sha256.doFinal(prehash.getBytes()), Base64.NO_WRAP); return shadone; } catch (InvalidKeyException | NoSuchAlgorithmException e) { e.printStackTrace(); throw new RuntimeException("Cannot set up authentication headers.", e); } }
Additional Notes
- Double-check that your
requestPathexactly matches GDAX's expected format (e.g.,/accountswithout extra slashes or unencoded query parameters unless required). - For POST requests, the
bodyparameter must be the exact JSON payload sent in the request—using an empty string is correct for GET requests as you have here.
内容的提问来源于stack exchange,提问作者someone

