3

For a new project I need to hash a NSString with SHA256. I have used the following code:

unsigned char hashedChars[32];
NSString *inputString;
inputString = [NSString stringWithFormat:@"hello"];
NSData * inputData = [inputString dataUsingEncoding:NSUTF8StringEncoding];
CC_SHA256(inputData.bytes, inputData.length, hashedChars);

I found this piece of code on stackoverflow. I do not really get all the things this code do here are some questions about the code:

1.The CC_SHA256 makes a hash but this hash will be stored in inputData again? What I mean can I do something like this:

NSString *string=CC_SHA256(..) //of course you can't put it directly in a NSString, but you get the point

2.In the end the hash has to be a hexadecimal string, but what is the type that CC_SHA256 outputs (UTF-8??)?

3.The first parameter of CC_SHA256 why do I have to put bytes at the end and is "inputData" enough?

4.What is the need of the length of the string (second parameter)?

5.And the last parameter does not make any sense to me, can somebody please explain and why the hashedChars has to be 32?

rmaddy
  • 314,917
  • 42
  • 532
  • 579

2 Answers2

7

The argument list for CC_SHA256 is:

 extern unsigned char *CC_SHA256(const void *data, CC_LONG len, unsigned char *md);

From the man page: https://developer.apple.com/library/ios/documentation/System/Conceptual/ManPages_iPhoneOS/man3/CC_SHA256.3cc.html

Parameters explained:

  • *data is the input string, what you want to be hashed. It's a C string-type. A way to get this is to call 'inputData.bytes', with inputData a NSData object.
  • len is the length of the input string. As you'll realize if you'll start working with C strings, it's pretty normal for functions working with strings to ask for the length. That's because in C strings are just a sequence of bytes, and while text strings are generally terminated by a null byte, binary strings can have any length. It's also for safety ("buffer overflows").
  • *md is the output. Again, this is returned as a C string, of fixed length 32 bytes for SHA256 (that's why you don't see an outputLength parameter).
  • The output is "not relevant", but can be used to check if the function ran properly: if(CC_SHA256(...)) { all ok; }

The result string is stored into *md, and it's a binary C string, 32 bytes long. It's 32 bytes long because that's the length of SHA256 digests; for example, 16 bytes for MD5, 20 bytes for SHA1, etc. It's just how the algorithm works!
The output is just a binary string. If you want to make it into hex format you need to store it into a NSData object, and then get a hex representation of it:

NSData *resultData = [NSData dataWithBytes:hashedChars length:32];

To get the hex representation then look at this SO answer: https://stackoverflow.com/a/25378464/192024

Community
  • 1
  • 1
ItalyPaleAle
  • 7,185
  • 6
  • 42
  • 69
  • I'm not sure your last point about the output being "not relevant" is correct. From `man CC_SHA256` on macOS Sierra: "`All routines return 1 except for the one-shot routines ( CC_SHA1(), etc.), which return the pointer passed in via the md parameter.`. This implies the return value is the pointer passed in as `md`, not any kind of success-indicating value? (the man page you link above is now dead, thanks Apple.) – Robert Atkins May 04 '17 at 12:45
  • 1
    Better to use `CC_SHA256_DIGEST_LENGTH` than the magic number 32. – alfwatt Mar 07 '18 at 23:47
  • @alfwatt `#define CC_SHA256_DIGEST_LENGTH 32` from the header: https://opensource.apple.com/source/CommonCrypto/CommonCrypto-36064/CommonCrypto/CommonDigest.h it really should make no difference, it's not like we can change the length of SHA256 :) – ItalyPaleAle Mar 20 '18 at 18:36
  • 1
    @Qualcuno it's not going to change but the original question wanted to know *why* it was 32, using the constant helps explain that it's an expected length – alfwatt Mar 21 '18 at 21:55
3

If anyone trying to find a similar function for Android, the below snippet produces the same output as CC_SHA256

  public static String calculateSH256(String secret){
        final MessageDigest digest;
        try {
            digest = MessageDigest.getInstance("SHA-256");
            byte[] bytes = secret.getBytes("UTF-8");
            digest.update(bytes, 0, bytes.length);
            String sig = bytesToHex(digest.digest());
            return sig;
        }
        catch (NoSuchAlgorithmException | UnsupportedEncodingException e){
         throw new RuntimeException("Cannot calculate signature");  
        }          
    }


    final protected static char[] hexArray = "0123456789abcdef".toCharArray();

    private static String bytesToHex(byte[] bytes) {
        char[] hexChars = new char[bytes.length * 2];
        for ( int j = 0; j < bytes.length; j++ ) {
            int v = bytes[j] & 0xFF;
            hexChars[j * 2] = hexArray[v >>> 4];
            hexChars[j * 2 + 1] = hexArray[v & 0x0F];
        }
        return new String(hexChars);
    }
scottyab
  • 23,621
  • 16
  • 94
  • 105