Unable to authenticate via Coinbase Websocket API, help!

Hello everyone!

I’m trying to subscribe to certain channels using Coinbase’s Websocket API, but I’m having some authentication issues. I’ve generated signatures according to Coinbase’s requirements, but there still seems to be some issues.

Below is the code I am using:

public void onOpen(ServerHandshake handshakedata) {
    String secretKey = "xxx";  // 注意:这是一个伪造的密钥
    String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
    String channel = "market_trades";
    String apiKey = "xxxx";  // 注意:这也是一个伪造的密钥
    
    String[] productIds = {"ETH-USD"};
    String signature = generateSignature(secretKey, timestamp, channel, productIds);
    
    // 创建JSON对象并插入数据
    JSONObject json = new JSONObject();
    json.put("type", "subscribe");
    json.put("product_ids", productIds);
    json.put("channel", "market_trades");
    json.put("signature", signature);
    json.put("api_key", apiKey);
    json.put("timestamp", timestamp);
    
    String string = json.toString();
    send(string);
    cancelReconnect();
    createTimer();
}

public static String generateSignature(String secretKey, String timestamp, String channel, String[] productIds) {
    String products = String.join(",", productIds);
    String message = timestamp + channel + products;
    
    String signature = "";
    try {
        Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
        SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(), "HmacSHA256");
        sha256_HMAC.init(secretKeySpec);
        
        byte[] hash = sha256_HMAC.doFinal(message.getBytes(StandardCharsets.UTF_8));
        StringBuilder hexString = new StringBuilder();
        for (int i = 0; i < hash.length; i++) {
            String hex = Integer.toHexString(0xff & hash[i]);
            if (hex.length() == 1) {
                hexString.append('0');
            }
            hexString.append(hex);
        }
        
        signature = hexString.toString();
    } catch (Exception e) {
        System.out.println("Error generating HMAC: " + e.getMessage());
    }
    
    return signature;
}

When I try to authenticate using the above code, I get an authentication failure error.

Am I implementing the signature process correctly?
Are there any other authentication steps I may have missed?
Any suggestions and solutions would be greatly appreciated! Thanks!

Hi @sosponge
We are not sure about how this code is running and don’t know what errors they’re getting, but we would advise you to look at the documentation for Advanced Trading Authentication:

If you still have an issue, you can send us the error message you are receiving.

I was following this document, I made sure my apikey and key were fine, both were in enable state, but the final return still kept coming back as
{“type”: “error”, “message”: “authentication failure”}

Did you try my suggestion?:

Use StandardCharsets.UTF_8 with both getBytes functions.

 Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
                SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
                sha256_HMAC.init(secretKeySpec);

                byte[] hash = sha256_HMAC.doFinal(message.getBytes(StandardCharsets.UTF_8));

Yes,Use StandardCharsets.UTF_8 in both getBytes functions and still get error {“type”: “error”, “message”: “authentication failure”}

Ok, my next suggestion it to verify if your code correctly generates signature.

My test data:

1673628642
market_trades
ETH-USD
8ULNnqdIJ17CoVL5t1Mqt5XMjVDsM4Qm

Last line is random secret only for testing. Using above test data my code generates this:

1673628642market_tradesETH-USD
966638555c9a658df42a6931d96260de6d78d50e9fe6c672a8cc6e1937068348

Use provided data and check if your codes generates same signature.

In other topic you initially tried to encode signature in base64… In case you are decoding secret, you should not do that either. In Advanced Trade secret is not base64 encoded! Maybe that is your problem?

Thank you for the test data you provided. I have verified it using the data you provided and my code logic and the resulting signature does match exactly the one you provided:
966638555c9a658df42a6931d96260de6d78d50e9fe6c672a8cc6e1937068348
I have ensured that my apikey is enabled and all relevant permissions are turned on. However, I’m still experiencing problems with authentication not passing and I’m getting an “Authentication Failed” error message.

Can you provide some advice or guidance to help me resolve this issue?

Thank you very much!

Did you see extra comment about secret? You are NOT base64 decoding it, right?

I don’t use Java, does System.currentTimeMillis() returns time as unix timestamp? Looking at other posts I see that other people has used Instant.now().getEpochSecond() to get timestamp.

Are you you using correct address for websocket?:

wss://advanced-trade-ws.coinbase.com

@sosponge try this. most people won’t help you here.

 	private static ArrayList<String> SignMessage(String data)
 	{
 		ArrayList<String> result = new ArrayList<String>();
 		try 
                 {
 			result.add(hmacWithJava(data, properties.getProperty("secret").toString()).toString());
 		} catch (InvalidKeyException e) {
 		} catch (NoSuchAlgorithmException e) {	
 		}
 		return result;
 	}
	private static void InitializeUserChannel(String feedName)
	{
		try {
			String timestamp = Instant.now().getEpochSecond() + "";
			String mData = timestamp + "user";
			
			ArrayList<String> signedMessage = SignMessage(mData);
			if(signedMessage.size() >0)
			{
			String subscribeUserMessages = 
					"{\"type\":\"subscribe\","
					+ "\"channel\":\"user\","
					+ "\"api_key\":\""+properties.getProperty("key")+"\","
					+ "\"timestamp\":\"" + timestamp +"\","
					+ "\"signature\":\"" + signedMessage.get(0)+"\"}";
			
			
			sockets[feedName].sendMessage(subscribeUserMessages);
			}
			}catch(Exception ex)
			{			
			}	
	}

Hey sosponge - did you manage to solve this issue? I’m having exactly the same issue coding this up in Java. The api key and secret are working fine when signing via the REST api

Many Thanks Conleth

Actually I had missed that the timestamp should be in string format. Once I’ve tried this it now works