密码学的一些算法分享 3

  1. 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),因为加解密一个分组的所有步骤都可以在恒定时间内完成。
  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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夜之王。

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值