基于shamir门限的秘密分存

基于shamir门限的秘密分存

一、秘密分存

将一个秘密拆分成几块,分给几个人保管,每个人保管一块,只有当n块组合在一起才能恢复出秘密,单独的一块对自己是没有用的,n即为门限。

二、shamir门限方案

  • SHAMIR门限方案是基于多项式的拉格朗日插值公式的,即已知Φ(x)在k个互相不同的点的函数值Φ(xi),可构造k-1次插值多项式为f(x)=∑Φ(xi)∏(x-xj)/(xi-xj)。
  • 如:已知Φ(2)=10,Φ(5)=3,Φ(4)=7,求Φ(1),则可以构造f(x)=10[(x-5)(x-4)]/[(2-5)(2-4)]+3[(x-2)(x-4)]/[(5-2)(5-4)]+7*[(x-2)(x-5)]/[(4-2)(4-5)],带入x=1即得
  • 因此,可以在一个GF(q)上构造一个多项式f(x)(q为一个随机大素数),秘密s为之上的一个随机数,n个参与者分到子密钥f(i),有k个能还原,即可采用拉格朗日插值公式构造多项式,f(0)即为秘密。

三、说明

  • 分解秘密过程:首先得到要分解的秘密,为一个整数(要小于2,147,483,647),然后利用Java中的函数BigInteger q = BigInteger.probablePrime(bit_num, rand)生成一个大于秘密的随机大整数,作为模值,rand为随机数因子,bit_num为生成的随机大素数的二进制的位数。之后生成随机的多项式的系数和要分配的子密钥(子密钥在程序中设置小于100),之后根据构造好的多项式算出子密钥的值,即子密钥包括子密钥及子密钥的值。
  • 还原秘密过程:先输入各个子密钥,再输入各子密钥对应的模值。因为秘密为f(0),实际上只要求出多项式的常数即可,得到每个拉格郎日插值多项式的各个部分的常数,相加模p即为秘密。
  • 用广义欧几里得除法求逆时,这两个数都为正数,开始忽视了这一点,导致写求逆函数时出现错误
  • java的BigInteger类,可以用该类的方法计算大数的加减乘除等运算,相关方法如下:
    BigInteger.add(b); //大整数加法,b也为一个大数
    BigInteger.subtract(b); //大整数减法
    BigInteger.multiply(b); //大整数乘法
    BigInteger.divide(b); //大整数除法(取整)
    BigInteger.remainder(b); //大整数取模
    BigInteger.pow(exponent); //大整数a的exponent次幂

结果

1503988-20190721181046184-1692262240.png

四、源代码

package shamir;

import java.math.BigInteger;
import java.util.Random;
import java.util.Scanner;

public class Shamir {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        while (true) {
            System.out.println("分解秘密请选择1,还原秘密选择2, 退出选择3");
            int flag = in.nextInt();
            if (flag == 1) {
                long a[] = new long[100];// 放系数
                long f[] = new long[100];// 放密钥的值
                long dian[] = new long[100];// 放密钥的点
                System.out.println("输入你的秘密");
                int secret = in.nextInt();// 输入为一个数小于2,147,483,647
                Random rand = new Random();
                int bit_num = rand.nextInt(29) + 2;// 随机2到31位
                BigInteger q = BigInteger.probablePrime(bit_num, rand);// 生成一个随机大素数,必须大于输入的随机整数
                long p = q.longValue();
                while (p < secret) {// 如果不大于输入的数
                    bit_num = rand.nextInt(29) + 2;
                    q = BigInteger.probablePrime(bit_num, rand);
                    p = q.longValue();
                }
                System.out.println("模为  " + p);
                System.out.println("输入门限值");// 门限值小于等于100
                int k = in.nextInt();
                System.out.println("输入得到几个子密钥");// 子密钥值小于等于100
                int zi_key = in.nextInt();
                for (int i = 0; i < k - 1; i++) {
                    a[i] = (long) (Math.random() * p);// 生成伪随机系数
                    if (a[i] == 0) {// 生成的随机系数不能为0
                        i--;
                    }
                }
                for (int i = 0; i < zi_key; i++) {
                    dian[i] = (long) (Math.random() * 100);// 生成100以内随机点,不能重复,如果大了,可能在以后的运算中超出数据类型范围,导致错误
                    if (dian[i] == 0) {// 生成的随机点不能为0
                        i--;
                    }
                }
                System.out.println("子密钥为(确保子密钥没有重复,如果有请重新生成)   ");
                for (int i = 0; i < zi_key; i++) {// 计算zi_key个子密钥
                    f[i] = secret;
                    for (int j = 0; j < k - 1; j++) {// 一个子密钥
                        long zhishu = j + 1;
                        f[i] = Math.floorMod((long) (a[j] * Math.pow(dian[i], zhishu)) + f[i], p);
                    }
                    System.out.print(dian[i] + " ");
                    System.out.println(f[i]);
                }
            } else if (flag == 2) {
                System.out.println("输入模值");
                int m = in.nextInt();
                System.out.println("输入门限");
                int k = in.nextInt();
                long key_d[] = new long[k];
                long key_z[] = new long[k];
                long num_x[] = new long[k];
                long num_s[] = new long[k];
                long s = 0;
                System.out.println("请输入密钥(先输入所有的点,再输入各点对应的值) ");
                for (int i = 0; i < k; i++) {
                    key_d[i] = in.nextLong();
                }
                for (int i = 0; i < k; i++) {
                    key_z[i] = in.nextLong();
                }
                for (int i = 0; i < k; i++) {
                    num_x[i] = 1;
                    num_s[i] = 1;
                    for (int j = 0; j < k; j++) {
                        if (j == i) {
                            j++;
                        }
                        if (j == k) {
                            break;
                        }
                        num_x[i] = num_x[i] * (key_d[i] - key_d[j]);
                        num_s[i] = (-key_d[j]) * num_s[i];
                    }
                    num_x[i] = qiu_ni(num_x[i], m);
                    s = num_x[i] * num_s[i] * key_z[i] + s;
                }
                s = Math.floorMod(s, m);
                System.out.println("秘密为 " + s);
            } else {
                break;
            }
        }
    }

    // 求逆函数
    static long qiu_ni(long a, long b) {// 最后s[1]为s即逆元,t[1]为t
        long s[] = { 1, 0, 0 };
        long t[] = { 0, 1, 0 };
        long r1 = a;
        long r2 = b;
        long tmp;
        int i = (int) (r1 / r2);
        tmp = r2;
        r2 = Math.floorMod(r1, r2);
        r1 = tmp;
        while (r2 != 0) {
            s[2] = s[0] - i * s[1];
            t[2] = t[0] - i * t[1];
            s[0] = s[1];
            s[1] = s[2];
            t[0] = t[1];
            t[1] = t[2];
            if (r2 != 0)
                i = (int) (r1 / r2);
            tmp = r2;
            r2 = Math.floorMod(r1, r2);
            r1 = tmp;
        }
        return s[1];
    }
}

转载于:https://www.cnblogs.com/Qi-Lin/p/11222067.html

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值