Calculating the hash of a row string in iOS

Let's look at a very simple task with you - calculating the hash sum of a string. The problem is ubiquitous, it is worth recalling at least user authentication through OAuth. We will consider the solution of the problem as part of the development of applications for iOS. Below, I will try to show the most beautiful (in my opinion) solution of the problem from the point of view of software code architecture.


So, we obtain the following conditions of the problem:
  • We are given some string as an instance NSString;
  • It is necessary to calculate the value of its hash sum also in the form of some string NSString;
  • We must try to make the selected solution the most compact and beautiful;


Actually calculating the amount

So let us be given some string NSString* string = @”Trololo”. Consider the calculation of its hash sum by the MD5 algorithm. This is done extremely simply:
  • We include the header file CommonCrypto / CommonDigest.h;
  • We get a representation of our string as const char *;
  • Configure the hash output buffer as unsigned char;
  • We calculate the actual value of the hash sum;
  • Finally, we wrap the raw bytes in an instance of NSData;

All this ultimately looks like this:
#import	
…
NSString*	string	=	@”Trololo”;
const char*	data	=	[string UTF8String];
unsigned char	hashBuffer[CC_MD5_DIGEST_LENGTH];
CC_MD5(data, strlen(data), hashBuffer);
NSData*		result	=	[NSData dataWithBytes:hashBuffer length:CC_MD5_DIGEST_LENGTH];

Accordingly, the function CC_MD5(...) and constant are CC_MD5_DIGEST_LENGTHdeclared in the file we connected.

In addition, let's look at a case in which the result of the hash function is processed by the HMAC algorithm. This, like the previous example, is done almost in one line - by calling a function CCHmac(...)that is passed the hash algorithm identifier as one of the parameters, in our case it is kCCHmacAlgMD5. Do not forget to connect in addition one more header file - .

#import 
#import 
NSString*	string	=	@”Trololo”;
NSString*	hmacKey	=	@”hmacKey”;
const char*	data	=	[string 	UTF8String];
const char*	hmacData=	[hmacKey	UTF8String];
unsigned char	hashBuffer[CC_MD5_DIGEST_LENGTH];
CCHmac(kCCHmacAlgMD5, hmacData, strlen(hmacData), data, strlen(data), hashBuffer);
NSData*		result	=	[NSData dataWithBytes:hashBuffer length:CC_MD5_DIGEST_LENGTH];


Data interpretation

Now we need to interpret the result in some way to get the output of the NSString. There are two most common options for this:
  • Getting a string by interpreting each byte as a hexadecimal number;
  • And the interpretation of data in a string through encoding in Base64;

As a hexadecimal string

The first option is very simple and is implemented as follows:
  • We delete the last step of the previous algorithm (we do not use NSData) and work with raw bytes;
  • We create an instance of a mutable string twice as long as the raw bytes received;
  • For each byte from the received hash buffer, add to the string its string representation as a hexadecimal number;

NSString*	string	=	@”Trololo”;
const	char*	data	=	[string	UTF8String];
unsigned char	hashBuffer[CC_MD5_DIGEST_LENGTH];
CC_MD5(data, strlen(data), hashBuffer);
NSMutableString* result	=	[[NSMutableString	alloc]	initWithCapacity:CC_MD5_DIGEST_LENGTH*2];
for (int i= 0;	i < CC_MD5_DIGEST_LENGTH; i++)
	[result	appenFormat:@”%02X”, hashBuffer[i]];


As a Base64 string

This option is somewhat more difficult to do with your own hands. But this, in fact, is not required, there is a fairly good implementation of encoding and decoding data in Base64, which can be found here . This library already has a ready-made static method that, for an instance of NSData, builds an interpretation in the form of Base64 at the output. Then the calculation of the interpretation will look like this:
NSData*		buffer	=	[NSData	dataWithBytes:hashBuffer	length:CC_MD5_DIGEST_LENGTH];
NSString* 	result  =	[Base64	encode:buffer];


Category Design

So, it remains to solve the last problem - how to make the presented code the most compact. Here we can recall one remarkable feature of the Objective-C language - namely, categories. Categories is a mechanism for expanding the functionality of an existing class by adding new methods to it. Moreover, you can expand with them absolutely any class, either your own or any system one. We will write just such a category for the class NSString. Create two files, respectively, header NSString+Hash.hand implementation fileNSString+Hash.m. A category is declared in almost the same way as any class, with the following exceptions: after the class name, the name of the category is indicated in brackets, the block for declaring class members is omitted. Thus, we obtain the following type of header file (all methods for working with the two most common hashing algorithms MD5 and SHA1 are laid down):

#import 
#import	
#import	
#import "Base64.h"
@interface NSString (NSString_NM_HASH)
    //RAW
- (NSData*) MD5;
- (NSData*) SHA1;
- (NSData*) HMAC_MD5:	(NSString*)hmacKey;
- (NSData*) HMAC_SHA1:	(NSString*)hmacKey;
    //INTERPRET Base64
- (NSString*) MD5_x64;
- (NSString*) SHA1_x64;
- (NSString*) HMAC_MD5_x64:(NSString*)hmacKey;
- (NSString*) HMAC_SHA1_x64:(NSString*)hmacKey;
    //INTERPRET HEX
- (NSString*) MD5_HEX;
- (NSString*) SHA1_HEX;
- (NSString*) HMAC_MD5_HEX:(NSString*)hmacKey;
- (NSString*) HMAC_SHA1_HEX:(NSString*)hmacKey;
@end


We will not completely write down the implementation file - it is absolutely the same, but we will show one method from each group for an example of the SHA1 algorithm.

#import “NSString+Hash.h”
@implementation NSString (NSString_NM_HASH)
- (NSData*) HMAC_SHA1: (NSString *)hmacKey{
	const char*	data	= [self		UTF8String];
	const char*	hashKey	= [hmacKey	UTF8String];
	unsigned char	hashingBuffer[CC_SHA1_DIGEST_LENGTH];
	CCHmac(kCCHmacAlgSHA1, hashKey, strlen(hashKey), data, strlen(data), hashingBuffer);
	return	[NSData	dataWithBytes:hashingBuffer length:CC_SHA1_DIGEST_LENGTH];
}
- (NSString*) HMAC_SHA1_x64:(NSString *)hmacKey{
    return [Base64  encode:[self    HMAC_SHA1:hmacKey]];
}
- (NSString*) HMAC_SHA1_HEX:(NSString *)hmacKey{
	const char*	data	= [self		UTF8String];
	const char*	hashKey	= [hmacKey	UTF8String];
	unsigned char	hashingBuffer[CC_SHA1_DIGEST_LENGTH];
	CCHmac(kCCHmacAlgSHA1, hashKey, strlen(hashKey), data, strlen(data), hashingBuffer);
	NSMutableString*    result  =   [[NSMutableString   alloc]  initWithCapacity:CC_SHA1_DIGEST_LENGTH*2];
	for (int i = 0; i   < CC_SHA1_DIGEST_LENGTH; i++)
		[result appendFormat:@"%02X", hashingBuffer[i]];
	return result;
}
@end


Conclusion

As a result, calculating the hash sum of any string can be very fast and compact. For example as follows:

#import “NSString+Hash.h”
NSString*	string		=	@”Trololo”;
NSString*	string_md5	=	[string	MD5_HEX];
NSString*	string_sh1	=	[string	SHA1_x64];

Also popular now: