密码协议实验——SIGMMA协议
最近密码学做了很多密码协议的实验,主要是在visual stdio 2019环境下运用miracl库、openssl库实现密码协议,利用socket实现通信。本文记录从配置环境开始,实现SIGMMA协议的每一步。
(马上要期末考试了,其他实验先鸽了)
实验1 DH认证密钥交换协议实现
SIGMA协议
原理
步骤
1.配置miracl和openssl库环境
配置miracl库环境
- 配置有点麻烦,首先是下载源码,贴一个miracl源码地址
链接:https://pan.baidu.com/s/17DXQYBwdNLyiIYq-GVjFdw
提取码:nht3
- 然后将源码编译生成静态链接库有点麻烦,可以直接用别人生成好的,贴一个生成好的静态库链接。(如果直接用别人生成好的可以不用下载源码)
链接:https://pan.baidu.com/s/1mVyfiZ-6LeUjW9S-WXf_8A
提取码:bvlz
然后将四个文件miracl.lib、miracl.pdb、miracl.h 和mirdef.h复制到项目文件夹下,并导入项目(右键项目->添加->现有项)
- 接下来配置环境:(右键项目->添加->属性)
- VC++目录->包含目录、库目录
包含目录中选择miracl源码中的include文件夹地址,库目录中选择miracl源码中的lib文件夹地址
-
链接器->常规->附加库目录:添加项目地址
-
链接器->输入->附加依赖项:输入miracl.lib
- C/C++目录->附件包含目录:选择包含目录中选择miracl源码中的include文件夹地址
注意:C/C++这一选项必须要在源文件中有.c/.cpp文件中的时候才会出现
注意:由于miracl库是由c编写的,如果要在c++文件中调用,需要加以下代码
extern "C"
{
#include "miracl.h"
#include "mirdef.h"
}
4.项目重新生成
配置openssl库环境
参考链接:<(15条消息) VS中OpenSSL环境配置示例_huang714的专栏-CSDN博客_vs配置openssl>
-
可选择类似于miracl库的源码编译,建议直接下载编译好的类库,贴一个地址
链接:https://pan.baidu.com/s/1Xw_mYFSDW59JGnkCvGUr2g
提取码:hsga -
配置环境
- VC++目录->包含目录、库目录
- 链接器->输入->附加依赖项:输入ssleay.lib、libeay.lib
- 将openssl文件夹中的libeay32.dll和ssleay32.dll文件项目所在文件夹中
- 项目重新生成
2.利用miral库实现以下功能
- 生成大素数
big g = mirvar(0); //循环群的生成元
big p = mirvar(0); //大素数
big q1 = mirvar(0); //利用q1辅助生成p
bigdig(150, 10, q1);//生成一个150位的随机数,512bit左右 200位660bit左右 300位1000bit左右
nxsafeprime(0, 0, q1, p);//生成一个比q大的安全素数P
printf("生成的大素数为:\n");
printf("p = ");
cotnum(p, stdout);
- 计算Alice、Bob密钥交换协议中的使用的公钥
big x1 = mirvar(0); //Alice的私钥
big x2 = mirvar(0); //Bob的私钥
big X1 = mirvar(0); //Alice的公钥
big X2 = mirvar(0); //Bob的公钥
copy(get_generator(p), g);
printf("循环群Gp的一个生成元为: \n");
printf("g=");
cotnum(g, stdout);
//计算各自公钥
convert(134, x1);
convert(2374, x2);
powmod(g, x1, p, X1); //计算Alice公钥
powmod(g, x2, p, X2); //计算Bob公钥
cout << "Alice、Bob公钥(g^x)分别为" << endl;
cotnum(X1, stdout);
cotnum(X2, stdout);
-
寻找循环群的生成元
寻找生成元是一个困难问题,难以找到一个循环群的所有生成元,可用遍历的方法,找到其中一个生成元。
原根的判定方法:
g 2 m o d p ! = 1 a n d g q m o d p ! = 1 a n d g p − 1 m o d p = 1 p = 2 q ; g^2 mod p!=1 \quad and\quad g^q mod p !=1 \quad and \quad g^{p-1} mod p=1\\ p=2q; g2modp!=1andgqmodp!=1andgp−1modp=1p=2q;
生成原根源程序:generatorg.cppextern "C" { #include "miracl.h" #include "mirdef.h" } #include<stdio.h> #include<math.h> #include <stdlib.h> #include <conio.h> #include "miracl.h" #include "generatorg.h" big get_generator(big p) //得到p的一个原根 { big one = mirvar(1); big two = mirvar(2); big g = mirvar(0); //原根 big r1 = mirvar(0); big r2 = mirvar(0); big q = mirvar(0); big temp_up = mirvar(0); decr(p, 1, temp_up); //temp_up=p-1 fdiv(temp_up, two, q); //q= while (TRUE) { bigrand(temp_up, g); // 随机生成一个小于p-1的数 powmod(g, two, p, r1); powmod(g, q, p, r2); if (mr_compare(r1, one) && mr_compare(r2, one)) { //mr_compare(big x,big y): x=y时,返回0 即g^2=1 mod p && g^(p-1/2)=1 mod p return g; } } }
头文件:generator.h
#pragma once #include<stdio.h> #include<math.h> #include "miracl.h" #include <stdlib.h> #include <conio.h> big get_generator(big p);
附:miracl常用库函数
参考链接https://blog.csdn.net/shuilan0066/article/details/8520337
详见miracl库中的manual.doc文档
函数原型 | 函数功能 |
---|---|
miracl *mirsys(int nd, int nb) | 初始化MIRACL系统,nd为位数,nb为进制 |
void mirkill(big x) | 释放内存大数所占的内存 |
void mirexit() | 清除MIRACL系统,释放所有内部变量 |
void absol(big x,big y) | 求绝对值 |
void add(big x,big y,big z) | 求大数加法 z=x+y |
void bigbits(int n,big x) | 随机产生一个n位的大数 |
void bigdig(int n, int b, big x) | 产生一个指定长度的进制的随机数,n为长度,b为进制 |
void bigrand(big w, big x) | 产生一个小于w的大数随机数 |
int cinstr(big x, char*s) | 将大数字符串转换成大数 |
int cotstr(big x, char *s) | 大数根据其进制转换成一个字符串,返回字符串长度 |
int mr_compare(big x,big y) | 比较两个数大小: x>y时返回+1, x=y时返回0, x<y时返回-1 |
void convert(int n, big x) | 将一个整数n转换成一个大数x |
void copy(big x, big y) | 将一个大数赋值给另一个大数 |
void incr(big x, int n, big z) | 一个大数加上一个整数 z=x+n |
void decr(big x, int n, big z) | 一个大数减去一个整数, z=x-n |
void divide(big x, big y, big z) | 两个大数相除,z=x/y; x=x mod y |
void multiply(big x, big y, big z) | 两个大数相乘,z=x.y |
void fft_mult(big x, big y, big z) | 使用Fast Fourier算法计算两个大数乘积,z=x.y |
BOOL subdivisible(big x, int n) | 测试n能否整除x |
BOOL divisible(big x, big y) | 测试大数x能否整除大数y |
void premult(big x, int n, big z) | 一个大数乘以一个整数,z=n.x |
int subdiv(big x, int n, big z) | 一个大数除以一个整数,z=x/n,返回余数 |
intigcd(int x, int y) | 返回两个整数的最大公约数 |
int egcd(bigx, big y, big z) | 计算两个大数的最大公约数,z=gcd(x,y) |
int xgcd(bigx, big y, big xd, big yd, big z) | 计算两个大数的扩展最大公约数,也可以用来计算模逆 |
void negify(big x, big y) | 大数取负号 |
int numdig(big x) | 返回大数x中数字的个数 |
void expb2(int n, big x) | 计算2的n次方的大数 |
void expint(intb, int n, big x) | 计算b的n次方的大数 |
unsigned int invers(unsigned int x, unsigned int y) | 计算两个无符号整数(要求互素)的模逆,返回x-1 mod y |
BOOL isprime(bigx) | 判断一个大数是否为素数 |
void powmod(big x, big y,big z, big w) | 模幂运算, w = x y m o d z w=x^y mod z w=xymodz |
void sftbit(big x, int n,big z) | 将一个大数左移或右移n位,n为正数时左移,负数时右移 |
3.利用openssl库实现以下功能
参考链接:<C/C++使用openssl进行摘要和加密解密(md5, sha256, des, rsa) - findumars - 博客园 (cnblogs.com)>
- 对身份信息进行摘要(以md5为例)
SIGMA协议中,需要对Alice、Bob的身份信息(姓名、身份证号等)进行摘要,
生成摘要函数源程序:mac.cpp
//利用openssl 生成md5摘要
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cassert>
#include <string>
#include <vector>
#include "openssl/md5.h"
#include "openssl/pem.h"
#include "mac.h"
using namespace std;
void md5(string& srcStr, string& encodedStr, string& encodedHexStr) //传入明文、摘要字符、摘要字符、摘要字符串
{
// 调用md5哈希
unsigned char mdStr[33] = { 0 };
MD5((const unsigned char*)srcStr.c_str(), srcStr.length(), mdStr);//c_str()函数返回一个指向正规C字符串的指针常量, 内容与本string串相同,为了与c兼容
// 哈希后的字符串
encodedStr = string((const char*)mdStr);
// 哈希后的十六进制串 32字节
char buf[65] = { 0 };
char tmp[3] = { 0 };
for (int i = 0; i < 32; i++)
{
sprintf(tmp, "%02x", mdStr[i]); //sprintf() 格式化输出函数(图形)
//x 表示以十六进制形式输出,02 表示不足两位, 前面补0输出,如果超过两位,则以实际输出
strcat(buf, tmp);
}
buf[32] = '\0'; // 后面都是0,从32字节截断
encodedHexStr = string(buf);
}
生成摘要函数头文件:mac.h
#pragma once
#include <iostream>
#include <cassert>
#include <string>
#include <vector>
#include "openssl/md5.h"
#include "openssl/pem.h"
using namespace std;
void md5(string& srcStr, string& encodedStr, string& encodedHexStr);
-
对公钥 ( g x , g y ) (g^x,g^y) (gx,gy)进行签名并验证
对公钥签名源程序:sign.cpp
#include<openssl/pem.h>
#include<openssl/ssl.h>
#include<openssl/rsa.h>
#include<openssl/evp.h>
#include<openssl/bio.h>
#include<openssl/err.h>
#include <stdio.h>
#include<iostream>
#include<fstream>
#include "sign.h"
using namespace std;
//把字符串写成public.pem文件
int createPublicFile(const char* file, const string& pubstr)
{
if (pubstr.empty())
{
printf("public key read error\n");
return (-1);
}
int len = pubstr.length();
string tmp = pubstr;
for (int i = 64; i < len; i += 64)
{
if (tmp[i] != '\n')
{
tmp.insert(i, "\n");
}
i++;
}
tmp.insert(0, "-----BEGIN PUBLIC KEY-----\n");
tmp.append("\n-----END PUBLIC KEY-----\n");
//写文件
ofstream fout(file);
fout << tmp;
return (0);
}
//把字符串写成private.pem文件
int createPrivateFile(char* file, const string& pristr)
{
if (pristr.empty())
{
printf("public key read error\n");
return (-1);
}
int len = pristr.length();
string tmp = pristr;
for (int i = 64; i < len; i += 64)
{
if (tmp[i] != '\n')
{
tmp.insert(i, "\n");
}
i++;
}
tmp.insert(0, "-----BEGIN RSA PRIVATE KEY-----\n");
tmp.append("-----END RSA PRIVATE KEY-----\n");
//写文件
ofstream fout(file);
fout << tmp;
return (0);
}
//读取密钥
RSA* createRSA(unsigned char* key, int publi)
{
RSA* rsa = NULL;
BIO* keybio;
keybio = BIO_new_mem_buf(key, -1);
if (keybio == NULL)
{
printf("Failed to create key BIO\n");
return 0;
}
if (publi)
rsa = PEM_read_bio_RSA_PUBKEY(keybio, &rsa, NULL, NULL);
else
rsa = PEM_read_bio_RSAPrivateKey(keybio, &rsa, NULL, NULL);
if (rsa == NULL)
printf("Failed to create RSA\n");
return rsa;
}
//私钥签名
int private_sign(const unsigned char* in_str, unsigned int in_str_len, unsigned char* outret, unsigned int* outlen, unsigned char* key)
{
RSA* rsa = createRSA(key, 0);
int result = RSA_sign(NID_sha1, in_str, in_str_len, outret, outlen, rsa);
if (result != 1)
{
printf("sign error\n");
return -1;
}
return result;
}
//公钥验签
int public_verify(const unsigned char* in_str, unsigned int in_len, unsigned char* outret, unsigned int outlen, unsigned char* key)
{
RSA* rsa = createRSA(key, 1);
int result = RSA_verify(NID_sha1, in_str, in_len, outret, outlen, rsa);
if (result != 1)
{
printf("verify error\n");
return -1;
}
return result;
}
对公钥签名头文件:sign.h
#pragma once
#include<openssl/pem.h>
#include<openssl/ssl.h>
#include<openssl/rsa.h>
#include<openssl/evp.h>
#include<openssl/bio.h>
#include<openssl/err.h>
#include <stdio.h>
#include<iostream>
#include<fstream>
using namespace std;
int createPublicFile(const char* file, const string& pubstr);
int createPrivateFile(char* file, const string& pristr);
RSA* createRSA(unsigned char* key, int publi);
int private_sign(const unsigned char* in_str, unsigned int in_str_len, unsigned char* outret, unsigned int* outlen, unsigned char* key);
int public_verify(const unsigned char* in_str, unsigned int in_len, unsigned char* outret, unsigned int outlen, unsigned char* key);
主程序:main.cpp
#define _CRT_SECURE_NO_WARNINGS
extern "C"
{
#include "miracl.h"
#include "mirdef.h"
}
#include <iostream>
#include <cstdio>
#include <string.h>
#include "miracl.h"
#include "mirdef.h"
#include "generatorg.h"
#include "mac.h"
#include "sign.h"
using namespace std;
//ALice公钥
char publicKey1[] = "-----BEGIN PUBLIC KEY-----\n"\
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy8Dbv8prpJ/0kKhlGeJY\n"\
"ozo2t60EG8L0561g13R29LvMR5hyvGZlGJpmn65+A4xHXInJYiPuKzrKUnApeLZ+\n"\
"vw1HocOAZtWK0z3r26uA8kQYOKX9Qt/DbCdvsF9wF8gRK0ptx9M6R13NvBxvVQAp\n"\
"fc9jB9nTzphOgM4JiEYvlV8FLhg9yZovMYd6Wwf3aoXK891VQxTr/kQYoq1Yp+68\n"\
"i6T4nNq7NWC+UNVjQHxNQMQMzU6lWCX8zyg3yH88OAQkUXIXKfQ+NkvYQ1cxaMoV\n"\
"PpY72+eVthKzpMeyHkBn7ciumk5qgLTEJAfWZpe4f4eFZj/Rc8Y8Jj2IS5kVPjUy\n"\
"wQIDAQAB\n"\
"-----END PUBLIC KEY-----\n";
//Alice私钥
char privateKey1[] = "-----BEGIN RSA PRIVATE KEY-----\n"\
"MIIEowIBAAKCAQEAy8Dbv8prpJ/0kKhlGeJYozo2t60EG8L0561g13R29LvMR5hy\n"\
"vGZlGJpmn65+A4xHXInJYiPuKzrKUnApeLZ+vw1HocOAZtWK0z3r26uA8kQYOKX9\n"\
"Qt/DbCdvsF9wF8gRK0ptx9M6R13NvBxvVQApfc9jB9nTzphOgM4JiEYvlV8FLhg9\n"\
"yZovMYd6Wwf3aoXK891VQxTr/kQYoq1Yp+68i6T4nNq7NWC+UNVjQHxNQMQMzU6l\n"\
"WCX8zyg3yH88OAQkUXIXKfQ+NkvYQ1cxaMoVPpY72+eVthKzpMeyHkBn7ciumk5q\n"\
"gLTEJAfWZpe4f4eFZj/Rc8Y8Jj2IS5kVPjUywQIDAQABAoIBADhg1u1Mv1hAAlX8\n"\
"omz1Gn2f4AAW2aos2cM5UDCNw1SYmj+9SRIkaxjRsE/C4o9sw1oxrg1/z6kajV0e\n"\
"N/t008FdlVKHXAIYWF93JMoVvIpMmT8jft6AN/y3NMpivgt2inmmEJZYNioFJKZG\n"\
"X+/vKYvsVISZm2fw8NfnKvAQK55yu+GRWBZGOeS9K+LbYvOwcrjKhHz66m4bedKd\n"\
"gVAix6NE5iwmjNXktSQlJMCjbtdNXg/xo1/G4kG2p/MO1HLcKfe1N5FgBiXj3Qjl\n"\
"vgvjJZkh1as2KTgaPOBqZaP03738VnYg23ISyvfT/teArVGtxrmFP7939EvJFKpF\n"\
"1wTxuDkCgYEA7t0DR37zt+dEJy+5vm7zSmN97VenwQJFWMiulkHGa0yU3lLasxxu\n"\
"m0oUtndIjenIvSx6t3Y+agK2F3EPbb0AZ5wZ1p1IXs4vktgeQwSSBdqcM8LZFDvZ\n"\
"uPboQnJoRdIkd62XnP5ekIEIBAfOp8v2wFpSfE7nNH2u4CpAXNSF9HsCgYEA2l8D\n"\
"JrDE5m9Kkn+J4l+AdGfeBL1igPF3DnuPoV67BpgiaAgI4h25UJzXiDKKoa706S0D\n"\
"4XB74zOLX11MaGPMIdhlG+SgeQfNoC5lE4ZWXNyESJH1SVgRGT9nBC2vtL6bxCVV\n"\
"WBkTeC5D6c/QXcai6yw6OYyNNdp0uznKURe1xvMCgYBVYYcEjWqMuAvyferFGV+5\n"\
"nWqr5gM+yJMFM2bEqupD/HHSLoeiMm2O8KIKvwSeRYzNohKTdZ7FwgZYxr8fGMoG\n"\
"PxQ1VK9DxCvZL4tRpVaU5Rmknud9hg9DQG6xIbgIDR+f79sb8QjYWmcFGc1SyWOA\n"\
"SkjlykZ2yt4xnqi3BfiD9QKBgGqLgRYXmXp1QoVIBRaWUi55nzHg1XbkWZqPXvz1\n"\
"I3uMLv1jLjJlHk3euKqTPmC05HoApKwSHeA0/gOBmg404xyAYJTDcCidTg6hlF96\n"\
"ZBja3xApZuxqM62F6dV4FQqzFX0WWhWp5n301N33r0qR6FumMKJzmVJ1TA8tmzEF\n"\
"yINRAoGBAJqioYs8rK6eXzA8ywYLjqTLu/yQSLBn/4ta36K8DyCoLNlNxSuox+A5\n"\
"w6z2vEfRVQDq4Hm4vBzjdi3QfYLNkTiTqLcvgWZ+eX44ogXtdTDO7c+GeMKWz4XX\n"\
"uJSUVL5+CVjKLjZEJ6Qc2WZLl94xSwL71E41H4YciVnSCQxVc4Jw\n"\
"-----END RSA PRIVATE KEY-----\n";
int main()
{
miracl* mips = mirsys(2400, 10);
big g = mirvar(0); //循环群的生成元
big p = mirvar(0); //大素数
big q1 = mirvar(0); //利用q1辅助生成p
big x1 = mirvar(0); //Alice的私钥
big x2 = mirvar(0); //Bob的私钥
big X1 = mirvar(0); //Alice的公钥
big X2 = mirvar(0); //Bob的公钥
bigdig(100, 10, q1);//生成一个150位的随机数,512bit左右 200位660bit左右 300位1000bit左右
nxsafeprime(0, 0, q1, p);//生成一个比q大的安全素数P
printf("生成的大素数为:\n");
printf("p = ");
cotnum(p, stdout);
copy(get_generator(p), g);
printf("循环群Gp的一个生成元为: \n");
printf("g=");
cotnum(g, stdout);
//计算各自公钥
convert(134, x1);
convert(2374, x2);
powmod(g, x1, p, X1); //计算Alice公钥
powmod(g, x2, p, X2); //计算Bob公钥
cout << "Alice、Bob公钥(g^x)分别为" << endl;
cotnum(X1, stdout);
cotnum(X2, stdout);
//对Alice、Bob各自身份的身份信息进行加密(以名字为例)
string srcText1 = "Alice";
string srcText2 = "Bob";
string encryptText1;
string encryptHexText1;
string encryptText2;
string encryptHexText2;
cout << "===Alice、Bob的身份信息分别为 ===" << endl;
cout << srcText1 <<" "<<srcText2<<endl;
cout << "===对Alice、Bob的身份信息进行md5摘要结果为 ===" << endl;
md5(srcText1, encryptText1, encryptHexText1);
md5(srcText2, encryptText2, encryptHexText2);
cout << "Alice身份信息摘要字符: " << encryptText1<< endl;
cout << "Alice身份信息摘要串(16进制32字节): " << encryptHexText1 << endl;
cout << "Bob身份信息摘要字符: " << encryptText2 << endl;
cout << "Bob身份信息摘要串(16进制32字节): " << encryptHexText2<< endl;
//签名
char gx[2048],gy[1024];
cotstr(X1, gx);
cotstr(X2, gy);
strcat(gx, gy);
printf("%s\n", gx);
cout << strlen(gx) << endl;
unsigned char signret[4098] = {};
unsigned int siglen;
cout << "===私钥签名公钥认证===" << endl;
int ret = private_sign((const unsigned char*)gx, strlen(gx), signret, &siglen, (unsigned char*)privateKey1);
if (ret) cout << "签名成功" << endl;
else cout << "签名失败" << endl;
ret = public_verify((const unsigned char*)gx, strlen(gx), signret, siglen, (unsigned char*)publicKey1);
if (ret) cout << "认证成功" << endl;
else cout << "认证失败" << endl;
return 0;
}
运行结果
4.利用socket实现解法双方通信
通信部分是小组其他成员写的,就直接加在main函数里了。
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
//创建套接字
SOCKET clntSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
//向服务器发送消息,处理地址等信息
sockaddr_in sockAddr;
memset(&sockAddr, 0, sizeof(sockAddr)); //每个字节都用0填充
sockAddr.sin_family = PF_INET;
sockAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
sockAddr.sin_port = htons(8888);
connect(clntSock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR));
//接收服务器消息
char szBuffer[0xffff] = { 0 };
send(clntSock, ga, strlen(ga) + sizeof(char), NULL);
recv(clntSock, szBuffer, 0xffff, NULL);//定义一次接收的缓存数组的大小要用16进制
//输出接收到的数据
// cout << "gy:" << szBuffer << endl;
send(clntSock, unit, strlen(unit) + sizeof(char), NULL);
//szBuffer[0xffff] = { 0 };
recv(clntSock, szBuffer, 0xffff, NULL);
char* pstring;
char message[4][1024];
pstring = strtok(szBuffer, "|");
cout << "接收到的Bob的信息:" <<endl;
int i = 0;
while (pstring)
{
strcpy(message[i], pstring);
i++;
//printf("%s\n", pstring);
pstring = strtok(NULL, "|");
}
printf("Bob的公钥gy:%s\n", message[0]);
printf("Bob的身份:%s\n", message[1]);
printf("Bob的签名:%s\n", message[2]);
printf("Bob的摘要:%s\n", message[3]);
//szBuffer[0xffff] = { 0 };
send(clntSock, MACA, strlen(MACA) + sizeof(char), NULL);
recv(clntSock, szBuffer, 0xffff, NULL);
//cout << "BobMac签名:" << szBuffer << endl;
//关闭套接字
closesocket(clntSock);
//终止dll
WSACleanup();
运行结果