一开始签名方案是基于PKI的,无证书签名起源于 基于身份密码体制, 2009 年第一篇无证书签名方案1被提出,随后出现了一些列方案2,3;包括无配对的无证书聚合签名方案4,更多内容参考文献5.
暂时没有看见无证书聚合签名方案实现相关的代码,本文基于JPBC库实现,使用方法可以参考B站视频。也可以使用C++和PBC库实现。
本文方案是基于论文3描述的。
Setup:
给定安全参数
κ
∈
Z
\kappa \in Z
κ∈Z,KGC选择两个循环群
G
1
G_1
G1 、
G
2
G_2
G2,其阶均为素数
q
q
q ,
G
1
G_1
G1 的生成元
P
P
P,计算可接受的配对
e
:
G
1
×
G
1
→
G
2
e:G_1×G_1→G_2
e:G1×G1→G2 。 KGC 随机选择主密钥
s
∈
Z
q
∗
s\in Z_q^\ast
s∈Zq∗ ,设置
P
p
u
b
=
s
P
P_{pub}=sP
Ppub=sP ,选择加密哈希函数
H
1
:
{
0
,
1
}
∗
→
G
1
H_1:\{0,1\}^\ast→G_1
H1:{0,1}∗→G1 ,
H
2
:
{
0
,
1
}
∗
→
G
1
H_2:\{0,1\}^\ast→G_1
H2:{0,1}∗→G1,
H
3
:
{
0
,
1
}
∗
→
Z
q
∗
H_3:\{0,1\}^\ast→Z_q^\ast
H3:{0,1}∗→Zq∗. 系统参数为
{
q
,
G
1
,
G
2
,
e
,
P
,
P
p
u
b
,
H
1
,
H
2
,
H
3
}
\{q,G_1,G_2,e,P,P_{pub},H_1,H_2,H_3 \}
{q,G1,G2,e,P,Ppub,H1,H2,H3}, 主密钥是s。
PartialKeyGen:
给定用户身份
I
D
i
∈
{
0
,
1
}
∗
ID_i\in \{0,1\}^\ast
IDi∈{0,1}∗,KGC首先计算
Q
I
D
i
=
H
1
(
I
D
i
)
Q_{ID_i }=H_1 (ID_i)
QIDi=H1(IDi)。 然后,它设置该用户的部分密钥
p
s
k
I
D
i
=
s
Q
(
I
D
i
)
psk_{ID_i }=sQ_(ID_i )
pskIDi=sQ(IDi)并将其通过安全通道传输给对应用户。 用户可以通过检查是否正确来检查其正确性
e
(
p
s
k
I
D
i
,
P
)
=
e
(
Q
I
D
i
,
P
p
u
b
)
e(psk_{ID_i },P)=e(Q_{ID_i },P_{pub})
e(pskIDi,P)=e(QIDi,Ppub)。
UserKeyGen:
用户
I
D
i
ID_i
IDi随机选择值
x
I
D
i
∈
Z
q
∗
x_{ID_i }\in Z_q^\ast
xIDi∈Zq∗作为他的用户私钥
u
s
k
I
D
i
usk_{ID_i }
uskIDi,并且计算用户公钥
u
p
k
I
D
i
=
x
I
D
i
P
upk_{ID_i}=x_{ID_i } P
upkIDi=xIDiP。
Sign:
对于消息
m
i
∈
0
,
1
∗
m_i∈{0,1}^\ast
mi∈0,1∗,选择状态信息
∆
∆
∆(选择公开参数作为状态信息),具有
I
D
i
ID_i
IDi身份的签名者执行以下步骤:
输出 ( U i , V i ) (U_i,V_i ) (Ui,Vi)作为 m i m_i mi的签名。
Verify :
给定一个消息
m
i
m_i
mi签名
(
U
i
,
V
i
)
(U_i,V_i )
(Ui,Vi),其对应的身份为
I
D
i
ID_i
IDi和公钥
u
p
k
I
D
i
upk_{ID_i}
upkIDi,计算
Q
(
I
D
i
)
=
H
1
(
I
D
i
)
,
Q
=
H
2
(
∆
)
Q_(ID_i )=H_1 (ID_i ),Q=H_2 (∆)
Q(IDi)=H1(IDi),Q=H2(∆) ,
h
i
=
H
3
(
m
i
,
I
D
i
,
u
p
k
I
D
i
,
U
i
)
h_i=H_3 (m_i,ID_i,upk_{ID_i },U_i )
hi=H3(mi,IDi,upkIDi,Ui)
如果等式
e
(
V
i
,
P
)
=
e
(
h
i
⋅
u
p
k
I
D
i
+
Q
I
D
i
,
P
p
u
b
)
e
(
U
i
,
Q
)
e(V_i,P)=e(h_i⋅upk_{ID_i }+Q_{ID_i },P_{pub} )e(U_i,Q)
e(Vi,P)=e(hi⋅upkIDi+QIDi,Ppub)e(Ui,Q) 成立,接收签名;否则拒绝。
Aggregate:
任何人都可以充当聚合签名生成器, 对于n个用户的聚合集
{
U
1
,
…
,
U
n
}
\{U_1,…,U_n\}
{U1,…,Un}其对应身份为
{
I
D
1
,
…
,
I
D
n
}
\{ID_1,…,ID_n \}
{ID1,…,IDn}以及对应公钥
{
u
p
k
1
,
…
,
u
p
k
n
}
\{upk_1,…,upk_n\}
{upk1,…,upkn},对应消息签名对为
{
(
m
1
,
σ
1
=
(
U
1
,
V
1
)
)
,
…
,
(
m
n
,
σ
n
=
(
U
n
,
V
n
)
)
}
\{(m_1,σ_1=(U_1,V_1 )),…,(m_n,σ_n=(U_n,V_n))\}
{(m1,σ1=(U1,V1)),…,(mn,σn=(Un,Vn))},
聚合签名计算:
V
=
∑
i
=
1
n
V
i
V=∑_{i=1}^n V_i
V=∑i=1nVi, 将
σ
=
(
U
1
,
…
,
U
n
,
V
)
σ=(U_1,…,U_n,V)
σ=(U1,…,Un,V)作为聚合签名。
Aggregate-Verify:
验证聚合签名
σ
=
(
U
1
,
…
,
U
n
,
V
)
σ=(U_1,…,U_n,V)
σ=(U1,…,Un,V)由n个用户
{
U
1
,
…
,
U
n
}
\{U_1,…,U_n\}
{U1,…,Un}身份为
{
I
D
1
,
…
,
I
D
n
}
\{ID_1,…,ID_n\}
{ID1,…,IDn}和对应的公钥
{
u
p
k
1
,
…
,
u
p
k
n
}
\{upk_1,…,upk_n\}
{upk1,…,upkn};关于消息
{
m
1
,
…
,
m
n
}
\{m_1,…,m_n \}
{m1,…,mn} 验证者执行以下操作:
计算
Q
I
D
i
=
H
1
(
I
D
i
)
,
Q
=
H
2
(
∆
)
,
h
i
=
H
3
(
m
i
,
I
D
i
,
u
p
k
I
D
i
,
U
i
)
,
i
=
1
,
…
,
n
Q_{ID_i }=H_1 (ID_i ),Q=H_2 (∆),h_i=H_3 (m_i,ID_i,upk_{ID_i },U_i ),i=1,…,n
QIDi=H1(IDi),Q=H2(∆),hi=H3(mi,IDi,upkIDi,Ui),i=1,…,n.
验证:
e
(
V
,
P
)
=
e
(
∑
i
=
1
n
[
h
i
⋅
u
p
k
I
D
i
+
Q
I
D
i
]
,
P
p
u
b
)
e
(
∑
i
=
1
n
U
i
,
Q
)
e(V,P)=e(∑_{i=1}^n [h_i⋅upk_{ID_i}+Q_{ID_i} ] ,P_{pub} )e(∑_{i=1}^n U_i ,Q)
e(V,P)=e(∑i=1n[hi⋅upkIDi+QIDi],Ppub)e(∑i=1nUi,Q).如果满足则通过;否则验证失败。
整体思路, 将相关密钥信息作为文件保存(序列化后可以通过网络传输),根据方案不同的功能设计不同的函数。
关于文件a_181_603.properties
的内容
type=a
q=98826429041171753291515535532523512299028170537954154869719707264887274916552228805607584116490046284509883309001532457986879277885241872021906840932513241346999389365188296460009947
h=32243626948934860887488490158437299489453513352745889246437755713701521031193083418924110592954582395114812811896992400310730276
r=3064991081731777546575510593831386635550174528483098623
exp2=181
exp1=127
sign1=-1
sign0=-1
代码
import it.unisa.dia.gas.jpbc.Element;
import it.unisa.dia.gas.jpbc.Pairing;
import it.unisa.dia.gas.plaf.jpbc.pairing.PairingFactory;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
public class CLAS {
//生成公私钥对,并保存到文件中
public static void Setup(String pairingParametersFileName, String pubParamFileName, String KGC_SK_FileName) {
Pairing bp = PairingFactory.getPairing(pairingParametersFileName);
//G1的生成元P
Element P = bp.getG1().newRandomElement().getImmutable();
//计算主私钥和公钥
Element s = bp.getZr().newRandomElement().getImmutable();
Element P_Pub = P.mulZn(s);
Properties pubParamProp = new Properties();
//后面对写的元素统一采用如下方法:首先将元素转为字节数组,然后进行Base64编码为可读字符串
pubParamProp.setProperty("P", Base64.getEncoder().encodeToString(P.toBytes()));
pubParamProp.setProperty("P_Pub", Base64.getEncoder().encodeToString(P_Pub.toBytes()));
storePropToFile(pubParamProp, pubParamFileName);
Properties param_s = new Properties();//私钥不存入公开参数中,由KGC自己保存
param_s.setProperty("s", Base64.getEncoder().encodeToString(s.toBytes()));
storePropToFile(param_s, KGC_SK_FileName);
}
//根据用户id生成私钥
public static void PartialPrivateKeyGen(String pairingParametersFileName, String id, String KGC_SK_FileName) throws NoSuchAlgorithmException {
Pairing bp = PairingFactory.getPairing(pairingParametersFileName);
//使用HASH 将 id 转为QID
byte[] idHash = HASH(id);
Element QID = bp.getG1().newElementFromHash(idHash, 0, idHash.length).getImmutable();
//从文件中读取 主私钥
Properties mskProp = loadPropFromFile(KGC_SK_FileName);
String sString = mskProp.getProperty("s");
Element s = bp.getZr().newElementFromBytes(Base64.getDecoder().decode(sString)).getImmutable(); //Base64编码后对应的恢复元素的方法
//计算用户私钥, 这里应该将私钥安全的传输给用户
//方便模拟,统一存入一个文件中
Element psk_ID = QID.powZn(s).getImmutable();
Properties pskProp = new Properties();
pskProp.setProperty("psk", Base64.getEncoder().encodeToString(psk_ID.toBytes()));
storePropToFile(pskProp, id + ".properties");
}
//生成用户私钥
public static void UserKeyGen(String pairingParametersFileName, String pubParamFileName, String id) {
Pairing bp = PairingFactory.getPairing(pairingParametersFileName);
//从文件中读取公钥
Properties pkProp = loadPropFromFile(pubParamFileName);
String PString = pkProp.getProperty("P");
Element P = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(PString)).getImmutable();
//生成随机数x,作为用户的私钥
Element x = bp.getZr().newRandomElement().getImmutable();
//计算用户公钥
Element upk = P.mulZn(x);
Properties userProp = new Properties();
userProp.setProperty("usk", Base64.getEncoder().encodeToString(x.toBytes()));
userProp.setProperty("upk", Base64.getEncoder().encodeToString(upk.toBytes()));
storePropToFile(userProp, id + ".properties");
}
//签名
public static Element[] Sign(String pairingParametersFileName, String pubParamFileName, String id, byte[] message) throws NoSuchAlgorithmException {
Pairing bp = PairingFactory.getPairing(pairingParametersFileName);
//获取公开参数
Properties pubProp = loadPropFromFile(pubParamFileName);
Element P = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(pubProp.getProperty("P"))).getImmutable();
Element P_Pub = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(pubProp.getProperty("P_Pub"))).getImmutable();
//获取用户自己的信息
Properties userProp = loadPropFromFile(id + ".properties");
Element upk = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(userProp.getProperty("upk"))).getImmutable();
Element x = bp.getZr().newElementFromBytes(Base64.getDecoder().decode(userProp.getProperty("usk"))).getImmutable();
Element psk = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(userProp.getProperty("psk"))).getImmutable();
//选择随机数
Element r = bp.getZr().newRandomElement();
Element U = P.mulZn(r).getImmutable();
//获取状态信息,将公开参数作为状态信息
String p = pubProp.getProperty("P");
String pPub = pubProp.getProperty("P_Pub");
byte[] hash = HASH(p + pPub);
Element Q = hashToG(bp, hash);
// 计算m, id, upk,U组合的hash值
byte[] res = hashCombination(message, id.getBytes(), upk.toBytes(), U.toBytes());
Element h = hashToZ(bp, res);
Element V = P_Pub.mulZn(x).mulZn(h).add(Q.mulZn(r)).add(psk).getImmutable();
Element[] sigma = new Element[2];
sigma[0] = U;
sigma[1] = V;
return sigma;
}
//验证
public static boolean Verify(String pairingParametersFileName, String pubParamFileName, String id, byte[] message, Element[] sigma) throws NoSuchAlgorithmException {
Pairing bp = PairingFactory.getPairing(pairingParametersFileName);
//使用sha1 将 id 转为QID
byte[] idHash = HASH(id);
Element QID = bp.getG1().newElementFromHash(idHash, 0, idHash.length).getImmutable();
//获取公开参数
Properties pubProp = loadPropFromFile(pubParamFileName);
Element P = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(pubProp.getProperty("P"))).getImmutable();
Element P_Pub = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(pubProp.getProperty("P_Pub"))).getImmutable();
//获取状态信息,将公开参数作为状态信息
String p = pubProp.getProperty("P");
String pPub = pubProp.getProperty("P_Pub");
byte[] hash = HASH(p + pPub);
Element Q = hashToG(bp, hash);
//获取用户的公钥,
Properties userProp = loadPropFromFile(id + ".properties");
Element upk = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(userProp.getProperty("upk"))).getImmutable();
// 计算m, id, upk,U组合的hash值
byte[] res = hashCombination(message, id.getBytes(), upk.toBytes(), sigma[0].toBytes());
Element h = hashToZ(bp, res);
Element left = bp.pairing(sigma[1], P);
Element right = bp.pairing(QID.add(upk.mulZn(h)), P_Pub).mul(bp.pairing(sigma[0], Q));
return left.isEqual(right);
}
//聚合签名, U_1,U_2,...,U_n,V
public static Element[] Aggregate(List<Element[]> sigmaList) {
Element[] res = new Element[sigmaList.size() + 1];
Element[] init = sigmaList.get(0);
res[0] = init[0];
Element V = init[1];
for (int i = 1; i < sigmaList.size(); i++) {
res[i] = sigmaList.get(i)[0];
V = V.add(sigmaList.get(1)[1]);
}
res[sigmaList.size()] = V;
return res;
}
// 聚合签名验证
public static boolean AggregateVerify(String pairingParametersFileName, String pubParamFileName, String[] idx, byte[][] message, Element[] sigma) throws NoSuchAlgorithmException {
Pairing bp = PairingFactory.getPairing(pairingParametersFileName);
Element[] QIDX = new Element[idx.length];
for (int i = 0; i < idx.length; i++) {
byte[] idHash = HASH(idx[i]);
Element QID = bp.getG1().newElementFromHash(idHash, 0, idHash.length).getImmutable();
QIDX[i] = QID;
}
//获取公开参数信息
Properties pubProp = loadPropFromFile(pubParamFileName);
Element P = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(pubProp.getProperty("P"))).getImmutable();
Element P_Pub = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(pubProp.getProperty("P_Pub"))).getImmutable();
String p = pubProp.getProperty("P");
String pPub = pubProp.getProperty("P_Pub");
byte[] hash = HASH(p + pPub);
Element Q = hashToG(bp, hash);
Element[] PKX = new Element[idx.length];
for (int i = 0; i < idx.length; i++) {
Properties userProp = loadPropFromFile(idx[i] + ".properties");
Element upk = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(userProp.getProperty("upk"))).getImmutable();
PKX[i] = upk;
}
Element[] hx = new Element[idx.length];
for (int i = 0; i < idx.length; i++) {
byte[] res = hashCombination(message[i], idx[i].getBytes(), PKX[i].toBytes(), sigma[i].toBytes());
Element h = hashToZ(bp, res);
hx[i] = h;
}
Element left = bp.pairing(sigma[idx.length], P);
Element U = sigma[0];
for (int i = 1; i < idx.length; i++) {
U = U.add(sigma[i]);
}
Element part = QIDX[0].add(PKX[0].mulZn(hx[0]));
for (int i = 1; i < idx.length; i++) {
part = part.add(QIDX[i].add(PKX[i].mulZn(hx[i])));
}
Element right = bp.pairing(part, P_Pub).mul(bp.pairing(U, Q));
return left.isEqual(right);
}
//一种可行的组合方法
public static byte[] hashCombination(byte[] message, byte[] id, byte[] upk, byte[] U) throws NoSuchAlgorithmException {
int m_len = message.length, id_len = id.length, upk_len = upk.length, u_len = U.length;
int total_len = m_len + id_len + upk_len + u_len;
byte[] res = new byte[total_len];
for (int i = 0; i < m_len; i++) {
res[i] = message[i];
}
for (int i = 0; i < id_len; i++) {
res[i + m_len] = id[i];
}
for (int i = 0; i < upk_len; i++) {
res[i + m_len + id_len] = upk[i];
}
for (int i = 0; i < u_len; i++) {
res[i + m_len + id_len + upk_len] = U[i];
}
MessageDigest instance = MessageDigest.getInstance("SHA-256");
instance.update(res);
return instance.digest();
}
public static void storePropToFile(Properties prop, String fileName) {
try (FileOutputStream out = new FileOutputStream(fileName, true)) {
prop.store(out, null);
} catch (IOException e) {
e.printStackTrace();
System.out.println(fileName + " save failed!");
System.exit(-1);
}
}
public static Properties loadPropFromFile(String fileName) {
Properties prop = new Properties();
try (FileInputStream in = new FileInputStream(fileName)) {
prop.load(in);
} catch (IOException e) {
e.printStackTrace();
System.out.println(fileName + " load failed!");
System.exit(-1);
}
return prop;
}
public static byte[] HASH(String content) throws NoSuchAlgorithmException {
MessageDigest instance = MessageDigest.getInstance("SHA-1");
instance.update(content.getBytes());
return instance.digest();
}
public static Element hashToG(Pairing pb, byte[] code) {
return pb.getG1().newElementFromHash(code, 0, code.length).getImmutable();
}
public static Element hashToZ(Pairing pb, byte[] code) {
return pb.getZr().newElementFromHash(code, 0, code.length).getImmutable();
}
public static void main(String[] args) throws Exception {
String pairingParametersFileName = "a_181_603.properties";
String idAlice = "alice@example.com";
String idBob = "bob@example.com";
String pubParamFileName = "data/pub.properties";
String KGCFileName = "data/kgc.properties";
Setup(pairingParametersFileName, pubParamFileName, KGCFileName);
PartialPrivateKeyGen(pairingParametersFileName, idAlice, KGCFileName);
PartialPrivateKeyGen(pairingParametersFileName, idBob, KGCFileName);
UserKeyGen(pairingParametersFileName, pubParamFileName, idAlice);
UserKeyGen(pairingParametersFileName, pubParamFileName, idBob);
String message_a = "Alice,This is a message from Alice!";
String message_b = "Bob,This is a message from Bob!";
Element[] sigma1 = Sign(pairingParametersFileName, pubParamFileName, idAlice, message_a.getBytes());
Element[] sigma2 = Sign(pairingParametersFileName, pubParamFileName, idBob, message_b.getBytes());
boolean result = Verify(pairingParametersFileName, pubParamFileName, idAlice, message_a.getBytes(), sigma1);
System.out.println("Alice 验证签名通过? " + result);
result = Verify(pairingParametersFileName, pubParamFileName, idBob, message_b.getBytes(), sigma2);
System.out.println("Bob 验证签名通过? " + result);
List<Element[]> sigmaList = new ArrayList<>();
sigmaList.add(sigma1);
sigmaList.add(sigma2);
Element[] SIGMA = Aggregate(sigmaList);
String[] idx = {idAlice, idBob};
byte[][] message = {message_a.getBytes(), message_b.getBytes()};
// message[0][1] = 1;//假如消息被篡改
result = AggregateVerify(pairingParametersFileName, pubParamFileName, idx, message, SIGMA);
System.out.println("聚合签名验证通过? " + result);
}
}
Cryptanalysis and improvement of a certificateless aggregate signature scheme
A certificateless aggregate signature scheme for healthcare wireless sensor network
Efficient certificateless aggregate signcryption scheme without bilinear pairings