RSA
RSA是一种使用不同的加密密钥与解密密钥,“由已知加密密钥推导出解密密钥在计算上是不可行的”密码体制。在公开密钥密码体制中,加密密钥(即公开密钥)PK是公开信息,而解密密钥(即秘密密钥)SK是需要保密的。加密算法E和解密算法D也都是公开的。虽然解密密钥SK是由公开密钥PK决定的,但却不能根据PK计算出SK。基于这种理论,1978年出现了著名的RSA算法,它通常是先生成一对RSA密钥,其中之一是保密密钥,由用户保存;另一个为公开密钥,可对外公开,甚至可在网络服务器中注册。RSA公开密钥密码体制的原理是:根据数论,寻求两个大素数比较简单,而将它们的乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥。
根据数字签名和加密解密算法最原始的流程,如果不加入任何其他安全手段,那么,将RSA加入公钥和加入私钥的部分互换,就实现了加密解密和数字签名的互换。这里只提供了使用RSA实现数字签名的C++代码。
#pragma once
#include<iostream>
#include<algorithm>
#include<math.h>
#include<string.h>
#include<time.h>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
#define MIN 100000
#define MAX 999999999999
#define MIN1 10000
#define MAX1 9999999999999
typedef long long LL;
LL p, q, n, n_Euler, d;
LL Plaintext[100];//明文
LL Ciphertext[100];//密文
LL Plaintext_C[100];
LL x, y;
//RSA加解密流程:
//随机产生两个大素数P和Q,并对PQ进行保密
//计算n=PQ,公开n
//求n的欧拉函数N,N=(P-1)(Q-1)
//求一个与N互素的数e,(e,n)为公钥
//e mod N的逆元为d,(d,n)为私钥,公钥和私钥可以互换
//签名明文M时,使用私钥,M的d次方mod n即可得到密文C,这是签名过程
// 一套完整的数字签名包含签名和认证两个部分,因此还需要一个认证的过程
//认证时,使用公钥,密文C的m次方 mod n即可得到明文M,这是认证过程
LL mul(LL x, LL y) {
return x * y;
}//得到mul,即加解密流程中的n的方法
/*用于判断一个数是否为素数*/
bool is_prime(LL x) {
if (x < 2)return false;
for (int i = 2; i <= x / i; i++) {
if (x % i == 0)return false;//一个大素数必然是一个奇数,且没有除自身和1以外的因子
}
return true;
}
/*用于输入两个素数p和q*/
void prime_create() {
cout << "请输入p的值:";
int i;
srand((unsigned)time(NULL));
while (true) {
p = MIN + (rand() % (MAX - MIN));//随机生成一个在规定范围内的数p
if (is_prime(p)) {//判断p是否为素数,如果是,则输出
cout << p << endl;//q与p同理
break;
}
else {
continue;
}
}
}
void prime_create2() {
cout << "请输入q的值:";
int i;
srand((unsigned)time(NULL));
while (true) {
q = MIN1 + (rand() % (MAX1 - MIN1));
if (is_prime(q)) {
cout << q << endl;
break;
}
else {
continue;
}
}
}
LL Randomly_e(LL t) {
LL Number = 0;
srand((LL)time(NULL));//以现在的系统时间作为随机数的种子来产生随机数
do {
Number = rand() % (t - 2) + 1;//限制e的范围在1到N之间
} while (!is_prime(Number));
cout << "随机数e为" << Number << endl;
return Number;
}
LL Cal_d(LL e, LL model, LL* d) {//生成d的方法
LL i;
LL over = e;
for (i = 1; i < model;) {//求e模n的逆元;e*d==1mod n
over = over % model;
if (over == 1) {
*d = i;
return 1;//当e*d==1 mod n时,得到d
}
else {//否则d将增加,往后进行遍历,直到找到逆元d
if (over + e <= model) {
do {
i++;
over += e;
} while (over + e <= model);
}
else {
i++;
over += e;
}
}
}
return 0;
}
//求模n需要用到大数幂乘算法,但为了提高运行效率,则使用while语句实现
// 这是一种迭代
//模n的大数幂乘的快速算法
LL Power_mul(LL a, LL b, LL c, LL n) {
//初始a=x,b=r,c=1
while (b != 0) {
while (b % 2 == 0) {
b /= 2;
a = (a * a) % n;
}
b -= 1;
c = (c * a) % n;
}
return c;
}
//明文读入,素数取的比较小,所以直接用了ASCII码来写
void Initialize() {
//char ch[] = "I LOVE YOU";
char ch[100];
cout << "请输入明文" << endl;
gets_s(ch);//通过键盘输入明文实现加密解密的演示
int counter = 0;
while (ch[counter] != '\0')
{
counter++;
}
int i, j = 0;
for (i = 0; i < 100; i++)
{
if (j != counter)
{
Plaintext[i] = ch[j];
j++;
}
else
{
// Plaintext[i] = 32;//空格
}
}
cout << "明文如下:";
for (i = 0; i < 100; i++)
{
printf("%c", Plaintext[i]);
}
cout << endl;
}
/*1.密钥的产生*/
void Key_generation() {
prime_create(); //产生两个大素数p和q
prime_create2();
n = mul(p, q);//计算n=p*q
n_Euler = mul(p - 1, q - 1);//计算n_Euler=(p-1)*(q-1)
cout << "计算出n为:" << n << "n的欧拉函数N为" << n_Euler << endl;
LL e = Randomly_e(n_Euler);
Cal_d(e, n_Euler, &d);
cout << "计算出d的值为:" << d << endl;
cout << "公钥私钥如下" << endl;
cout << "{e,n}为公钥:" << "e为:" << e << " n为:" << n << endl;
cout << "{d,n}为私钥:" << "d为:" << d << " n为:" << n << endl;
cout << "密钥产生完成" << endl;
}
void RSA_ssa() {
Initialize();
LL e = Randomly_e(n_Euler);
Cal_d(e, n_Euler, &d);
for (int i = 0; i < 100; i++)
Ciphertext[i] = Power_mul(Plaintext[i], d, 1, n);
cout << endl;
cout << "用私钥(d, n)做出的签名如下:";
for (int i = 0; i < 100; i++)
cout << Ciphertext[i] << " ";
cout << endl;
cout << "RSA签名完成!" << endl;
}
//RSA解密
void RSA_pss()
{
LL e = Randomly_e(n_Euler);
for (int i = 0; i < 100; i++)
Plaintext_C[i] = Power_mul(Ciphertext[i], e, 1, n);
cout << endl;
cout << "用公钥(e, n)做出的认证如下:";
for (int i = 0; i < 100; i++)
printf("%c", Plaintext_C[i]);
cout << endl;
cout << "RSA认证完成!" << endl;
}
void RSA() {
/*密钥的产生*/
Key_generation();
/*签名*/
RSA_ssa();
/*认证*/
RSA_pss();
}
int main()
{
cout << "RSA" << endl;
RSA();
return 0;
}
//最终结果可能会出现锟斤拷的现象,并非由于算法有误,这是源于字符集之间存在转换问题
//在算P、Q、n、e和d等等未知数时,由于使用了while语句,因此时间复杂度为O(log(n))
//RSA加密和解密算法的时间复杂度为O(1),因为加解密一个分组的所有步骤都可以在恒定时间内完成。
使用ECC进行数字签名
椭圆加密算法(ECC)数学基础是利用椭圆曲线上的有理点构成Abel加法群上椭圆离散对数的计算困难性。ECC的主要优势是在某些情况下它比其他的方法使用更小的密钥,比如RSA——提供相当的或更高等级的安全。ECC的另一个优势是可以定义群之间的双线映射,基于Weil对或是Tate对;双线性映射已经在密码学中发现了大量的应用,例如基于身份的加密。不过一个缺点是加密和解密操作的实现比其他机制花费的时间长。ECC被广泛认为是在给定密钥长度的情况下,最强大的非对称算法,因此在对带宽要求十分紧的连接中会十分有用。
椭圆曲线(ECC):
虽说是叫椭圆曲线,但是跟高中数学提到的椭圆曲线长得完全不一样嘛(吐槽)
以下代码仅供参考
#include <iostream>
using namespace std;
#include <stdio.h>
#include <math.h>
//椭圆曲线相较于RSA,能够用少得多的比特取得和RSA相同强度的安全性
//ECC加密算法使用的是有限域上的椭圆曲线
//根据数论,有限域GF(p)指给定某个质数p,由0、1、2......p-1共p个元素组成的整数集合中定义的加减乘除运算。
//这个时候,得到的将不再是光滑的曲线,而是一些不连续的点。
int xy[22];
#define N 23
#define A 1
//#define M 29
#define a1 1
#define b1 4
//进行ECC,首先需要得到椭圆曲线上的所有点,这里一共有29个点,再打印点集
//在ECC里,需要用到求逆元,求模等等运算。
//ECC的流程:
//1.选择一个范围在1到n-1的d,即选择生成元,d为私钥
//2.计算Q=d*G,G为点集中的一个拥有素数阶的点,Q为公钥
//3.ECC签名应该包含p,a,b,G,n,h,私钥d,最后输出r,s,r和s均属于1到n-1
//
typedef struct point {
int x;
int y;
}Point;
typedef struct ecc {
struct point p[100];
int len;
}ECCPoint;
typedef struct generator {
Point p;
int p_class;
}GENE_SET;
ECCPoint eccPoint;
Point mul(Point p1, Point p2);
Point add_two_points(Point p1, Point p2);
GENE_SET geneSet[100];
int geneLen;
//判断平方根是否为整数
int int_sqrt(int s)
{
int temp;
temp = (int)sqrt(s);//转为整型
if (temp * temp == s)
{
return temp;
}
else {
return -1;
}
}
//取模函数
int mod_p(int s)
{
int i; //保存s/p的倍数
int result; //模运算的结果
i = s / N;
result = s - i * N;
if (result >= 0)
{
return result;
}
else
{
return result + N;
}
}
//打印点集
void print()
{
int i;
int len = eccPoint.len;
printf("\n该椭圆曲线上共有%d个点(包含无穷远点)\n", len + 1);
for (i = 0; i < len; i++)
{
if (i % 8 == 0)
{
printf("\n");
}
printf("(%2d,%2d)\t", eccPoint.p[i].x, eccPoint.p[i].y);
}
printf("\n");
}
//求出椭圆曲线上所有点
void get_all_points()
{
int i = 0;
int j = 0;
int s, y = 0;
int n = 0, q = 0;
int modsqrt = 0;
int flag = 0;
if (a1 * a1 * a1 + b1 * b1 + 4 != 0)
{
for (i = 0; i <= N - 1; i++)
{
flag = 0;
n = 1;
y = 0;
s = i * i * i + a1 * i + b1;
while (s < 0)
{
s += N;
}
s = mod_p(s);
modsqrt = int_sqrt(s);
if (modsqrt != -1)
{
flag = 1;
y = modsqrt;
}
else {
while (n <= N - 1)
{
q = s + n * N;
modsqrt = int_sqrt(q);
if (modsqrt != -1)
{
y = modsqrt;
flag = 1;
break;
}
flag = 0;
n++;
}
}
if (flag == 1)
{
eccPoint.p[j].x = i;
eccPoint.p[j].y = y;
j++;
if (y != 0)
{
eccPoint.p[j].x = i;
eccPoint.p[j].y = (N - y) % N;
j++;
}
}
}
eccPoint.len = j;//点集个数
print(); //打印点集
}
}
//求生成元以及阶
void get_generetor_class()
{
int i, j = 0;
int count = 1;
Point p1, p2;
get_all_points();
printf("\n**********************************输出生成元以及阶:*************************************\n");
for (i = 0; i < eccPoint.len; i++)
{
count = 1;
p1.x = p2.x = eccPoint.p[i].x;
p1.y = p2.y = eccPoint.p[i].y;
while (1)
{
p2 = add_two_points(p1, p2);
if (p2.x == -1 && p2.y == -1)
{
break;
}
count++;
if (p2.x == p1.x)
{
break;
}
}
count++;
if (count <= eccPoint.len + 1)
{
geneSet[j].p.x = p1.x;
geneSet[j].p.y = p1.y;
geneSet[j].p_class = count;
printf("(%d,%d)--->>%d\t", geneSet[j].p.x, geneSet[j].p.y, geneSet[j].p_class);
j++;
if (j % 6 == 0) {
printf("\n");
}
}
geneLen = j;
}
cout << endl;
}
// 求 a mod b 的逆元
void exGcd(int a, int b) {
if (b == 0) {
xy[0] = 1;
xy[1] = 0;
}
else {
exGcd(b, a % b);
int x = xy[0];
xy[0] = xy[1];
xy[1] = x - (a / b) * xy[1];
}
}
int calculate3(int y, int k, int p) {
int l = 1;
for (int i = 0; i < k; i++) {
l = l * y;
l = l % p;
}
return l;
}
Point eccmutiply(int n, Point p) {
int a, b, l, k, m;
a = p.x;
b = p.y;
for (int i = 0; i < n - 1; i++) {
if (a == p.x && b == p.y) {
exGcd(2 * p.y, N);
k = xy[0];
if (k < 0)k = k + N;
printf("逆元=%d\n", k);
l = (3 * p.x * p.x + A) * k;
l = calculate3(l, 1, N);
if (l < 0) {
l = l + N;
}
}
else {
exGcd(a - p.x + N, N);
k = xy[0];
if (k < 0)k = k + N;
printf("else逆元=%d\n", k);
l = (b - p.y) * k;
l = calculate3(l, 1, N);
if (l < 0) {
l = l + N;
}
printf("l=%d\n", l);
}
m = p.x;
a = l * l - a - p.x;
a = calculate3(a, 1, N);
if (a < 0) {
a = a + N;
}
b = l * (m - a) - p.y;
b = calculate3(b, 1, N);
if (b < 0) {
b = b + N;
}
printf("%d(a,b)=(%d,%d)\n", i + 2, a, b);
//if(a==4&&b==5)break;
}
Point p3;
p3.x = a;
p3.y = b;
return p3;
}
Point mul(Point p1, Point p2) {
int k, l;
exGcd(p2.x - p1.x + N, N);
k = xy[0];
if (k < 0)k = k + N;
//printf("else逆元=%d\n",k);
l = (p2.y - p1.y) * k;
l = calculate3(l, 1, N);
if (l < 0) {
l = l + N;
}
//printf("l=%d\n",l);
Point p3;
p3.x = l * l - p1.x - p2.x;
p3.x = calculate3(p3.x, 1, N);
if (p3.x < 0)p3.x = p3.x + N;
p3.y = l * (p1.x - p3.x) - p1.y;
p3.y = calculate3(p3.y, 1, N);
if (p3.y < 0)p3.y = p3.y + N;
return p3;
}
//求b关于n的逆元
int inverse(int n, int b)
{
int q, r, r1 = n, r2 = b, t, t1 = 0, t2 = 1, i = 1;
while (r2 > 0)
{
q = r1 / r2;
r = r1 % r2;
r1 = r2;
r2 = r;
t = t1 - q * t2;
t1 = t2;
t2 = t;
}
if (t1 >= 0)
return t1 % n;
else {
while ((t1 + i * n) < 0)
i++;
return t1 + i * n;
}
}
//两点的加法运算
Point add_two_points(Point p1, Point p2)
{
long t;
int x1 = p1.x;
int y1 = p1.y;
int x2 = p2.x;
int y2 = p2.y;
int tx, ty;
int x3, y3;
int flag = 0;
//求
if ((x2 == x1) && (y2 == y1))
{
//相同点相加
if (y1 == 0)
{
flag = 1;
}
else {
t = (3 * x1 * x1 + a1) * inverse(N, 2 * y1) % N;
}
//printf("inverse(p,2*y1)=%d\n",inverse(p,2*y
}
else {
//不同点相加
ty = y2 - y1;
tx = x2 - x1;
while (ty < 0)
{
ty += N;
}
while (tx < 0)
{
tx += N;
}
if (tx == 0 && ty != 0)
{
flag = 1;
}
else {
t = ty * inverse(N, tx) % N;
}
}
if (flag == 1)
{
p2.x = -1;
p2.y = -1;
}
else {
x3 = (t * t - x1 - x2) % N;
y3 = (t * (x1 - x3) - y1) % N;
//使结果在有限域GF(P)上
while (x3 < 0)
{
x3 += N;
}
while (y3 < 0)
{
y3 += N;
}
p2.x = x3;
p2.y = y3;
}
return p2;
}
//倍点运算的递归算法
Point timesPiont(int k, Point p0)
{
if (k == 1) {
return p0;
}
else if (k == 2) {
return add_two_points(p0, p0);
}
else {
return add_two_points(p0, timesPiont(k - 1, p0));
}
}
int main() {
get_generetor_class();
Point p1, p2, p, p4, p5, p6, p7, p8, p9, p10;
int na, k, h, r, s, u1, u2;
cout << "选择生成元" << endl;
cin >> p1.x >> p1.y;
int j = 0;
while (j < N) {
if (geneSet[j].p.x == p1.x && geneSet[j].p.y == p1.y) {
break;
}
printf("j=%d ", j);
++j;
}
int M = geneSet[j].p_class;
printf("M=%d", M);
cout << endl;
cout << "请输入私钥" << endl;
cin >> na;
p2 = eccmutiply(na, p1);
printf("公钥为(%d,%d)", p2.x, p2.y);
//p2=mul(p,p1);
//printf("(%d,%d)",p2.x,p2.y);
//签名过程
cout << endl;
printf("输入随机数k ");
cin >> k;
printf("输入hash ");
cin >> h;
p4 = eccmutiply(k, p1);
r = calculate3(p4.x, 1, N);
if (r < 0)r = r + N;
s = inverse(M, k) * (h + na * r) % M;
if (s < 0)s = s + N;
printf("签名为(%d,%d) ", r, s);
printf("========================");
//验证过程
u1 = h * inverse(M, s) % M;
if (u1 < 0)u1 = u1 + M;
u2 = r * inverse(M, s) % M;
if (u2 < 0)u2 = u2 + M;
printf("u1u2=%d,%d ", u1, u2);
p5 = eccmutiply(u1, p1);
printf("sG=(%d,%d) ", p5.x, p5.y);
p6 = eccmutiply(u2, p2);
printf("HP=(%d,%d) ", p6.x, p6.y);
p7 = add_two_points(p5, p6);
printf("sG+HP=(%d,%d) ", p7.x, p7.y);
cout << endl;
if (calculate3(p7.x, 1, N) == r) {
printf("通过");
}
else {
printf("失败");
}
return 0;
}
3. MD5&SHA1
MD5信息摘要算法是一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。
MD5算法的原理可简要的叙述为:MD5码以512位分组来处理输入的信息,且每一分组又被划分为16个32位子分组,经过了一系列的处理后,算法的输出由四个32位分组组成,将这四个32位分组级联后将生成一个128位散列值。
总体流程如下图所示,每次的运算都由前一轮的128位结果值和当前的512bit值进行运算。
SHA1(中文名:安全散列算法1)也是一种密码散列函数,由美国国家安全局设计,并由美国国家标准技术研究所(NIST)发布为联邦数据处理标准(FIPS)。SHA-1可以生成一个被称为消息摘要的160位(20字节)散列值,散列值通常的呈现形式为40个十六进制数。
MD5:
#include <iostream>
#include <vector>//vector是一个能够存放任意类型的动态数组,能够增加和压缩数据
#include <cstdlib>
#include <string>
using namespace std;
//MD5能够做认证,是因为输入给MD5的每一个明文都有唯一对应的128位(即16字节)的散列(摘要信息)
//实现MD5认证,需要实现以下的功能:
//
//1.填充字符串:
//在长度为K bits的原始消息尾部填充长度为P bits的标识(1000…00),其中1 <= P <= 512(即至少需要填充一个bit),
// 使得填充后的消息位数为 K+P ≡ 448(mod 512)。
// 当K ≡ 448(mod 512)时,填充的字节数 P = 512 bit。
// 填充得到上述消息后,在尾部附加K值的低64位,最后得到一个长度为 K+P+64 ≡ 0(mod 512)的消息。
//
// 2.字符串分块:保证每一个分组的长度都为512bit
//
// 3.缓冲区初始化:生成四个幻数,用来做循环计算的初始值
//
// 4.循环压缩:
// 对于每一个长度为512bit的分组,都进行64轮的迭代运算。
//
// MD5算法就是将输入的明文,先进行填充,再将其分段,然后使用预设值初始化缓冲区间,并将分段的字符串进行压缩,
// 其压缩后的结果将作为下一块的输入;直至所有分块都压缩完毕。
//
#define A 0x67452301
#define B 0xefcdab89
#define C 0x98badcfe
#define D 0x10325476
//A,B,C,D将会在64轮迭代运算中作为缓冲区初始化的幻数使用
const char str16[] = "0123456789abcdef";
const unsigned int T[] = {
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 };//伪随机过程,用于消除数据的规律性
const unsigned int s[] = { 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 };//表示每一轮将字符串左移的位数
class MD5 {
private:
unsigned int tempA, tempB, tempC, tempD, strlength;
public:
MD5() {
tempA = A;
tempB = B;
tempC = C;
tempD = D;
strlength = 0;
}
//迭代运算:
// F函数、H函数、G函数、I函数为4个逻辑函数,每个都将迭代16次,一共64次迭代运算。
// 每个逻辑函数都有7个输入值,这包括:A,B,C和D共4个幻数,X(一个4字节的数据),常数T和常数S
// T是一个伪随机过程,用于消除数据的规律性
// S是每一轮将字符串左移的位数。
//通过与或非抑或,加法运算和移位运算来改变第一个参数的值。
// F函数
unsigned int F(unsigned int b, unsigned int c, unsigned int d) {
return (b & c) | ((~b) & d);
}
// G函数
unsigned int G(unsigned int b, unsigned int c, unsigned int d) {
return (b & d) | (c & (~d));
}
// H函数
unsigned int H(unsigned int b, unsigned int c, unsigned int d) {
return b ^ c ^ d;
}
// I函数
unsigned int I(unsigned int b, unsigned int c, unsigned int d) {
return c ^ (b | (~d));
}
// 移位操作函数
unsigned int shift(unsigned int a, unsigned int n) {
return (a << n) | (a >> (32 - n));
}
// 编码函数
string encode(string src) {
vector<unsigned int> rec = padding(src);
for (unsigned int i = 0; i < strlength / 16; i++) {
unsigned int num[16];
for (int j = 0; j < 16; j++) {
num[j] = rec[i * 16 + j];
}
iterateFunc(num, 16);
}
return format(tempA) + format(tempB) + format(tempC) + format(tempD);
}
// 循环压缩
//在一步迭代中,将B,C,D带入一个基本逻辑函数中计算,使其结果和A相加,再将这个结果和Ti相加,
//将得到的结果左移S位,和B相加传输给B,作为新的B。原本B的值不变传输给C,
// C的传输给D,D的传输给A,开启下一轮迭代。
// 以此类推进行16轮迭代后完成第一大组的迭代,得到四个更新后的幻数,
// 将它们分别与第一轮的输入相加,得到四个新的幻数A’,B’,C’,D’,
// 并将它们作为下一轮的四个标准幻数输入下一组的Hmd5处理。
void iterateFunc(unsigned int* X, int size = 16) {
unsigned int a = tempA,
b = tempB,
c = tempC,
d = tempD,
rec = 0,
g, k;
for (int i = 0; i < 64; i++) {
if (i < 16) {
// F迭代
g = F(b, c, d);
k = i;
}
else if (i < 32) {
// G迭代
g = G(b, c, d);
k = (1 + 5 * i) % 16;
}
else if (i < 48) {
// H迭代
g = H(b, c, d);
k = (5 + 3 * i) % 16;
}
else {
// I迭代
g = I(b, c, d);
k = (7 * i) % 16;
}
rec = d;
d = c;
c = b;
b = b + shift(a + g + X[k] + T[i], s[i]);
a = rec;
}
tempA += a;
tempB += b;
tempC += c;
tempD += d;
}
// 填充字符串
//在长度为K bits的原始消息尾部填充长度为P bits的标识(1000…00),其中1 <= P <= 512(即至少需要填充一个bit),
// 使得填充后的消息位数为 K+P ≡ 448(mod 512)。
// 当K ≡ 448(mod 512)时,填充的字节数 P = 512 bit。
// 填充得到上述消息后,在尾部附加K值的低64位,最后得到一个长度为 K+P+64 ≡ 0(mod 512)的消息。
vector<unsigned int> padding(string src) {
// 以512位,64个字节为一组
unsigned int num = ((src.length() + 8) / 64) + 1;
vector<unsigned int> rec(num * 16);
strlength = num * 16;
for (unsigned int i = 0; i < src.length(); i++) {
// 一个unsigned int对应4个字节,保存4个字符信息
rec[i >> 2] |= (int)(src[i]) << ((i % 4) * 8);
}
// 补充1000...000
rec[src.length() >> 2] |= (0x80 << ((src.length() % 4) * 8));
// 填充原文长度
rec[rec.size() - 2] = (src.length() << 3);
return rec;
}
// 整理输出
string format(unsigned int num) {
string res = "";
unsigned int base = 1 << 8;
for (int i = 0; i < 4; i++) {
string tmp = "";
unsigned int b = (num >> (i * 8)) % base & 0xff;
for (int j = 0; j < 2; j++) {
tmp = str16[b % 16] + tmp;
b /= 16;
}
res += tmp;
}
return res;
}
};
int main() {
MD5 test;
string a = "";
cout << "明文: ";
getline(cin, a);
cout << "结果: " << test.encode(a) << endl;
}
//MD5和SHA1的最显著的区别在于:SHA1摘要要比MD5摘要长32bit,MD5是2128数量级的操作,而SHA1则是2160的,
//MD5和SHA1都是从MD4发展而来的,他们的结构和强度相似,但SHA1会进行80次迭代运算,而MD5只进行64次。
//SHA1的算法逻辑规定了最大长度为2^64-1bit,而MD5则没有规定。
SHA1:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#pragma warning(disable:4996)
//SHA1和MD5均源于MD4,他们进行的是一个不可逆的过程,常用于认证。
//与MD5不同的是,SHA1摘要要比MD5摘要长32bit,MD5是2128数量级的操作,而SHA1则是2160的
// SHA1会进行80次迭代运算
// SHA1算法规定最大长度为2^64-1,MD5则没有规定
//SHA1流程与MD5类似,先对输入的明文进行填充,再将其分段,
//然后进行缓冲区初始化,初始运算值为ABCDE这5个幻数,最后进行80次迭代运算
//初始化链接变量
unsigned int A = 0x67452301, B = 0xEFCDAB89, C = 0x98BADCFE, D = 0x10325476, E = 0xC3D2E1F0; //第一次迭代的链接变量
unsigned int K[4] = { 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6 }; //循环中用到的常量
unsigned int A0 = 0x67452301, B0 = 0xEFCDAB89, C0 = 0x98BADCFE, D0 = 0x10325476, E0 = 0xC3D2E1F0;
// 字节转换,将四个字节转换为一个整型
int CharToWord(unsigned char* context, int i)
{
return (((int)context[i] & 0x000000ff) << 24) | (((int)context[i + 1] & 0x000000ff) << 16) | (((int)context[i + 2] & 0x000000ff) << 8) | ((int)context[i + 3] & 0x000000ff);
}
// 填充补位获得原始明文
//消息必须进行填充,以使其长度在对512取模以后的余数是448。
// 也就是说,(填充后的消息长度)%512 = 448。即使长度已经满足对512取模后余数是448,填充也必须要进行。
//填充的规则是填充一个“1”和若干个“0”使其长度模512和448同余。
//然后附加64比特的无符号整数,其值为原始消息的长度
void SHA1_fill(unsigned char* plaintext, unsigned int* group, int length)
{
printf("补位后获得的明文:\n");
int temp = length / 32, len = length;
while (len > 0)
{
if (len = len / 32)
{
for (int j = 0; j < temp; j++)
{
group[j] = CharToWord(plaintext, 4 * j);
printf("%08X\n", group[j]);
}
}
else
{
plaintext[length / 8] = 0x80;
group[temp] = CharToWord(plaintext, temp * 4);
printf("%08X\n", group[temp]);
break;
}
}
group[15] = length;
for (int i = temp + 1; i < 16; i++)
printf("%08X\n", group[i]);
}
// f函数
//f函数是一个非线性函数,通过与或非抑或,加法运算和移位运算来改变第一个参数的值。
unsigned int f(int B, int C, int D, int t)
{
return (t >= 0 && t <= 19) ? ((B & C) | (~B & D)) : ((t >= 20 && t <= 39) ? (B ^ C ^ D) : ((t >= 40 && t <= 59) ? ((B & C) | (B & D) | (C & D)) : ((t >= 60 && t <= 79) ? B ^ C ^ D : 0)));
}
//获得Kr
//对于第r1,r2,r3,r4这几步,Kr的四个值的获取分别是2、3、5和10的平方根,
//然后乘以2^30 = 1 073 741 824最后取乘积的整数部分
unsigned int GetK(int r)
{
return (r >= 0 && r <= 19) ? K[0] : ((r >= 20 && r <= 39) ? K[1] : ((r >= 40 && r <= 59) ? K[2] : ((r >= 60 && r <= 79) ? K[3] : 0)));
}
//获得 Wt
//W一共分为80组,其中从W[0]到W[15]为获得的原始消息均分为16组。这里类似于MD5,依然会进行循环左移n比特
void GetW(unsigned int w[])
{
for (int i = 16; i < 80; w[i++] = ((w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]) << 1) | ((w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]) >> 31));
}
// 步函数
//其中t是步数,0 <= t <= 79,r为轮数, 0 <= r <= 4.
void StepFunction(unsigned int w[], int t)
{
unsigned int temp = ((A << 5) | (A >> 27)) + f(B, C, D, t) + E + w[t] + GetK(t);
E = D, D = C, C = ((B << 30) | (B >> 2)), B = A, A = temp;
}
// 获得密文
void GetCipher(unsigned int* cipher)
{
cipher[0] = A0 + A;
cipher[1] = B0 + B;
cipher[2] = C0 + C;
cipher[3] = D0 + D;
cipher[4] = E0 + E;
}
void SHA1(unsigned char* context, unsigned int* cipher)
{
int len = strlen((char*)context) * 8;
unsigned int group[80] = { 0 };
SHA1_fill(context, group, len);
GetW(group);
for (int t = 0; t < 80; t++)
{
StepFunction(group, t);
}
GetCipher(cipher);
}
int main()
{
unsigned char m[56];
unsigned int c[5] = { 0 };
printf("请输入长度小于56且不包含空格的明文:");
scanf("%s", m);
SHA1(m, c);
//输出消息摘要
printf("结果为:");
for (int j = 0; j <= 4; j++) printf("%08X", c[j]);
printf("\n");
system("pause");
return 0;
}