密码学通信设计报告
🎇

密码学通信设计报告

创建时间
Sep 2, 2022 02:14 AM
标签
Author
百川
Published
July 4, 2022

1. 描述应用场景

本次设计的应用场景为:通信双方想要通过对称密码体制、非对称密码体制、哈希函数、数字签名、公钥证书等密码学常用的方法或者工具,来实现一个可以供通信双方(假设为A和B)之间进行安全的传输数据的安全通信系统,即保证通信双方通信过程中的绝对安全性,即A和B之间需要进行信息交流和通信,因此需要考虑信息传递过程中出现的各种安全隐患和可能存在的一些攻击.

2. 应用场景需求分析

在该场景的通信下可能会遇到一系列的攻击:

1.唯密文攻击(Ciphtext Only Attack,COA)唯密文攻击(COA)是指仅仅知道密文的情况下进行分析,求解明文或密钥的密码分析方法。假定密码分析者拥有密码算法及明文统计特性,并截获了一个或者多个用同一密钥加密的密文,通过对这些密文进行分析求出明文或密钥。COA已知条件最少,经不起唯密文攻击的密码是被认为不安全的。

2.已知明文攻击已知明文攻击(KPA)是指攻击者掌握了部分的明文M和对应的密文C,从而求解或破解出对应的密钥和加密算法。

3. 选择明文攻击(Chosen Plaintext Attack,CPA)选择明文攻击(CPA)是指攻击者除了知道加密算法外,还可以选定明文消息,从而得到加密后的密文,即知道选择的明文和加密的密文,但是不能直接攻破密钥。

4.选择密文攻击(Chosen Ciphertext Attack,CCA)选择密文攻击(CCA)是指攻击者可以选择密文进行解密,除了知道已知明文攻击的基础上,攻击者可以任意制造或选择一些密文,并得到解密的明文,是一种比已知明文攻击更强的攻击方式。若一个密码系统能抵抗选择密文攻击,那必然能够抵抗COA和KPA攻击。 密码分析者的目标是推出密钥,CCA主要应用于分析公钥密钥体制。

如果要实现安全的通信,那么我们需要保证以下方面:
  1. 通信双方之间传输的信息是要经过加密的,如果是明文传输,那么所有人都可以看到通信双方之间传输的内容,即通信双方传输的消息是机密的。
  1. 通信双方之间可以确认对方发给自己的消息是原本的,即没有经过任何人的修改,即消息是完整的。
  1. 通信双方之间需要确认对方的身份,即发送方和接收方都需要有一个能够唯一证明自己身份的标识,同时,任何一方只要发送了消息,就不能对自己发送的内容进行否认,即具有不可否认性。

3. 需要用到的安全技术

针对2中描述的安全需求,我们可以利用如下的安全技术:
  1. 对于需求1,我们可以使用对称密码体制中的加密算法对消息内容进行加密,最常用的算法就是AES算法,该算法的256bit,CBC模式可以提供很强的安全性。
  1. 对于需求2,我们可以使用哈希函数,一个足够好的哈希函数,拥有单向性和抗强弱碰撞性,不同的输入很难生成相同的输出,且攻击者无法利用哈希值来求出原值,因此我们只需要在每次的消息之后附加上计算得到的消息的哈希值即可实现消息的完整性认证。
  1. 对于需求3,我们需要用到的是非对称密码体制、数字签名、公钥证书这三个技术或者工具,数字签名属于非对称密码体制中的一个子类。应用场景的前提是:所有参与方均各自生成了公私钥对,并能够生成会话密钥;均已经从同一个CA中心获取了各自的公钥证书。那么,我们就可以从CA获取通信对方的公钥,用来验证对方的身份,同时使用自己的私钥可以对发送的消息进行数字签名,实现自不可否认性。

4. 总体设计流程

4.1 系统架构图

为了简介明了,本文首先直接给出系统的架构图,上述系统中AB之间发送、接收数据均使用Windows Socket网络通信实现。
notion image

1.1 总体的设计思路和相关技术原理

主要内容方面,本次实验通过C++实现AB双方的简易安全数据传输系统,A方在进行传输数据时,使用AES-CBC模式加密算法对明文加密,其中密钥长度为128bit,对称加密加密算法使用RSA算法。
发送方:
A对选取的数据通过SHA256计算哈希值,然后使用A的私钥对哈希数据生成数字签名,A通过提取B的证书中的公钥来加密A的AES密钥,并把以上过程得到的密文C,用B的公钥加密后的密钥key,以及A的数字签名sigA通过TCP数据包发送给B。
接收方:
在B接收到A发送的所有消息后,首先使用自己的私钥解密得到AES的密钥K,然后使用AES的密钥K对加密文件进行解密,然后提取A的证书中的公钥,解密签名SigA,然后B再对之前解密得到的明文计算自己的SHA256哈希值,将二者进行对比,验证签名的正确性。最后,通过比对文本的方式判断明文文件和恢复明文文件是否相同。
notion image
4.2.1 采用的安全技术分析
相关原理方面,这里只需要介绍AES、RSA、SHA256的相关原理即可。
AES的全称是Advanced Encryption Standard,意思是高级加密标准。它的出现主要是为了取代DES加密算法的,因为我们都知道DES算法的密钥长度是56Bit,因此算法的理论安全强度是2的56次方。但二十世纪中后期正是计算机飞速发展的阶段,元器件制造工艺的进步使得计算机的处理能力越来越强,虽然出现了3DES的加密方法,但由于它的加密时间是DES算法的3倍多,64Bit的分组大小相对较小,所以还是不能满足人们对安全性的要求。于是1997年1月2号,美国国家标准技术研究所宣布希望征集高级加密标准,用以取代DES。AES也得到了全世界很多密码工作者的响应,先后有很多人提交了自己设计的算法。最终有5个候选算法进入最后一轮:Rijndael,Serpent,Twofish,RC6和MARS。最终经过安全性分析、软硬件性能评估等严格的步骤,Rijndael算法获胜。
1977年,三位数学家Rivest、Shamir 和 Adleman 设计了一种算法,可以实现非对称加密。这种算法用他们三个人的名字命名,叫做RSA算法。从那时直到现在,RSA算法一直是最广为使用的"非对称加密算法"。毫不夸张地说,只要有计算机网络的地方,就有RSA算法。RSA公钥加密算法是一种非对称加密技术,也就是加密使用的密钥(公钥)和解密用的密钥(私钥)不是同一把。在加密信息数据之前,接收方会把加密用的公钥对外公开,发送方用公钥进行加密信息数据成为密文,而解密的密钥一直都保存在接收方手中。但是普通的非对称加密技术很容易被暴力破解,因此为了保证信息数据的安全,需要更安全的公钥加密技术,RSA公钥加密算法在这种情况下应运而生。RSA公钥加密算法是基于一个简单的数论事实:将两个大的质数相乘很容易得到乘积,但要把乘积进行因式分解却非常困难。这种算法非常可靠,密钥越长,它就越难破解。根据已经披露的文献,1999年就已经破解了512位的密钥,2009年又破解了768位的密钥,因此目前的RSA密钥通常采用的1024位的密钥,但随着量子计算机的发展,1024位的密钥的安全性也受到了挑战,在未来十年内1024位的RSA密钥会被逐渐淘汰,进而选择更长的钥匙。
SHA256是SHA-2下细分出的一种算法。SHA-2(安全哈希算法2)是由美国国家安全局(NSA)设计的一组加密哈希函数。SHA-2系列由六个具有224、256、384或512位摘要(哈希值)的哈希函数组成:SHA-224,SHA-256,SHA-384,SHA-512,SHA-512 / 224,SHA -512/256。SHA-256和SHA-512是分别用32位和64位字计算的哈希函数。它们使用不同的移位量和加性常数,但是它们的结构实际上是相同的,只是轮数不同。SHA256其实就是一个哈希函数。哈希函数,又称散列算法,是一种从任何一种数据中创建小的数字“指纹”的方法。散列函数把消息或数据压缩成摘要,使得数据量变小,将数据的格式固定下来。该函数将数据打乱混合,重新创建一个叫做散列值(或哈希值)的指纹。散列值通常用一个短的随机字母和数字组成的字符串来代表。对于任意长度的消息,SHA256都会产生一个256bit长的哈希值,称作消息摘要。这个摘要相当于是个长度为32个字节的数组,通常用一个长度为64的十六进制字符串来表示。

1. 系统详细设计

5.1AES模块整体设计

本模块的主要功能是实现AES加密,使用openSSL库中的AES加密功能来实现,主要包含2个函数,分别实现2功能。

5.2AES模块中各函数设计

5.2.1 AES-128加密函数设计
本函数的主要功能是实现AES加密功能,输入为明文和密钥,由于CBC模式需要进行填充,当明文长度不是加密块的整数倍时进行填充。输出为密文。核心算法是AES算法,加密模式为CBC,密钥长度为128bit。
5.2.2 AES-128解密函数设计
本函数的主要功能是实现AES解密功能,输入为密文和密钥,输出为明文。核心算法是AES算法。

5.3 SHA256模块整体设计

本模块的主要功能是实现SHA256哈希值的计算,使用openSSL库中的SHA来实现。

5.4SHA256模块中各函数设计

5.4.1sha256函数设计
本函数的主要功能是实现SHA256的计算,输入为待计算的数据,输出为256bit的SHA256计算结果。核心算法是SHA256算法。

5.5RSA模块整体设计

本模块的主要功能是实现RSA加密和解密算法,以及解析、提取公钥证书文件功能。使用openSSL库中的RSA和X509证书相关功能接口来实现。主要包含6个函数,分别实现6功能。

5.6RSA模块中各函数设计

RSA公钥加密函数设计
本函数的主要功能是实现RSA公钥加密功能,输入为RSA公钥和待加密数据,输出为RSA加密后的结果。核心算法是RSA加密算法。
RSA公钥解密函数设计
本函数的主要功能是实现RSA公钥解密功能,输入为RSA公钥和待解密数据,输出为RSA解密后的结果。核心算法是RSA解密算法。
RSA私钥加密函数设计
本函数的主要功能是实现RSA私钥加密功能,输入为RSA私钥和待加密数据,输出为RSA加密后的结果。核心算法是RSA加密算法。
RSA公钥解密函数设计
本函数的主要功能是实现RSA私钥解密功能,输入为RSA私钥和待解密数据,输出为RSA解密后的结果。核心算法是RSA解密算法。
X509证书解析函数设计
本函数的主要功能是实现对外部存储器上的X509格式的证书解析,输入为证书路径,输出为X509证书对象。
证书公钥提取函数设计
本函数的主要功能是实现从证书对象中提取出所需的RSA公钥,以用于后续的各种加密、解密过程。输入为证书对象,输出为证书提取出的RSA公钥。

5.7 Socket通信模块

本模块的主要功能是实现AB双方的基本通信过程,保证信息的交互。主要使用winSock2的相关API来设计发送方和接收方的通信逻辑
通信协议设计如下:
notion image

1. 系统设计实现

实现细节和文件说明

工程文件说明:
notion image
根目录下共含有三个文件夹 A:发送方 B:接收方 Common:AB两个项目共同需要用到的公用函数
A文件夹: 内容如下:
notion image
/A/A:包含A的私钥文件 Aprikey.pem ./cert:包含证书文件 libXXXXX.dll:openSSL所需的两个动态链接库 ./A/源.cpp:使用winsocket写的发送方通信代码
Common文件夹: 内容如下:
notion image
./Common/AES.hpp: 实现AES加密解密
./Common/RSA.hpp: RSA算法和证书的解析、公钥提取
./Common/sha256.hpp: SHA256算法
./Common/winSockinit.hpp:winSocket相关的初始化函数

主要函数代码:

AES加密、解密

1. #include <openssl/bio.h> 2. #include <openssl/pem.h> 3. #include <openssl/aes.h> 4. //AES加密 5. int aes128_encrypt(unsigned char* str_in,size_t inlen, unsigned char* out,unsigned char * key) 6. { 7. int i; 8. AES_KEY aes; 9. unsigned char iv[AES_BLOCK_SIZE] = { 0 }; 10. if (!str_in || !out) 11. return 0; 12. for (i = 0; i < 16; ++i) //生成IV 13. iv[i] = i + 32; 14. if (AES_set_encrypt_key((unsigned char*)key, 128, &aes) < 0) { 15. return 0; 16. } 17. AES_cbc_encrypt((unsigned char*)str_in, (unsigned char*)out, inlen, &aes, iv, AES_ENCRYPT); 18. return 1; 19. } 20. 21. //AES解密 22. int aes128_decrypt(unsigned char* str_in, size_t inlen, unsigned char* out, unsigned char* key) 23. { 24. int i; 25. AES_KEY aes; 26. unsigned char iv[AES_BLOCK_SIZE] = { 0 }; 27. 28. if (!str_in || !out) 29. return -1; 30. //确保里面的内容加密解密一样 31. for (i = 0; i < 16; ++i) 32. iv[i] = i + 32; 33. 34. if (AES_set_decrypt_key((unsigned char*)key, 128, &aes) < 0) 35. { 36. return -1; 37. } 38. 39. AES_cbc_encrypt((unsigned char*)str_in, (unsigned char*)out, inlen, &aes, iv, AES_DECRYPT); 40. return 0; 41. }
SHA256:
1. #include <openssl/sha.h> 2. #include <string> 3. std::string sha256(const std::string str); 4. std::string sha256(const std::string str) 5. { 6. char buf[2]; 7. unsigned char hash[SHA256_DIGEST_LENGTH]; 8. SHA256_CTX sha256; 9. SHA256_Init(&sha256); 10. SHA256_Update(&sha256, str.c_str(), str.size()); 11. SHA256_Final(hash, &sha256); 12. std::string NewString = ""; 13. for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) 14. { 15. sprintf(buf, "%02x", hash[i]); 16. NewString = NewString + buf; 17. } 18. return NewString; 19. }
RSA加密解密:
1. #include <openssl/bio.h> 2. #include <openssl/rsa.h> 3. #include <openssl/pem.h> 4. #include <iostream> 5. //RSA私钥加密 6. //返回值:RSA加密后的数据 7. std::string rsa_pri_encrypt(unsigned char * clearText,size_t inlen, RSA * rsa) 8. { 9. std::string strRet; 10. 11. int len = RSA_size(rsa); 12. char* encryptedText = (char*)malloc(len + 1); 13. memset(encryptedText, 0, len + 1); 14. 15. // 加密 16. int ret = RSA_private_encrypt(inlen, clearText, (unsigned char*)encryptedText, rsa, RSA_PKCS1_PADDING); 17. if (ret >= 0) 18. strRet = std::string(encryptedText, ret); 19. 20. // 释放内存 21. free(encryptedText); 22. //BIO_free_all(keybio); 23. //RSA_free(rsa); 24. 25. return strRet; 26. } 27. 28. //公钥加密 29. std::string rsa_pub_encrypt(unsigned char* clearText, size_t inlen, RSA* rsa) 30. { 31. std::string strRet; 32. 33. int len = RSA_size(rsa); 34. char* encryptedText = (char*)malloc(len + 1); 35. memset(encryptedText, 0, len + 1); 36. 37. // 加密 38. int ret = RSA_public_encrypt(inlen, clearText, (unsigned char*)encryptedText, rsa, RSA_PKCS1_PADDING); 39. if (ret >= 0) 40. strRet = std::string(encryptedText, ret); 41. 42. // 释放内存 43. free(encryptedText); 44. //BIO_free_all(keybio); 45. //RSA_free(rsa); 46. 47. return strRet; 48. } 49. 50. // 公钥解密 51. //返回解密后的数据 52. std::string rsa_pub_decrypt(unsigned char * clearText,size_t inlen,RSA* rsa) 53. { 54. std::string strRet; 55. 56. int len = RSA_size(rsa); 57. char* encryptedText = (char*)malloc(len + 1); 58. memset(encryptedText, 0, len + 1); 59. 60. //解密 61. int ret = RSA_public_decrypt(inlen, clearText, (unsigned char*)encryptedText, rsa, RSA_PKCS1_PADDING); 62. if (ret >= 0) 63. strRet = std::string(encryptedText, ret); 64. 65. // 释放内存 66. free(encryptedText); 67. //BIO_free_all(keybio); 68. //RSA_free(rsa); 69. 70. return strRet; 71. } 72. //私钥解密 73. std::string rsa_pri_decrypt(unsigned char* clearText, size_t inlen, RSA* rsa) 74. { 75. std::string strRet; 76. 77. int len = RSA_size(rsa); 78. char* encryptedText = (char*)malloc(len + 1); 79. memset(encryptedText, 0, len + 1); 80. 81. //解密 82. int ret = RSA_private_decrypt(inlen, clearText, (unsigned char*)encryptedText, rsa, RSA_PKCS1_PADDING); 83. if (ret >= 0) 84. strRet = std::string(encryptedText, ret); 85. 86. // 释放内存 87. free(encryptedText); 88. //BIO_free_all(keybio); 89. //RSA_free(rsa); 90. 91. return strRet; 92. }
证书提取和解析、公钥获取
1. //读取证书文件到内存X509格式 2. void p_getX509FromCertFile(const std::string filePath, X509** x509) 3. { 4. BIO* bio = NULL; 5. bio = BIO_new_file(filePath.c_str(), "r"); 6. if (!bio) 7. { 8. goto free_all; 9. } 10. 11. *x509 = PEM_read_bio_X509(bio, x509, 0, NULL); 12. 13. if (!*x509) 14. { 15. std::cout << "解析X509 Base64证书失败\n"; 16. goto free_all; 17. } 18. 19. free_all: 20. if (bio) 21. BIO_free(bio); 22. 23. } 24. //从证书数据中获取RSA 25. void p_getRSAKeyFromX509(X509* x509, RSA** rsa) 26. { 27. EVP_PKEY* pKey = X509_get_pubkey(x509); 28. if (!pKey) 29. { 30. std::cout << "提取证书公钥失败\n"; 31. return; 32. } 33. *rsa = EVP_PKEY_get1_RSA(pKey); 34. }

证书的签发流程:

使用OpenSSL命令行完成证书的签发
  1. 先用openSSL工具生成一个CA证书,放在cert文件夹里面。步骤如下:
利用openssl工具 制作CA
(1)创建CA私钥,ca-key.pem,这里用openssl命令生成
(2)使用openssl 自颁发CA证书
命令:openssl req -new -x509 -days 3650 -key ca-key.pem -out ca.cer
  1. 用openSSL分别生成A和B的RSA私钥,然后通过openSSL工具签发证书,步骤如下:
  • 利用openssl工具生成私钥 pem Bprikey.pem
openssl genrsa -out Aprikey.pem 1024
openssl genrsa -out Bprikey.pem 1024
(2)生成csr请求文件
openssl req -new -key Aprikey.pem -out A.csr
openssl req -new -key Bprikey.pem -out B.csr
(3)使用CA签发证书 得到cer证书文件
openssl x509 -req -days 365 -sha1 -extensions v3_req -CA ca.cer -CAkey ca-key.pem -CAserial ca.srl -CAcreateserial -in A.csr -out A.cer
openssl x509 -req -days 365 -sha1 -extensions v3_req -CA ca.cer -CAkey ca-key.pem -CAserial ca.srl -CAcreateserial -in B.csr -out B.cer
完成上述步骤后将证书文件统一放在cert文件夹下。并保留一份自己的私钥文件。
notion image