当前位置: 首页 > news >正文

MD5加密原理解析及OC版原理实现

MD5加密原理解析及OC版原理实现

一、MD5算法基础概念

  MD5算法是Hash算法的一种,叫做讯息摘要演算法。所谓摘要,从字面意思理解,是指内容的大概。在MD5算法中,这个摘要是指将任意数据映射成一个128位长的摘要信息。并且其是不可逆的,即从摘要信息无法反向推演中原文,在演算过程中,原文的内容也是有丢失的。

  因为MD5算法最终生成的是一个128位长的数据,从原理上说,有2^128种可能,这是一个非常巨大的数据,约等于3.4乘10的38次方,虽然这个是个天文数字,但是世界上可以进行加密的数据原则上说是无限的,因此是可能存在不同的内容经过MD5加密后得到同样的摘要信息,但这个碰中的概率非常小。

二、MD5的使用场景

  MD5常用在密码加密中,一般为了保证用户密码的安全,在数据库中存储的都是用户的密码经过MD5加密后的值,在客户端用户输入密码后,也会使用MD5进行加密,这样即使用户的网络被窃听,窃听者依然无法拿到用户的原始密码,并且即使用户数据库被盗,没有存储明文的密码对用户来说也多了一层安全保障。

  MD5签名技术还常用于防止信息的篡改。使用MD5可以对进行进行签名,接收者拿到信息后只要重新计算签名和原始签名进行对比,即可知道数据信息是否中途被篡改了。

三、MD5算法原理

  MD5算法大致分为4步完成:

第1步:进行数据填充整理

  这一步是对要加密的数据进行填充和整理,将要加密的二进制数据对512取模,得到的结果如果不够448位,则进行补足,补足的方式是第1位填充1,后面全部填充0。

第2步:记录数据长度

 经过第一步整理完成后的数据的位数可以表示为N*512+448,再向其后追加64位用来存储数据的长度,比如数据的长度为16字节,则用10000来填充后64位。这一步做完后,数据的位数将变成(N+1)*512。

第3步:以标准的幻数作为输入

MD5的实现需要每512个字节进行一次处理,后一次处理的输入为前一次处理的输出,因此,在循环处理开始之前,需要拿4个标准数作为输入,它们分别是:

unsigned int A=0x67452301,B=0xefcdab89,C=0x98badcfe,D=0x10325476;
第4步:进行N轮循环处理,将最后的结果输出

这一步重要的是每一轮的处理算法,每一轮处理也要循环64次,这64次循环被分为4各组,每16次循环为一组,每组循环使用不同的逻辑处理函数,处理完成后,将输出作为输入进入下一轮循环。

四、MD5核心算法的实现

这里演示的是每轮循环的核心算法:

首先进行3个函数的声明:

//将大端字节序转换为小端字节序
void convertToLittleEndian(unsigned int *data, int len);
//进行循环左移函数
void ROL(unsigned int *s, unsigned short cx);
//MD5加密函数
void MD5(NSString *str);
MD5算法中处理的数据都是小端字节序的,而使用Objective-C处理的NSData对象的字节序是大端字节序,因此我们需要做一下转换。函数实现如下:

void convertToLittleEndian(unsigned int *data, int len)
{

for (int index = 0; index < len; index ++) {
    
    *data = ((*data & 0xff000000) >> 24)
    | ((*data & 0x00ff0000) >>  8)
    | ((*data & 0x0000ff00) <<  8)
    | ((*data & 0x000000ff) << 24);
    
    data ++;
}

}
在MD5中有需要对字节数据进行循环左移的操作,循环左移函数实现如下:

void ROL(unsigned int *s, unsigned short cx)
{

if (cx > 32)cx %= 32;
*s = (*s << cx) | (*s >> (32 - cx));

return;
}
下面是MD5函数的核心实现:

void MD5(NSString *str){

const void * bytes[str.length];
//字符串转字节流
[str getBytes:bytes maxLength:str.length usedLength:nil encoding:NSUTF8StringEncoding options:NSStringEncodingConversionExternalRepresentation range:NSMakeRange(0, str.length) remainingRange:nil];
//使用NSData存储
NSMutableData * data = [[NSMutableData alloc] initWithBytes:bytes length:str.length];
BOOL first = YES;
//进行数据填充
if (data.length<56) {
    do {
        if (first) {
            int byte = 0b10000000;
            [data appendBytes:&byte length:1];
            first = NO;
        }else{
            int byte = 0b00000000;
            [data appendBytes:&byte length:1];
        }
    } while (data.length<56);
}
int length = (int)str.length*8%((int)pow(2, 64));
[data appendBytes:&length length:8];
void * newBytes[64];
memcpy(newBytes, [data bytes], 64);
//大小端转换
convertToLittleEndian(newBytes, 64);
NSData * newData = [NSData dataWithBytes:newBytes length:data.length];
NSMutableArray * subData = [NSMutableArray array];
//进行分组
for (int i = 0; i<16; i++) {
    [subData addObject: [newData subdataWithRange:NSMakeRange(i*4, 4)]];
}
//初始输入
unsigned int A=0x67452301,B=0xefcdab89,C=0x98badcfe,D=0x10325476;
unsigned int a=A,b=B,c=C,d=D;
unsigned int s[64] = { 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 4, 11,16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 6, 10, 15,21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21 };
unsigned int  k[64] = {
    0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
    0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
    0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
    0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
    0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
    0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
    0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
    0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
    0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
    0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
    0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
    0xd9d4d039,  0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
    0xf4292244, 0x432aff97,  0xab9423a7, 0xfc93a039,
    0x655b59c3, 0x8f0ccc92, 0xffeff47d,  0x85845dd1,
    0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
    0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 };

//64次循环处理
for (int i = 0; i <= 64; i++) {
    
    if (i<64) {
        unsigned int f;
        unsigned int g;
        if (i<16) {   
            f = (b&c)|((~b)&d);
            g = i;
        }else if(i<32) {
            f = (b&d)|((~d)&c);
            g = (5*i+1)%16;
        }else if(i<48){
            f = b^c^d;
            g = (3*i+5)%16;
        }else{
            f = c^((~d)|b);
            g = (7*i)%16;
        }
        unsigned int * temp = (unsigned int *) [subData[g] bytes];
        unsigned int  *tem = malloc(sizeof(unsigned int));
        memcpy(tem, temp, 4);
        convertToLittleEndian(tem, 4);
        unsigned int res = (a+f+*tem+k[i]);
        ROL(&res,s[i]);
        unsigned int t = res+b;
        a = d;
        d = c;
        c = b;
        b = t;
       
    }else{
        A = a+A;
        B = b+B;
        C = c+C;
        D = d+D;
    }
}
//大小端转换
unsigned int * newA = malloc(sizeof(unsigned int));
memcpy(newA, &A, 4);
NSLog(@"%0x",*newA);
convertToLittleEndian(newA, 4);
unsigned int * newB = malloc(sizeof(unsigned int));
memcpy(newB, &B, 4);
convertToLittleEndian(newB, 4);
unsigned int * newC = malloc(sizeof(unsigned int));
memcpy(newC, &C, 4);
convertToLittleEndian(newC, 4);
unsigned int * newD = malloc(sizeof(unsigned int));
memcpy(newD, &D, 4);
convertToLittleEndian(newD, 4);
NSLog(@"AAA:%0x %0x %0x %0x ",*newA,*newB,*newC,*newD);

}

相关文章:

  • linux环境安装golang
  • 国际乒联2月世界排名:樊振东、丁宁持续领跑
  • 李嘉诚23岁长孙女登场接班!出任地产公司董事,照片曝光气质获赞
  • Nginx实现动静分离
  • Java基础教程,第四讲,字符串使用以及常用字符串处理函数
  • 树链剖分算法解析
  • 数据库还原失败System.Data.SqlClient.SqlError: 无法执行 BACKUP LOG,因为当前没有数据库备份...
  • 前端技术周刊 2019-02-11 Serverless
  • 杂篇:一代版本一代神[-Gradle-]
  • MySQL 大表迁移简单方案
  • cenots6.5安装 git version 2.0.5
  • Codeforces Global Round1 简要题解
  • Terraform入门 - 1. 安装Terraform
  • ps/kill/pkill简单应用
  • 测试工程师年度计划制定
  • 【React系列】如何构建React应用程序
  • 【技术性】Search知识
  • Android开发 - 掌握ConstraintLayout(四)创建基本约束
  • angular学习第一篇-----环境搭建
  • egg(89)--egg之redis的发布和订阅
  • HTML5新特性总结
  • IDEA常用插件整理
  • java概述
  • java架构面试锦集:开源框架+并发+数据结构+大企必备面试题
  • JS+CSS实现数字滚动
  • laravel 用artisan创建自己的模板
  • python学习笔记 - ThreadLocal
  • UEditor初始化失败(实例已存在,但视图未渲染出来,单页化)
  • 阿里中间件开源组件:Sentinel 0.2.0正式发布
  • 从输入URL到页面加载发生了什么
  • 工作踩坑系列——https访问遇到“已阻止载入混合活动内容”
  • 力扣(LeetCode)965
  • 漫谈开发设计中的一些“原则”及“设计哲学”
  • 前端技术周刊 2018-12-10:前端自动化测试
  • 如何胜任知名企业的商业数据分析师?
  • 使用Swoole加速Laravel(正式环境中)
  • 用 Swift 编写面向协议的视图
  • 资深实践篇 | 基于Kubernetes 1.61的Kubernetes Scheduler 调度详解 ...
  • ​LeetCode解法汇总1410. HTML 实体解析器
  • ​LeetCode解法汇总307. 区域和检索 - 数组可修改
  • ###项目技术发展史
  • #100天计划# 2013年9月29日
  • #我与Java虚拟机的故事#连载01:人在JVM,身不由己
  • (4)logging(日志模块)
  • (NO.00004)iOS实现打砖块游戏(九):游戏中小球与反弹棒的碰撞
  • (办公)springboot配置aop处理请求.
  • (二)pulsar安装在独立的docker中,python测试
  • (二十一)devops持续集成开发——使用jenkins的Docker Pipeline插件完成docker项目的pipeline流水线发布
  • (附源码)springboot工单管理系统 毕业设计 964158
  • (附源码)springboot教学评价 毕业设计 641310
  • (四) 虚拟摄像头vivi体验
  • (五)关系数据库标准语言SQL
  • (转)nsfocus-绿盟科技笔试题目
  • (转)关于pipe()的详细解析
  • ******之网络***——物理***