ElGamal算法NTL实现

算法流程

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进制数,然后拼接起来构成一个大整数即可。

ElGamal算法是一种非对称加密算法,可以用于数据加密和数字签名。下面是使用C++实现ElGamal算法的示例代码: ``` #include <iostream> #include <cmath> using namespace std; int gcd(int a, int b) { if (a == 0) { return b; } return gcd(b % a, a); } int modExp(int base, int exponent, int modulus) { int result = 1; base = base % modulus; while (exponent > 0) { if (exponent % 2 == 1) { result = (result * base) % modulus; } exponent = exponent >> 1; base = (base * base) % modulus; } return result; } int main() { int p, g, x, y, k, m, r, s, inverseK; cout << "Enter the value of p: "; cin >> p; cout << "Enter the value of g: "; cin >> g; cout << "Enter the value of x: "; cin >> x; y = modExp(g, x, p); cout << "Public key (p, g, y): (" << p << ", " << g << ", " << y << ")" << endl; cout << "Enter the value of k (0 < k < p-1): "; cin >> k; inverseK = 0; while (inverseK < 1 || inverseK > p - 2) { cout << "Enter the value of inverse k (1 < k < p-1): "; cin >> inverseK; } cout << "Enter the message m (0 < m < p-1): "; cin >> m; r = modExp(g, k, p); s = (m - x*r) * inverseK % (p - 1); if (s < 0) { s += p - 1; } cout << "Signature (r, s): (" << r << ", " << s << ")" << endl; int v1 = modExp(y, r, p) * modExp(r, s, p) % p; int v2 = modExp(g, m, p); if (v1 == v2) { cout << "Signature is valid" << endl; } else { cout << "Signature is invalid" << endl; } return 0; } ``` 在这个示例中,我们输入了p、g和x,然后计算出公钥(y)。我们还要求输入k和m,然后计算出签名(r, s)。与EIGamal算法不同的是,我们还需要输入k的逆元(inverseK)。最后,我们验证签名是否有效。 需要注意的是,这里的modExp函数是用来计算模幂的。同时,我们在输入inverseK时,需要确保它在1到p-2之间,以确保它有一个逆元。在实际应用中,还需要注意选择合适的p、g和k值。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值