公钥密码复习

公钥密码复习

DH协议#

1976年Diffie和Hellman首次提出公钥密码体制,并介绍了DH密钥协商协议,该协议基于离散对数问题,可以在一个两方的安全信道中协商出一个安全共享密钥,可解决对称加密中的密钥传输问题。

DH协议起初是在有限域上计算,即基于的是有限域上的离散对数问题。

有限域上的DH#

image-20220919162744317

  • 即给定一个有限域Gp,其中g是生成元,参数p,g公开。
  • 两方交互后各自计算出gab(modp),作为共享密钥。
  • 安全性依赖于有限遇上的离散对数问题:即使敌手获取了ga(modp)或者gb(modp),也不能得到a或者b

下面介绍基于椭圆曲线(ECC)上离散对数问题的DH协议。

椭圆曲线上的DH#

image-20220919163643411

  • 这里是由椭圆曲线上及上面的点构成的阿贝尔群Ep(a,b),其中点$G(x_1,y_1)为群的生成元。
  • 椭圆曲线的阶(即所有点,包括无穷远点)是一个很大的素数;生成元G的阶(即满足nG=O的最小正整数)是n
  • 两方交互后各自计算出nAnBG作为共享密钥。
  • 安全性依赖于椭圆曲线上的离散对数问题,即即使敌手获取了nAG或者nBG,也得不到nA或者nB

下面举一个例子:

image-20220919165225031

DH协议还可以扩展为多方,以三方为例:

扩展为多方#

image-20220919165452402

  • 就是先各自计算出gx,gy,gz,A发给B,B发给C,C再发给A。
  • 再发送一次,各方得到gayz

多方DH,会随着人数增加,通信轮数迅速增加,因此不适合用于群密钥协商。

攻击方式#

DH密钥协商不包含通信双方的身份认证,因此会受到中间人攻击,即敌手能截获替换的交互消息,从而监听通信内容。

image-20220919170444036

  • 中间人(敌手)可以获得gamgbm,在接下来两方的加密通信中可以获取通信内容。
  • 为了抵抗这种攻击,就是在通信中进行身份认证。

认证密钥协商#

下面介绍一种DH秘钥协商协议的改进协议-端对端STS协议,即引入签名进行身份认证。

image-20220919175416411

  • 引入一个可信的CA,用于生成密钥。
  • 两方在第一次交互后,需要将计算的消息签名后发送给对方。
  • 收到后,先验证签名的有效性,再去计算最终的密钥。

RSA#

1978年由Rivest,Shamir和Adleman提出的一种成熟的公钥加密方案,目前还在广泛应用,比如HTTPS中的加密。

RSA原方案:A Method for Obtaining Digital Signatures and Public-Key Cryptosystems-1978

因为RSA是一个公钥加密算法,密钥生成都是基于一个数学困难问题,即所谓的“只要给出一个数学困难问题,就能构造一个公钥加密算法”。

RSA算法基于的是大整数难分解问题,简单说就是:给定一个大整素数n=pq,找出pq是困难的。

加密方案#

密钥生成#

  • 选取两个保密得大素数qp
  • 计算n=pqφ(n)=(p1)(q1),其中φ(n)n的欧拉函数。(因为pq都是素数)
  • 选择一个整素数e,满足1<e<φ(n),且gcd(φ(n),e)=1。(即φ(n)e互素)
  • 计算d,且满足de=1modφ(n),即d=e1(modφ(n))
  • 所以形成公钥(e,n),私钥(d),在系统初始化后两个素数qpφ(n)是可以销毁的,但不能泄漏

下面举一个例子说明:

image-20220917205030129

加密#

  • 明文mMM={m|0<m<n}是明文空间,若明文较大(大于n),即将明文按比特分组,使得每个分组对应的十进制数小于n,即分组长度小于log2n
  • 加密函数E(m)=me(modn)
  • 计算出密文c=me(modn)

解密#

  • 解密函数D(c)=cd(modn)
  • 解密出明文m=cd(modn)

正确性#

  • cd(modn)=med(modn)=mkφ(n)+1(modn)
  • 分两种情况讨论:
    • gcd(m,n)=1,由欧拉定理得:mφ(n)=1(modn),mkφ(n)=1(modn),mkφ(n)+1=m(modn),即cd(modn)=m
    • gcd(m,n)1,参考(现代密码学-杨波),也能证明cd(modn)=m

安全性#

RSA的安全性基于大整数难分解问题的假定,因为大整数难分解问题至今未能证明是NP问题。

大整数难分解问题:给定大整数n=pq,难求出p,q

在RSA方案中,因为根据n,难以求出p,q,也就难以求出φ(n)=(p1))(q1),继而推不出私钥d=e1(modφ(n))

乘法同态#

给定两个密文c1=m1e(modn),c2=m2e(modn)

D(c1c2)=D((m1m2)e(modn)=(m1m2)de(modn)=m1m2

程序实现#

1、实现上述例子

只能实现小数计算

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#define randomInt(a,b) (rand()%(b-a)+a)//生成[a,b]之间的随机数

//是否为素数
int prime(int n)
{
    int i;
    if (n < 2) {
        return -1;
    }
    else {
        for (i = 2; i < n; i++) {//判断n在2~n-1中有没有因数
            if (n % i == 0)//如果用可以除尽的数,则非素数
                break;
        }
        if (i < n) {//存在2~n-1之间有因数
            return -1;
        }
        else
            return 0;
    }
    return 0;
}
//素数生成
int creat_Prime(int a,int b)
{
    int res,k;
    srand((unsigned)time(NULL));
    do
    {
        res = randomInt(a, b);
        k = prime(res);
    } while (k == -1);
    return res;
}

//求最大公约数,判断两个数是否互素
int gcd(int x, int y)
{
    int t;
    while (y) t = x, x = y, y = t % y;
    return x;
}

//扩展欧几里得算法 (C++实现)
int exgcd(int a, int b, int &x, int &y)
{
    if (b == 0)
    {
        x = 1, y = 0;
        return a;
    }
    int ret = exgcd(b, a % b, y, x);
    y -= a / b * x;
    return ret;
}

//求逆元:基于费马定理
int reverse(int a, int mod)
{
    int x, y;
    int d = exgcd(a, mod, x, y);
    return d == 1 ? (x % mod + mod) % mod : -1;
}

//快速幂模(a^b mod p)
int power(int a, int b, int p)
{
    int ans = 1 % p;
    while(b)
    {
        if(b & 1)
        {
            ans = ans * a % p;
        }
        b >>= 1;
        a = a * a % p;
    }
    return ans;
}

//加密算法测试
void encrypt_test()
{
    printf("\n------------RSA加密算法------------\n");

    printf("\n\t    1、密钥生成\n");
    //p和q是大素数;n=pq;z=(p-1)(q-1);任取e,使满足gcd(e,z)=1;d是e的逆;m是明文;c是密文;m1是解密后明文
    int p, q, n, z, e, d, m, c, m1;

    //随机生成p和q

    p = creat_Prime(1, 5);
    q = creat_Prime(5, 10);
    /*
    printf("请输入p:");
    scanf("%d", &p);
    printf("请输入q:");
    scanf("%d", &q);
    */

    //求n和z
    n = q * p;
    z = (p - 1) * (q - 1);

    //随机生成e:e和z互素,e < n

    do
    {
        e = creat_Prime(1, n);
    } while (gcd(e, z) != 1);

    /*
    printf("请输入e:");
    scanf("%d", &e);
    */

    //求d:d是e的逆元,mod z
    d = reverse(e, z);

    printf("p=%d\nq=%d\nn=%d\nz=%d\ne=%d\nd=%d\n", p,q,n,z,e,d);

    //输出公私钥
    printf("公钥为:{n,e}={%d,%d}\n", n, e);
    printf("私钥为:{d}={%d}\n", d);

    printf("\n\t    2、加密\n");

    //明文生成\输入
    printf("请输入明文m:");
    scanf("%d",&m);

    //加密
    c = power(m,e,n);

    printf("明文为:{m}={%d}\n", m);
    printf("密文为:{c}={%d}\n", c);

    printf("\n\t    3、解密\n");
    //解密
    m1 =power(c,d,n);
    printf("解密后明文:{m1}={%d}\n\n", m1);
}

//乘法同态测试
void mult_test()
{
    printf("\n------------RSA乘法同态------------\n");

    printf("\n\t    1、密钥生成\n");
    //p和q是大素数;n=pq;z=(p-1)(q-1);任取e,使满足gcd(e,z)=1;d是e的逆;m是明文;c是密文;m1是解密后明文
    int p, q, n, z, e, d, m_1,m_2,c_1,c_2,m,c;

    //随机生成p和q

    p = creat_Prime(1, 5);
    q = creat_Prime(5, 10);
    /*
    printf("请输入p:");
    scanf("%d", &p);
    printf("请输入q:");
    scanf("%d", &q);
    */

    //求n和z
    n = q * p;
    z = (p - 1) * (q - 1);

    //随机生成e:e和z互素,e < n

    do
    {
        e = creat_Prime(1, n);
    } while (gcd(e, z) != 1);

    /*
    printf("请输入e:");
    scanf("%d", &e);
    */

    //求d:d是e的逆元,mod z
    d = reverse(e, z);

    printf("p=%d\nq=%d\nn=%d\nz=%d\ne=%d\nd=%d\n", p,q,n,z,e,d);

    //输出公私钥
    printf("公钥为:{n,e}={%d,%d}\n", n, e);
    printf("私钥为:{d}={%d}\n", d);

    printf("\n\t    2、加密\n");

    //明文生成\输入
    printf("请输入明文m1:");
    scanf("%d",&m_1);
    printf("请输入明文m2:");
    scanf("%d",&m_2);
    //加密
    c_1 = power(m_1,e,n);
    c_2 =  power(m_2,e,n);
    printf("明文为:{m_1}={%d},{m_2}={%d}\n", m_1,m_2);
    printf("密文为:{c_1}={%d},{c_2}={%d}\n", c_1,c_2);

    printf("\n\t    3、乘法同态\n");
    //乘法同态
    c=c_1*c_2%n;
    printf("密文乘为:{c_1*c_2}={%d}\n",c);

    printf("\n\t    4、解密\n");
    //解密
    m = power(c,d,n);
    printf("解密后明文:{m_1*m_2}={%d}\n\n", m);
}

int main()
{
    int choose;
    printf("请输入序号:【1】加解密测试,【2】乘法同态性测试\n");
    scanf("%d",&choose);
    if(choose ==1)
    {
        //加解密测试
        encrypt_test();
    }else if(choose ==2)
    {
        //乘法同态性测试
        mult_test();
    }else
        printf("请重新输入!");
    system("pause");
    return 0;
}

image-20220919222052811

image-20220919221929301

2、不使用miracl库实现大数计算,可加解密txt文件(数字和字母)+

参考

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>

#define ACCURACY 5
#define SINGLE_MAX 10000
#define EXPONENT_MAX 1000
#define BUF_SIZE 1024

/**
 * 快速模幂:a^b mod c
 */
int modpow(long long a, long long b, int c) {
    int res = 1;
    while (b > 0) {
        /* Need long multiplication else this will overflow... */
        if (b & 1) {
            res = (res * a) % c;
        }
        b = b >> 1;
        a = (a * a) % c; /* Same deal here */
    }
    return res;
}

/**
 * Computes the Jacobi symbol, (a, n)
 */
int jacobi(int a, int n) {
    int twos, temp;
    int mult = 1;
    while (a > 1 && a != n) {
        a = a % n;
        if (a <= 1 || a == n) break;
        twos = 0;
        while (a % 2 == 0 && ++twos) a /= 2; /* Factor out multiples of 2 */
        if (twos > 0 && twos % 2 == 1) mult *= (n % 8 == 1 || n % 8 == 7) * 2 - 1;
        if (a <= 1 || a == n) break;
        if (n % 4 != 1 && a % 4 != 1) mult *= -1; /* Coefficient for flipping */
        temp = a;
        a = n;
        n = temp;
    }
    if (a == 0) return 0;
    else if (a == 1) return mult;
    else return 0; /* a == n => gcd(a, n) != 1 */
}

/**
 * Check whether a is a Euler witness for n
 */
int solovayPrime(int a, int n) {
    int x = jacobi(a, n);
    if (x == -1) x = n - 1;
    return x != 0 && modpow(a, (n - 1) / 2, n) == x;
}

/**
 * 使用k的精度测试 n是否可能是素数( Solovay-Strassen概率性素性检测法)
 */
int probablePrime(int n, int k) {
    if (n == 2) return 1;
    else if (n % 2 == 0 || n == 1) return 0;
    while (k-- > 0) {
        if (!solovayPrime(rand() % (n - 2) + 2, n)) return 0;
    }
    return 1;
}

/**
 * 随机生成[3,n]之间的素数,分布趋于随机
 */
int randPrime(int n) {
    int prime = rand() % n;
    n += n % 2; /* n needs to be even so modulo wrapping preserves oddness */
    prime += 1 - prime % 2;
    while (1) {
        if (probablePrime(prime, ACCURACY)) //4
            return prime;
        prime = (prime + 2) % n;
    }
}

/**
 * Compute gcd(a, b)
 */
int gcd(int a, int b) {
    int temp;
    while (b != 0) {
        temp = b;
        b = a % b;
        a = temp;
    }
    return a;
}

/**
 * 随机生成[3,n]之间的素数,且满足gcd(c,phi)=1,分布趋于随机
 */
int randExponent(int phi, int n) {
    int e = rand() % n;
    while (1) {
        if (gcd(e, phi) == 1) return e;
        e = (e + 1) % n;
        if (e <= 2) e = 3;
    }
}

/**
 * Compute n^-1 mod m by 扩展欧几里得算法
 */
int inverse(int n, int modulus) {
    int a = n, b = modulus;
    int x = 0, y = 1, x0 = 1, y0 = 0, q, temp;
    while (b != 0) {
        q = a / b;
        temp = a % b;
        a = b;
        b = temp;
        temp = x; x = x0 - q * x; x0 = temp;
        temp = y; y = y0 - q * y; y0 = temp;
    }
    if (x0 < 0) x0 += modulus;
    return x0;
}

/**
 * 将文件读入准备加密的字节数组。数组将用零填充,直到它除以每个块加密的字节数。返回读取的字节数。
 */
int readFile(FILE* fd, char** buffer, int bytes) {
    int len = 0, cap = BUF_SIZE, r;
    char buf[BUF_SIZE];
    *buffer = (char*)malloc(BUF_SIZE * sizeof(char));
    while ((r = fread(buf, sizeof(char), BUF_SIZE, fd)) > 0) {
        if (len + r >= cap) {
            cap *= 2;
            *buffer = (char*)realloc(*buffer, cap);
        }
        memcpy(&(*buffer)[len], buf, r);
        len += r;
    }
    /* 用零填充最后一个块,表示密码结束。如果没有空间,则添加一个附加块 */
    if (len + bytes - len % bytes > cap) *buffer = (char*)realloc(*buffer, len + bytes - len % bytes);
    do {
        (*buffer)[len] = '\0';
        len++;
    } while (len % bytes != 0);
    return len;
}

/**
 * 加密, c = m^e mod n
 */
int encode(int m, int e, int n) {
    return modpow(m, e, n);
}

/**
 * 解密, m = c^d mod n
 */
int decode(int c, int d, int n) {
    return modpow(c, d, n);
}

/**
 * Encode the message of given length, using the public key (exponent, modulus)
 * The resulting array will be of size len/bytes, each index being the encryption
 * of "bytes" consecutive characters, given by m = (m1 + m2*128 + m3*128^2 + ..),
 * encoded = m^exponent mod modulus
 */
int* encodeMessage(int len, int bytes, char* message, int exponent, int modulus) {
    int* encoded = (int*)malloc((len / bytes) * sizeof(int));
    int x, i, j;
    for (i = 0; i < len; i += bytes) {
        x = 0;
        for (j = 0; j < bytes; j++)
            x += message[i + j] * (1 << (7 * j));
        encoded[i / bytes] = encode(x, exponent, modulus);
#ifndef MEASURE
        printf("%d ", encoded[i / bytes]);
#endif
    }
    return encoded;
}

/**
 * Decode the cryptogram of given length, using the private key (exponent, modulus)
 * Each encrypted packet should represent "bytes" characters as per encodeMessage.
 * The returned message will be of size len * bytes.
 */
int* decodeMessage(int len, int bytes, int* cryptogram, int exponent, int modulus) {
    int* decoded = (int*)malloc(len * bytes * sizeof(int));
    int x, i, j;
    for (i = 0; i < len; i++) {
        x = decode(cryptogram[i], exponent, modulus);
        for (j = 0; j < bytes; j++) {
            decoded[i * bytes + j] = (x >> (7 * j)) % 128;
#ifndef MEASURE
            if (decoded[i * bytes + j] != '\0')
                printf("%c", decoded[i * bytes + j]);
#endif
        }
    }
    return decoded;
}

/**
 * RSA算法加解密 text.txt 文件
 */
int main(void) {
    int p, q, n, phi, e, d, bytes, len;
    int* encoded, * decoded;
    char* buffer;
    FILE* f;
    char str[600];
    srand(time(NULL));
    printf("\n------------RSA加密算法------------\n");
    printf("\n\t    1、密钥生成\n");
    while (1) {
        p = randPrime(SINGLE_MAX);//10000
        printf("p = %d\n", p);

        q = randPrime(SINGLE_MAX);
        printf("q= %d\n", q);

        n = p * q;
        printf("n = pq = %d\n", n);
        if (n < 128) {
            printf("模数小于128,无法编码单个字节,再试一次\n");
        }
        else
            break;
    }
    if (n >> 21)
        bytes = 3;
    else if (n >> 14)
        bytes = 2;
    else
        bytes = 1;

    phi = (p - 1) * (q - 1);
    printf("phi = %d\n", phi);

    e = randExponent(phi, EXPONENT_MAX);
    printf("e = %d\n公钥为:(n,e)=(%d, %d)\n\n", e, n, e);

    d = inverse(e, phi);
    printf("d = %d\n私钥为:(d)=(%d)", d,d);

    printf("\n\n\t    2、加密\n");
    printf("读取明文,");
    f = fopen("text.txt", "r");
    if (f == NULL) {
        printf("打开文件失败!是否存在?\n");
        return EXIT_FAILURE;
    }
    len = readFile(f, &buffer, bytes); /* len will be a multiple of bytes, to send whole chunks */
    fclose(f);

    printf("每次 %d bit读取,一共 %d bit\n密文为:",bytes,len);
    encoded = encodeMessage(len, bytes, buffer, e, n);

    printf("\n\n\t    3、解密\n解密后的明文为:");
    decoded = decodeMessage(len / bytes, bytes, encoded, d, n);

    printf("\n\n");
    free(encoded);
    free(decoded);
    free(buffer);
    system("pause");
    return EXIT_SUCCESS;
}

image-20220920091203523

3、使用大数库miracl,加密1000次

#define  _CRT_SECURE_NO_WARNINGS
#include "miracl.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#pragma comment(lib,"miracl.lib")

//char *primetext="155315526351482395991155996351231807220169644828378937433223838972232518351958838087073321845624756550146945246003790108045940383194773439496051917019892370102341378990113959561895891019716873290512815434724157588460613638202017020672756091067223336194394910765309830876066246480156617492164140095427773547319";
char* text = "";
time_t begin, end;

int main()
{
    /*
        加解密
    */
    big a, b, p, q, n, p1, q1, phi, pa, pb, key, e, d, dp, dq, t, m, c;
    big primes[2], pm[2];
    big_chinese ch;
    long l = 12;
    long cum = 0;
    miracl* mip;
    char input[256];
#ifndef MR_NOFULLWIDTH   
    mip = mirsys(100, 0);
#else
    mip = mirsys(100, MAXBASE);
#endif
    a = mirvar(0);
    b = mirvar(0);
    p = mirvar(0);
    q = mirvar(0);
    n = mirvar(0);
    p1 = mirvar(0);
    q1 = mirvar(0);
    phi = mirvar(0);
    pa = mirvar(0);
    pb = mirvar(0);
    e = mirvar(0);
    d = mirvar(0);
    dp = mirvar(0);
    dq = mirvar(0);
    t = mirvar(0);
    m = mirvar(0);
    c = mirvar(0);
    pm[0] = mirvar(0);
    pm[1] = mirvar(0);
    printf("*******************RSA加、解密程序*******************\n\n");

    printf("\n1、密钥生成\n");
    do
    {
        bigbits(l, p);
        if (subdivisible(p, 2)) incr(p, 1, p);
        while (!isprime(p)) incr(p, 2, p);

        bigbits(l, q);
        if (subdivisible(q, 2)) incr(q, 1, q);
        while (!isprime(q)) incr(q, 2, q);

        multiply(p, q, n);      /* n=p.q */

        lgconv(65537L, e);
        decr(p, 1, p1);
        decr(q, 1, q1);
        multiply(p1, q1, phi);  /* phi =(p-1)*(q-1) */
    } while (xgcd(e, phi, d, d, t) != 1);
    printf("p=");
    cotnum(p, stdout);
    printf("q=");
    cotnum(q, stdout);
    printf("n = p*q =");
    cotnum(n, stdout);
    printf("phi =(p-1)*(q-1)=");
    cotnum(phi, stdout);

    /* set up for chinese remainder thereom */
    primes[0] = p;
    primes[1] = q;
    crt_init(&ch, 2, primes);
    copy(d, dp);
    copy(d, dq);
    divide(dp, p1, p1);   /* dp=d mod p-1 */
    divide(dq, q1, q1);   /* dq=d mod q-1 */


    printf("\n\n2、加密\n");
    printf("输入明文:");
    scanf("%s", input);
    text = input;
    mip->IOBASE = l;
    cinstr(m, text);
    mip->IOBASE = 10;
    begin = clock();
    while (cum < 10000)
    {
        powmod(m, e, n, c);
        cum++;
    }
    printf("密文为:");
    cotnum(c, stdout);
    end = clock();
    printf("加密时间: %f seconds\n", (double)(end - begin) / CLOCKS_PER_SEC);
    zero(m);

    printf("\n\n3、解密\n");
    begin = clock();
    powmod(c, dp, p, pm[0]);    /* get result mod p */
    powmod(c, dq, q, pm[1]);    /* get result mod q */
    crt(&ch, pm, m);           /* combine them using CRT */

    printf("明文为:");
    mip->IOBASE = l;
    cotnum(m, stdout);
    crt_end(&ch);
    end = clock();
    printf("解密时间: %f seconds\n\n", (double)(end - begin) / CLOCKS_PER_SEC);
    system("pause");
    return 0;
}

image-20220920173801641

签名方案#

利用RSA构造签名方案,与加密方案类似,不同之处就是用私钥签名,用公钥验签

原始方案#

(1)密钥生成(Bob)

  • 选取两个大素数pq
  • 计算n=pq,z=(p1)(q1)
  • 任取一个正整数e,满足e<ngcd(e,z)=1
  • d,使其满足edmodz=1
  • 得到公钥(n,e),私钥(d)

(2)签名(Alice)

  • 消息mZn,对其签名s=md(modn),得到签名s

(3)验签(Bob)

  • 对签名进行验证m=se(modn),若等式成立,则验证成功

(4)正确性

由于de=1modz,则se(modn)=mde(modn)=m(modn)

(5)问题

  • 签名可以被伪造

比如Alice的消息x1,x2对应的签名是y1,y2,则敌手窃取到y1,y2,再由x1=y1e(modn),x2=y2e(modn)得到x1,x2,再重新自己签名发送给Bob,即伪造了签名

  • 签名速度慢

由于带签名的消息是xZn,所以每次只能对log2n位长的消息签名

(6)程序实现

import random

import math


# 快速幂取模

def power(a, b, n):  # 计算a**b mod n

    if b == 0:

        return 1  # 如果b值为0则返回1

    elif b % 2 == 0:  # 如果二进制b最后一位为0

        p = power(a, b / 2, n)  # 递归实现

        return (p * p) % n  # 取模运算

    else:

        return (a * power(a, b - 1, n)) % n  # 返回结果


# 欧几里得算法求最大公约数

def gcd(a, b):
    if a < b:

        return gcd(b, a)  # 如果a小于b,交换两个数

    elif a % b == 0:

        return b  # 如果a整除于b则返回b

    else:

        return gcd(b, a % b)  # 递归实现欧几里得算法


# 确定是否是素数

def isPrime(num):
    if (num < 2):

        return False  # 如果num的值小于2返回false

    else:

        i = 2

        flag = True

        while i < num:  # 如果num能被i整除,说明num不是质数

            if num % i == 0:
                flag = False  # 只要num不是质数,将flag的值修改为 False

            i += 1

        return flag  # 最后返回flag的值


# 生成大素数函数

def randPrime(n):
    Start = 10 ** (n - 1)  # n的值为5,计算开始值10**4

    End = (10 ** n) - 1  # 计算结束10**5-1

    while True:

        num = random.randint(Start, End)  # 返回从start到end之间任意一个数表示大素数

        if isPrime(num):  # 判断是否是质数,如果是则生成

            return num  # 返回大素数的值num


# 扩展的欧几里得算法,即ab=1 (mod n), 得到a在模n下的乘法逆元b

def Extended_Eulid(a, n):
    x1, x2, x3 = 1, 0, n

    y1, y2, y3 = 0, 1, a

    while y3 != 1 and y3 != 0 and y3 > 0:
        Q = math.floor(x3 / y3)

        t1, t2, t3 = x1 - Q * y1, x2 - Q * y2, x3 - Q * y3

        x1, x2, x3 = y1, y2, y3

        y1, y2, y3 = t1, t2, t3

    if y3 == 0:
        return 0

    if y3 == 1:

        if y2 > 0:

            return y2

        else:

            return n + y2


# 生成公钥和私钥

def KeyGen(p, q):  # 分别计算n,e,d的值

    n = p * q

    e = random.randint(1, (p - 1) * (q - 1))

    while gcd(e, (p - 1) * (q - 1)) != 1:  # 运用欧几里得算法判断

        e = random.randint(1, (p - 1) * (q - 1))

    d = Extended_Eulid(e, (p - 1) * (q - 1))

    return n, e, d


# 利用快速幂取模计算签名

def Sign(x, d, n):
    s = power(x, d, n)

    return s


# 利用快速幂取模判断是否有效签名

def Verify(s, e, n):
    x_ = power(s, e, n)

    return x_


# 主函数

if __name__ == '__main__':

    key_size = 5

    p = randPrime(key_size)  # p与q分别为随机生成的大素数

    q = randPrime(key_size)

    n, e, d = KeyGen(p, q)  # 用p与q生成公钥和私钥

    # 输入消息

    x = int(input("请输入加密信息(必须为整数): "))

    # 计算签名

    s = Sign(x, d, n)

    # 验证签名

    x_ = Verify(s, e, n)

    Valid = (x_ == x)

    # 输出

    print("私钥: ")

    print("N: ", n)

    print("d: ", d)

    print("公钥: ")

    print("N: ", n)

    print("e: ", e)

    print("签名: ")

    print("s: ", s)

    print("验证m的签名: ",x_)

    if Valid:

        print("签名有效!")

    else:

        print("签名无效!")

image-20220920215526817

优化方案#

解决办法就是加入Hash函数

(1)密钥生成(Bob)

  • 选取两个大素数pq
  • 计算n=pq,z=(p1)(q1)
  • 任取一个正整数e,满足e<ngcd(e,z)=1
  • d,使其满足edmodz=1
  • 得到公钥(n,e),私钥(d)

(2)签名(Alice)

  • 消息mZn,使用哈希算法(比如SHA512)生成摘要h,再用私钥对其签名s=hd(modn),得到签名s

(3)验签(Bob)

  • 使用哈希算法(比如SHA512)对消息m生成消息摘要h,再用公钥对其验签h=ce(modn)
  • 比较hh,若等式相同,则验证成功

(4)程序实现

# -*-coding:utf-8-*-
"""
File Name: RSA签名.py

Program IDE: PyCharm

Create Time: 2021-10-31 14:20

Create By Author: 陆依依

"""
import hashlib    # 实现哈希
import random


# 产生大素数(w位)
def generate_prime(w):
    while True:
        # 产生一个奇数()
        num = random.randint(2 ** (w - 1), 2 ** w - 1) | 1
        # 对素数进行50次素性检验, 错误概率为:7.888609052210118e-31, 可以忽略
        for i in range(50):
            if not Miller_Rabin(num):
                break
            if i == 49:
                return num


# Miller-Rabin素性检测
def Miller_Rabin(num):
    m = num - 1
    k = 0
    while m % 2 == 0:
        m = m // 2
        k = k + 1
    a = random.randint(2, num)

    b = Mod_P(a, m, num)
    if b == 1:
        return True
    for i in range(k):
        if b == num - 1:
            return True
        else:
            b = b * b % num
    return False


# 非递归求a^n mod p, 快速幂思想
def Mod_P(a, n, p):
    c = 1
    binstr = bin(n)[2:][::-1]  # 通过切片去掉开头的0b,截取后面,然后反转
    for item in binstr:
        if item == '1':
            c = (c * a) % p
            a = (a ** 2) % p
        elif item == '0':
            a = (a ** 2) % p
    return c


# 求最大公因子.欧几里得算法
def gcd(a, b):
    if a % b == 0:
        return b
    else:
        return gcd(b, a % b)


# 求逆元,扩展欧几里得算法
def Ex_Euclid(x, n):
    r0 = n
    r1 = x % n
    if r1 == 1:
        y = 1
    else:
        s0 = 1
        s1 = 0
        t0 = 0
        t1 = 1
        while r0 % r1 != 0:
            q = r0 // r1
            r = r0 % r1
            r0 = r1
            r1 = r
            s = s0 - q * s1
            s0 = s1
            s1 = s
            t = t0 - q * t1
            t0 = t1
            t1 = t
            if r == 1:
                y = (t + n) % n
    return y


# 产生公私钥
def Build_key():
    p = generate_prime(512)
    q = generate_prime(512)

    n = p * q                   # n的长度近似为1024位,即秘钥长度1024
    _n = (p - 1) * (q - 1)      # n的欧拉函数

    while True:
        e = random.randint(2, _n-1)  # 随机选择一个与_n互质的整数,一般选择65537。
        if gcd(e, _n) == 1:        # 模拟计算
            break
    d = Ex_Euclid(e, _n)           # 计算e对_n的模反元素
    return n, e, d                  # 返回公私钥,公钥(n,e),私钥(n,d)


def sign(m, n, d):
    s = [Mod_P(ord(i), d, n) for i in m]
    return m, s


def verify(m, s, n, e):
    return m == ''.join([chr(Mod_P(i, e, n)) for i in s])


# demo
if __name__ == '__main__':
    choose = int(input('请选择加密对象:1)文件  2)非文件\t'))

    if choose == 2:
        message = input('请输入待加密内容:').encode('utf-8')
    else:
        path = input('请输入完整文件路径:')
        with open(path, 'rb') as f:
            message = f.read()

    m = hashlib.sha512(message).hexdigest()

    print('散列后的消息:', m)

    n, e, d = Build_key()

    m, s = sign(m, n, d)
    print('签名列表长度:', len(s))

    # 将签名写入新文件
    with open('sign.txt', 'w') as f:
        f.write(str(s).replace('[', '').replace(']', '').replace(',', '\n').replace(' ', ''))
    print('签名写入成功!!!')

    print('签名验证结果?', verify(m, s, n, e))

image-20220920225052647

ElGamal#

1985年提出ElGamal公钥密码体制, 基于的是离散对数问题(或者说是难解的循环群),主要为数字签名而设计,缺点是密文比明文长,通常是密文长度的两倍。

ElGamal既可以在有限域上也可以在椭圆曲线上构成的循环群中计算。

椭圆曲线密码体制相比于基于有限域的密码体制有以下优点

(1)安全性高

(2)密钥量小

(3)灵活性高

有限域加密方案#

密钥生成#

G是有限域Zp上的乘法循环群,p是一个素数,gZp的生成元(Zpg是公开的)

随机选取一个整数a(1,p1),计算b=ga(modp)

则私钥为(a),公钥为(p,g,b)

加密#

对于明文消息m,随机选择一个整数k(1,p1),计算r=gk(modp)v=mbk(modp),得到密文c=(r,v)

解密#

计算vra=mbkgka(modp))=m(modp)

乘法同态#

给定两个密文c1=(r1,v1),c2=(r2,v2)

D(c1c2)=D(r1r2,v1v2)=D(gk1+k2(modp),m1m2bk1+k2(modp))=m1m2bk1+k2ga(k1+k2)(modp)=m1m2(modp)

正确性#

因为b=ga(modp),则vra=mbkgka(modp))=mgakgka(modp))=m(modp)

安全性#

体现在公钥生成中,即根据公钥(p,g,b)不能恢复出私钥(a)

由于b=ga(modp),根据离散对数问题,已知p,b,g,难以求出a

下面给出一个例子:

image-20220921102937102

程序实现#

/*
 * ElGamal算法实现
 * 功能:加解密、乘法同态
 */
#include<iostream>
#include<cstdio>
#define randomInt(a,b) (rand()%(b-a)+a) //生成指定范围的随机数
using namespace std;
typedef long long ll;
typedef struct C{
    ll c_1;
    ll c_2;
};

ll p,a,x,y,r,m1,m2;
C c1,c2,c3;

ll qpow(ll r, ll n, ll mod){//计算a^n % mod
    ll re = 1;
    while(n){
        if(n & 1)
            re = (re * r) % mod;
        n >>= 1;
        r = (r * r) % mod;
    }
    return re % mod;
}

ll byy(ll lp){//求Zlp*的生成元
    bool flag;
    for(ll i=2;i<lp;i++){
        flag=true;
        for(ll j=2;j<lp-1;j++){
            if((lp-1)%j==0){
                if(qpow(i,j,lp)==1) flag=false;
            }
        }
        if(flag) return i;
    }
}

ll inv(ll la, ll lp){//求逆元——扩展欧几里得算法
    if(la == 1) return 1;
    return inv(lp%la,lp)*(lp-lp/la)%lp;
}

C encode(ll la,ll lp,ll ly,ll lm){
    C c;
    printf("\n======加密======\n");
    ll lr=randomInt(1,lp-1);
    printf("随机数r=%lld\n",lr);
    c.c_1=qpow(la,lr,lp);
    c.c_2=(lm*qpow(ly,lr,lp))%lp;
    printf("得到的密文为c_1=%lld   c_2=%lld\n",c.c_1,c.c_2);
    return c;
}

void decode(ll lx,ll lp,C c){
    printf("\n======解密======\n");
    c.c_1=qpow(c.c_1,lx,lp);
    c.c_1=inv(c.c_1,lp);
    ll m=(c.c_1*c.c_2)%lp;
    printf("得到的明文为m=%lld\n",m);
}

C mult_c(ll lp,C lc1,C lc2){ //同态乘法
    C c;
    printf("\n======同态乘法======\n");
    c.c_1=(lc1.c_1*lc2.c_1)%lp;
    c.c_2=(lc1.c_2*lc2.c_2)%lp;
    printf("密文相乘后c_1=%lld   c_2=%lld\n",c.c_1,c.c_2);
    return c;
}

int main(){
    printf("请输入参数p:");
    scanf("%lld",&p);
    a=byy(p);
    printf("计算出本原元a=%lld   \n",a);
    printf("\n======密钥生成======\n");
    x=randomInt(1,p-1);
    y=qpow(a,x,p);
    printf("公钥pk={%lld,%lld,%lld},私钥sk={%lld}\n",p,a,y,x);
    printf("测试加解密,输入1;测试乘法同态,输入2\n");
    int choose;
    scanf("%d",&choose);
    if(choose==1)
    {
        printf("请输入要加密的明文m:");
        scanf("%lld",&m1);
        c1=encode(a,p,y,m1);  //加密
        decode(x,p,c1);  //解密
    }else if(choose==2)
    {
        printf("请输入要加密的明文m1和m2:");
        scanf("%lld%lld",&m1,&m2);
        c1=encode(a,p,y,m1);  //加密m1
        c2=encode(a,p,y,m2);  //加密m2
        c3= mult_c(p,c1,c2);
        decode(x,p,c3);  //解密c3
        printf("验证结果m1*m2=%lld\n",m1*m2%p);
    }else
        printf("选择错误,请重新选择!\n");
    return 0;
}

image-20220921103729156

椭圆曲线加密方案#

密钥生成#

选取一条椭圆曲线,得到群Ep(a,b),取GEp(a,b)的生成元

选取整数nA作为私钥,计算出PA=nAG作为公钥

加密#

对于明文消息m,先将其嵌入到曲线上的点Pm,选取一个随机正整数k,计算出密文:

Cm={kG,Pm+kPA}

解密#

使密文点对中的第二点减去私钥与第一个点的倍乘,即Pm+kPAnAkG=Pm

加法同态#

给定两个密文Cm1,Cm2,有:

D(Cm1+Cm2)=D((k1+k2)G,(Pm1+Pm2)+(k1+k2)PA)=(Pm1+Pm2)+(k1+k2)PAnA((k1+k2)G)=Pm1+Pm2

正确性#

由于PA=nAG,则Pm+kPAnAkG=Pm+knAGnAkG=Pm

安全性#

椭圆曲线上的点构成了Abel群Ep(a,b),在密钥生成阶段,给出公钥PA=nAG,难以求出私钥nA,这就是椭圆曲线上的离散对数问题。

程序实现#

实现重点:椭圆曲线上的计算(倍乘、累加)

#include "ElGamal.h"
//代码:https://github.com/ProbeTS/Cryptography/tree/master/ELGama

int main() {
    int p=751,a=-1,b=188;
    ELGama* e = new ELGama(p, a, b);
    //生成元
    Point G={0,376};
    //密钥
    int n_A=58; //私钥
    Point P_A=e->kPcal(G, n_A); //公钥
    //加密
    Point P_m={562,201};
    int k=386;
    Point c1=e->kPcal(G, k);
    Point c2=e->PplusQcal(P_m,e->kPcal(P_A, k));
    cout <<"密文:{("<<c1.x<<","<<c1.y<<"),("<<c2.x<<","<<c2.y<<")}"<< endl;
    //解密
    Point M=e->PplusQcal(c2, add_Reverse(e->kPcal(c1,n_A)));
    cout <<"("<<M.x<<","<<M.y<<")"<< endl;
    return 0;
}

image-20220921150613005

EC-ElGamal加密方案(铜锁)#

来自:https://blog.csdn.net/SOFAStack/article/details/123366669

密钥生成#

选取一条椭圆曲线,得到群Ep(a,b),取GEp(a,b)的生成元

随机选取整数nA(1,p)作为私钥,计算出PA=nAG作为公钥

加密#

对于明文消息m,选取一个随机正整数k,计算出密文:Cm=E(m,k)=(kG,mG+kPA)

这里要求m取值很小,否则椭圆曲线的离散对数问题(ECDLP)无法求解,因为这里解密是需要查表的

解密#

(1)先计算nACm[0]=nAkG=kPA

(2)再计算Cm[1]kPA=mG+kPAkPA=mG

(3)通过查表的方式,求解ECDLP问题,进而获得m

同态性#

(1)加法

对于两个密文Cm1=(k1G,m1G+k1PA),Cm2=(k2G,m2G+k2PA),有:

Cm1+Cm2=((k1+k2)G,(m1+m2)G+(k1+k2)PA)=E((m1+m2),k1+k2)

(2)减法

对于两个密文Cm1=(k1G,m1G+k1PA),Cm2=(k2G,m2G+k2PA),有:

Cm1Cm2=((k1k2)G,(m1m2)G+(k1k2)PA)=E((m1m2),k1k2)

(3)标量乘

对于密文Cm1=(k1G,m1G+k1PA),一个明文m2有:

m2Cm1=(m2k1G,m2m1G+m2k1PA)=E((m1m2),k1m2)

程序实现#

//参考:https://blog.csdn.net/SOFAStack/article/details/123366669   https://tongsuo.readthedocs.io/zh/latest/Tutorial/PHE/ec-elgamal-sample/

#include <stdio.h>
#include <time.h>
#include <openssl/ec.h>
#include <openssl/pem.h>
#define CLOCKS_PER_MSEC (CLOCKS_PER_SEC/1000)

int main(int argc, char *argv[])
{
    int ret = -1;
    uint32_t r;
    //密钥生成
    clock_t begin, end;
    EC_KEY *sk_eckey = NULL, *pk_eckey = NULL;
    EC_ELGAMAL_CTX *ctx1 = NULL, *ctx2 = NULL;
    EC_ELGAMAL_CIPHERTEXT *c1 = NULL, *c2 = NULL, *c3 = NULL;
    EC_ELGAMAL_DECRYPT_TABLE *table = NULL;
    FILE *pk_file = fopen("ec-pk.pem", "rb");
    FILE *sk_file = fopen("ec-sk.pem", "rb");
    if ((pk_eckey = PEM_read_EC_PUBKEY(pk_file, NULL, NULL, NULL)) == NULL)
        goto err;
    if ((sk_eckey = PEM_read_ECPrivateKey(sk_file, NULL, NULL, NULL)) == NULL)
        goto err;

    if ((ctx1 = EC_ELGAMAL_CTX_new(pk_eckey)) == NULL)
        goto err;
    if ((ctx2 = EC_ELGAMAL_CTX_new(sk_eckey)) == NULL)
        goto err;

    //创建解密表
    begin = clock();
    if ((table = EC_ELGAMAL_DECRYPT_TABLE_new(ctx2, 0)) == NULL)
        goto err;

    EC_ELGAMAL_CTX_set_decrypt_table(ctx2, table);
    end = clock();
    printf("EC_ELGAMAL_DECRYPT_TABLE_new(1) cost: %lfms\n", (double)(end - begin)/CLOCKS_PER_MSEC);

    if ((c1 = EC_ELGAMAL_CIPHERTEXT_new(ctx1)) == NULL)
        goto err;
    if ((c2 = EC_ELGAMAL_CIPHERTEXT_new(ctx1)) == NULL)
        goto err;

    //加密20000021
    begin = clock();
    if (!EC_ELGAMAL_encrypt(ctx1, c1, 20000021))
        goto err;
    end = clock();
    printf("EC_ELGAMAL_encrypt(20000021) cost: %lfms\n", (double)(end - begin)/CLOCKS_PER_MSEC);

    //加密500
    begin = clock();
    if (!EC_ELGAMAL_encrypt(ctx1, c2, 500))
        goto err;
    end = clock();
    printf("EC_ELGAMAL_encrypt(500) cost: %lfms\n", (double)(end - begin)/CLOCKS_PER_MSEC);

    if ((c3 = EC_ELGAMAL_CIPHERTEXT_new(ctx1)) == NULL)
        goto err;

    //密文相加:20000021+500
    begin = clock();
    if (!EC_ELGAMAL_add(ctx1, c3, c1, c2))
        goto err;
    end = clock();
    printf("EC_ELGAMAL_add(c: 2000021,c: 500) cost: %lfms\n", (double)(end - begin)/CLOCKS_PER_MSEC);

    //解密:20000021+500
    begin = clock();
    if (!(EC_ELGAMAL_decrypt(ctx2, &r, c3)))
        goto err;
    end = clock();
    printf("EC_ELGAMAL_decrypt(c: 20000021,c: 500) result: %d, cost: %lfms\n", r, (double)(end - begin)/CLOCKS_PER_MSEC);

    //标量乘:500*800
    begin = clock();
    if (!EC_ELGAMAL_mul(ctx1, c3, c2, 800))
        goto err;
    end = clock();
    printf("EC_ELGAMAL_mul(c: 500,m: 800) cost: %lfms\n", (double)(end - begin)/CLOCKS_PER_MSEC);

    //解密:500*800
    begin = clock();
    if (!(EC_ELGAMAL_decrypt(ctx2, &r, c3)))
        goto err;
    end = clock();
    printf("EC_ELGAMAL_decrypt(c: 500,m: 800) result: %d, cost: %lfms\n", r, (double)(end - begin)/CLOCKS_PER_MSEC);

    //将密文500*800编码为二进制文件
    printf("EC_ELGAMAL_CIPHERTEXT_encode size: %zu\n", EC_ELGAMAL_CIPHERTEXT_encode(ctx2, NULL, 0, NULL, 1));

    ret = 0;
    err:
    EC_KEY_free(sk_eckey);
    EC_KEY_free(pk_eckey);
    EC_ELGAMAL_DECRYPT_TABLE_free(table);
    EC_ELGAMAL_CIPHERTEXT_free(c1);
    EC_ELGAMAL_CIPHERTEXT_free(c2);
    EC_ELGAMAL_CIPHERTEXT_free(c3);
    EC_ELGAMAL_CTX_free(ctx1);
    EC_ELGAMAL_CTX_free(ctx2);
    fclose(sk_file);
    fclose(pk_file);
    return ret;
}

image-20220921214824058

阈值加密方案#

签名方案#

1985年提出基于离散对数的签名方案,1991年进行改进后,叫做DSS。

下面给出的是在有限域上的

密钥生成(Bob)#

G是有限域Zp上的乘法循环群,p是一个素数,gZp的生成元(Zpg是公开的)

随机选取一个整数a(1,p1),计算b=ga(modp)

则私钥为(a),公钥为(p,g,b)

签名(Alice)#

对于消息m,随机取整数k(1,p1),计算签名值:r=gk(modp),s=(h(m)ra)k1mod(p1),其中h(.)是hash函数,将签名(r,s)和消息m发送给Bob

验签(Bob)#

先计算gh(m),然后计算brrs,判断两者是相等,若相等则验证成功。

正确性#

由于s=(h(m)ra)k1mod(p1),则sk+ar=(h(m)ra+ra)mod(p1)=h(m)mod(p1),所以gh(m)=gsk+ar=gskgar=rsbr(modp)

程序实现#

import random

# 求最大公约数
def gcd(a, b):
    if a < b:
        return gcd(b, a)
    elif a % b == 0:
        return b
    else:
        return gcd(b, a % b)

# 快速幂+取模
def power(a, b, c):
    ans = 1
    while b != 0:
        if b & 1:
            ans = (ans * a) % c
        b >>= 1
        a = (a * a) % c
    return ans

# 卢卡斯-莱墨素性检验
def Lucas_Lehmer(num) -> bool:  # 快速检验pow(2,m)-1是不是素数
    if num == 2:
        return True
    if num % 2 == 0:
        return False
    s = 4
    Mersenne = pow(2, num) - 1  # pow(2, num)-1是梅森数
    for x in range(1, (num - 2) + 1):  # num-2是循环次数,+1表示右区间开
        s = ((s * s) - 2) % Mersenne
    if s == 0:
        return True
    else:
        return False

# 大素数检测
def Miller_Rabin(n):
    a = random.randint(2,n-2) #随机第选取一个a∈[2,n-2]
    # print("随机选取的a=%lld\n"%a)
    s = 0 #s为d中的因子2的幂次数。
    d = n - 1
    while (d & 1) == 0: #将d中因子2全部提取出来。
        s += 1
        d >>= 1

    x = power(a, d, n)
    for i in range(s): #进行s次二次探测
        newX = power(x, 2, n)
        if newX == 1 and x != 1 and x != n - 1:
            return False #用二次定理的逆否命题,此时n确定为合数。
        x = newX

    if x != 1:  # 用费马小定理的逆否命题判断,此时x=a^(n-1) (mod n),那么n确定为合数。
        return False

    return True  # 用费马小定理的逆命题判断。能经受住考验至此的数,大概率为素数。

# 扩展的欧几里得算法,ab=1 (mod m), 得到a在模m下的乘法逆元b
def Extended_Eulid(a: int, m: int) -> int:
    def extended_eulid(a: int, m: int):
        if a == 0:  # 边界条件
            return 1, 0, m
        else:
            x, y, gcd = extended_eulid(m % a, a)  # 递归
            x, y = y, (x - (m // a) * y)  # 递推关系,左端为上层
            return x, y, gcd  # 返回第一层的计算结果。
        # 最终返回的y值即为b在模a下的乘法逆元
        # 若y为复数,则y+a为相应的正数逆元

    n = extended_eulid(a, m)
    if n[1] < 0:
        return n[1] + m
    else:
        return n[1]

# 生成域参数p,长度大约为512bits
def Generate_p() -> int:
    a = random.randint(10**150, 10**160)
    while gcd(a, 2) != 1:
        a = random.randint(10**150, 10**160)
    return a

# 生成域参数alpha
def Generate_alpha(p: int) -> int:
    return random.randint(2, p)

# 生成一个小于p的素数作为私钥,长度大约为512bits
def Generate_private_key(p: int) -> int:
    pri = random.randint(2, p - 2)
    while gcd(pri, p) != 1:
        pri = random.randint(2, p - 2)
    return pri

# 快速幂
def quick_power(a: int, b: int) -> int:
    ans = 1
    while b != 0:
        if b & 1:
            ans = ans * a
        b >>= 1
        a = a * a
    return ans

def Generate_prime(key_size: int) -> int:
    while True:
        num = random.randrange(quick_power(2, key_size - 1), quick_power(2, key_size))
        if Miller_Rabin(num):
            return num

# 计算签名
def Sign(x, p, alpha, d) -> []:
    temp_key = random.randint(0, p - 2)
    while gcd(temp_key, p - 1) != 1:
        temp_key = random.randint(0, p - 2)
    r = power(alpha, temp_key, p)
    s = (x - d * r) * Extended_Eulid(temp_key, p - 1) % (p - 1)
    return r, s

# 签名验证
def Verify(x, p, alpha, beta, r, s):
    t = (power(beta, r, p) * power(r, s, p)) % p
    if t == power(alpha, x, p):
        return True
    else:
        return False

if __name__ == '__main__':
    x = int(input("Message:       "))
    if type(x) != int:
        raise ValueError("Must be an integer!")
    p = Generate_prime(512)
    alpha = Generate_alpha(p)
    a = Generate_private_key(p)
    beta = power(alpha, a, p)

    r, s = Sign(x, p, alpha, a)
    Valid = Verify(x, p, alpha, beta, r, s)

    print("Private Key: ")
    print("a:            ", a)
    print("Public key : ")
    print("p:            ", p)
    print("alpha:        ", alpha)
    print("beta:         ", beta)
    print("Signature: ")
    print("r:            ", r)
    print("s:            ", s)
    print("Verify (r, s) of x: ")
    if Verify(x, p, alpha, beta, r, s):
        print("valid")
    else:
        print("invalid")

image-20220922090118227

Paillier#

Paillier加密方案,最初于1999年首次提出,此后又进行优化,下面给出原版和优化后的方案,该方案因为效率高,安全性证明完备,又具有同态性质在隐私计算中广泛应用。

原方案:Public-key cryptosystems based on composite degree residuosity classes

优化方案:

原方案#

密钥生成#

  • 随机选择两个大素数p,q,满足gcd(pq,(p1)(q1))=1,且p,q长度相等
  • 计算出n=pqλ=lcm(p1,q1),其中lcm()表示最小公倍数
  • 随机选择一个整数gZn2
  • 定义L函数:L(x)=x1n,计算u=(L(gλmodn2))1modn
  • 得到公钥:(n,g),私钥:(λ,u)

加密#

  • 对于明文消息m[0,n]
  • 选择一个随机数r[0,n],且rZn
  • 计算密文c=E(m,r)=gmrnmodn2

解密#

  • 对于密文消息cZn2
  • 计算出明文m=D(c)=L(cλmodn2)u(modn)

同态性#

(1)加法

对于两个密文c1,c2,有D((c1c2)modn2)=D((gm1r1ngm2r2n)modn2)=D((gm1+m2(r1r2)n)modn2),即E(m1+m2,r1r2)=c1c2

对于密文c1和明文m2,有:

D(c1gm2modn2)=D((gm1m2rnmodn2)=m1+m2,即E(m1m2,rm2)

(2)标量乘法

对于密文c1和明文m2,有:

D(c1m2modn2)=D((gm1m2(rm2)nmodn2)=m1m2,即E(m1m2,rm2)

正确性#

D(c)=L(cλmodn2)u(modn)=L((gmrn)λ(modn2)L(gλmodn2)=m

具体证明见:

安全性#

Paillier加密基于的是复合剩余类问题,即给定合数n和整数z,难以确定z是否是模n2n次剩余,对于z=yn(modn2)

程序实现#

(1)Python实现

#!/usr/bin/env python
from paillier.Paillier import *

# Press the green button in the gutter to run the script.
if __name__ == '__main__':
    print("Generating keypair...")
    priv, pub = generate_keypair(512)

    x = 3
    print("x =", x)
    print("Encrypting x...")
    cx = encrypt(pub, x)
    print("cx =", cx)

    y = 5
    print("y =", y)
    print("Encrypting y...")
    cy = encrypt(pub, y)
    print("cy =", cy)

    print("Computing cx + cy...")
    cz = e_add(pub, cx, cy)
    print("cz =", cz)

    print("Decrypting cz...")
    z = decrypt(priv, pub, cz)
    print("z =", z)

    print("Computing decrypt((cz + 2) * 3) ...")
    print("result =", decrypt(priv, pub, e_mul_const(pub, e_add_const(pub, cz, 2), 3)))

image-20221003213608499

(2)C++实现(NTL库)

#include <iostream>
#include "paillier.h"

using namespace std;
using namespace NTL;

ZZ lcm(ZZ x, ZZ y){
  ZZ ans = (x * y) / NTL::GCD(x,y);
  return ans;
}

int main()
{
    ZZ p = ZZ(43);
    ZZ q = ZZ(41);
    ZZ lambda = lcm(p - 1, q - 1);
    Paillier paillier(p*q, lambda);
    ZZ n = p * q;

    cout << "p = " << p << endl;
    cout << "q = " << q << endl;
    cout << "n = " << n << endl;
    //generator=lambda+1
    cout << "lamdba = " << lambda << endl;

    cout << "------------加解密测试------------"<<endl;
    ZZ m = ZZ(100); //明文消息
    ZZ c = paillier.encrypt(m);
    cout << "m = "<<m<< ", c = " << c << endl;
    ZZ m_ = paillier.decrypt(c);
    cout << "m2 = " << m_ << endl;
    if (m == m_){
        cout << "m = m2, encryption and decryption successful" << endl;
    }

    cout << "------------同态性测试------------"<<endl;
    ZZ m1 = ZZ(100); //明文消息
    ZZ m2 = ZZ(8);
    ZZ c1 = paillier.encrypt(m1, (ZZ)131 );
    ZZ c2 = paillier.encrypt(m2, (ZZ)223 );
    cout << "m1 = "<<m1<<", c1 = " << c1 << endl;
    cout << "m2 = "<<m1<<", c2 = " << c2 << endl;

    ZZ c_add=paillier.hom_add(c1,c2);
    ZZ c_add_const=paillier.hom_add_const(c1,m2);
    ZZ c_mult=paillier.hom_mult(c1,m2);

    ZZ m_add = paillier.decrypt(c_add);
    ZZ m_add_const = paillier.decrypt(c_add_const);
    ZZ m_mult = paillier.decrypt(c_mult);

    cout << "m_add = " << m_add << endl;
    cout << "m_add_const = " << m_add_const << endl;
    cout << "m_mult = " << m_mult << endl;
    return 0;
}

image-20221003184459447

优化方案#

待补充

参考#

1、现代密码学-杨波

2、密码学-基础理论与应用

程序#

github

作者:Hang Shao

出处:https://www.cnblogs.com/pam-sh/p/16778293.html

版权:本作品采用「知识共享」许可协议进行许可。

声明:欢迎交流! 原文链接 ,如有问题,可邮件(mir_soh@163.com)咨询.

posted @   PamShao  阅读(433)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
历史上的今天:
2021-10-11 密码法及密码技术应用知识竞赛
点击右上角即可分享
微信分享提示
more_horiz
keyboard_arrow_up light_mode palette
选择主题
menu