2

I am trying to do a signed API request with HMAC SHA256 signature.

How can I make the signed request? I am not able to generate the correct signature:

{"code":-1022,"msg":"Signature for this request is not valid."}

  static func binanceAccountSnapshot(timeStamp: Int) {
        
        let semaphore = DispatchSemaphore (value: 0)
        let urlWithoutSignature = "https://api.binance.com/sapi/v1/capital/config/getall?timestamp=\(timeStamp)"
        let secretString = "aN345refdcx78iygkhbrefdoyilhukB6prefd98uoixjk(api secret)"
        let key = SymmetricKey(data: secretString.data(using: .utf8)!)
        
        let signature = HMAC<SHA256>.authenticationCode(for: urlWithoutSignature.data(using: .utf8)!, using: key)
        let signatureString = Data(signature).map { String(format: "%02hhx", $0) }.joined()
         
        var request = URLRequest(url: URL(string: "https://api.binance.com/sapi/v1/capital/config/getall?timestamp=\(timeStamp)&signature=\(signatureString)")!,timeoutInterval: Double.infinity)
        request.addValue("application/json", forHTTPHeaderField: "Content-Type")
        request.addValue("p4t98weflsudichjkxwtrfsduoxhckjnwe8isdokjx(api key)", forHTTPHeaderField: "X-MBX-APIKEY")
        request.httpMethod = "GET"
         
        let task = URLSession.shared.dataTask(with: request) { data, response, error in
            guard let data = data else {
                print(String(describing: error))
                semaphore.signal()
                return
            }
            print(String(data: data, encoding: .utf8)!)
            semaphore.signal()
        }
        task.resume()
    }
Maruta
  • 1,063
  • 11
  • 24
  • Usually, you don't separate URL params with a `?` but with a `&`. The `?` if to separate the rest of the request and the params in the url form. You can use `QueryItem` to do so. – Larme Apr 02 '21 at 16:24
  • 3
    Now, for your issue. In the doc, there is a Postman collection https://binance-docs.github.io/apidocs/spot/en/#postman-collections where you can add this to POSTMAN. Postman can generate code, so you can ask POSTMAN to generate Swift code (for a starter), see how it's done, and replicate (with better Swift code, because it's not beautiful code), but you'll se where to put the params (request, header, etc.) – Larme Apr 02 '21 at 16:25
  • Regarding how to use your private key, I guess after reading https://binance-docs.github.io/apidocs/spot/en/#endpoint-security-type that it should be in HTTP Headers (could be confirmed with the Postman samples). but indeed https://binance-docs.github.io/apidocs/spot/en/#signed-trade-user_data-and-margin-endpoint-security would have been a better link/doc to give read on how get that. Because "GET /sapi/v1/accountSnapshot (HMAC SHA256)" and "expected output", are not helpful at all. – Larme Apr 02 '21 at 16:27
  • Thank you , this is really useful. How can I create the signature? Postman automatically generates one on each request so I need to know how to generate it in Swift – Maruta Apr 05 '21 at 13:40
  • See the doc to know which params. The rest look for Swift how to HMac 256. Plenty of questions about that. – Larme Apr 05 '21 at 13:46
  • is there anything else that could make that my request doesn't work? I am using the same code as postman gives, just changing the timestamp and the signature, but still i get no result – Maruta Apr 06 '21 at 07:30
  • Did you look at the terminal example https://binance-docs.github.io/apidocs/spot/en/#signed-trade-user_data-and-margin-endpoint-security (on the right) they show what they crypt for the signature, etc. Read the doc of the commands to understand them, replicate them with their own hard coded examples in Swift, then use them with your params. – Larme Apr 06 '21 at 08:21
  • thank you for all the help. Now finally I am getting a response of the url request but is that the signature is not correct: {"code":-1022,"msg":"Signature for this request is not valid."} – Maruta Apr 06 '21 at 08:52
  • And starting with the sample, you have the same encryption? Could you edit your question at least? It's hard now to tell what's wrong... Also, is that YOUR private key (might want to not share it). – Larme Apr 06 '21 at 09:16
  • I have now used the example of postman and I am not generating the same signature. I edited the question and the code, thanks a lot for your help. No, it was a fake private key hehe – Maruta Apr 06 '21 at 09:43
  • I thought that the signature wasn't generate with `https://api.binance.com/sapi/v1/capital/config/getall?`, but only with what's after, as stated there https://stackoverflow.com/questions/66958017/hmac-256-signature-in-swift-5-with-cryptokit#comment118369322_66958017 – Larme Apr 06 '21 at 09:48
  • ...that was it! oops – Maruta Apr 06 '21 at 11:56
  • Not sure if that's your real api key, but you should probably remove it, and delete it from binance and create a new one if it is – Sam Jun 01 '21 at 04:05

1 Answers1

2

Your code almost works, the string for the signature should only be the params for the url and not the whole url though (the string should only be those characters which come after the ? in the url)

  static func binanceAccountSnapshot(timeStamp: Int) {
        
        let semaphore = DispatchSemaphore (value: 0)
        let params = timestamp=\(timeStamp)
        let urlWithoutSignature = "https://api.binance.com/sapi/v1/capital/config/getall?\(params)"
        let secretString = "aN345refdcx78iygkhbrefdoyilhukB6prefd98uoixjk(api secret)"
        let key = SymmetricKey(data: secretString.data(using: .utf8)!)
        
        let signature = HMAC<SHA256>.authenticationCode(for: params.data(using: .utf8)!, using: key)
        let signatureString = Data(signature).map { String(format: "%02hhx", $0) }.joined()
         
        var request = URLRequest(url: URL(string: "https://api.binance.com/sapi/v1/capital/config/getall?timestamp=\(timeStamp)&signature=\(signatureString)")!,timeoutInterval: Double.infinity)
        request.addValue("application/json", forHTTPHeaderField: "Content-Type")
        request.addValue("p4t98weflsudichjkxwtrfsduoxhckjnwe8isdokjx(api key)", forHTTPHeaderField: "X-MBX-APIKEY")
        request.httpMethod = "GET"
         
        let task = URLSession.shared.dataTask(with: request) { data, response, error in
            guard let data = data else {
                print(String(describing: error))
                semaphore.signal()
                return
            }
            print(String(data: data, encoding: .utf8)!)
            semaphore.signal()
        }
        task.resume()
    }
Sam
  • 1,765
  • 11
  • 82
  • 176