HMAC authentication in Swift and Java (iOS/Android)

Patrick Edge
2 min readMar 25, 2021

--

Recently we have been dealing with sending a message authentication hash in our request to server on both Android and iOS. First of all why would you wanna use a HMAC instead of just a standard SHA 512 or SHA 256? I'll leave that explanation do Dr. Mike Pound:

https://www.youtube.com/watch?v=wlSG3pEiQdc

We had a share of problems computing the same hash on both platforms with the same input. The rule is you want to hash the same message with the same key on both platforms and have the same result. As there are some differences between the platforms, the hash would be always different. Here I'm going to provide you with an working example how to implement HMAC hash on both platforms:

First we need a shared key that is only known to the iOS app, Android app and the server:

Swift:

let key = "f8jdsd5fhk9d1r5jkx1sh7d"

Java:

String key = “f8jdsd5fhk9d1r5jkx1sh7d”;

And we need a message to hash.

Swift:

let message = "Send Patrick 1000€"

Java:

String message = "Send Patrick 1000€";

Lets get hashin'. Our hashing function of choice will be SHA 512 and we’re gonna convert our computed hash that is represented in array of bytes / array of UInt8 to base64 String.

For Swift we have couple of options. We can either write the hash function ourselves, or we can use a library like CryptoSwift (https://github.com/krzyzanowskim/CryptoSwift). The native CryptoKit unfortunately does not know how to compute keyed hash just yet, the app would also work only on iOS 13 and up which might pose a problem if you aim to support lower iOS versions.

Swift (CryptoSwift method):

let key = "f8jdsd5fhk9d1r5jkx1sh7d"
let message = "Send Patrick 1000€"
do {let bytes = try CryptoSwift.HMAC(key: key, variant: .sha512).authenticate(mesaage.bytes)let data = Data(bytes)let final = data.base64EncodedString(options: Data.Base64EncodingOptions(rawValue: 0))compareHash(hash: final, name: "CryptoSwift")} catch {print("Hash computation failed")}

Swift (our implementation):

func hmac(key: String) -> String {var digest = [UInt8](repeating: 0, count: Int(CC_SHA512_DIGEST_LENGTH))CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA512), key, key.count, self, self.count, &digest)let data = Data(digest)return data.base64EncodedString(options: Data.Base64EncodingOptions(rawValue: 0))}//Usage:
let key = "f8jdsd5fhk9d1r5jkx1sh7d"
let hash = mesaage.hmac(key: key)

For Java we're just gonna use the Mac (Message Authentication Code) class. If your back end application is written in java as well your work is already done because you can reuse the implementation there as well.

Java:

String secret = “f8jdsd5fhk9d1r5jkx1sh7d”;
String message = "Send Patrick 1000€";
Mac sha_HMAC = Mac.getInstance(“HmacSHA512");
SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes(), “HmacSHA512”);
sha_HMAC.init(secret_key);
byte[] bytes = sha_HMAC.doFinal(message.getBytes());
String thash= Base64.getEncoder().encodeToString(bytes)

And we're done! Now you should get the same hash computed for the same input on both iOS and Android.

--

--