哈希 HASH·数字签名

HASH

现在在处理App的用户敏感信息方面,大部分都是采用HASH加密的方式来进行处理。

关于哈希算法 详细看这篇 转载:哈希算法详解(附带iOS开发中实际应用)
概念

  • Hash,一般翻译做散列,也有直接音译为哈希的.
    就是把任意长度的输入通过散列算法变换成固定长度的输出,该输出就是散列值
  • 这种转换是一种压缩映射,也就是散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,所以不可能从散列值来确定唯一的输入值
    简单的说,就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数
  • 哈希破解(散列碰撞):不同的N多种数据哈希之后得到相同的结果
  • 单向散列函数:又称单向Hash函数、杂凑函数,就是把任意长的输入消息串变化成固定长的输出串且由输出串难以得到输入串的一种函数。这个输出串称为该消息的散列值。一般用于产生消息摘要密钥加密等.

特点

  • 算法是公开的
  • 对相同数据运算,得到的结果是一样的
  • 对不同数据运算,如MD5得到的结果默认是128位,32个字符(16进制标识)。
  • 没法进行逆运算
  • 信息摘要,信息“指纹”,是用来做数据识别的。
  • 加密后密文的长度是定长的

用途

  • 用户密码的加密
  • 搜索引擎,关键字识别(搜索多个关键字,先对每个关键字进行散列,然后多个关键字进行或运算,如果值一致则搜索结果一致)
  • 版权标注 对文件进行散列判断该文件是否是正版或原版的
  • 数字签名 (文件完整性验证 对整个文件进行散列,比较散列值判断文件是否完整或被篡改)

常见的哈希加密算法

MD5,HMAC,SHA1,SHA256

MD5

MD5Message - Digest Algorithm 5(信息-摘要算法5),用于确保信息传输完整一致。是计算机广泛使用的杂凑算法之一(又译摘要算法、哈希算法),主流编程语言普遍已有MD5实现。将数据(如汉字)运算为另一固定长度值,是杂凑算法的基础原理,MD5的前身有MD2MD3MD4
-----百度百科

  • 从一段字符串中通过相应特征生成一段32位的数字字母混合码。对输入信息生成唯一128位散列值(32个16进制的数字)
  • 所有的数据(视频音频文件只要存在于硬盘或内存中的)都是可以被MD5加密的,得到的都是32个字符。

特征

  • 压缩性任意长度的数据,算出的MD5值的长度都是固定的。
  • 容易计算:从原数据计算出MD5值很容易
  • 抗修改性:对原数据进行任何改动,哪怕只修改一个字节,所得到的MD5值都有很大区别
  • 弱抗碰撞:已知原数据和其MD5值,想找到一个具有相同MD5值的数据(即伪造数据)是非常困难的
  • 强抗碰撞:想找到两个不同数据,使他们具有相同的MD5值,是非常困难的。

使用第三方框架:NSString+Hash

#pragma mark – MD5 加密
//String转化为md5加密String
+ (NSString *)md5:(NSString *)str
{
const char *cStr = [str UTF8String];
unsigned char result[16];
CC_MD5(cStr, strlen(cStr), result); // This is the md5 call
return [NSString stringWithFormat:
@”%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x”,
result[0], result[1], result[2], result[3],
result[4], result[5], result[6], result[7],
result[8], result[9], result[10], result[11],
result[12], result[13], result[14], result[15]
];
}

应用

  • 一致性验证
    md5典型应用是对一段信息(Message)产生信息摘要(Message-Digest),以防止被篡改。我们常常在某些软件下载站点的某软件信息中看到其MD5值,它的作用就在于我们可以在下载该软件后,对下载回来的文件用专门的软件(如Windows MD5 Check等)做一次MD5校验,以确保我们获得的文件与该站点提供的文件为同一文件
    具体来说文件的MD5值就像是这个文件的“数字指纹”。每个文件的MD5值是不同的,如果任何人对文件做了任何改动,其MD5值也就是对应的“数字指纹”就会发生变化。比如下载服务器针对一个文件预先提供一个MD5值,用户下载完该文件后,用我这个算法重新计算下载文件的MD5值,通过比较这两个值是否相同,就能判断下载的文件是否出错,或者说下载的文件是否被篡改了。
    利用MD5算法来进行文件校验的方案被大量应用到软件下载站论坛数据库系统文件安全等方面。
  • 数字签名
    MD5的典型应用是对一段Message(字节串)产生fingerprint(指纹),以防止被“篡改”。举个例子,你将一段话写在一个叫 readme.txt文件中,并对这个readme.txt产生一个MD5的值并记录在案,然后你可以传播这个文件给别人,别人如果修改了文件中的任何内容,你对这个文件重新计算MD5时就会发现(两个MD5值不相同)。如果再有一个第三方的认证机构,用MD5还可以防止文件作者的“抵赖”,这就是所谓的数字签名应用。
  • 安全访问认证
    MD5还广泛用于操作系统的登陆认证上,如Unix、各类BSD系统登录密码、数字签名等诸多方面。

MD5认证图解


这里还要注意:

  • 一定要和后台开发人员约定好,MD5加密的位数是16位还是32位(大多数都是32位的),16位的可以通过32位的转换得到。
  • MD5加密区分 大小写,使用时要和后台约定好。
    MD5是不可逆转和破解的,但是,网上还是有很多暴力破解工具,举一个例子,这里就有个网站

    对比输出结果,可见是正确的,这里暴力破解并不是算法上的破解,而是将很多组合的MD5存储,到时候直接查询和尝试获得的结果。
    MD5加密进阶
    1)先加盐,然后再进行MD5
    2)先乱序,再进行MD5加密
    3)乱序|加盐,多次MD5加密等
    4)使用消息认证机制,即HMAC-MD5-先对密钥进行加密,加密之后进行两次MD5散列
    5)加密命令行
 MD5加密-字符串    $ echo -n "520it" |md5
  MD5加密-文件1     $ md5 abc.png
  SHA1加密:        $ echo -n "520it" |openssl sha -sha1
  SHA256            $ echo -n "520it" |openssl sha -sha256
  SHA512            $ echo -n "520it" |openssl sha -sha512
  hmacMD5加密       $ echo -n "520it" |openssl dgst -md5 -hmac "123"
//先加密,后乱序

- (void)MD5Reorder {

    NSString *pwd = @"123456789";

    NSString *pwdMD5 = [pwd md5String];

    NSLog(@"oldpwdMD5=%@",pwdMD5);

    NSString *prefix = [pwdMD5 substringFromIndex:3]; //从下标为3的开始截取(包含3)

    NSString *subfix = [pwdMD5 substringToIndex:3];   //截取0到3的字符串(不包含3)

    pwdMD5 = [prefix stringByAppendingString:subfix];

    NSLog(@"newpwdMD5=%@",pwdMD5);

}
 // md5加密调用
 NSLog(@"%@",[@"520it" md5String]);
 // (明文+加盐)MD5加密调用
 NSLog(@"%@",[[@"520it" stringByAppendingString:salt] md5String]);
 // hmacMD5加密调用(先加密+乱序)
 NSLog(@"%@",[@"520it" hmacMD5StringWithKey:@"xiaomage"]);

传统方法是加盐(Salt):
在明文的固定位置插入位数足够多、足够复杂的随机串,然后再进行MD5。

NSString *salt = @"fdsfjf)*&*JRhuhew7HUH^&udn&&86&*";

NSString *str = @"123456";    // md5 加密

str = [str stringByAppendingString:salt];

str = [str md5String];

NSLog(@"%@",str);

缺点:盐是固定不变的,一旦泄露后果不堪设想;使用加盐通过MD5解密之后,很容易发现规律从而破解。

image
解决方案
HMAC :使用一个密钥加密并且两次的散列!

消息认证机制(HMAC)简单说明
1)原理
①消息的发送者和接收者有一个共享密钥
②发送者使用共享密钥对消息加密计算得到MAC值(消息认证码)
③消息接收者使用共享密钥对消息加密计算得到MAC值
④比较两个MAC值是否一致
2)使用
①客户端需要在发送的时候把(消息)+(消息·HMAC)一起发送给服务器
②服务器接收到数据后,对拿到的消息用共享的KEY进行HMAC,比较是否一致,如果一致则信任

/** HMAC
     *  使用一个密钥加密,并且做两次散列!
     *  在实际开发中,密钥(KEY)来自于服务器(动态的)!
     *  一个账号,对应一个KEY,而且还可以跟新!
     */
    NSString*serverKey =@"chh";
    pwd = [pwd hmacMD5StringWithKey:serverKey];

HMAC优化

     /** HMAC优化
      * 由于直接一直使用HMAC的话,容易被逆向获取HMAC,
      * 所以考虑在传输HMAC的前向服务器请求时间戳(忽略秒),
      * 然后在HMAC基础上加上服务器的时间戳并进行md5加密后进行传输 
      * 服务器存储的是HMAC(密码+KEY)的值和KEY,key用于返给客户端,校验的时候只需拿HMAC这个值再加上时间戳进行MD5进行比较
     */
    pwd = [[pwd hmacMD5StringWithKey:serverKey] stringByAppendingString:@"服务器时间戳"].md5String;

简单示例

#pragma mark - md5加密方法
 - (NSString *)md5String {
     const char *str = self.UTF8String;
     uint8_t buffer[CC_MD5_DIGEST_LENGTH];
     CC_MD5(str, (CC_LONG)strlen(str), buffer);
     return [self stringFromBytes:buffer length:CC_MD5_DIGEST_LENGTH];
 }
 #pragma mark - HMACMD5加密方法
 - (NSString *)hmacMD5StringWithKey:(NSString *)key {
     const char *keyData = key.UTF8String;
     const char *strData = self.UTF8String;
     uint8_t buffer[CC_MD5_DIGEST_LENGTH];
     CCHmac(kCCHmacAlgMD5, keyData, strlen(keyData), strData, strlen(strData), buffer);
     return [self stringFromBytes:buffer length:CC_MD5_DIGEST_LENGTH];
 }
  
 /**
  *  返回二进制 Bytes 流的字符串表示形式
  *  @param bytes  二进制 Bytes 数组
  *  @param length 数组长度
  *  @return 字符串表示形式
  */
 - (NSString *)stringFromBytes:(uint8_t *)bytes length:(int)length {
     NSMutableString *strM = [NSMutableString string];
     for (int i = 0; i < length; i++) {
         [strM appendFormat:@"%02x", bytes[i]];
     }
     return [strM copy];
 }

以用户账号登录注册为例
注册:
1.客户端向服务器发起注册请求,这时服务器生成一个密钥key(并保存),然后将密钥返回给客户端。
2.客户端根据密钥对密码进行HMAC加密生成密文,传给服务器,服务器保存(不允许明文保存用户的隐私信息)。
3.服务器返回注册成功后,客户端将密钥保存到手机钥匙串(减少密钥传递次数,提高安全性)。

登录:
1.客户端读取手机钥匙串中的密钥,对密码进行HMAC加密后,向服务器发出登录请求。
2.服务器根据账号读取数据库中的密文与客户端提交的进行比较。
上代码:

- (NSString *)hmacMD5StringWithKey:(NSString *)key {
    const char *keyData = key.UTF8String;
    const char *strData = self.UTF8String;
    uint8_t buffer[CC_MD5_DIGEST_LENGTH];
    CCHmac(kCCHmacAlgMD5, keyData, strlen(keyData), strData, strlen(strData), buffer)
    return [self stringFromBytes:buffer length:CC_MD5_DIGEST_LENGTH];
}

引申:可以利用这个做出类型于设备锁或QQ手机令牌的功能,目的就是为了在他人设备上无法登录自己的账号。
这里说下大致的流程,在客户端发出登录请求时,在手机钥匙串读取密钥,当这个密钥不存在的时,就说明是在他人设备上操作的,这时可以利用推送通知或短信告诉自己,是否允许他人设备登录,只允许此次登录还是授权永久登录。如果仅登陆一次则不保存密钥到钥匙串,下次登录重复以上操作;如果授权永久登录可以再他人设备保存密钥。

那么这样就安全了吗?too young!!!
如果在第2步中,拦截post内容获取加密后的密文,再模拟客户端向服务器发出请求,就成功黑了!

再次提高安全性:添加时间戳

第一步:客户端将HMAC得到的数据拼接当前服务器时间,再用MD5加密。
(HMAC+201802271633).md5String,将加密后的数据post到服务器。
:201802271633 须为当前服务器时间,避免客户端时间不统一。

第二步:服务器开始验证:根据账号在数据库中读取HMAC,将当前服务器时间拼接后,再用MD5加密,得到32位字符与客户端提交的32位字符进行比较。这里会有两种情况,1.服务器拼接的时间与客户端拼接的时间一致,得到相同的密文,则通过验证;2.服务器拼接的时间与客户端拼接的时间不一致,得到的密文不相同。这是服务器就拼接上一个时间,即:客服端密文是:(HMAC+201802271633).md5String,服务器第一次拼接时间密文是:(HMAC+201802271634).md5String。两者不一致,服务器第二次拼接上一个时间段密文:(HMAC+201802271633).md5String,一致则通过。

进行这样的操作就是让客户端发出的请求的时间有效性只维持1分钟,如果还觉得不安全可以再拼接时间的时候添加秒,进一步缩短时间的有效性,以提高安全性。

SHA

这篇写的很全面 转载:https://www.jianshu.com/p/df04fff20c2b

SHA即Secure Hash Algorithm(安全哈希算法),主要适用于数字签名标准(Digital Signature Standard DSS)里面定义的数字签名算法(Digital Signature Algorithm DSA)。对于长度小于2^64位的消息,SHA1会产生一个160位的消息摘要

终端查看SHA函数散列值

  • 字符串HSA1值
echo -n "string" | openssl sha -sha1
或
echo -n "string" | openssl sha -sha256
或
echo -n "string" | openssl sha -sha512
  • 文件SHA1值
openssl sha -sha1 file.dat(文件路径)
或
openssl sha -sha256 file.dat
或
openssl sha -sha512 file.dat

代码

- (NSString *)sha1String {
    const char *str = self.UTF8String;
    // 修改参数,变换散列计算SHA1、SHA256、SHA512
    // uint8_t buffer[CC_SHA256_DIGEST_LENGTH];
    // CC_SHA256(str, (CC_LONG)strlen(str), buffer);
    uint8_t buffer[CC_SHA1_DIGEST_LENGTH];
    CC_SHA1(str, (CC_LONG)strlen(str), buffer);
    return [self stringFromBytes:buffer length:CC_SHA1_DIGEST_LENGTH];
}
- (NSString *)stringFromBytes:(uint8_t *)bytes length:(int)length {
    NSMutableString *strM = [NSMutableString string];
    for (int i = 0; i < length; i++) {
        [strM appendFormat:@"%02x", bytes[i]];
    }
    return [strM copy];
}

Hmac-SHA256加密算法

/**
 Hmac-SHA256加密算法
 */
+ (NSData *)hmacSha256:(NSString *)hashString hmacKey:(NSString *)key {
    NSData *hashData = [hashString dataUsingEncoding:NSUTF8StringEncoding];
    
    unsigned char *digest;
    digest = malloc(CC_SHA256_DIGEST_LENGTH);
    const char *cKey = [key cStringUsingEncoding:NSUTF8StringEncoding];
    
    CCHmac(kCCHmacAlgSHA256, cKey, strlen(cKey), [hashData bytes], [hashData length], digest);
    
    NSData *data = [NSData dataWithBytes:digest length:CC_SHA256_DIGEST_LENGTH];
    
    free(digest);
    cKey = nil;
    
    return data;
}

SHA512加密

//SHA512加密
+ (NSString) getSHA512String:(NSString)s
{
const char cstr = [s cStringUsingEncoding:NSUTF8StringEncoding];
NSData *data = [NSData dataWithBytes:cstr length:s.length];
uint8_t digest[CC_SHA512_DIGEST_LENGTH];
CC_SHA512(data.bytes, data.length, digest);
NSMutableString output = [NSMutableString stringWithCapacity:CC_SHA512_DIGEST_LENGTH * 2];
for(int i = 0; i < CC_SHA512_DIGEST_LENGTH; i++)
[output appendFormat:@”%02x”, digest[i]];
return output;
}

数字签名

数字签名本质上是通过HASH算法和RSA加密来实现的。

HASH算法专门用来做文件数据的识别.那么在网络数据传递的过程中,我们可以将明文数据,和数据的HASH值一起传递给对方.对方可以拿出HASH值来进行验证。

但是在这个过程中,如何做到数据的保护呢?明文数据和HASH值如果直接传递就有都被篡改的风险.所以这里我们要对数据进行加密。明文数据有时会比较大,不适合使用RSA非对称加密算法,那么数据的HASH值是比较小的。这个数据是用于校验的,它完全可以使用RSA来加密。所以在数据传递的时候,我们将明文数据加上通过RSA加密的校验数据一并传递给对方。那么这个通过RSA加密的校验数据,我们称之为签名.

image

验证数字签名

当对方拿到数据之后,如何进行验证呢?

首先传递数据时会将原始的数据和数字签名一起发送

对方拿到数据后,先进行校验,拿到原始数据,通过同样的HASH算法得到数据的HASH值。

然后通过非对称加密,将数字签名中的校验HASH值解密出来。

最后对比两个HASH值是否一致,这样可以很好的判断数据是否被篡改。

image

MD5算法原理

对MD5算法简要的叙述可以为:
MD5512位分组来处理输入的信息,且每一分组又被划分为1632位子分组,经过了一系列的处理后,算法的输出由432位分组组成,将这432位分组级联后将生成一个128位散列值。
总体的流程图如下所示,
表示第i个分组,每次的运算都由前一轮的128位结果值和第i块512bit值进行运算。

流程图

第一步· 填充
MD5算法中,首先需要对信息进行填充,使其位长对512求余的结果等于448,并且填充必须进行,即使其位长对512求余的结果等于448。因此,信息的位长(Bits Length)将被扩展至N*512+448,N为一个非负整数,N可以是零。

填充的方法如下:

  • 在信息的后面填充一个1无数个0,直到满足上面的条件时才停止用0对信息的填充。
  • 在这个结果后面附加一个以64位二进制表示的填充前信息长度(单位为Bit),如果二进制表示的填充前信息长度超过64位,则取低64位
  • 经过这两步的处理,信息的位长 = N512 + 448 + 64= (N+1)512,即长度恰好是512的整数倍。这样做的原因是为满足后面处理中对信息长度的要求

第二步· 初始化变量
初始的128位值为初试链接变量,这些参数用于第一轮的运算,以大端字节序来表示,他们分别为:A = 0x01234567,B = 0x89ABCDEF,C = 0xFEDCBA98,D = 0x76543210。(每一个变量给出的数值是高字节存于内存低地址,低字节存于内存高地址,即大端字节序。在程序中变量A、B、C、D的值分别为0x67452301,0xEFCDAB89,0x98BADCFE,0x10325476

第三步· 处理分组数据
每一分组的算法流程如下:
第一分组需要将上面四个链接变量复制到另外四个变量中:A到a,B到b,C到c,D到d。
从第二分组开始的变量为上一分组的运算结果,即A = a, B = b, C = c, D = d。
主循环有四轮(MD4只有三轮),每轮循环都很相似。第一轮进行16次操作。每次操作对a、b、c和d中的其中三个作一次非线性函数运算,然后将所得结果加上第四个变量,文本的一个子分组和一个常数。再将所得结果向左环移一个不定的数,并加上a、b、c或d中之一。最后用该结果取代a、b、c或d中之一。
以下是每次操作中用到的四个非线性函数(每轮一个)。
F( X ,Y ,Z ) = ( X & Y ) | ( (~X) & Z )
G( X ,Y ,Z ) = ( X & Z ) | ( Y & (~Z) )
H( X ,Y ,Z ) =X ^ Y ^ Z
I( X ,Y ,Z ) =Y ^ ( X | (~Z) )

(&是与(And),|是或(Or),~是非(Not),^是异或(Xor))

这四个函数的说明:如果X、Y和Z的对应位是独立均匀的,那么结果的每一位也应是独立和均匀的。

F是一个逐位运算的函数。即,如果X,那么Y,否则Z。函数H是逐位奇偶操作符。假设Mj表示消息的第j个子分组(从0到15),常数ti是4294967296*abs( sin(i) )的整数部分,i 取值从1到64,单位是弧度。(4294967296=232)
现定义:
FF(a ,b ,c ,d ,Mj ,s ,ti ) 操作为a = b + ( (a + F(b,c,d) + Mj + ti) << s)
GG(a ,b ,c ,d ,Mj ,s ,ti )操作为a = b + ( (a + G(b,c,d) + Mj + ti) << s)
HH(a ,b ,c ,d ,Mj ,s ,ti)操作为a = b + ( (a + H(b,c,d) + Mj + ti) << s)
II(a ,b ,c ,d ,Mj ,s ,ti)操作为a = b + ( (a + I(b,c,d) + Mj + ti) << s)

注意:“<<”表示循环左移位,不是左移位。

这四轮(共64步)是:
第一轮

FF(a ,b ,c ,d ,M0 ,7 ,0xd76aa478 )
FF(d ,a ,b ,c ,M1 ,12 ,0xe8c7b756 )
FF(c ,d ,a ,b ,M2 ,17 ,0x242070db )
FF(b ,c ,d ,a ,M3 ,22 ,0xc1bdceee )
FF(a ,b ,c ,d ,M4 ,7 ,0xf57c0faf )
FF(d ,a ,b ,c ,M5 ,12 ,0x4787c62a )
FF(c ,d ,a ,b ,M6 ,17 ,0xa8304613 )
FF(b ,c ,d ,a ,M7 ,22 ,0xfd469501)
FF(a ,b ,c ,d ,M8 ,7 ,0x698098d8 )
FF(d ,a ,b ,c ,M9 ,12 ,0x8b44f7af )
FF(c ,d ,a ,b ,M10 ,17 ,0xffff5bb1 )
FF(b ,c ,d ,a ,M11 ,22 ,0x895cd7be )
FF(a ,b ,c ,d ,M12 ,7 ,0x6b901122 )
FF(d ,a ,b ,c ,M13 ,12 ,0xfd987193 )
FF(c ,d ,a ,b ,M14 ,17 ,0xa679438e )
FF(b ,c ,d ,a ,M15 ,22 ,0x49b40821 )

第二轮

GG(a ,b ,c ,d ,M1 ,5 ,0xf61e2562 )
GG(d ,a ,b ,c ,M6 ,9 ,0xc040b340 )
GG(c ,d ,a ,b ,M11 ,14 ,0x265e5a51 )
GG(b ,c ,d ,a ,M0 ,20 ,0xe9b6c7aa )
GG(a ,b ,c ,d ,M5 ,5 ,0xd62f105d )
GG(d ,a ,b ,c ,M10 ,9 ,0x02441453 )
GG(c ,d ,a ,b ,M15 ,14 ,0xd8a1e681 )
GG(b ,c ,d ,a ,M4 ,20 ,0xe7d3fbc8 )
GG(a ,b ,c ,d ,M9 ,5 ,0x21e1cde6 )
GG(d ,a ,b ,c ,M14 ,9 ,0xc33707d6 )
GG(c ,d ,a ,b ,M3 ,14 ,0xf4d50d87 )
GG(b ,c ,d ,a ,M8 ,20 ,0x455a14ed )
GG(a ,b ,c ,d ,M13 ,5 ,0xa9e3e905 )
GG(d ,a ,b ,c ,M2 ,9 ,0xfcefa3f8 )
GG(c ,d ,a ,b ,M7 ,14 ,0x676f02d9 )
GG(b ,c ,d ,a ,M12 ,20 ,0x8d2a4c8a )

第三轮

HH(a ,b ,c ,d ,M5 ,4 ,0xfffa3942 )
HH(d ,a ,b ,c ,M8 ,11 ,0x8771f681 )
HH(c ,d ,a ,b ,M11 ,16 ,0x6d9d6122 )
HH(b ,c ,d ,a ,M14 ,23 ,0xfde5380c )
HH(a ,b ,c ,d ,M1 ,4 ,0xa4beea44 )
HH(d ,a ,b ,c ,M4 ,11 ,0x4bdecfa9 )
HH(c ,d ,a ,b ,M7 ,16 ,0xf6bb4b60 )
HH(b ,c ,d ,a ,M10 ,23 ,0xbebfbc70 )
HH(a ,b ,c ,d ,M13 ,4 ,0x289b7ec6 )
HH(d ,a ,b ,c ,M0 ,11 ,0xeaa127fa )
HH(c ,d ,a ,b ,M3 ,16 ,0xd4ef3085 )
HH(b ,c ,d ,a ,M6 ,23 ,0x04881d05 )
HH(a ,b ,c ,d ,M9 ,4 ,0xd9d4d039 )
HH(d ,a ,b ,c ,M12 ,11 ,0xe6db99e5 )
HH(c ,d ,a ,b ,M15 ,16 ,0x1fa27cf8 )
HH(b ,c ,d ,a ,M2 ,23 ,0xc4ac5665 )

第四轮

II(a ,b ,c ,d ,M0 ,6 ,0xf4292244 )
II(d ,a ,b ,c ,M7 ,10 ,0x432aff97 )
II(c ,d ,a ,b ,M14 ,15 ,0xab9423a7 )
II(b ,c ,d ,a ,M5 ,21 ,0xfc93a039 )
II(a ,b ,c ,d ,M12 ,6 ,0x655b59c3 )
II(d ,a ,b ,c ,M3 ,10 ,0x8f0ccc92 )
II(c ,d ,a ,b ,M10 ,15 ,0xffeff47d )
II(b ,c ,d ,a ,M1 ,21 ,0x85845dd1 )
II(a ,b ,c ,d ,M8 ,6 ,0x6fa87e4f )
II(d ,a ,b ,c ,M15 ,10 ,0xfe2ce6e0 )
II(c ,d ,a ,b ,M6 ,15 ,0xa3014314 )
II(b ,c ,d ,a ,M13 ,21 ,0x4e0811a1 )
II(a ,b ,c ,d ,M4 ,6 ,0xf7537e82 )
II(d ,a ,b ,c ,M11 ,10 ,0xbd3af235 )
II(c ,d ,a ,b ,M2 ,15 ,0x2ad7d2bb )
II(b ,c ,d ,a ,M9 ,21 ,0xeb86d391 )

所有这些完成之后,将a、b、c、d分别在原来基础上再加上A、B、C、D。即 a = a + A,b = b + B,c = c + C,d = d + D,然后用下一分组数据继续运行以上算法。

第四步· 输出
最后的输出是a、b、c和d的级联。当你按照我上面所说的方法实现MD5算法以后,你可以用以下几个信息对你做出来的程序作一个简单的测试,看看程序有没有错误。

MD5 ("") = d41d8cd98f00b204e9800998ecf8427e
MD5 ("a") = 0cc175b9c0f1b6a831c399e269772661
MD5 ("abc") = 900150983cd24fb0d6963f7d28e17f72
MD5 ("message digest") = f96b697d7cb7938d525a2f31aaf161d0
MD5 ("abcdefghijklmnopqrstuvwxyz") = c3fcd3d76192e4007dfb496cca67e13b
MD5 ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz") =
f29939a25efabaef3b87e2cbfe641315
MD5 ("8a683566bcc7801226b3d8b0cf35fd97") =cf2cb5c89c5e5eeebef4a76becddfcfd

MD5一个简单实例

现以字符串“jklmn”为例。

该字符串在内存中表示为:6A 6B 6C 6D 6E(从左到右为低地址到高地址,后同),信息长度为40 bits, 即0x28。
对其填充,填充至448位,即56字节。结果为:

6A 6B 6C 6D 6E 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

剩下64位,即8字节填充填充前信息位长,按小端字节序填充剩下的8字节,结果为:

6A 6B 6C 6D 6E 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 28 00 00 00 00 00 00 00

(64字节,512 bits)
初始化A、B、C、D四个变量。
将这64字节填充后数据分成16个小组(程序中对应为16个数组),即:

M0:6A 6B 6C 6D (这是内存中的顺序,按照小端字节序原则,对应数组M(0)的值为0x6D6C6B6A,下同)
M1:6E 80 00 00
M2:00 00 00 00
.....
M14:28 00 00 00
M15:00 00 00 00

经过3. 分组数据处理后,a、b、c、d值分别为0xD8523F60、0x837E0144、0x517726CA、0x1BB6E5FE
在内存中为:

a:60 3F 52 D8
b:44 01 7E 83
c:CA 26 77 51
d:FE E5 B6 1B

a、b、c、d按内存顺序输出即为最终结果:603F52D844017E83CA267751FEE5B61B。这就是字符串“jklmn”的MD5值。

MD5在ios系统中的应用

下面我们就看一下ios中MD5是如何加密的,下面还是直接看代码。

#import "JJMD5VC.h"
#import <CommonCrypto/CommonDigest.h>

@interface JJMD5VC ()

@end

@implementation JJMD5VC

#pragma mark - Override Base Function

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.view.backgroundColor = [UIColor lightGrayColor];

    [self beginMD5ProcessWithString:@"jklmn"];
}

#pragma mark - Object Private Function

- (void)beginMD5ProcessWithString:(NSString *)string
{
    //传入参数,转化成char
    const char * str = [string UTF8String];

    //开辟一个16字节(128位:md5加密出来就是128位/bit)的空间(一个字节=8字位=8个二进制数)
    unsigned char md[CC_MD5_DIGEST_LENGTH];

     /*
       *extern unsigned char * CC_MD5(const void *data, CC_LONG len, unsigned char *md)官方封装好的加密方法
      *      把str字符串转换成了32位的16进制数列(这个过程不可逆转) 存储到了md这个空间中
      *
      */
     CC_MD5(str, (int)strlen(str), md);

     //创建一个可变字符串收集结果
    NSMutableString * ret = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH];
    for (int i = 0; i < CC_MD5_DIGEST_LENGTH; i++) {

         /**
          X 表示以十六进制形式输入/输出
          02 表示不足两位,前面补0输出;出过两位不影响
          printf("%02X", 0x123); //打印出:123
          printf("%02X", 0x1); //打印出:01

          */
        [ret appendFormat:@"%02X",md[i]];
     }

    NSLog(@"%@",ret);
}

@end

注意要引入头文件

#import <CommonCrypto/CommonDigest.h>

下面看输出结果

2017-08-19 18:08:25.595 JJOC[9099:303472] 603F52D844017E83CA267751FEE5B61B

可见,实现了加密,输出了加密结果。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,717评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,501评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,311评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,417评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,500评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,538评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,557评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,310评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,759评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,065评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,233评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,909评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,548评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,172评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,420评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,103评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,098评论 2 352