Java密码学原型算法实现——第三部分:双线性对
背景介绍
技术博客已经好久没更新了。倒不是因为没得写,是因为实在是太忙了,而且研究也到了一个瓶颈期,需要大量阅读文献。
本来打算很长一段时间都不更新博客了,甚至打算等我毕业工作后再更新一些有价值的博客,但是最近在CSDN私信上和知乎上经常收到求救帖子,希望我能写一个jPBC使用方法的博客。甚至实验室的硕士生们也在各种咨询我相关的问题。于是,我打算一劳永逸,写一篇有关jPBC使用的博客。希望这个博客出来后,能解决绝大多数人的问题吧…
本篇博客期望解决的问题:
- 如何使用jPBC库进行双线性群初始化,包括:
- 质数阶双线性群(Prime-Order Bilinear Groups);
- 合数阶双线性群(Composite-Order Bilinear Groups);
- 如何使用jPBC库执行双线性群运算,包括:
- 指数群Z\mathbb{Z}的加法和乘法;
- 双线性群G\mathbb{G}的乘法和指数幂;
- 目标群GT\mathbb{G}_T的乘法和指数幂
- 双线性群G\mathbb{G}映射到目标群GT\mathbb{G}_T的对(Pairing)运算;
- 使用jPBC库的一些注意事项。
本篇博客不会涉及到的问题:
- 如何配置jPBC库到Eclipse中;这方面的内容请参考我的另一篇博客:jPBC 2.0.0配置与测试(补充版);
- 有关双线性对的数学知识;这方面我在第二章会稍微介绍一下,但是不会详谈,因为内容太多了。
- 对偶双线性群向量空间群(Dual Pairing Vector Space,DPVS);这个群在理论上被用于替代合数阶双线性群。其可以在保证同等安全性的条件下,使双线性对运算时间较短,而代价是存储开销会变大。这个工具在2012年得到了广泛的应用。但是这两年普遍认为这个工具的进一步应用场景有限,而且表示并不直观,还不如和合数阶双线性群好用。jPBC 2.0.0实际上提供了DPVS的实现,也是正确的。有兴趣的朋友们可以自己研究一下,我在这里就不详述了。
- 如何使用jPBC 2.0.0的多线性对(Multilinear Maps)函数库;这方面我自己一直没找时间测试一下多线性对函数库,实际上近期我也不太想测试这个库,主要有两方面的原因。
- 现在所构造出来的多线性对并非密码学中的理想多线性对(Ideal Multilinear Maps),而是候选多线性对(Candidate Multilinear Maps),后者在使用上有很多的限制。
- jPBC 2.0.0实现的多线性对是[CLT-14]的方案,但这个方案已经被证明是不安全的了。
双线性群简介
这里我直接引用自己的二篇水文来介绍(都是凑数用的…)选择密文安全的身份及广播加密方案,密码学报,Experimental performance comparisons between (H) IBE schemes over composite-order and prime-order bilinear groups,IBCAST 2014。
质数阶双线性群(Prime-Order Bilinear Groups)
质数双线性群可以由五元组(p,G1,G2,GT,e)(p, \mathbb{G}_1, \mathbb{G}_2, \mathbb{G}_T, e)来描述。五元组中pp是一个与给定安全常数λ\lambda相关的大质数,G1,G2,GT\mathbb{G}_1, \mathbb{G}_2, \mathbb{G}_T均是阶为pp的乘法循环群,ee为双线性映射e:G1×G2→GTe: \mathbb{G}_1 \times \mathbb{G}_2 \rightarrow \mathbb{G}_T,它满足以下3个条件:
- 双线性(Bilinearity):对于任意的g∈G1g \in \mathbb{G}_1,h∈G2h \in \mathbb{G}_2,a,b∈Zpa,b \in \mathbb{Z}_p,有e(ga,hb)=e(g,h)abe(g^a, h^b) = e(g, h)^{ab};
- 非退化性(Non-degeneracy):至少存在元素g1∈G1,g2∈G2g_1 \in \mathbb{G}_1, g_2 \in \mathbb{G}_2,满足e(g1,g2)≠1e(g_1, g_2) \neq 1;
- 可计算性(Efficiency):对于任意的u∈G1,v∈G2u \in \mathbb{G}_1, v \in \mathbb{G}_2,存在一个与给定安全常数λ\lambda相关的多项式时间算法,可以高效地计算e(u,v)e(u, v);
现在的密码学相关论文中,习惯将G1,G2\mathbb{G}_1, \mathbb{G}_2设置为乘法循环群。但是,基于椭圆曲线的双线性群构造中,G1,G2\mathbb{G}_1, \mathbb{G}_2是加法群。所以在大约2005年以前的论文中,双线性群一般写成加法群形式。jPBC中将G1,G2\mathbb{G}_1, \mathbb{G}_2表示称为了乘法循环群,因此在实现写成加法群形式的方案时,要注意将加法群改成乘法群的写法再进行实现。如何修改呢?很简单,把加法群中的加法运算写成乘法运算、把加法群中的乘法运算写成幂指数运算即可。
合数阶双线性群(Composite-Order Bilinear Groups)
合数阶双线性群和质数阶双线性群很类似,区别是G1,G2,GT\mathbb{G}_1, \mathbb{G}_2, \mathbb{G}_T的阶数是一个合数NN,其中NN是一些大质数的乘积,如N=p1p2⋯pnN=p_1 p_2 \cdots p_n。同样,ee为双线性映射e:G1×G2→GTe: \mathbb{G}_1 \times \mathbb{G}_2 \rightarrow \mathbb{G}_T,它满足双线性性、非退化性以及可计算性。
与质数阶双线性群不同,合数阶双线性群中,GN\mathbb{G}_N有阶数分别为p1,p2,⋯,pnp_1, p_2, \cdots, p_n的子群Gp1,⋯,Gpn\mathbb{G}_{p_1}, \cdots, \mathbb{G}_{p_n}。这些子群进一步满足正交特性。
对于所有的h_i \in \mathbb{G}_{p_i}和h_j \in \mathbb{G}_{p_j},如果i \neq j,那么e(h_i, h_j) = 1
简单地说就是,子群之间进行双线性运算的结果必为1。
一些说明
首先,由于双线性群现在的构造是基于椭圆曲线的,而椭圆曲线上的元素是由坐标(x,y)(x, y)表示的,所以如果我们将G1,G2\mathbb{G}_1, \mathbb{G}_2的结果输出到Java的控制台,我们得到的是一个坐标。不过,GT\mathbb{G}_T是一个普通的Z\mathbb{Z}群,所以其元素的表示是一个数。
其次,在密码学中,如果G1=G2\mathbb{G}_1 = \mathbb{G}_2,我们称这个双线性群是对称双线性群(Symmetric Bilinear Group),否则称之为非对称双线性群(Asymmetric Bilinear Group)。
是否为对称双线性群由选取的椭圆曲线种类决定。一般认为,非对称双线性群要比对称双线性群更安全。特别地,现在已经证明一些特定的对称双线性群是不安全的了。
现在jPBC可以使用的曲线为如下几类:
- Type A
- Type A1
- Type D
- Type E
- Type F
- Type G
现在密码学实现基本只使用Type A和Type A1的。前者为对称质数阶双线性群,后者为合数阶对称双线性群。本博客也只在这两类曲线上实验。其他类曲线的实现类似。由于是对称双线性群,本博客中G1,G2\mathbb{G}_1, \mathbb{G}_2统一写为G\mathbb{G}。
双线性群初始化
在jPBC中,双线性群的使用都是通过叫做Pairing的对象来实现的。双线性群的初始化在jPBC中就是对Pairing对象的初始化。双线性群有两种初始化的方法。第一种是通过代码动态产生一个双线性群,第二种是从文件中读取参数而产生群。
通过代码动态产生
动态产生的方法非常简单,大概有如下步骤:指定椭圆曲线的种类、产生椭圆曲线参数、初始化Pairing。Type A曲线需要两个参数:rBit是Zp\mathbb{Z}_p中阶数pp的比特长度;qBit是G\mathbb{G}中阶数的比特长度。代码为:
TypeACurveGenerator pg = new TypeACurveGenerator(rBit, qBit);
PairingParameters typeAParams = pg.generate();
Pairing pairing = PairingFactory.getPairing(typeAParams);
Type A1曲线需要二个参数:numPrime是阶数N中有几个质数因子;qBit是每个质数因子的比特长度。注意,Type A1涉及到的阶数很大,其参数产生的时间也比较长。代码为:
TypeA1CurveGenerator pg = new TypeA1CurveGenerator(numPrime, qBit);
PairingParameters typeA1Params = pg.generate();
Pairing pairing = PairingFactory.getPairing(typeA1Params);
通过文件读取产生
我们也可以选择事先产生好参数,存放在文件中。以后再初始化的时候,直接从文件中读取参数,就可以非常快速的初始化双线性群。
PairingParameters支持toString()函数。实际上,我们可以直接将PairingParametersd的toString()存放在文件中。读取的时候,通过读取文件就可以直接初始化双线性群了。
Type A曲线从文件中读取参数初始化的代码为:
TypeACurveGenerator pg = new TypeACurveGenerator(rBit, qBit);
PairingParameters typeAParams = pg.generate();
//将参数写入文件a.properties中,我用了Princeton大学封装的文件输出库
Out out = new Out("a.properties");
out.println(typeAParams);
//从文件a.properties中读取参数初始化双线性群
Pairing pairing = PairingFactory.getPairing("a.properties");
Type A1曲线从文件中读取参数初始化的代码为:
TypeA1CurveGenerator pg = new TypeA1CurveGenerator(numPrimes, qBit);
PairingParameters typeA1Params = pg.generate();
//将参数写入文件a1.properties中,同样使用了Princeton大学封装的文件输出库
Out out = new Out("a1.properties");
out.println(typeA1Params);
//从文件a1.properties中读取参数初始化双线性群
Pairing pairing = PairingFactory.getPairing("a1.properties");
产生双线性群中的随机数
Type A中产生随机数的方法很简单,代码为:
//随机产生一个Z_p群的元素
Element Z_p = pairing.getZr().newRandomElement().getImmutable();
//随机产生一个G_1群的元素
Element G_1 = pairing.getG1().newRandomElement().getImmutable();
//随机产生一个G_2群的元素
Element G_2 = pairing.getG2().newRandomElement().getImmutable();
//随机产生一个G_T群的元素
Element G_T = pairing.getGT().newRandomElement().getImmutable();
Type A1中产生随机数的方法稍微有点麻烦。对于ZN\mathbb{Z}_N和GT\mathbb{G}_T方法和Type A一样。代码为:
//随机产生一个Z_N群的元素
Element Z_N = pairing.getZr().newRandomElement().getImmutable();
//随机产生一个G_T群的元素
Element G_T = pairing.getGT().newRandomElement().getImmutable();
但是对于G\mathbb{G}就不同了。因为G\mathbb{G}有子群Gpi\mathbb{G}_{p_i},Type A1产生随机数时需要指定生成元,椭圆曲线的参数,产生哪个子群的元素,以及Type A1一共有多少个子群。
假定我们产生的Type A1共有n个子群,这n个子群的阶分别为p1,⋯,pnp_1, \cdots, p_n,产生随机数的代码如下:
TypeA1CurveGenerator pg = new TypeA1CurveGenerator(numPrimes, qBit);
PairingParameters typeA1Params = pg.generate();
Pairing pairing = PairingFactory.getPairing(typeA1Params);//设定并存储一个生成元。由于椭圆曲线是加法群,所以G群中任意一个元素都可以作为生成元
Element generator = pairing.getG1().newRandomElement().getImmutable();
//随机产生一个G_p_1中的元素
Element G_p_1 = ElementUtils.getGenerator(pairing, generator, typeA1Params, 0, numPrimes).getImmutable();
//随机产生一个G_p_2中的元素
Element G_p_2 = ElementUtils.getGenerator(pairing, generator, typeA1Params, 1, numPrimes).getImmutable();
// ......
//随机产生一个G_p_n中的元素
Element G_p_n = ElementUtils.getGenerator(pairing, generator, typeA1Params, 1, numPrimes).getImmutable();
将指定的元素哈希到双线性群中
由于双线性群最初是用在基于身份的加密(Identity-Based Encryption)系统中,我们经常会需要将一个特定的String或者byte[]哈希到双线性群中。
jPBC支持将byte[]哈希到双线性群的Z,G,GT\mathbb{Z}, \mathbb{G}, \mathbb{G}_T中。但是,jPBC说明文档中没有提到的是,byte[]数组长度不能太长,如果过长会抛出异常。因此,我建议首先将byte[]用一个SHA256或者其他通用哈希函数哈希到固定长度,再用jPBC提供的函数哈希到双线性群上。在这里我略去SHA256步骤,直接给出哈希到Z,G,GT\mathbb{Z}, \mathbb{G}, \mathbb{G}_T群的代码:
//将byte[] byteArray_Z_p哈希到Z_p群
Element hash_Z_p = pairing.getZr().newElement().setFromHash(byteArray_Z_p, 0, byteArray_Z_p.length);
//将byte[] byteArray_G_1哈希到G_1群
Element hash_G_1 = pairing.getG1().newElement().setFromHash(byteArray_G_1, 0, byteArray_G_1.length);
//将byte[] byteArray_G_2哈希到G_2群
Element hash_G_2 = pairing.getG2().newElement().setFromHash(byteArray_G_2, 0, byteArray_G_2.length);
//将byte[] byteArray_G_T哈希到G_T群
Element hash_G_T = pairing.getGT().newElement().setFromHash(byteArray_G_T, 0, byteArray_G_T.length);
注意,对于Type A1来说,这个代码无法指定哈希到指定子群Gpi\mathbb{G}_{p_i}中。解决方法是将byte[]先哈希到Z\mathbb{Z}群,然后利用G,GT\mathbb{G}, \mathbb{G}_T的生成元计算幂指数,从而达到哈希到G,GT\mathbb{G}, \mathbb{G}_T上的效果。
双线性群的运算
双线性群之间有如下运算:
- G\mathbb{G}相关运算:GZ\mathbb{G}^{\mathbb{Z}}, G×G\mathbb{G} \times \mathbb{G};
- GT\mathbb{G}_T相关运算:GZT\mathbb{G}_T^{\mathbb{Z}}, GT×GT\mathbb{G}_T \times \mathbb{G}_T;
- Z\mathbb{Z}相关运算:Z+Z\mathbb{Z} + \mathbb{Z}, Z×Z\mathbb{Z} \times \mathbb{Z};
- Pairing运算
做运算的时候要注意一下几点:
- Java的运算结果都是产生一个新的Element来存储,所以我们需要把运算结果赋值给一个新的Element;
- Java在进行相关运算时,参与运算的Element值可能会改变。所以,如果需要在运算过程中保留参与运算的Element值,在存储的时候一定要调用getImmutable(),具体方法见代码中的初始化相关参数部分。
- 其实为了保险起见,防止Element在运算的过程中修改了Element原本的数值,可以使用Element.duplicate()方法。这个方法将返回一个与Element数值完全一样的Element,但是是个新的Element对象。举例来说,如果做G1×G1\mathbb{G_1} \times \mathbb{G}_1的运算,可以写成:
Element G_1_m_G_1 = G_1.duplicate().mul(G_1_p.duplicate());
- G\mathbb{G}和G\mathbb{G}其实也是可以进行幂指数运算的,即GG\mathbb{G}^{\mathbb{G}},调用的函数为Element e1.pow(Element e2)。特别注意,我们再写G\mathbb{G}群的Z\mathbb{Z}次方运算时,用的函数为powZn(),而不是pow(),这个调用错误很容易使得程序的运算结果不正确。
代码如下:
//初始化相关参数
Element G_1 = pairing.getG1().newRandomElement().getImmutable();
Element G_2 = pairing.getG2().newRandomElement().getImmutable();
Element Z = pairing.getZr().newRandomElement().getImmutable();
Element G_T = pairing.getGT().newRandomElement().getImmutable();Element G_1_p = pairing.getG1().newRandomElement().getImmutable();
Element G_2_p = pairing.getG2().newRandomElement().getImmutable();
Element Z_p = pairing.getZr().newRandomElement().getImmutable();
Element G_T_p = pairing.getGT().newRandomElement().getImmutable();//G_1的相关运算
//G_1 multiply G_1
Element G_1_m_G_1 = G_1.mul(G_1_p);
//G_1 power Z
Element G_1_e_Z = G_1.powZn(Z);//G_2的相关运算
//G_2 multiply G_2
Element G_2_m_G_2 = G_2.mul(G_2_p);
//G_2 power Z
Element G_2_e_Z = G_2.powZn(Z);//G_T的相关运算
//G_T multiply G_T
Element G_T_m_G_T = G_T.mul(G_T_p);
//G_T power Z
Element G_T_e_Z = G_T.powZn(Z);//Z的相关运算
//Z add Z
Element Z_a_Z = Z.add(Z_p);
//Z multiply Z
Element Z_m_Z = Z.mul(Z_p);//Pairing运算
Element G_p_G = pairing.pairing(G_1, G_2);
Java密码学原型算法实现——第三部分:双线性对相关推荐
- java密码学原型算法_java密码学原型算法实现——双线性对.pdf
java密码学原型算法实现--双线性对 Java 密码学原型算法实现--双线性对 1.背景介绍 如何使用jPBC 库进行双线性群初始化,包括: (1)质数阶双线性群(Prime-Order Bilin ...
- Java密码学原型算法实现——第二部分:单钥加密算法
题注 本部分为单钥加密算法的实现.单钥加密体制是密码学加密中的核心密码学原型之一,很早很早前人类就已经开始了单钥密码学体制的研究.本部分的所有实现基于Bouncy Castle库,其地址详见我上一篇博 ...
- 【Java数据结构与算法】第三章 双向链表和约瑟夫问题
第三章 第三章 双向链表.环形链表以及约瑟夫问题 文章目录 第三章 第三章 双向链表.环形链表以及约瑟夫问题 一.双向链表 1.双线链表对比单向链表的优点: 2.分析双向链表的遍历.添加.修改和删除的 ...
- Java 密码学算法
Java 密码学算法 候捷老师在< 深入浅出MFC 2e(电子版)>中引用林语堂先生的一句话: 只用一样东西,不明白它的道理,实在不高明 只知道How,不知道Why,出了一点小问题时就无能 ...
- Java - 密码学
文章目录 前言 一.概述 1 名词概念 2 密钥证书管理体系 3 常见的加密手段 4 DES加解密Demo 5 区别toString与newString 6 编码算法 URL编码 Base64编码 7 ...
- Java 数据结构与算法系列之冒泡排序
一.前言 相信大部分同学都已经学过数据结构与算法这门课了,并且我们可能都会发现一个现象就是我们所学过的数据结构与算法类的书籍基本都是使用 C 语言来写的,好像没见过使用 Java 写的数据结构与算法. ...
- Java数据结构与算法——树(基本概念,很重要)
声明:码字不易,转载请注明出处,欢迎文章下方讨论交流. 有网友私信我,期待我的下一篇数据结构.非常荣幸文章被认可,也非常感谢你们的监督. 前言:Java数据结构与算法专题会不定时更新,欢迎各位读者监督 ...
- 刚从阿里、头条面试回来,尚硅谷java数据结构与算法百度云
二.回顾整理阿里面试题 基本就这样了,还有一些零星的问题想不起来了,答案也整理出来了. 自我介绍 JVM如何加载一个类的过程,双亲委派模型中有哪些方法? HashMap如何实现的? HashMap和C ...
- JAVA版连连看算法研究
JAVA连连看之算法: 连连看连接方式的类型:<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:off ...
- Java数据结构和算法(六)——前缀、中缀、后缀表达式
前面我们介绍了三种数据结构,第一种数组主要用作数据存储,但是后面的两种栈和队列我们说主要作为程序功能实现的辅助工具,其中在介绍栈时我们知道栈可以用来做单词逆序,匹配关键字符等等,那它还有别的什么功能吗 ...
最新文章
- Linux正确的关机方式
- Python中bytes和str区别详细介绍
- Qt-ros插件:创建工程,编译实现操控小乌龟(二)
- html调用js函数_Java Web初学者探索学习笔记10—网络API的js数据接口调用解决方案...
- Selenium Webdriver概述(转)
- macaca web(4)
- java代码传中文参数乱码
- 基于OpenCV的计算机视觉入门(4)线段和形状的绘制
- oracle归档日志满了的处理方法
- jetty client 与apache http client的实现、分析
- CrossApp应用源码集合贴
- Echarts 模拟飞机飞行动态图
- Pranava Pra 使用教程
- 哈工大慕课 学生成绩管理系统V1.0~5.0
- 基于loongnix20系统,libvlc代码直接集成测试
- win10 无法删除 注册表 蓝牙_编辑设置Win10删除蓝牙设备后无法重新添加的修复办法...
- MySQL数据库的官网下载、安装及卸载(2018年最新)
- Java算法完美解决五位哲学家用餐问题
- 基于vue框架的在线问卷的设计
- 南阳市区彩礼 wribao.php230.com,2021南阳彩礼钱一般给多少 河南南阳2021年结婚彩礼...
热门文章
- 使用sklearn加载波士顿房价数据集
- python ray定时任务_python定时任务APScheduler
- cgi一键还原 linux分区,cgi plus一键备份恢复工具
- 已知两点坐标,求过这两点的直线一般方程式,并求点(x0, y0)到直线的距离
- UAV021(六):系统架构优化、SBUS协议、遥控器控制电机转动
- 嵌入式工具 | 嵌入式开发常用几款的代码编辑器
- 【机器学习+NER】手把手教你用机器学习CRF模型构建NER系统(CCL2021)
- DB2错误码sqlcode对应表
- Linux命令之打包tar
- FLTK--轻量级C++跨平台GUI库