加密算法介绍(对称,非对称,MD5)

1、对称加密:
(1)特点:
加密和解密的密钥是相同的。
假设客户端A与服务器B进行通信。A和B用同一个密钥进行加密解密。
风险:密钥一旦被截获, 通信内容就能够被破解。
(2)过程:
过程:
A主机将要发送的数据,用密钥加密得到密文。将密文发送至B主机,B主机用相同的密钥解密得到明文。
在这里插入图片描述
(3)常用的对称加密算法:

  • DES:Data Encryption Standard;
  • 3DES:Triple DES;
  • AES:Advanced Encryption Standard; (128bits, 192bits, 256bits, 384bits)
  • Blowfish
  • Twofish
  • IDEA
  • RC6
  • CAST5
    2、非对称加密:
    (1)简介:
    非对称性加密,也叫公钥加密,加密解密的过程使用不同的密钥。
    (2)特点:
    密钥分为公钥与私钥:
  • 公钥:从私钥中提取产生;可公开给所有人;pubkey
  • 私钥:通过工具创建,使用者自己留存,必须保证其私密性;secret key;
    1》如果使用 公钥 对数据 进行加密,只有用对应的 私钥 才能 进行解密。
    2》如果使用 私钥 对数据 进行加密,只有用对应的 公钥 才能 进行解密
    私钥一般只有一个,而公钥可以有多个主机同时拥有。

例子:甲方生成 一对密钥 并将其中的一把作为 公钥 向其它人公开,得到该公钥的 乙方 使用该密钥对机密信息 进行加密 后再发送给甲方,甲方再使用自己保存的另一把 专用密钥 (私钥),对 加密 后的信息 进行解密。
(3)常见的非对称加密算法:

  • RSA
  • DSA /DSS
  • ELGamal

(4)用途:
数字签名:一是能确定消息确实是由发送方签名并发出来的,因为别人假冒不了发送方的签名。二是数字签名能确定消息的完整性。因为数字签名的特点是它代表了文件的特征,文件如果发生改变,数字摘要的值也将发生变化。不同的文件将得到不同的数字摘要。 一次数字签名涉及到一个哈希函数、发送者的公钥、发送者的私钥。”;
过程:将发送的数据进行一次hash,再用私钥对hash后的数据摘要加密,对方收到密文之后用公钥解密,然后再对数据进行一次hash比对。如果与数字摘要值相同,则数据是完整的。
在这里插入图片描述
密钥交换:发送方用对方公钥加密一个对称密钥,并发送给对方;
过程:对称加密中有一个问题,就是如果密钥被截获了,这时候就算传输是密文,第三方也很容易就可以解密成。所以非对称加密也可以用来传送密钥,这样就可以保证密钥的安全。具体如下:
在这里插入图片描述数据加密:可以做到数据加密但是一般不用来传输数据,因为非对称加密过程,速度要比对称加密慢3个数量级。

3、常见的签名加密算法:
(1)MD5:
MD5 用的是 哈希函数,它的典型应用是对一段信息产生 信息摘要,以 防止被篡改。
无论是多长的输入,MD5 都会输出长度为 128bits 的一个串 (通常用 16 进制 表示为 32 个字符)。
1》特点:
------MD5值不唯一,一个原始数据,只对应一个md5值;但是一个md5值,可能 对应多个原始数据。
------md5是不可逆的,也就是没有对应的算法,从生产的md5值逆向得到原始数据。
但是如果使用暴力破解,那就另说了。
撞库破解:
这是概率极低的破解方法,原理就是:
1.建立一个大型的数据库,把日常的各个语句,通过MD5加密成为密文,不断的积累大量的句子,放在一个庞大的数据库里.
2.比如一个人拿到了别人的密文,想去查询真实的密码,就需要那这个密文去到提供这个数据库的公司网站去查询.
这就是撞库的概念.
md5加盐:
比如我的银行密码是”12345”
1.得到的MD5是:827ccb0eea8a706c4c34a16891f84e7b
2.一个人截取到这个密文,那么通过撞库肯定容易撞出12345.
3.我们要做的就是加盐,银行密码还是”12345”,然后我把银行密码加上我特定的字符串才计算MD5
所以密码还是那个密码,但是变成求”12345密码加密987”的MD5值,然后再得到MD5,那么这个MD5起码可以确认那个数据库不会有.
MD5工具制作:
一般加密都是加密字符串或者文件,所以我们的工具就有加密字符串和文件的两种方法,两个方法同名,通过重载完成。
1》加密字符串:
1.获取信息摘要对象:md5
通过信息摘要单例的构造函数获取:

MessageDigest md5 = MessageDigest.getInstance("MD5");

2.信息摘要对象是对字节数组进行摘要的,所以先获取字符串的字节数组.

byte[] bytes = str.getBytes();

3、信息摘要对象对字节数组进行摘要,得到摘要字节数组:

byte[] digest = md5.digest(bytes);

4、把摘要数组中的每一个字节转换成16进制,并拼在一起就得到了MD5值.
2》加密文件:
方法传入的是文件对象 : file
1.使用到信息摘要对象更新:md5.update(byte[] input)方法,用法是通过读取流,不断的更新从流中读到的”信息数组”.
2.然后通过”信息摘要对象”获取摘要,不用参数:md5.digest(),此时返回的数组就已经是包含内容的摘要数组了
详细代码:

public class MD5Tool {
    public static void main(String[] args) throws Exception {
        /*--------------字符串--------------*/
        String str = "12345";
        String md1 = getMD5(str);
        System.out.println(md1);//827ccb0eea8a706c4c34a16891f84e7b
 
        /*--------------文件--------------*/
        File file = new File("D:\\1.mp3");
        String md2 = getMD5(file);
        System.out.println(md2);//9068aaead9a5b75e6a54395d8183ec9
    }
    /**
     * 逻辑:
     *
     * 1.获取md5对象,通过"信息摘要"获取实例构造("MD5").
     * 2.md5对象对("字符串的"字节形式"-得到的数组)进行摘要",那么会返回一个"摘要的字节数组"
     * 3.摘要字节数组中的"每个二进制值"字节形式,"转成十六进制形式",然后再把这些值给拼接起来,就是MD5值了
     *      (PS:为了便于阅读,把多余的fff去掉,并且单个字符前加个0)
     *
     */
    public static String getMD5(String str) throws Exception {
 
        String MD5 = "";
 
        MessageDigest md5 = MessageDigest.getInstance("MD5");
        byte[] bytes = str.getBytes();
        byte[] digest = md5.digest(bytes);
 
        for (int i = 0; i < digest.length; i++) {
            //摘要字节数组中各个字节的"十六进制"形式.
            int j = digest[i];
             j = j & 0x000000ff;
            String s1 = Integer.toHexString(j);
 
            if (s1.length() == 1) {
                s1 = "0" + s1;
            }
            MD5 += s1;
        }
        return MD5;
    }
    //重载,所以用户传入"字符串"或者"文件"都可以解决.
 
    /**
     * 处理逻辑:
     * 1.现在传入的是"文件",不是字符串
     * 2.所以信息摘要对象.进行摘要得到数组不能像上面获得:md5.digest(bytes),因为不是str.getBytes得到bytes
     * 3.其实还是通过mdt.digest();获取到字节数组,但是前期必须要有一个方法必须是md5.update(),即"信息摘要对象"要先更新
     * 4."信息摘要更新"里面有(byte[] input),说明是读取流获取到的数组,所以我们就用这个方法.
     * 5.所以最终的逻辑就是:
     *
     *      1.获取文件的读取流
     *      2.不停的读取流中的"内容"放入字符串,放一部分就"更新"一部分.直到全部完毕
     *      3.然后调用md5.digest();就会得到有内容的字节数组,剩下的就和上边一样了.
     */
    public static String getMD5(File file) throws Exception {
        String MD5 = "";
 
        MessageDigest md5 = MessageDigest.getInstance("MD5");
        FileInputStream fis = new FileInputStream(file);
 
        byte[] bytes = new byte[1024 * 5];
 
        int len = -1;
        while ((len=fis.read(bytes))!=-1) {
            //一部分一部分更新
            md5.update(bytes, 0, len);
        }
        byte[] digest = md5.digest();
        for (int i = 0; i <digest.length; i++) {
            int n = digest[i] & 0x000000ff;
            String s = Integer.toHexString(n);
 
            MD5 += s;
        }
        return MD5;
    }
}
 

MD5算法原理:
MD5消息摘要算法,属Hash算法一类。MD5算法对输入任意长度的消息进行运行,产生一个128位的消息摘要。
以下所描述的消息长度、填充数据都以位(Bit)为单位,字节序为小端字节。

1、数据填充
对消息进行数据填充,使消息的长度对512取模得448,设消息长度为X,即满足X mod 512=448。根据此公式得出需要填充的数据长度。
填充方法:在消息后面进行填充,填充第一位为1,其余为0。

2、添加消息长度
在第一步结果之后再填充上原消息的长度,可用来进行的存储长度为64位。如果消息长度大于264,则只使用其低64位的值,即(消息长度 对 264取模)。
在此步骤进行完毕后,最终消息长度就是512的整数倍。

3、数据处理
准备需要用到的数据:
4个常数: A = 0x67452301, B = 0x0EFCDAB89, C = 0x98BADCFE, D = 0x10325476;
4个函数: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));
把消息分以512位为一分组进行处理,每一个分组进行4轮变换,以上面所说4个常数为起始变量进行计算,重新输出4个变量,以这4个变量再进行下一分组的运算,如果已经是最后一个分组,则这4个变量为最后的结果,即MD5值。
具体计算的实现较为复杂,建议查阅相关书籍,下面给出在C++上的实现代码。

#MD5.h


 1 #ifndef MD5H
 2 #define MD5H
 3 #include <math.h>
 4 #include <Windows.h>
 5 
 6 void ROL(unsigned int &s, unsigned short cx); //32位数循环左移实现函数
 7 void ltob(unsigned int &i); //B\L互转,接受UINT类型
 8 unsigned int* MD5(const char* mStr); //接口函数,并执行数据填充,计算MD5时调用此函数
 9 
10 #endif

#MD5.cpp


  1 #include "MD5.h"
  2 
  3 /*4组计算函数*/
  4 inline unsigned int F(unsigned int X, unsigned int Y, unsigned int Z)
  5 {
  6     return (X & Y) | ((~X) & Z);
  7 }
  8 inline unsigned int G(unsigned int X, unsigned int Y, unsigned int Z)
  9 {
 10     return (X & Z) | (Y & (~Z));
 11 }
 12 inline unsigned int H(unsigned int X, unsigned int Y, unsigned int Z)
 13 {
 14     return X ^ Y ^ Z;
 15 }
 16 inline unsigned int I(unsigned int X, unsigned int Y, unsigned int Z)
 17 {
 18     return Y ^ (X | (~Z));
 19 }
 20 /*4组计算函数结束*/
 21 
 22 /*32位数循环左移实现函数*/
 23 void ROL(unsigned int &s, unsigned short cx)
 24 {
 25     if (cx > 32)cx %= 32;
 26     s = (s << cx) | (s >> (32 - cx));
 27     return;
 28 }
 29 
 30 /*B\L互转,接收UINT类型*/
 31 void ltob(unsigned int &i)
 32 {
 33     unsigned int tmp = i;//保存副本
 34     byte *psour = (byte*)&tmp, *pdes = (byte*)&i;
 35     pdes += 3;//调整指针,准备左右调转
 36     for (short i = 3; i >= 0; --i)
 37     {
 38         CopyMemory(pdes - i, psour + i, 1);
 39     }
 40     return;
 41 }
 42 
 43 /*
 44 MD5循环计算函数,label=第几轮循环(1<=label<=4),lGroup数组=4个种子副本,M=数据(16组32位数指针)
 45 种子数组排列方式: --A--D--C--B--,即 lGroup[0]=A; lGroup[1]=D; lGroup[2]=C; lGroup[3]=B;
 46 */
 47 void AccLoop(unsigned short label, unsigned int *lGroup, void *M)
 48 {
 49     unsigned int *i1, *i2, *i3, *i4, TAcc, tmpi = 0; //定义:4个指针; T表累加器; 局部变量
 50     typedef unsigned int(*clac)(unsigned int X, unsigned int Y, unsigned int Z); //定义函数类型
 51     const unsigned int rolarray[4][4] = {
 52         { 7, 12, 17, 22 },
 53         { 5, 9, 14, 20 },
 54         { 4, 11, 16, 23 },
 55         { 6, 10, 15, 21 }
 56     };//循环左移-位数表
 57     const unsigned short mN[4][16] = {
 58         { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
 59         { 1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12 },
 60         { 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2 },
 61         { 0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9 }
 62     };//数据坐标表
 63     const unsigned int *pM = static_cast<unsigned int*>(M);//转换类型为32位的Uint
 64     TAcc = ((label - 1) * 16) + 1; //根据第几轮循环初始化T表累加器
 65     clac clacArr[4] = { F, G, H, I }; //定义并初始化计算函数指针数组
 66 
 67     /*一轮循环开始(16组->16次)*/
 68     for (short i = 0; i < 16; ++i)
 69     {
 70         /*进行指针自变换*/
 71         i1 = lGroup + ((0 + i) % 4);
 72         i2 = lGroup + ((3 + i) % 4);
 73         i3 = lGroup + ((2 + i) % 4);
 74         i4 = lGroup + ((1 + i) % 4);
 75 
 76         /*第一步计算开始: A+F(B,C,D)+M[i]+T[i+1] 注:第一步中直接计算T表*/
 77         tmpi = (*i1 + clacArr[label - 1](*i2, *i3, *i4) + pM[(mN[label - 1][i])] + (unsigned int)(0x100000000UL * abs(sin((double)(TAcc + i)))));
 78         ROL(tmpi, rolarray[label - 1][i % 4]);//第二步:循环左移
 79         *i1 = *i2 + tmpi;//第三步:相加并赋值到种子
 80     }
 81     return;
 82 }
 83 
 84 /*接口函数,并执行数据填充*/
 85 unsigned int* MD5(const char* mStr)
 86 {
 87     unsigned int mLen = strlen(mStr); //计算字符串长度
 88     if (mLen < 0) return 0;
 89     unsigned int FillSize = 448 - ((mLen * 8) % 512); //计算需填充的bit数
 90     unsigned int FSbyte = FillSize / 8; //以字节表示的填充数
 91     unsigned int BuffLen = mLen + 8 + FSbyte; //缓冲区长度或者说填充后的长度
 92     unsigned char *md5Buff = new unsigned char[BuffLen]; //分配缓冲区
 93     CopyMemory(md5Buff, mStr, mLen); //复制字符串到缓冲区
 94 
 95     /*数据填充开始*/
 96     md5Buff[mLen] = 0x80; //第一个bit填充1
 97     ZeroMemory(&md5Buff[mLen + 1], FSbyte - 1); //其它bit填充0,另一可用函数为FillMemory
 98     unsigned long long lenBit = mLen * 8ULL; //计算字符串长度,准备填充
 99     CopyMemory(&md5Buff[mLen + FSbyte], &lenBit, 8); //填充长度
100     /*数据填充结束*/
101 
102     /*运算开始*/
103     unsigned int LoopNumber = BuffLen / 64; //以16个字为一分组,计算分组数量
104     unsigned int A = 0x67452301, B = 0x0EFCDAB89, C = 0x98BADCFE, D = 0x10325476;//初始4个种子,小端类型
105     unsigned int *lGroup = new unsigned int[4]{ A, D, C, B}; //种子副本数组,并作为返回值返回
106     for (unsigned int Bcount = 0; Bcount < LoopNumber; ++Bcount) //分组大循环开始
107     {
108         /*进入4次计算的小循环*/
109         for (unsigned short Lcount = 0; Lcount < 4;)
110         {
111             AccLoop(++Lcount, lGroup, &md5Buff[Bcount * 64]);
112         }
113         /*数据相加作为下一轮的种子或者最终输出*/
114         A = (lGroup[0] += A);
115         B = (lGroup[3] += B);
116         C = (lGroup[2] += C);
117         D = (lGroup[1] += D);
118     }
119     /*转换内存中的布局后才能正常显示*/
120     ltob(lGroup[0]);
121     ltob(lGroup[1]);
122     ltob(lGroup[2]);
123     ltob(lGroup[3]);
124     delete[] md5Buff; //清除内存并返回
125     return lGroup;
126 }

 

再给出调用实例(以win32控制台应用程序为例):

#main.cpp


 1 #include <iostream>
 2 #include <string.h>
 3 #include <stdlib.h>
 4 #include "MD5.h"
 5 
 6 int main(int argc, char **argv)
 7 {
 8     char tmpstr[256], buf[4][10];
 9     std::cout << "请输入要加密的字符串:";
10     std::cin >> tmpstr;
11     unsigned int* tmpGroup = MD5(tmpstr);
12     sprintf_s(buf[0], "%8X", tmpGroup[0]);
13     sprintf_s(buf[1], "%8X", tmpGroup[3]);
14     sprintf_s(buf[2], "%8X", tmpGroup[2]);
15     sprintf_s(buf[3], "%8X", tmpGroup[1]);
16     std::cout <<"MD5:"<< buf[0] << buf[1] << buf[2] << buf[3] << std::endl;
17     
18     delete[] tmpGroup;
19     return 0; //在此下断点才能看到输出的值
20 }

拓展:
0xfffffff代表的含义:
0x:代表16进制;
一个f代表:4个1,即(1111);
所以0xffffffff代表:8组4个1
1111 - 1111 - 1111 - 1111 - 1111 - 1111 - 1111 - 1111
所以刚才的0xffffff82就是前面6组都是1,后面两组是
1111 - 1111 - 1111 - 1111 - 1111 - 1111 - 0111 - 0010
所以先与上0x000000ff,即
0000 - 0000 - 0000 - 0000 - 0000 - 0000 - 1111 - 1111
就得到了82了
上面的方法也可以写写成:

 for (int i = 0; i < digest.length; i++) {
            //摘要字节数组中各个字节的"十六进制"形式.
            String s1 = Integer.toHexString( digest[i]);
 
            //如果是8个长度的,把前面的6个f去掉,只获取后面的
            if (s1.length() == 8) {
                s1 = s1.substring(6);
            }
            if (s1.length() == 1) {
                s1 = "0" + s1;
            }
            MD5 += s1;
        }
 

其他算法详解参考博客:

————————————————
版权声明:本文为CSDN博主「零壹技术栈」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/baidu_22254181/article/details/82594072
参考博客:
https://blog.csdn.net/L835311324/article/details/81540641
https://blog.csdn.net/baidu_22254181/article/details/82594072
https://blog.csdn.net/mp624183768/article/details/80575843
https://blog.csdn.net/love_moon821/article/details/79843845

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值