0

I'm fairly new to Swift and am currently working on HTTP signatures. I'm using the approach from this answer but the implementation doesn't work and gives me a different hash than I get from an alternate implementation I have in Python (which I'm more familiar with but have only rudimentary knowledge of cryptographic principles).

Here's the Swift code:

let secret = "my-secret"

let headersForSig = "single-string-combining-headers-with-\n"

let keyForHash = SymmetricKey(data: secret.data(using: .utf8)!)
let signatureHash = HMAC<SHA256>.authenticationCode(for: headersForSig.data(using: .utf8)!, using: keyForHash)

--EDIT--

Following @sulthan's comment below, changed the final formatting of the signature to this:

let basedSignatureHash = Data(signatureHash).base64EncodedString()

This all follows the above linked answer. But the API call with this signature doesn't work and doesn't look like the hash from the Python implementation.

An alternate approach in Python does work:

sig_value = "".join(list-of-headers-for-signature)

sig_value_string = str(sig_value)

sig_value_utf = bytes(sig_value_string, encoding='utf-8')

secret = base64.b64decode("my-secret")

hash_value = hmac.new(secret, sig_value_utf, hashlib.sha256)

signature = base64.b64encode(hash_value.digest()).decode("utf-8")

This works and unsurprisingly the signature hash is different! How can I mimic this approach in Swift? Or is there something else like order of headers that might be different between the two that's messing this up?

The structure of setting up the request object is nearly identical in both languages: create a request object with the target URL, add the required non-signature headers, then create the signature and add it as the last header.

tchaymore
  • 3,728
  • 13
  • 55
  • 86
  • Well, is your input the same? Your `headersForSig` seems to be a constant. – Sulthan Jan 30 '22 at 00:24
  • 1
    Also note that you are not doing Base64 in Swift. You are just encoding the bytes in hex format `let textSignatureHash = Data(signatureHash).base64EncodedString()` – Sulthan Jan 30 '22 at 00:26
  • Yes, thanks for the catch base64. Fixed. Inputs are the same, a couple of generic headers (date, target) and one custom header, all separated by \n. – tchaymore Jan 30 '22 at 00:55
  • 2
    You are using different secrets, in Python you try to decode the secret, assuming it's base64 encoded, in Swift implementation however, you treat the secret as a raw string (utf-8 encoded). This will result in different keys for the HMAC, and conversely different signatures. – Adam Kopeć Jan 30 '22 at 11:19
  • Well, as promised, I'm not very good at cryptography. Thanks so much, that did the trick. I reformatted the SymmetricKey this way: `let base64Secret = Data(base64Encoded: sharedSecret)! let keyForHash = SymmetricKey(data: base64Secret)`. If you post an answer I'll accept it. – tchaymore Jan 30 '22 at 16:33

0 Answers0