算法流程
1、ElGamal密钥生成
(1)随机选择一个大素数p,且要求p-1有大素数因子。再选择一个模p的本原元α。将p和α公开。
(2)随机选择两个随机数作为密钥,2≤d≤p-2 。
(3)计算y=α^d mod p,取y为公钥。
2、ElGamal加密
(1)对于明文M加密,随机地选取一个整数k,2≤k≤p-2
(2)C1=α^k mod p
(3)C2=MY^k mod p
(4)密文为(C1,C2)
3、ElGamal解密
由密文可得明文M,M=C2/C1^d mod p
二、使用步骤
1.引入NTL库
链接: NTL库
2.具体实现代码如下
#include<iostream>
#include<NTL/ZZ.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include<NTL/ZZ_p.h>
#include <string>
#include <sstream>
#include <iomanip>
#include<vector>
#include<random>
using namespace std;
using namespace NTL;
NTL_CLIENT
/*1、ElGamal密钥生成
(1)随机选择一个大素数p,且要求p-1有大素数因子。再选择一个模p的本原元α。将p和α公开。
(2)随机选择两个随机数作为密钥,2≤d≤p-2 。
(3)计算y=α^d mod p,取y为公钥。
2、ElGamal加密
(1)对于明文M加密,随机地选取一个整数k,2≤k≤p-2
(2)C1=α^k mod p
(3)C2=MY^k mod p
(4)密文为(C1,C2)
3、ElGamal解密
由密文可得明文M,M=C2/C1^d mod p
*/
/*计算a^b mod p*/
ZZ pow_mod(ZZ a, ZZ b, ZZ p) {
ZZ ans = conv<ZZ>(1);
ZZ tmp = a % p;
while (b != 0) {
if (bit(b, 0) == 1)
ans = (ans * tmp) % p;
b >>= 1;
tmp = (tmp * tmp) % p;
}
return ans % p;
}
void Elgamal_Encrypt(ZZ message, ZZ pub, ZZ p, ZZ g, ZZ* C1, ZZ* C2)
{
//long k1 = generateRandomNumber(1, 10000000);//生成随机数
SetSeed(ZZ(time(NULL))); // 设置随机数生成器的种子为当前时间
// 生成范围在[0, p-1]的随机大整数
ZZ Random = conv<ZZ>("100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003");
ZZ k = RandomBnd(Random);
std::cout << "随机数k:" << k << std::endl;
// ZZ k = conv<ZZ>("9");
//PowerMod(*C1, g, k, p);
*C1 = pow_mod(g, k, p);
// std::cout << "C1 : " << *C1 << std::endl;
//PowerMod(*C2, pub, k, p);
*C2 = message * pow_mod(pub, k, p) % p;
/* ZZ c;
c = message * (*C2);
rem(*C2, c, p);*/
//std::cout << "_C2 : " << *C2 << std::endl;
// std:: cout <<"C2 : "<< * C2 << std::endl;
}
/*`elgamal_en` 函数用于进行ElGamal加密。它接受明文 `m`、公钥 `pub`、素数 `p`、生成元 `g` 以及用于加密的随机数 `k`。
首先计算密文的第一个分量 `c1`,即 `c1 = g^k mod p
然后计算密文的第二个分量 `c2`,即 `c2 = M*y^k mod p */
//传入密文c1,c2,私钥pri,素数p,生成元g
ZZ Elgamal_Decrypt(ZZ C1, ZZ C2, ZZ pri, ZZ p, ZZ g)
{
ZZ m;//明文
//ZZ C1_;//C1逆元
//PowerMod(C1_, C1, p - 2, p);
//PowerMod(C1_, C1_, pri, p);
//rem(m, C2 * C1_, p);
//rem(m, C2, p);
ZZ C1_ = pow_mod(C1, p - 2, p);
m = C2 * pow_mod(C1_, pri, p) % p;
return m;
}
/*3. `elgamal_de` 函数用于进行ElGamal解密。
它接受密文的两个分量 `c1` 和 `c2`、私钥 `pri`、素数 `p` 以及生成元 `g`。首先计算 `c1` 的逆元 `c1_`,
即m=M=C2/C1^pri mod p
*/
/*素性判定*/
bool isPrime(const ZZ& n) {
if (ProbPrime(n))
return true;
return false;
}
//消息转16进制
std::string messageToHex(const std::string& message) {
std::stringstream ss;
for (char c : message)
{
ss << std::hex << std::setw(2) << std::setfill('0') << (int)(unsigned char)c;
}//16进制格式,setw(2) 设置输出的最小宽度为 2,setfill('0') 设置用零填充输出
return ss.str();
}
//16进制转消息
std::string hexToMessage(const std::string& hexString) {
std::string message;
std::stringstream ss;
// 每两个字符为一组,将十六进制字符串拆分
for (size_t i = 0; i < hexString.length(); i += 2) {
std::string hexByte = hexString.substr(i, 2);
// 将十六进制数转换为十进制数
int decimal = std::stoi(hexByte, nullptr, 16);
// 将十进制数转换为对应的 ASCII 字符
char c = static_cast<char>(decimal);
ss << c;
}
message = ss.str();
return message;
}
// 将16进制转换对应的10进制数值
int hexCharToInt(char c) {
if (c >= '0' && c <= '9') {
return c - '0';
}
else if (c >= 'a' && c <= 'f') {
return c - 'a' + 10;
}
else if (c >= 'A' && c <= 'F') {
return c - 'A' + 10;
}
// 对于非法字符,返回-1
return -1;
}
// 将16进制字符串转换为对应的10进制ZZ类型数值
NTL::ZZ hexToZZ(std::string hexStr) {
NTL::ZZ result = conv<ZZ>("0");
for (char c : hexStr)
{
int val = hexCharToInt(c);
if (val == -1)
{
// 如果遇到非法字符,抛出异常
throw std::invalid_argument("Invalid hex string encountered");
}
result = result * 16 + val;
}
return result;
}
//将10进制转为对应的16进制
char intToHexChar(int val) {
if (val >= 0 && val <= 9) {
return '0' + val;
}
else if (val >= 10 && val <= 15) {
return 'A' + val - 10;
}
// 非法值
return -1;
}
string ZZToHex(const ZZ& num) {
string hexStr;
ZZ quotient = num;
ZZ remainder;
while (quotient > 0) {
DivRem(quotient,remainder , quotient, ZZ(16));
int hexVal = conv<int>(remainder);
char hexChar = intToHexChar(hexVal);
if (hexVal == -1) {
// 如果遇到非法值,抛出异常
throw invalid_argument("Invalid decimal number encountered");
}
hexStr = hexChar + hexStr;
}
return hexStr;
}
/*消息转大数*/
ZZ MessagetoZZ(std::string message)
{
std::string hexString = messageToHex(message);//消息转转16进制
/* std::cout << "Message: " << message << std::endl;
std::cout << "Hex: " << hexString << std::endl;*/
NTL::ZZ zzNum = hexToZZ(hexString);//消息转16进制转大数
//std::cout << "Hexadecimal string " << hexString <<endl<< " ZZ number: "<< zzNum << std::endl;
return zzNum;
}
int main() {
ZZ p = conv<ZZ>("6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057151");//素数
// ZZ p = conv<ZZ>("2147483647");
//ZZ p = conv<ZZ>("13");
/*170141183460469231731687303715884105727
*
*
6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057151
*/
std::cout << "素数为:" << p << std::endl;
ZZ g = conv<ZZ>("2");//生成元;/*2为其他素数的生成元,具体有证明的 可以自行搜查*/
ZZ pri = conv<ZZ>("2001");//私钥
ZZ pub;//公钥
PowerMod(pub, g, pri, p);
// printf("用户A的公钥为:%d\n", pub);
std:: cout << "公钥为:" << pub << std::endl;
//std::string message="暗号问";
std::string message = "刘安问说是知音";//明文
NTL::ZZ message_Num =MessagetoZZ(message);//消息转16进制转大数
std::cout << "消息为: " << message_Num << std::endl;
if (message_Num > p)
{
std::cout << "明文长度过长" << std::endl;
}
ZZ C1, C2;
Elgamal_Encrypt(message_Num, pub, p, g, &C1, &C2);
std::cout << "公钥加密的密文 C1: " << C1 << " C2: " << C2 << std::endl;
ZZ message_ = Elgamal_Decrypt(C1, C2, pri, p, g);
std::cout << "私钥解密的解密文 :" << message_ << std::endl;
if (message_Num == message_)
{
std::cout << "Decrypting Right!" << std::endl;
}
string hex_message = ZZToHex(message_);
string de_message = hexToMessage(hex_message);
std::cout << "解密后的密文:" << de_message << std::endl;
return 0;
}
总结
这只是一个简单示例,具体参考其他博客。有意思的点,其实可以直接把每一个明文转为对应10进制数,然后拼接起来构成一个大整数即可。