说明:本次的密码算法采用C++编写,使用clion开发平台,Cmake编译配置工具;通过集成OpenSSL密码软件库,实现加解密功能。

1 对称加解密(AES)1.1 AES简介:

AES(Advanced Encryption Standard)是分组密码,每组的长度相同,为128位,即16个字节。密钥长度可以使用128位,192位或256位。密钥长度不同,加密轮数也不同。

AES的处理单位是字节,128位的明文分组P和密钥K都分为16个字节,其中P=(p0,“`,p15),K=(k0,“`,k15)。明文分组使用以节为单位的4*4矩阵描述,即状态矩阵;状态矩阵中字节排列顺序是从上到下,从左到右;每轮加密,状态矩阵发生变化,最终输出为密文。

同样,128位密钥也使用方阵表示,矩阵的每一列称为1个32位比特字。通过密钥扩展,该密钥矩阵被排列成44个字组成的序列W=(W0,“`,W43),其中W[0-3]为原始密钥,后面的密钥分成10组,每组的4个字作为轮密钥,用于10轮的加密。

图1.1 明文矩阵 图1.2密钥矩阵

1.2 AES加解密规则:

AES加密操作分为10轮,第1轮到第9轮加密的轮函数一样,包括4个操作:字节代换,行移位,列混淆和轮密钥加。第一轮开始时,先将原文和原密钥进行一次异或;最后一轮加密,不包括列混淆。AES解密过程与加密类似,每一轮的操作是加密操作的逆。

1.2.1字节代换

AES中定义了一个S盒和逆S盒,即映射表,分别对应加密和解密。字节代换操作就是查表的过程,将状态矩阵中的元素按照一定规则映射成新的一个字节。即,将原有元素(16进制数)的高4位作为行,低4位作为列,选中行列相交的元素,然后替代原来的元素。例如,原来输出的元素Si是0x7f,将其拆分为0x7,0xf;选择0x7行,0xf列,挑选出相应的元素0x68。在新的状态表中,用0x68代替0x7f。

1.2.2 行移位

行移位是对状态矩阵的每行进行相关的移位操作。加密过程,状态矩阵的第1行循环左移0位,第2行循环左移1位,第3行循环左移2位,第4行循环左移3位。解密过程,将状态矩阵的每行循环右移,位数与左移相同。

1.2.3 列混合

列混合操作是通过矩阵的乘法来实现的,即S’=PS。其中P为固定的矩阵,S为变换前的矩阵,S’为变换后的矩阵。矩阵元素的乘法和加法都是定义在基于GF(2^8)上的二元运算。 列混合的逆操作,相当于将矩阵P变为P的逆。

1.2.4 密钥轮加

将轮密钥W=(W[4i],W[4i+1],W[4i+2],W[4i+3])同状态矩阵S(S[0-3],S[4-7],s[8-11],s[12-15])进行异或操作。其中状态矩阵的每列可组成一个32位的字,与W[i]大小相同。

1.3 AES加解密的源代码(OpenSSL实现)

 1 #include  2 #include  3 #include  4 #include  5  6 #define AES_KEY_SIZE 128 7 #define AES_BLOCK_SIZE 16 8 #define N 3600 9 10 using namespace std;11 12 void aes_encrypt(unsigned char *plaintext, unsigned char *ciphertext, const unsigned char *key) {13     AES_KEY aesKey;14     AES_set_encrypt_key(key, AES_KEY_SIZE, &aesKey);15     AES_encrypt(plaintext, ciphertext, &aesKey);16 }17 18 void aes_decrypt(unsigned char *ciphertext, unsigned char *plaintext, const unsigned char *key) {19     AES_KEY aesKey;20     AES_set_decrypt_key(key, AES_KEY_SIZE, &aesKey);21     AES_decrypt(ciphertext, plaintext, &aesKey);22 }23 24 void inputPlain(char *&plainText, size_t &length) {25     char str[N],ch;26     length = 0;27     while (true) {28         ch = getchar();29         if (ch == '\n') break;30         str[length++] = ch;31     }32     str[length]='\0';33     plainText=str;34 }35 36 int main() {37     char *plainText;38     unsigned char *cipherText, *decryptedText;39     size_t plainTextLen, cipherTextLen;40     const unsigned char aesKey[] = {41             0x7b, 0xf3, 0x5c, 0xd6, 0x9c, 0x47, 0x5d, 0x5e,42             0x6f, 0x1d, 0x7a, 0x23, 0x18, 0x7b, 0xf9, 0x3443     };44 45     //输入46     printf("请输入明文(换行结束):\n");47     fflush(stdout);48     inputPlain(plainText, plainTextLen);49 50     //分配密文空间51     cipherTextLen = (plainTextLen + AES_BLOCK_SIZE - 1) / AES_BLOCK_SIZE;52     cipherTextLen *= AES_BLOCK_SIZE;53     cipherText = new unsigned char[cipherTextLen];54 55     //对明文进行AES加密56     aes_encrypt((unsigned char*)plainText, cipherText, aesKey);57     *(cipherText+cipherTextLen)='\0';58 59     printf("加密后的密码:");60     for (size_t i = 0; i < cipherTextLen; i++) {61         printf("%02x", cipherText[i]);62     }63     printf("\n");64 65     decryptedText=new unsigned char[plainTextLen];66 67     aes_decrypt(cipherText,decryptedText,aesKey);68 69     printf("解密后的密码:%s\n",decryptedText);70 71     free(cipherText);72     free(decryptedText);73 74     return 0;75 }

备注:代码参考 https://blog.csdn.net/buhuidage/article/details/1291927272 非对称加密(RSA)

RSA建立在大数分解的困难性之上。RSA 加密包括:密钥生成,加解密和签名验证。

2.1密钥生成

(1)选择两个素数p和q,保密的。

(2)计算整数n,n=p*q,公开的。

(3)选择整数e,e与θ(n)互素(θ(n)=(p-1)(q-1)),1<e<θ(n),公开的。

(4)计算d,d为e的乘法逆(modθ(n)),e*d=k*θ(n)+1

则公钥pubKey={e,n},私钥priKey={d,p,q}

2.2 RSA算法的加解密

RSA使用公钥加密,私钥解密。

加密:C=Me mod n ,其中M为明文分组,C为密文分组,M<n。

解密:M=Cd mod n = Med mod n = M(mod n)

情景:收发双方都知道n,发方知道e,收方知道d。

此时破解者Oscar,知道n,需要找到e,d,使得M=Med mod n。

2.3 RSA算法的签名模式RSA使用私钥签名,公钥验签
签名:已有消息m,私钥(d,p,q),计算签名s。——s = md mod n
验签:已有消息m,公钥(e,n),验证s是不是有效的签名。 ——验证:se = ? m mod n2.4 RSA加解密源代码(OpenSSL实现)

  1 #include   2 #include <string>  3 #include   4 #include   5 #include   6   7 using namespace std;  8   9  10 /** 11  * 生成密钥对,并保存为文件 12  * @param pubFile 13  * @param priFile 14  * @param bits 15  */ 16 void genKeyFile(const string &pubFile,const string &priFile,int bits){ 17     //生成公钥和私钥 18     RSA *rsa= RSA_generate_key(bits,RSA_F4, nullptr, nullptr); 19     BIO *bp= BIO_new(BIO_s_file()); 20     BIO_write_filename(bp,(void *) pubFile.c_str()); 21     PEM_write_bio_RSAPublicKey(bp,rsa); 22     BIO_free_all(bp); 23  24     bp=BIO_new(BIO_s_file()); 25     BIO_write_filename(bp,(void *) priFile.c_str()); 26     PEM_write_bio_RSAPrivateKey(bp,rsa,nullptr,nullptr,0,nullptr,nullptr); 27     CRYPTO_cleanup_all_ex_data(); 28     BIO_free_all(bp); 29     RSA_free(rsa); 30 } 31  32 //获取公钥和私钥 33 void genKeyPair(string &pubKey,string &priKey,int bits){ 34     RSA *rsa= RSA_generate_key(bits,RSA_F4,nullptr,nullptr); 35     BIO *kPub=BIO_new(BIO_s_mem()); 36     BIO *kPri=BIO_new(BIO_s_mem()); 37     int priKeyLen,pubKeyLen; 38  39     PEM_write_bio_RSAPrivateKey(kPri,rsa,nullptr,nullptr,0,nullptr, nullptr); 40     PEM_write_bio_RSA_PUBKEY(kPub,rsa); 41  42     priKeyLen= BIO_pending(kPri); 43     pubKeyLen= BIO_pending(kPub); 44  45     priKey.resize(priKeyLen); 46     pubKey.resize(pubKeyLen); 47  48     BIO_read(kPri,priKey.c_str(),priKeyLen); 49     BIO_read(kPub, pubKey.c_str(), pubKeyLen); 50  51     RSA_free(rsa); 52     BIO_free_all(kPri); 53     BIO_free_all(kPub); 54 } 55  56 //读取公钥 57 RSA* readPublicKey(const string &pubKey){ 58     BIO *bio= BIO_new_mem_buf(pubKey.data(),pubKey.length()); 59     RSA *rsa= PEM_read_bio_RSA_PUBKEY(bio,nullptr,nullptr,nullptr); 60     BIO_free_all(bio); 61     return rsa; 62 } 63  64 //读取私钥 65 RSA* readPrivateKey(const string &priKey){ 66     BIO *bio= BIO_new_mem_buf(priKey.data(),priKey.length()); 67     RSA *rsa= PEM_read_bio_RSAPrivateKey(bio,nullptr, nullptr, nullptr); 68     BIO_free_all(bio); 69     return rsa; 70 } 71  72  73 /** 74  * 公钥加密 75  * @param in 76  * @param out 77  * @param pubKey 78  */ 79 bool encrypt(const string &in,string &out,const string &pubKey){ 80     int keySize,inLen,readLen=0,len; 81     const unsigned char *from; 82     string to; 83     RSA *rsa= readPublicKey(pubKey); 84     if(rsa==nullptr)  return false; 85  86     //分段加密 87     keySize= RSA_size(rsa); 88     inLen=in.length(); 89     from=(const unsigned char*)in.data(); 90     to.resize(keySize); 91  92     do{ 93         len=(keySize-11)<inLen? (keySize-11):inLen; 94         RSA_public_encrypt(len,(from+readLen),(unsigned char*)to.c_str(),rsa,RSA_PKCS1_PADDING); 95         inLen-=len; 96         readLen+=len; 97         out.append(to); 98     }while(inLen>0); 99     RSA_free(rsa);100 101     return true;102 }103 104 105 /**106  * 私钥解密107  * @param in108  * @param out109  * @param priKey110  */111 bool decrypt(const string &in,string &out,const string &priKey){112     unsigned char *from;113     string to;114     int keySize,inLen,readLen=0,len;115     RSA *rsa= readPrivateKey(priKey);116     if(rsa==nullptr)  return false;117 118     keySize= RSA_size(rsa);119     from=(unsigned char*)in.data();120     inLen=in.length();121     to.resize(keySize);122 123     do{124         len=RSA_private_decrypt(keySize,(from+readLen),(unsigned char*)to.c_str(),rsa,RSA_PKCS1_PADDING);125         inLen-=keySize;126         readLen+=keySize;127         out.append(to.data(),len);128     }while(inLen>0);129     RSA_free(rsa);130     return true;131 }132 133 134 /**135  * 签名136  * @param digest137  * @param sign138  * @param priKey139  */140 bool signa(const string &digest,string &sign,string &priKey){141     string out;142     unsigned int signLen=0;143     RSA* rsa= readPrivateKey(priKey);144     if(rsa==nullptr)  return false;145     out.resize(RSA_size(rsa));146 147     RSA_sign(NID_sha1, (const unsigned char*)digest.c_str(), digest.length(),148              (unsigned char*)out.c_str(), &signLen, rsa);149 150     sign.clear();151     sign.append(out);152     RSA_free(rsa);153     return true;154 }155 156 157 /**158  * 验签159  * @param digest160  * @param sign161  * @param pubKey162  */163 bool verify(const string &digest,string &sign,string &pubKey){164     RSA *rsa= readPublicKey(pubKey);165     if(rsa== nullptr)166         return false;167 168     int res=RSA_verify(NID_sha1,(const unsigned char*)digest.c_str(),digest.length(),169                (const unsigned char*)sign.c_str(),sign.length(),rsa);170     RSA_free(rsa);171 172     if(res==1)173         return true;174     return false;175 }176 177 //准备数据178 void prepareData(string &str,int size){179     for(int i=0;i<size;i++){180         str+=to_string((i%128));181     }182 }183 184 //哈希摘要185 string hashDigests(string name,string data){186     unsigned char mdValue[EVP_MAX_MD_SIZE]={0};187     unsigned int mdLen=0;188     const EVP_MD *md= nullptr;189     string out;190 191     OpenSSL_add_all_digests();192     md= EVP_get_digestbyname(name.c_str());193     if(!md){194         return NULL;195     }196 197     EVP_MD_CTX *mdCtx=EVP_MD_CTX_new();198     EVP_MD_CTX_init(mdCtx);199     EVP_DigestInit_ex(mdCtx,md,nullptr);200     EVP_DigestUpdate(mdCtx,data.data(),data.length());201     EVP_DigestFinal_ex(mdCtx,mdValue,&mdLen);202     EVP_MD_CTX_free(mdCtx);203 204     for(int i=0;mdValue[i]!=0;i++){205         out+=mdValue[i];206     }207     return out;208 }209 210 void testRSA(string data){211     string priKey,pubKey;212     string plainText=data,encryptedText,decryptedText;213     string digest,sign;214     int flag=1;215 216     genKeyFile("./pub.pem","./pri.pem",1024);217     genKeyPair(pubKey, priKey, 1024);218     if(!encrypt(plainText,encryptedText,pubKey)){219         flag=0;220         printf("RSA加密失败");221     }222     if(!decrypt(encryptedText,decryptedText,priKey)){223         flag=0;224         printf("RSA解密失败");225     }226     if(!flag)  return;227 228     if(decryptedText==plainText){229         printf("加解密验证成功\n");230     }231 232     digest=hashDigests("SHA256","i lost love");233     signa(digest,sign,priKey);234     bool res=verify(digest,sign,pubKey);235     if(res){236         printf("RSA 签名验证成功");237     }238     else{239         printf("RSA签名验证失败");240     }241 }242 243 int main(){244     string str;245     prepareData(str,1024*512+9);246     testRSA(str);247 248     return 0;249 }

备注:参考https://blog.csdn.net/zyhse/article/details/113844114?

3 哈希计算3.1 哈希函数的定义和特点

哈希函数可将任意长度的消息压缩成固定长度的消息摘要。用于数字签名和消息鉴别码的构造。哈希函数表示为h=H(M)。

哈希函数输入M为变长的消息,输出为定长的hash值h。

单向性:从M计算h容易,从h计算M不可能。

抗碰撞性>

①抗弱碰撞:对任何给定的消息x,找到满足y!=x且H(x)=H(y)的y,在计算上不可行。

②抗强碰撞:找到任何满足H(x)=H(y)的偶对(x,y)在计算上不可行。

输入的微小变化,会引起输出的巨大变化。

3.2 MD5加密过程

MD5以512位分组来处理输入的信息,且每一分组又被划分为16个子分组。算法的输出由4个32位分组组成,将这4个分组级联后生成一个128位散列值。

(1)根据消息x构造M:M = x | 1 | 0…0 | Length。

填充方法:信息的后面填充一个1和无数个0,满足其长度len mod512=448。接着在其后附加64位,表示填充前的长度。

图3.1明文填充

(2)哈希值计算

①设置链接变量A=67452301, B=efcdab89, C=98badcfe, D=10325476

②对每个消息分组做压缩计算

图3.2 哈希计算过程

3.3 MD5摘要算法源码(OpenSSL实现)

 1 #include  2 #include <string> 3 #include  4  5 using namespace std; 6 //准备数据 7 void prepareData(string &str,int size){ 8     for(int i=0;i<size;i++){ 9         str+=to_string((i%128));10     }11 }12 //摘要算法13 string hashDigests(string name,string data){14     unsigned char mdValue[EVP_MAX_MD_SIZE]={0};15     unsigned int mdLen=0;16     const EVP_MD *md= nullptr;17     string out;18 19     OpenSSL_add_all_digests();20     md= EVP_get_digestbyname(name.c_str());21     if(!md){22         return NULL;23     }24 25     EVP_MD_CTX *mdCtx=EVP_MD_CTX_new();26     EVP_MD_CTX_init(mdCtx);27     EVP_DigestInit_ex(mdCtx,md,nullptr);28     EVP_DigestUpdate(mdCtx,data.data(),data.length());29     EVP_DigestFinal_ex(mdCtx,mdValue,&mdLen);30     EVP_MD_CTX_free(mdCtx);31 32     for(int i=0;mdValue[i]!=0;i++){33         out+=mdValue[i];34     }35     return out;36 }37 38 int main(){39     string str;40     prepareData(str,1*1024*1024+7);41 42     string sha256=hashDigests("SHA256",str);43     string sha1=hashDigests("SHA1",str);44     string md5=hashDigests("MD5",str);45 46     char *chSha256=(char*)sha1.c_str();47     char *chSha1=(char*)sha256.c_str();48     char *chMd5=(char*)md5.c_str();49 50     for(int i=0;chSha1[i]!='\0';i++){51         printf("%x",chSha1[i]);52     }53     printf("\n");54 55     for(int i=0;chSha256[i]!='\0';i++){56         printf("%x",chSha256[i]);57     }58     printf("\n");59 60     for(int i=0;chMd5[i]!='\0';i++){61         printf("%x",chMd5[i]);62     }63     printf("\n");64 65     return 0;66 }

备注:参考https://blog.csdn.net/zyhse/article/details/113844114?

4 C++模拟RSA加解密

使用vs2022编写 程序模拟RSA的加解密。

(1)RSA.h

 1 #pragma once   2     #include    3     #include    4     #include    5     #include    6     #define N 1024   7        8     class RSA {   9         int p, q, len;  10         long n, r, e, d;  11         long long* pText, * cText, * dText;  12     public:  13         void init();  14         RSA();  15         bool isPrime(long);  16         long gcd(long, long);  17         void inputPQ();  18         void genKeyPair();  19             long quickPower(long long, long, long);  20         char* encrypt(char*);  21         char* decrypt();  22     };

(2)RSA.cpp

1.    #include "RSA.h"  2.      3.    using namespace std;  4.    string res = "\0";  5.    char ds[N] = { 0 };  6.      7.    void  mLtoa(long long num, char* str, int radix)  8.    {  9.        int i = 0;  10.        int j = 0;  11.        long long sum;  12.        unsigned long long num1 = num;  //如果是负数求补码,必须将他的绝对值放在无符号位中在进行求反码  13.        char str1[33] = { 0 };  14.        if (num < 0) {              //求出负数的补码  15.            num = -num;  16.            num1 = ~num;  17.            num1 += 1;  18.        }  19.        if (num == 0) {  20.            str1[i] = '0';  21.      22.            i++;  23.        }  24.        while (num1 != 0) {                      //进行进制运算  25.            sum = num1 % radix;  26.            str1[i] = (sum > 9) ? (char)((sum - 10) + 'a') : (char)(sum + '0');  27.            num1 = num1 / radix;  28.            i++;  29.        }  30.        i--;  31.      32.        for (i; i >= 0; i--) {               //逆序输出   33.            str[i] = str1[j];  34.            j++;  35.        }  36.      37.    }  38.      39.    void RSA::init() {  40.        this->p = 0;  41.        this->q = 0;  42.        this->n = 0;  43.        this->d = 0;  44.        this->e = 0;  45.        this->r = 0;  46.        this->len = 0;  47.        cText = new long long[N];  48.        pText = new long long[N];  49.        dText = new long long[N];  50.    }  51.      52.    RSA::RSA() {  53.        init();  54.    }  55.      56.    bool RSA::isPrime(long t) {  57.        if (t == 1) return false;  58.        for (int i = 2; i <= t / i; i++) {  59.            if (t % i == 0) {  60.                return false;  61.            }  62.        }  63.        return true;  64.    }  65.      66.    long RSA::gcd(long a, long b) {  67.        return b == 0 ? a : gcd(b, a % b);  68.    }  69.      70.    void RSA::inputPQ() {  71.        int p, q;  72.        while (1) {  73.            printf("请输入两个大于200的素数(p,q):");  74.            cin >> p >> q;  75.      76.            if (p <= 200 || q <= 200) {  77.                printf("输入的素数太小,请重新输入\n");  78.                continue;  79.            }  80.            if (!isPrime(p)) {  81.                printf("p不是素数,请重新输入!\n");  82.                continue;  83.            }  84.            if (!isPrime(q)) {  85.                printf("q不是素数,请重新输入!\n");  86.                continue;  87.            }  88.      89.            this->p = p;  90.            this->q = q;  91.            this->n = p * q;  92.            this->r = (p - 1) * (q - 1);  93.            break;  94.        }  95.    }  96.      97.    void RSA::genKeyPair() {  98.        long value = 1;  99.        inputPQ();  100.      101.        for (int i = 2; i < r; i++) {  102.            if (gcd(r, i) == 1) {  103.                this->e = i;  104.                break;  105.            }  106.        }  107.      108.        for (long j = 1;; j++) {  109.            value = j * this->r + 1;  110.            if (value % this->e == 0 && value / this->e < this->r) {  111.                this->d = value / this->e;  112.                break;  113.            }  114.        }  115.      116.        printf("设定的公钥为(%ld,%ld)\n", this->e, this->n);  117.        printf("生成的私钥为(%ld,%d,%d)\n", this->d, this->p, this->q);  118.    }  119.      120.    long RSA::quickPower(long long a, long b, long n) {  121.        long res = 1;  122.        while (b > 0) {  123.            if ((b & 1) != 0)  124.                res = (res * a) % n;  125.            b = b >> 1;  126.            a = (a * a) % n;  127.        }  128.        return res;  129.    }  130.      131.    char* RSA::encrypt(char* str) {  132.        unsigned char ch;  133.        char* es, cipher[N];  134.      135.        for (int i = 0; str[i] != 0; i++) {  136.            if (i > N) {  137.                realloc(cText, N * ((i / 1024) + 1) * sizeof(int64_t));  138.                realloc(pText, N * ((i / 1024) + 1) * sizeof(int64_t));  139.                realloc(dText, N * ((i / 1024) + 1) * sizeof(int64_t));  140.            }  141.      142.            ch = (unsigned char)str[i];  143.            pText[i] = ch;  144.            cText[i] = quickPower(pText[i], this->e, this->n);  145.            memset(cipher, 0, sizeof(cipher));  146.            mLtoa(cText[i], cipher, 16);  147.            res.append(cipher);  148.            this->len++;  149.        }  150.        es = (char*)res.c_str();  151.      152.        return es;  153.    }  154.      155.    char* RSA::decrypt() {  156.        for (int i = 0; i < len; i++) {  157.            dText[i] = quickPower(cText[i], this->d, this->n);  158.            ds[i] = (char)dText[i];  159.        }  160.        return ds;  161.    } 
(3)demo.cpp
1.    #include "RSA.h"  2.      3.    using namespace std;  4.    char* cipherText, * decryptedText;  5.    char plainText[N], ch;  6.    int len = 0;  7.    RSA rsa;  8.      9.    int main() {  10.      11.        memset(plainText, 0, sizeof(plainText));  12.      13.        printf("请输入待加密的明文(以换行结束):\n");  14.        while (true) {  15.            ch = getchar();  16.            if (ch == '\n') break;  17.            plainText[len++] = ch;  18.        }  19.        fflush(stdout);  20.      21.        rsa.genKeyPair();  22.      23.        cipherText = rsa.encrypt(plainText);  24.        decryptedText = rsa.decrypt();  25.      26.        printf("加密密文:");  27.        printf("%s\n", cipherText);  28.      29.        printf("解密原文:");  30.        printf("%s\n", decryptedText);  31.      32.        return 0;  33.    }