ElGamal算法进行加密和解密的基本原理及实现

1、准备步骤

1)取大素数 p 和 g(g < p,g 最好是 p 的素根)

注解:若 g 是素数 p 的一个素根,则 g mod p, g^2 mod p , …, g^p-1 mod p 是 1 到 p - 1 的排列

2)随机选取一整数 x (2 <= x <= (p - 2),(p,g,x) 是私钥)

3)计算 y = g^x (mod p) ( (p,g,y) 是公钥)

 

2、加密过程

1)随机选取一整数 k (2 <= k <= (p - 2) 且 k 与 (p - 1) 互素)

2)计算 a = g^k mod p,b = m*y^k mod p(m 为要加密的明文)

3)密文 C = (a, b)

 

3、解密过程

1)m = b * a^(-x) mod p

注解:b * a^(-x) mod p Ξ m * y^k * g^(-xk) Ξ m * g^(xk) * g^(-xk) Ξ m

 

加密及解密的实现(Java):

import java.util.ArrayList;

public class Main {
    static ArrayList<Integer> suArr = new ArrayList<>();
    public static void main(String[] args) {
        int max = 8096, p, g, x, y, k, a, b, buffer_data;
        char[] m = "abc".toCharArray(); // 加密字符串"abc"
        ArrayList<Integer> C = new ArrayList<>();   // 加密后的密文
        int size1;
        suArr.add(2);

        // 随机取一个小于2048的素数g
        for (int i = 3; i <= 2048; i++) {
            if (isSuShu(i)) suArr.add(i);
        }
        size1 = suArr.size();
        g = getRanNum(size1);

        // 随机取一个大素数p
        for (int i = 2049; i <= max; i++) {
            if (isSuShu(i)) suArr.add(i);
        }
        p = getRanNum(suArr.size() - size1, size1);

        // x的范围是[2,p-2]
        x = (int)(Math.random() * (p-3))+2;
        // k的范围是[2,p-2]且k与p-1互素
        k = (int)(Math.random() * (p-3))+2;
        while (isHuZhi(k, p-1) != 1) {
            k = (int)(Math.random() * (p-3))+2;
        }

        // y = g^x mod p
        y = myPow(g, x, p);

        // a = g^k mod p
        a = myPow(g, k, p);
        C.add(a);

        // 特殊数据test
        // a = 1434;
        // g=1117;
        // k=2403;
        // p=6101;
        // x=714;
        // y=2271;

        // 加密过程,即计算b = m*y^k mod p (m是明文)
        for (char c : m) {
            C.add((int)c*myPow(y, k, p) % p);
        }

        buffer_data = myPow(a, x, p);
        buffer_data = exGcd(buffer_data, p)[0]; // 求(a^x)^(-1) mod p等价于求a^(-x) mod p
        if (buffer_data < 0) buffer_data += p;

        // 将解密后的明文输出
        for (int i = 1; i < C.size(); i++) {
            System.out.print((char)(C.get(i) * buffer_data % p));
        }
    }

    // 判断一个数是否为素数
    public static boolean isSuShu(int num) {
        int max = (int) Math.sqrt(num);
        for (int i = 2; i <= max; i++) {
            if (num % i == 0)
                return false;
        }
        return true;
    }

    // 在素数数组中随机取一个数
    public static int getRanNum(int size) {
        return suArr.get((int) (Math.random() * (size)));
    }

    // 在素数数组中的(left, arr.size())之间随机取一个数
    public static int getRanNum(int size, int left) {
        return suArr.get((int) (Math.random() * (size)) + left);
    }

    // 判断两个数是否互质
    public static int isHuZhi(int a, int b) {
        return b == 0 ? a : isHuZhi(b, a % b);
    }

    public static int myPow(int a, int b, int m) {
        int res = 1;
        a %= m;
        while (b != 0) {
            if ((b & 1) == 1)
                res = (res * a) % m;
            a = (a * a) % m;
            b >>= 1;
        }
        return res;
    }

    public static int getSuGen(int p) {
        boolean isSuGen;
        for (int g : suArr) {
            isSuGen = true;
            for (int i = 1; i < p; i++) {
                if (myPow(g, i, p) != i) isSuGen = false;
            }
            if (isSuGen) return g;
        }
        return 2; // 如果在素数数组中找不到p的素根,则返回一个默认值
    }

    // 扩展欧几里得算法求a模b的逆元
    public static int[] exGcd(int a, int b) {
        if (b == 0) {
            int[] arr = new int[]{1, 0};
            return arr;
        } else {
            int[] arr = exGcd(b, a % b);
            int x = arr[0];
            arr[0] = arr[1];
            arr[1] = x - (a / b) * arr[1];
            return arr;
        }
    }
}

/*
 * 参考:
 * 素根的定义:a是素数p 的一个素根,如果a mod p, a^2 mod p , …, a^p-1 mod p 是1到p-1的排列,称a是P的一个素根
 * 加密时x和k的选取:https://blog.csdn.net/qq_34490018/article/details/79758620
 */

 

posted @ 2019-09-18 16:33  Qi-BJ  阅读(7481)  评论(0编辑  收藏  举报