Hmac Sha256 in C# VS Hmac Sha256 in Swift Don't Match

HMAC SHA256 in C# vs HMAC SHA256 in Swift don't match

The issue here seems to be a difference in the base64 strings between the given swift solution and then C# solution.

The output of the hash algorithm was the same at a byte level but it was being manipulated through various string conversions before being returned.

The function

private func stringFromResult(result: UnsafeMutablePointer<CUnsignedChar>, length: Int) -> String {
let hash = NSMutableString()
for i in 0..<length {
hash.appendFormat("%02x", result[i])
}
return String(hash).lowercased()
}

Which is part of the solution converts the raw hashed data to a string, which is then converted back to a data object in the calling class, which is then cast into a base64 string to be returned. As we are getting a base64 string of of the string that was returned and not the raw bytes, I think this is where the issue was.

By changing the hmac code from the solution to this, we can cast the raw output of the has to a base64 string and avoid the other steps. This matches the C# value.

func hmac(algorithm: CryptoAlgorithm, key: String) -> String {
let str = self.cString(using: String.Encoding.utf8)
let strLen = Int(self.lengthOfBytes(using: String.Encoding.utf8))
let digestLen = algorithm.digestLength
let result = UnsafeMutablePointer<CUnsignedChar>.allocate(capacity: digestLen)
let keyStr = key.cString(using: String.Encoding.utf8)
let keyLen = Int(key.lengthOfBytes(using: String.Encoding.utf8))

CCHmac(algorithm.HMACAlgorithm, keyStr!, keyLen, str!, strLen, result)
let a = UnsafeMutableBufferPointer(start: result, count: digestLen)
let b = Data(a)
result.deallocate()
let digest = b.base64EncodedString()
return digest
}

HMAC SHA256 in Swift 4

I've been using this:

import Foundation

enum CryptoAlgorithm {
case MD5, SHA1, SHA224, SHA256, SHA384, SHA512

var HMACAlgorithm: CCHmacAlgorithm {
var result: Int = 0
switch self {
case .MD5: result = kCCHmacAlgMD5
case .SHA1: result = kCCHmacAlgSHA1
case .SHA224: result = kCCHmacAlgSHA224
case .SHA256: result = kCCHmacAlgSHA256
case .SHA384: result = kCCHmacAlgSHA384
case .SHA512: result = kCCHmacAlgSHA512
}
return CCHmacAlgorithm(result)
}

var digestLength: Int {
var result: Int32 = 0
switch self {
case .MD5: result = CC_MD5_DIGEST_LENGTH
case .SHA1: result = CC_SHA1_DIGEST_LENGTH
case .SHA224: result = CC_SHA224_DIGEST_LENGTH
case .SHA256: result = CC_SHA256_DIGEST_LENGTH
case .SHA384: result = CC_SHA384_DIGEST_LENGTH
case .SHA512: result = CC_SHA512_DIGEST_LENGTH
}
return Int(result)
}
}

extension String {

func hmac(algorithm: CryptoAlgorithm, key: String) -> String {
let str = self.cString(using: String.Encoding.utf8)
let strLen = Int(self.lengthOfBytes(using: String.Encoding.utf8))
let digestLen = algorithm.digestLength
let result = UnsafeMutablePointer<CUnsignedChar>.allocate(capacity: digestLen)
let keyStr = key.cString(using: String.Encoding.utf8)
let keyLen = Int(key.lengthOfBytes(using: String.Encoding.utf8))

CCHmac(algorithm.HMACAlgorithm, keyStr!, keyLen, str!, strLen, result)

let digest = stringFromResult(result: result, length: digestLen)

result.deallocate(capacity: digestLen)

return digest
}

private func stringFromResult(result: UnsafeMutablePointer<CUnsignedChar>, length: Int) -> String {
let hash = NSMutableString()
for i in 0..<length {
hash.appendFormat("%02x", result[i])
}
return String(hash).lowercased()
}
}

You'll need to add #import <CommonCrypto/CommonHMAC.h> to your Objective-C bridging header.

Source: @thevalyreangroup on this github thread

Issue regarding Hmac SHA256 in different programming languages

There is a difference between SHA-256 and HMAC-SHA-256.
HMAC-SHA-256 (see also HMAC) is an algorithm for generating a MAC using the SHA-256-hash-algorithm.

The result of the first Java-code and the C#-code are different, because the Java-code uses SHA-256 and the C#-code HMAC-SHA-256.

Since you want to use HMAC-SHA-256 I focus on that and I ignore the first Java-code.

The result of the C#-code for the text

This is an arbitrary text!

with the key

1234567890

is

25583e6e0b6c2c3a5c50ebd9ea48138a960a7ca2a215fae2b4b82ee99734deb4

The second Java-code uses HMAC-SHA-256 as well. If you replace the lines

data = bytesToHex(sha256Hmac.doFinal(data.getBytes(StandardCharsets.UTF_8)));
System.out.println(data);

with

data = bytesToHex(sha256Hmac.doFinal(value.getBytes(StandardCharsets.UTF_8)));
return data;

then, the ouput is the same (provided that your (not posted) bytesToHex-method works in a proper way, see e.g. How to convert a byte array to a hex string in Java?).

Btw, in the second Java-code you should change the labeling salt to something like key since that's the common wording in the context of a MAC. A salt is typically used in combination with a password hashing like in your first Java-code.

Note: In the Java-code it's not allowed to use an empty byte-array as SecretKeySpec-input. This throws an IllegalArgumentException (Empty key). However, the HMACSHA256-ctor in the C#-code accepts an empty byte-array and internally pads it with 0-values. Thus, with regard to your testcase (text: abcd, empty key) you can simulate in the Java-code the empty byte-array by a byte-array containing a single 0-value. Then, the output of the Java-code equals the output of the C#-code. Sure, an empty key should only be used for this testcase.

HMAC SHA256 hash computation in C#

Your result is correct, the difference is because the tool you link to does not decode Base64 for the key value and treats it as a series of characters.

E.g. To duplicate its result treat your key as a string:

var shaKeyBytes = System.Text.Encoding.UTF8.GetBytes("AgQGCAoMDhASFAIEBggKDA4QEhQCBAYICgwOEBIUAgQ=");

Which yields

559bd871bfd21ab76ad44513ed5d65774f9954d3232ab68dab1806163f806447

(This is obviously not the right way to do it)

SHA256 in swift

You have to convert explicitly between Int and CC_LONG, because Swift does not
do implicit conversions, as in (Objective-)C.

You also have to define hash as an array of the required size.

func sha256(data : NSData) -> NSData {
var hash = [UInt8](count: Int(CC_SHA256_DIGEST_LENGTH), repeatedValue: 0)
CC_SHA256(data.bytes, CC_LONG(data.length), &hash)
let res = NSData(bytes: hash, length: Int(CC_SHA256_DIGEST_LENGTH))
return res
}

Alternatively, you can use NSMutableData to allocate the needed buffer:

func sha256(data : NSData) -> NSData {
let res = NSMutableData(length: Int(CC_SHA256_DIGEST_LENGTH))
CC_SHA256(data.bytes, CC_LONG(data.length), UnsafeMutablePointer(res.mutableBytes))
return res
}

Update for Swift 3 and 4:

func sha256(data : Data) -> Data {
var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
data.withUnsafeBytes {
_ = CC_SHA256($0, CC_LONG(data.count), &hash)
}
return Data(bytes: hash)
}

Update for Swift 5:

func sha256(data : Data) -> Data {
var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
data.withUnsafeBytes {
_ = CC_SHA256($0.baseAddress, CC_LONG(data.count), &hash)
}
return Data(hash)
}

Need to generate HMAC SHA256 hash in Objective C as in Java

You need to fix your Java hmac printer, because 4effffffd8ffffffce7cffffffc4ffffffc71b2f72ffffffdc21ffffffa1ffffffe0ffffffe62d32550b0771296bffffff9c1159ffffffdeffffff8675ffffff9928654c isn't valid. All those ffffff in there are a giveaway that you are sign-extending the bytes to 32-bit signed integers before converting them to hex. Presumably the correct hmac is 4ed8ce7cc4c71b2f72dc21a1e0e62d32550b0771296b9c1159de86759928654c.

Anyway, I suspect you are calling your method incorrectly. I copied your code into a test program which gives me this output for your key and data:

2011-12-10 13:03:38.231 hmactest[8251:707] test hmac = <4ed8ce7c c4c71b2f 72dc21a1 e0e62d32 550b0771 296b9c11 59de8675 9928654c>

That matches your desired output (except for the sign-extension errors).

Here's my test program:

#import <Foundation/Foundation.h>
#import <CommonCrypto/CommonHMAC.h>

NSData *hmacForKeyAndData(NSString *key, NSString *data)
{
const char *cKey = [key cStringUsingEncoding:NSASCIIStringEncoding];
const char *cData = [data cStringUsingEncoding:NSASCIIStringEncoding];
unsigned char cHMAC[CC_SHA256_DIGEST_LENGTH];
CCHmac(kCCHmacAlgSHA256, cKey, strlen(cKey), cData, strlen(cData), cHMAC);
return [[NSData alloc] initWithBytes:cHMAC length:sizeof(cHMAC)];
}

int main (int argc, const char * argv[])
{
@autoreleasepool {
// Compare to http://en.wikipedia.org/wiki/HMAC#Examples_of_HMAC_.28MD5.2C_SHA1.2C_SHA256_.29
NSLog(@"empty hmac = %@", hmacForKeyAndData(@"", @""));
NSLog(@"test hmac = %@", hmacForKeyAndData(@"YARJSuwP5Oo6/r47LczzWjUx/T8ioAJpUK2YfdI/ZshlTUP8q4ujEVjC0seEUAAtS6YEE1Veghz+IDbNQb+2KQ==", @"id=456|time=19:10|nonce=8"));
}
return 0;
}

HMAC-SHA256 issue in Shopify oauth (Output does not match)

The example is wrong apparently. Your hash code is OK. You'll need to make sure you include all parameters from the Shopify response e.g. the input for verification of a response would look like:

code={code}&protocol=https://&store={store}×tamp={timestamp}

See: https://ecommerce.shopify.com/c/shopify-apis-and-technology/t/you-broke-my-build-hmac-verification-broken-282951

AES256 CBC + HMAC SHA256 ensuring confidentiality *and* authentication?

Basically you are recreating SSL/TLS. This implies the usual caveats about building your own protocol, and you are warmly encouraged to use TLS with an existing library instead of rewriting your own.

That being said, using AES with CBC for encryption, and HMAC for integrity, is sound. There are combined encryption+integrity modes (that you are aware of), and CBC+HMAC is kind of "old school", but it cannot hurt. You are doing things in the "science-approved" way: encrypt, then MAC the encrypted string (and you do not forget the IV: forgetting the IV is the classical mistake).

Your key derivation may be somewhat weak. It is perfect if SHA-256 behaves like a perfect random oracle, but it is known that SHA-256 does not behave like a random oracle (because of the so-called length-extension attack). It is similar to the reason why HMAC is HMAC, with two nested hash function invocations, instead of simple hashing (once) the concatenation of the MAC key and the data. TLS uses a specific key derivation function (which is called "the PRF" in the TLS specification) which should avoid any trouble. That function is built over SHA-256 (actually, over HMAC/SHA-256) and can be implemented around any typical SHA-256 implementation.

(I am not saying that I know how to attack your key derivation process; only that this is a tricky thing to make properly, and that its security may be assessed only after years of scrutiny from hundreds of cryptographers. Which is why reusing functions and protocols which have already been thoroughly examined is basically a good idea.)

In TLS there are two nonces, called the "client random" and the "server random". In your proposal you only have the "client random". What you lose here, security-wise, is kind of unclear. A cautious strategy would be to include a server random (i.e. another nonce chosen by Bob). The kind of things we want to avoid is when Alice and Bob run the protocol in both directions, and an attacker feeds messages from Alice to Alice herself. Complete analysis of what an attacker could do is complex (it is a whole branch of cryptography); generally speaking, nonces in both directions tend to avoid some issues.

If you send several packets, then you may have some issues about lost packets, duplicated packets ("replay attacks"), and packets arriving out of order. In the context of TLS, this should not "normally" happen because TLS is used over a medium which already ensures (under normal conditions, not counting active attacks) that data is transferred in strict order. Thus, TLS includes a sequence number into the data which goes in the MAC. This would detect any alteration from an attacker, include replay, lost records and record reordering. If possible, you should also use a sequence number.

Equivalent Hashing in C# and Objective-C using HMAC256

Note the second line of code in the objective-c portion of the question.

NSString *nonceTest = [zeroNumber base64String]; 

but it should be this:

NSString *nonceTest = [[NSString alloc] initWithData:zeroNumber encoding:NSASCIIStringEncoding];

It was a case of converting the string to base64 when we didn't need to for the hmac seeeding.

We now get: Q1KybjP+DXaaiSKmuikAQQnwFojiasyebLNH5aWvxNo= as the hash on both platforms.



Related Topics



Leave a reply



Submit