SHA1算法实现及详解
1 SHA1算法简介
安全哈希算法(Secure Hash Algorithm)主要适用于数字签名标准(Digital Signature Standard DSS)里面定义的数字签名算法(Digital Signature Algorithm DSA)。对于长度小于2^64位的消息,SHA1会产生一个160位的消息摘要。当接收到消息的时候,这个消息摘要可以用来验证数据的完整性。在传输的过程中,数据很可能会发生变化,那么这时候就会产生不同的消息摘要。
SHA1有如下特性:不可以从消息摘要中复原信息;两个不同的消息不会产生同样的消息摘要。
2 术语和概念
2.1位(Bit),字节(Byte)和字(Word)
SHA1始终把消息当成一个位(bit)字符串来处理。本文中,一个“字”(Word)是32位,而一个“字节”(Byte)是8位。比如,字符串“abc”可以被转换成一个位字符串:01100001 01100010 01100011。它也可以被表示成16进制字符串: 0x616263.
2.2 运算符和符号
下面的逻辑运算符都被运用于“字”(Word)
X^Y = X, Y逻辑与
X \/ Y = X, Y逻辑或
X XOR Y= X, Y逻辑异或
~X = X逻辑取反
X+Y定义如下:
字 X 和 Y 代表两个整数 x 和y, 其中 0 <= x < 2^32 且 0 <= y < 2^32. 令整数z = (x + y) mod 2^32. 这时候 0 <= z < 2^32. 将z转换成字Z, 那么就是 Z = X + Y.
循环左移位操作符Sn(X)。X是一个字,n是一个整数,0<=n<=32。Sn(X) = (X<<n)OR(X>>32-n)
X<<n定义如下:抛弃最左边的n位数字,将各个位依次向左移动n位,然后用0填补右边的n位(最后结果还是32位)。X>>n是抛弃右边的n位,将各个位依次向右移动n位,然后在左边的n位填0。因此可以叫Sn(X)位循环移位运算
3 SHA1算法描述
在SHA1算法中,我们必须把原始消息(字符串,文件等)转换成位字符串。SHA1算法只接受位作为输入。假设我们对字符串“abc”产生消息摘要。首先,我们将它转换成位字符串如下:
01100001 01100010 01100011
―――――――――――――
‘a’=97 ‘b’=98 ‘c’=99
这个位字符串的长度为24。下面我们需要5个步骤来计算MD5。
3.1 补位
消息必须进行补位,以使其长度在对512取模以后的余数是448。也就是说,(补位后的消息长度)%512 = 448。即使长度已经满足对512取模后余数是448,补位也必须要进行。
补位是这样进行的:先补一个1,然后再补0,直到长度满足对512取模后余数是448。总而言之,补位是至少补一位,最多补512位。还是以前面的“abc”为例显示补位的过程。
原始信息: 01100001 01100010 01100011
补位第一步:01100001 01100010 01100011 1
首先补一个“1”
补位第二步:01100001 01100010 01100011 10…..0
然后补423个“0”
我们可以把最后补位完成后的数据用16进制写成下面的样子
61626380 00000000 00000000 00000000
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000
00000000 00000000
现在,数据的长度是448了,我们可以进行下一步操作。
3.2 补长度
所谓的补长度是将原始数据的长度补到已经进行了补位操作的消息后面。通常用一个64位的数据来表示原始消息的长度。如果消息长度不大于2^64,那么第一个字就是0。在进行了补长度的操作以后,整个消息就变成下面这样了(16进制格式)
61626380 00000000 00000000 00000000
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000018
如果原始的消息长度超过了512,我们需要将它补成512的倍数。然后我们把整个消息分成一个一个512位的数据块,分别处理每一个数据块,从而得到消息摘要。
3.3 使用的常量
一系列的常量字K(0), K(1), ... , K(79),如果以16进制给出。它们如下:
Kt = 0x5A827999 (0 <= t <= 19)
Kt = 0x6ED9EBA1 (20 <= t <= 39)
Kt = 0x8F1BBCDC (40 <= t <= 59)
Kt = 0xCA62C1D6 (60 <= t <= 79).
3.4 需要使用的函数
在SHA1中我们需要一系列的函数。每个函数ft (0 <= t <= 79)都操作32位字B,C,D并且产生32位字作为输出。ft(B,C,D)可以如下定义
ft(B,C,D) = (B AND C) OR ((NOT B) AND D) ( 0 <= t <= 19)
ft(B,C,D) = B XOR C XOR D (20 <= t <= 39)
ft(B,C,D) = (B AND C) OR (B AND D) OR (C AND D) (40 <= t <= 59)
ft(B,C,D) = B XOR C XOR D (60 <= t <= 79).
3.5 计算消息摘要
必须使用进行了补位和补长度后的消息来计算消息摘要。计算需要两个缓冲区,每个都由5个32位的字组成,还需要一个80个32位字的缓冲区。第一个5个字的缓冲区被标识为A,B,C,D,E。第一个5个字的缓冲区被标识为H0,H1, H2, H3, H4
。80个字的缓冲区被标识为W0, W1,..., W79
另外还需要一个一个字的TEMP缓冲区。
为了产生消息摘要,在第4部分中定义的16个字的数据块M1, M2,..., Mn
会依次进行处理,处理每个数据块Mi 包含80个步骤。
在处理每个数据块之前,缓冲区{Hi} 被初始化为下面的值(16进制)
H0 = 0x67452301
H1 = 0xEFCDAB89
H2 = 0x98BADCFE
H3 = 0x10325476
H4 = 0xC3D2E1F0.
现在开始处理M1, M2, ... , Mn。为了处理 Mi,需要进行下面的步骤
(1). 将 Mi 分成 16 个字 W0, W1, ... , W15, W0 是最左边的字
(2). 对于 t = 16 到 79 令 Wt = S1(Wt-3 XOR Wt-8 XOR Wt- 14 XOR Wt-16).
(3). 令 A = H0, B = H1, C = H2, D = H3, E = H4.
(4) 对于 t = 0 到 79,执行下面的循环
TEMP = S5(A) + ft(B,C,D) + E + Wt + Kt;
E = D; D = C; C = S30(B); B = A; A = TEMP;
(5). 令 H0 = H0 + A, H1 = H1 + B, H2 = H2 + C, H3 = H3 + D, H4 = H4 + E.
在处理完所有的 Mn, 后,消息摘要是一个160位的字符串,以下面的顺序标识
H0 H1 H2 H3 H4.
对于SHA256,SHA384,SHA512。你也可以用相似的办法来计算消息摘要。对消息进行补位的算法完全是一样的。
4 参考文献
1: FIPS 180-1 Secure Hash Standard: http://www.itl.nist.gov/fipspubs/fip180-1.htm
2: Secure Hash Standard: http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf
实现代码如下:
using System;
using System.Collections;
using System.IO;
using System.Text; namespace VerifySHA1
{
public class MySHA1
{
// state variables
private static UInt32 Message_Digest1 = 0x67452301;
private static UInt32 Message_Digest2 = 0xEFCDAB89;
private static UInt32 Message_Digest3 = 0x98BADCFE;
private static UInt32 Message_Digest4 = 0x10325476;
private static UInt32 Message_Digest5 = 0xC3D2E1F0; private static UInt32 SHA1CircularShift(int bits,UInt32 word)
{
return ((word << bits) & 0xFFFFFFFF) | (word) >> (32-(bits));
} private static void SHA1_Init()
{
Message_Digest1 = 0x67452301;
Message_Digest2 = 0xEFCDAB89;
Message_Digest3 = 0x98BADCFE;
Message_Digest4 = 0x10325476;
Message_Digest5 = 0xC3D2E1F0;
} private static UInt32[] SHA1_Append(byte[] input)
{
int zeros=0;
int ones =1;
int size=0;
int n = input.Length;
int m = n%64;
if( m < 56 )
{
zeros = 55-m;
size=n-m+64;
}
else if (m==56)
{
zeros = 63;
ones = 1;
size=n+8+64;
}
else
{
zeros = 63-m+56;
size=n+64-m+64;
} ArrayList bs = new ArrayList(input);
if(ones==1)
{
bs.Add( (byte)0x80 ); // 0x80 = 10000000
}
for(int i=0;i<zeros;i++)
{
bs.Add( (byte)0 );
} UInt64 N = (UInt64) n * 8;
byte h8=(byte)(N&0xFF);
byte h7=(byte)((N>>8)&0xFF);
byte h6=(byte)((N>>16)&0xFF);
byte h5=(byte)((N>>24)&0xFF);
byte h4=(byte)((N>>32)&0xFF);
byte h3=(byte)((N>>40)&0xFF);
byte h2=(byte)((N>>48)&0xFF);
byte h1=(byte)(N>>56);
bs.Add(h1);
bs.Add(h2);
bs.Add(h3);
bs.Add(h4);
bs.Add(h5);
bs.Add(h6);
bs.Add(h7);
bs.Add(h8);
byte[] ts=(byte[])bs.ToArray(typeof(byte)); /* Decodes input (byte[]) into output (UInt32[]). Assumes len is
* a multiple of 4.
*/
UInt32[] output = new UInt32[size/4];
for(Int64 i=0,j=0;i<size;j++,i+=4)
{
UInt32 temp = 0;
temp=temp|(((UInt32)ts[i])<<24);
temp=temp|(((UInt32)ts[i+1])<<16);
temp=temp|(((UInt32)ts[i+2])<<8);
temp=temp|(((UInt32)ts[i+3]));
output[j] = temp;
}
return output;
}
private static UInt32[] SHA1_Transform(UInt32[] x)
{
SHA1_Init(); UInt32[] K = {
0x5A827999,
0x6ED9EBA1,
0x8F1BBCDC,
0xCA62C1D6
};
int t;
UInt32 temp;
UInt32[] W = new UInt32[80];
UInt32 A, B, C, D, E; for(int k=0; k<x.Length; k+=16)
{
for(t = 0; t < 16; t++)
{
W[t] = x[t+k];
} for(t = 16; t < 80; t++)
{
W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
} A = Message_Digest1;
B = Message_Digest2;
C = Message_Digest3;
D = Message_Digest4;
E = Message_Digest5; for(t = 0; t < 20; t++)
{
temp = SHA1CircularShift(5,A) +
((B & C) | ((~B) & D)) + E + W[t] + K[0];
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
} for(t = 20; t < 40; t++)
{
temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
} for(t = 40; t < 60; t++)
{
temp = SHA1CircularShift(5,A) +
((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
} for(t = 60; t < 80; t++)
{
temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
} Message_Digest1 = (Message_Digest1 + A) & 0xFFFFFFFF;
Message_Digest2 = (Message_Digest2 + B) & 0xFFFFFFFF;
Message_Digest3 = (Message_Digest3 + C) & 0xFFFFFFFF;
Message_Digest4 = (Message_Digest4 + D) & 0xFFFFFFFF;
Message_Digest5 = (Message_Digest5 + E) & 0xFFFFFFFF;
} return new UInt32[]{Message_Digest1,Message_Digest2,Message_Digest3,Message_Digest4,Message_Digest5};
}
public static string SHA1Array(UInt32[] input)
{
StringBuilder sb = new StringBuilder(); for(int i=0; i<input.Length; i++)
{
sb.Append( String.Format("{0:X8}", input[i]).ToUpper() );
} return sb.ToString();
} public static string MySHA1String(string message)
{
char[] c = message.ToCharArray();
byte[] b = new byte[c.Length];
for(int i=0;i<c.Length;i++)
{
b[i]=(byte)c[i];
}
UInt32[] output = SHA1_Append( b );
UInt32[] str = SHA1_Transform( output );
return SHA1Array(str); }
public static string MySHA1File(string fileName)
{
FileStream fs=File.Open(fileName,FileMode.Open,FileAccess.Read);
byte[] array=new byte[fs.Length];
fs.Read(array,0,(int)fs.Length);
fs.Close();
UInt32[] output = SHA1_Append( array );
UInt32[] str = SHA1_Transform( output );
return SHA1Array(str);
}
#region Unit Test
public static string Test(string message)
{
return "\r\nSHA1 (\""+message+"\") = " + MySHA1String(message);
}
public static string TestSuite()
{
string s = "";
s+=Test("");
s+=Test("a");
s+=Test("abc");
s+=Test("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq");
s+=Test("abcdefghijklmnopqrstuvwxyz");
s+=Test("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
s+=Test("12345678901234567890123456789012345678901234567890123456789012345678901234567890");
// StringBuilder sb = new StringBuilder();
// for(int i=0; i<1000000; i++)
// sb.Append("a");
// s+=Test(sb.ToString()); return s;
} public static void Main()
{
Console.WriteLine(MySHA1.TestSuite());
Console.ReadLine();
}
#endregion
}
}
OC 代码:
- (NSString *)stringWithSHA_1 {
// 1. 转化为 c 类型
const char *cString = [self UTF8String];
// 2. 求长度
CC_LONG len = (CC_LONG)strlen(cString);
unsigned char bytes[CC_SHA1_DIGEST_LENGTH];
// 3. SHA_1转换
CC_SHA1(cString, len, bytes);
NSMutableString *finalSHA1 = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH];
for (int i = ; i < CC_SHA1_DIGEST_LENGTH; i++) {
[finalSHA1 appendFormat:@"%02x", bytes[i]];
}
return finalSHA1;
}
SHA1算法实现及详解的更多相关文章
- 【密码学】SHA1算法实现及详解
1 SHA1算法简介 安全哈希算法(Secure Hash Algorithm)主要适用于数字签名标准(Digital Signature Standard DSS)里面定义的数字签名算法(Digit ...
- JVM垃圾回收算法及回收器详解
引言 本文主要讲述JVM中几种常见的垃圾回收算法和相关的垃圾回收器,以及常见的和GC相关的性能调优参数. GC Roots 我们先来了解一下在Java中是如何判断一个对象的生死的,有些语言比如Pyth ...
- 【机器学习】【条件随机场CRF-2】CRF的预测算法之维特比算法(viterbi alg) 详解 + 示例讲解 + Python实现
1.CRF的预测算法条件随机场的预测算法是给定条件随机场P(Y|X)和输入序列(观测序列)x,求条件概率最大的输出序列(标记序列)y*,即对观测序列进行标注.条件随机场的预测算法是著名的维特比算法(V ...
- c++ LeetCode(初级数组篇)十一道算法例题代码详解(一)
原文作者:aircraft 原文链接:https://www.cnblogs.com/DOMLX/p/10940636.html 唉!最近忙着面试找实习,然后都是面试的很多是leetcode的算法题, ...
- 最短路径Floyd算法【图文详解】
Floyd算法 1.定义概览 Floyd-Warshall算法(Floyd-Warshall algorithm)是解决任意两点间的最短路径的一种算法,可以正确处理有向图或负权的最短路径问题,同时也被 ...
- KMP算法 Next数组详解
题面 题目描述 如题,给出两个字符串s1和s2,其中s2为s1的子串,求出s2在s1中所有出现的位置. 为了减少骗分的情况,接下来还要输出子串的前缀数组next.如果你不知道这是什么意思也不要问,去百 ...
- Dijkstra算法之 Java详解
转载:http://www.cnblogs.com/skywang12345/ 迪杰斯特拉算法介绍 迪杰斯特拉(Dijkstra)算法是典型最短路径算法,用于计算一个节点到其他节点的最短路径. 它的主 ...
- $PollardRho$ 算法及其优化详解
\(PollardRho\) 算法总结: Pollard Rho是一个非常玄学的算法,用于在\(O(n^{1/4})\)的期望时间复杂度内计算合数n的某个非平凡因子(除了1和它本身以外能整除它的数). ...
- Kruskal算法 - C语言详解
最小生成树 在含有n个顶点的连通图中选择n-1条边,构成一棵极小连通子图,并使该连通子图中n-1条边上权值之和达到最小,则称其为连通网的最小生成树. 例如,对于如上图G4所示的连通网可以有多棵权值总 ...
随机推荐
- MySQL之正则表达式
一.介绍 正则表达式用来描述或者匹配符合规则的字符串.它的用法和like比较相似,但是它又比like更强大,能够实现一些很特殊的规则匹配:正则表达式需要使用REGEXP命令,匹配上返回"1& ...
- scala学习5--函数二
to def test() : Unit = { // for(i <- 1.to(100)){ // println(i) // } for(i <- 1 to 100 ){ prin ...
- MySql学习笔记——存储函数
在学习完存储过程后,今天主要回顾一下mysql中的存储函数的知识. 函数与存储过程的区别 首先,存储函数也是过程式对象之一,与存储过程相似.它们都是由SQL和过程式语句组成的代码片断,并且可以从应用程 ...
- 开源IDS系列--解决barnyard2 停止运行 libmysqlclient.so.16.0.0
现象: barnyard2运行一段时间后,会自行停止,未在/var/log/barnyard2或/var/log/suricata中发现相关日志. 排查: 在/var/log/message中存在以下 ...
- 初识 Fuzzing 工具 WinAFL
转:https://paper.seebug.org/323/ 初识 Fuzzing 工具 WinAFL 作者:xd0ol1(知道创宇404实验室) 0 引子 本文前两节将简要讨论 fuzzing 的 ...
- 洛谷P3639 [APIO2013] 道路费用 [生成树的特殊算法]
题目传送门 道路费用 格式难调,题面就不放了. 分析: 这是一道要细(yan)心(jing)的生成树的好(gui)题. 首先我们看到$k$的范围非常小,那么我们就可以直接$2^k$枚举每一条加边是否选 ...
- Java多线程编程——volatile关键字
(本篇主要内容摘自<Java多线程编程核心技术>) volatile关键字的主要作用是保证线程之间变量的可见性. package com.func; public class RunThr ...
- [转]如何在 JS 代码中消灭 for 循环
一,用好 filter,map,和其它 ES6 新增的高阶遍历函数 二,理解和熟练使用 reduce 三,用递归代替循环(可以break!) 四,使用高阶函数遍历数组时可能遇到的陷阱 五,死磕到底,T ...
- Struts2 MVC基础介绍
流程介绍 我们模拟一个请求/响应的情景,来介绍Struts的工作流程.注意,下面的序号和图中的序号没有严格的对应关系. 浏览器向系统发出请求,请求的地址是ac.action 请求被StrutsPrep ...
- python虚拟环境virtualenv下安装MySQL-python
1.先在windows安装:https://github.com/konglingxi/mysqldb_for_python27 2.按照提示将python主环境中的mysqldb相关文件及文件夹移到 ...