参考
《基于代理重加密的区块链数据受控共享方案》贵州大学 郭庆
《基于国密算法的秘密文件的加、解密方法及保护系统》 北京航空航天大学 伍前红
b站视频:可厉害的土豆、妈咪说
代理重加密(Proxy Re-Encryption)技术原理和Java代码实现代理重加密算法实现区块链之美的博客-CSDN博客
上面这篇文章里提到github上有Go(GitHub - SherLzp/goRecrypt: Proxy Re-Encryption go implementation)和Java(GitHub - uheqiang/java-sdk: java sdk for Proxy re-encryption functionality)两个版本的实现方案,go方案里有数学原理的描述,java方案有代码,可以两者结合来看。如果要理解数学原理,需要提前了解一些椭圆双曲线的知识(b站里有ecc原理的视频)。这样就可以按照下文尝试去探究代理重加密的原理了。看看代码的数学理论依据是什么。
一、代理重加密的过程
代理重加密关键在于如何安全的交换对称密钥K,这个对称密钥是用来解密存储在服务端的加密文件的。密钥K不能被服务端知道,且只能由发送方生成、由接收方还原。
交换对称密钥K的整个过程分为5个步骤:
- 双方各自生成PKA, SKA, PKB, SKB
- 发送方根据PKA生成对称密钥K、以及Capsule(封装的参数集),然后把Capsule给到代理Server。
- 生成重加密密钥rk,把rk和Capsule给到代理。(跟rk一起给到代理的还有一个随机出来的公钥XA,下面会介绍)
- Server重加密Capsule,得到新的reCapsule,然后把reCapsule给到接收方。
- 接收方用SKB和reCapsule恢复K
下面将详细证明上述过程。
二、交换对称密钥的数学证明
1、双方各自生成PKA, SKA, PKB, SKB ;过程略,总之SKA、SKB是随机大数,g为椭圆双曲线基点,有:
g^SKA = PKA
g^SKB = PKB
2、发送方生成K和Capsule
E = g^e, V = g^v , 其中e、v随机
s = e + v*hash(E, V)
Capsule = (E, V, s, null)
上面的hash在Java实现代码里用的是sha256,接下来生成K:
K = hash( PKA^(e+v) )
发送方将Capcule给到Server.
生成的K,发送方是可以用自己的私钥SKA根据Capsule来恢复回来的,使用如下计算即可:
(E+V)^SKA = (g^e + g^v)^SKA = (g^(e+v))^SKA = (g^SKA)^(e+v) = PKA^(e+v) 即:hash( (E+V)^SKA ) = hash( PKA^(e+v) ) = K 证毕.
3、发送方生成rk
XA = g^xa
d = hash( XA, PKB, PKB^xa )
rk = SKA * d^(-1)
发送方将rk、XA给到Server
4、Server二重加密Capsule
E' = E^rk
V' = V^rk
reCapsule = (E', V', s, XA)
Server将reCapsule给到接收方
5、接收方恢复K
因为:
PKB^xa = (g^SKB)^xa = (g^xa)^SKB = XA^SKB
所以:
d = hash(XA, PKB, XA^SKB)
接收方按hash( (E' + V')^hash(XA,PKB,XA^SKB) )
计算方式即可得到K,下面是证明:
因为:
(E' + V')^hash(XA,PKB,XA^SKB) = (E^rk + V^rk)^d = ((g^e)^rk + (g^v)^rk)^d
= (g^((e+v)*rk))^d = (g^((e+v)*SKA*d^(-1)))^d
= g^((e+v)*SKA) = (g^SKA)^(e+v)
= PKA^(e+v)
所以:
hash( (E' + V')^d ) = hash(PKA^(e+v)) = K
证毕.
三、重点代码注释
代码:GitHub - uheqiang/java-sdk: java sdk for Proxy re-encryption functionality
1、生成密钥对
public static PrivateKey generate(Curve curve)
{
ECKeyPairGenerator gen = new ECKeyPairGenerator();
SecureRandom secureRandom = new SecureRandom();
//生成椭圆双曲线secp256k1
X9ECParameters secnamecurves = SECNamedCurves.getByName(curve.getName());
//EC Param对象,G基点、N是点G的阶、H是椭圆曲线上所有点的个数m与N相除的整数部分
ECDomainParameters ecParams = new ECDomainParameters(secnamecurves.getCurve(), secnamecurves.getG(), secnamecurves.getN(), secnamecurves.getH());
//生成密钥对Param对象
ECKeyGenerationParameters keyGenParam = new ECKeyGenerationParameters(ecParams, secureRandom);
gen.init(keyGenParam);//初始化
AsymmetricCipherKeyPair kp = gen.generateKeyPair();//生成密钥对
ECPrivateKeyParameters privatekey = (ECPrivateKeyParameters)kp.getPrivate();
ECPublicKeyParameters publickey = (ECPublicKeyParameters)kp.getPublic();
//privatekey.getD()返回的是BigInteger,表示私钥是个大整数
//publickey.getQ()返回的是ECPoint,表示公钥本质上是个EC曲线上的点
return new PrivateKey(new Scalar(privatekey.getD(), curve), new PublicKey(new GroupElement(curve, publickey.getQ())));
}
2、生成Capsule和对称密钥K
public static List<Object> encapsulate(PublicKey publicKey) throws NoSuchAlgorithmException {
KeyPair kp1 = Proxy.generateKeyPair();
KeyPair kp2 = Proxy.generateKeyPair();
Scalar sk1 = kp1.getPrivateKey().getValue(); //e
Scalar sk2 = kp2.getPrivateKey().getValue(); //v
GroupElement pk1 = kp1.getPublicKey().getValue(); //E
GroupElement pk2 = kp2.getPublicKey().getValue(); //V
GroupElement[] tmpHash = {pk1, pk2};
Scalar hash = ProxyUtils.hashToScalar(tmpHash);
Scalar partS = sk1.add(sk2.mul(hash));
GroupElement pkPoint = publicKey.getValue();
//EC曲线上的点pkPoint乘以标量整数(sk1+sk2)
//相当于从点A开始做切线、再次相交曲线的点就是2A,以此类推... nA也可以记作A^n
GroupElement pointSymmetric = pkPoint.mul(sk1.add(sk2));//PKA^(e+v)
Scalar symmetricKey = ProxyUtils.SHA256(pointSymmetric); //K = hash( PKA^(e+v) )
Capsule capsule = new Capsule(pk1, pk2, partS, null, false);
return Arrays.asList(capsule, symmetricKey);
}
3、生成rk
public static ReEncryptionKey generateReEncryptionKey(PrivateKey privateKey, PublicKey publicKey) throws NoSuchAlgorithmException{
KeyPair kp = KeyPair.generateKeyPair();
Scalar tmpSk = kp.getPrivateKey().getValue(); //xa
GroupElement tmpPk = kp.getPublicKey().getValue(); //XA
GroupElement pkPoint = publicKey.getValue(); //PKB
GroupElement[] pointsForHash = {tmpPk, pkPoint, pkPoint.mul(tmpSk)};
Scalar hash = ProxyUtils.hashToScalar(pointsForHash); //hash(XA, PKB, PKB^xa)
Scalar sk = privateKey.getValue(); //SKA
Scalar hashInv = hash.invm(); // d^(-1)
Scalar rk = sk.mul(hashInv); //rk = SKA * d^(-1)
return new ReEncryptionKey(rk, tmpPk); //将rk和XA给到服务端Proxy
}
4、服务端重加密Capsule
public static Capsule reEncryptCapsule(Capsule capsule, ReEncryptionKey rk) throws NoSuchAlgorithmException{
GroupElement primeE = capsule.getE().mul(rk.getReKey()); //E' = E^rk
GroupElement primeV = capsule.getV().mul(rk.getReKey()); //V' = V^rk
Scalar primeS = capsule.getS();
//reCapsule = (E', V', s, XA)
return new Capsule(primeE, primeV, primeS, rk.getInternalPublicKey(), true);
}
5、从reCapsule恢复K
public static Scalar decapsulateReEncrypted(Capsule capsule, PrivateKey privateKey) throws NoSuchAlgorithmException{
GroupElement primeXG = capsule.getXG(); //XA
GroupElement primeE = capsule.getE(); //E'
GroupElement primeV = capsule.getV(); //V'
// concat prime_XG, publicKey point, prime_XG * sk
GroupElement[] pointsForHash = {primeXG, privateKey.getPublicKey().getValue(), primeXG.mul(privateKey.getValue())};
//hash(XA, PKB, XA^SKB)
Scalar hash = ProxyUtils.hashToScalar(pointsForHash);
// (capsule.E + capsule.V) * hash_bn
//(E'+V')^hash(XA, PKB, XA^SKB)
GroupElement tmpKdfPoint = primeE.add(primeV).mul(hash);
// K = hash( (E'+V')^hash(XA, PKB, XA^SKB) )
Scalar symmetricKey = ProxyUtils.SHA256(tmpKdfPoint);
return symmetricKey;
}
4、The Next
研究国密算法,基于国密SM2算法如何实现PRE,以及用Java实现该过程。
https://learnblockchain.cn/article/1516
https://learnblockchain.cn/article/1515
国密SM2与PRE
《基于代理重加密的区块链数据受控共享方案》 贵州大学 郭庆,田有亮,万良
sm2算法步骤:
1、KDF密钥导出函数
2、明文长度klen在解密阶段如何获得
解密阶段的klen对应的是密文的长度。
PRE步骤:
1、初始化和生成密钥对
这里H1-H4是4个哈希函数,可以统一用SM3
基点P、有限域Fp上的椭圆曲线E(p是有限域Fp的规模)、q阶循环群G ,这些都是算法规则公开的部分。
2、初始加密和解密
r是用群里边的整数i做hash得到的,那么i是否可以用skA, r是A需要保密的; 也有可能是个随机生成的私钥,那C1就是对应的公钥;
r = hash(skA)
C1 = rP = (x0 , y0)
rpkA = (xA, yA)
t = hash(xA||yA)
C2 = M⊕t
C3 = hash(xA||M||yA)
C4 = hash(M||C1||C3)
C = (C1 , C2, C3, C4)
发送方保管好(skA, r),然后把C托管到Proxy
3、重加密密钥生成
生成重加密密钥参量、重加密密钥本体
数据拥有者自身定义的授权参数α其实可以用他的数字签名,签名内容α = signSkA(pkB+订单号)
重加密密钥参量β,由数据拥有者生成,rpkA、rpkB,所以r = hash(skA)
是个不错的方案,这样(skA, r)作为一个封装的私钥需要A妥善保存。
重加密密钥:
r = hash(skA)
rkA->B = hash(r*pkA)⊕hash(r*pkB||α)
发送方把rkA->B和α给到Proxy ;
代理服务器Proxy使用A的公钥pkA去验证授权参数α (α=signSkA(pkB+订单号)
),证明A确实同意这笔交易。
然后Proxy使用rkA->B来进行二重加密。
4、二重加密和解密
二重加密实质上是rkA->B⊕C2
, C2 = M⊕t, 所以t是个秘密,t = hash(r*pkA) ; r=hash(skA)
重加密:
C2' = rkA->B⊕C2
C1' = C1
C3' = C3
C4' = C4
C' = (C1',C2',C3',C4')
Proxy把C'和α给到接收方。
解密:
M = C2'⊕hash(skBC1'||α)
= rkA->B⊕C2⊕hash(skB*r*P||α)
= hash(r*pkA)⊕hash(r*pkB||α)⊕M⊕t⊕hash(r*pkB||α)
= hash(r*pkA)⊕M⊕t
= hash(r*pkA)⊕M⊕hash(r*pkA)
= M
https://github.com/xjfuuu/SM2_SM3_SM4Encrypt/blob/master/src/main/java/cn/xjfme/encrypt/utils/sm2/SM2.java
https://www.cnblogs.com/goodAndyxublog/p/15654531.html
SM2曲线推荐参数 http://www.sca.gov.cn/sca/xwdt/2010-12/17/1002386/files/b965ce832cc34bc191cb1cde446b860d.pdf
SM2国密标准 https://openstd.samr.gov.cn/bzgk/gb/std_list?p.p1=0&p.p90=circulation_date&p.p91=desc&p.p2=SM2%E6%A4%AD%E5%9C%86%E6%9B%B2%E7%BA%BF%E5%85%AC%E9%92%A5%E5%AF%86%E7%A0%81%E7%AE%97%E6%B3%95
基于SM2构造的PRE Java代码实现
pom.xml依赖:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>sm</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.57</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15to18</artifactId>
<version>1.69</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.16</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.28</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.28</version>
</dependency>
</dependencies>
</project>
SM2曲线类:
import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECFieldElement;
import org.bouncycastle.math.ec.ECPoint;
import java.math.BigInteger;
import java.security.SecureRandom;
public class SM2Curve {
//国密参数
public static String[] ecc_param = {
"FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF",
"FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC",
"28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93",
"FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123",
"32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7",
"BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0"
};
public static SM2Curve Instance()
{
return new SM2Curve();
}
public final BigInteger ecc_p;
public final BigInteger ecc_a;
public final BigInteger ecc_b;
public final BigInteger ecc_n;
public final BigInteger ecc_gx;
public final BigInteger ecc_gy;
public final ECCurve ecc_curve;
public final ECPoint ecc_point_g;
public final ECDomainParameters ecc_bc_spec;
public final ECKeyPairGenerator ecc_key_pair_generator;
public final ECFieldElement ecc_gx_fieldelement;
public final ECFieldElement ecc_gy_fieldelement;
public SM2Curve()
{
this.ecc_p = new BigInteger(ecc_param[0], 16);
this.ecc_a = new BigInteger(ecc_param[1], 16);
this.ecc_b = new BigInteger(ecc_param[2], 16);
this.ecc_n = new BigInteger(ecc_param[3], 16);
this.ecc_gx = new BigInteger(ecc_param[4], 16);
this.ecc_gy = new BigInteger(ecc_param[5], 16);
this.ecc_gx_fieldelement = new ECFieldElement.Fp(this.ecc_p, this.ecc_gx);
this.ecc_gy_fieldelement = new ECFieldElement.Fp(this.ecc_p, this.ecc_gy);
this.ecc_curve = new ECCurve.Fp(this.ecc_p, this.ecc_a, this.ecc_b);
this.ecc_point_g = new ECPoint.Fp(this.ecc_curve, this.ecc_gx_fieldelement, this.ecc_gy_fieldelement);
this.ecc_bc_spec = new ECDomainParameters(this.ecc_curve, this.ecc_point_g, this.ecc_n);
ECKeyGenerationParameters ecc_ecgenparam;
ecc_ecgenparam = new ECKeyGenerationParameters(this.ecc_bc_spec, new SecureRandom());
this.ecc_key_pair_generator = new ECKeyPairGenerator();
this.ecc_key_pair_generator.init(ecc_ecgenparam);
}
}
SM2密钥对Object:
import cn.hutool.core.util.HexUtil;
import org.bouncycastle.math.ec.ECPoint;
import java.math.BigInteger;
public class SM2KeyPair {
BigInteger privateKey ;
ECPoint publicKey ;
public BigInteger getPrivateKey() {
return privateKey;
}
public void setPrivateKey(BigInteger privateKey) {
this.privateKey = privateKey;
}
public ECPoint getPublicKey() {
return publicKey;
}
public void setPublicKey(ECPoint publicKey) {
this.publicKey = publicKey;
}
//HardPubKey:3059301306072A8648CE3D020106082A811CCF5501822D03420004+X+Y
//SoftPubKey:04+X+Y
public String getPubHexInSoft(){
return HexUtil.encodeHexStr(publicKey.getEncoded(false));
}
/*public String getPubHexInHard(){
return SecurityTestAll.SM2PubHardKeyHead +Util.byteToHex(publicKey.getEncoded(false));
}*/
public String getPriHexInSoft(){
return HexUtil.encodeHexStr(privateKey.toByteArray());
}
}
密钥对生成类:
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.math.ec.ECPoint;
import java.math.BigInteger;
public class KeyGenerator {
public static SM2KeyPair generateKeyPair(){
SM2Curve sm2 = SM2Curve.Instance();
AsymmetricCipherKeyPair key = null;
while (true){
key = sm2.ecc_key_pair_generator.generateKeyPair();
if(((ECPrivateKeyParameters) key.getPrivate()).getD().toByteArray().length == 32){
break;
}
}
ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters) key.getPrivate();
ECPublicKeyParameters ecpub = (ECPublicKeyParameters) key.getPublic();
BigInteger privateKey = ecpriv.getD();
ECPoint publicKey = ecpub.getQ();
SM2KeyPair keyPair = new SM2KeyPair();
keyPair.setPublicKey(publicKey);
keyPair.setPrivateKey(privateKey);
return keyPair;
}
}
封装类,即算法中的(C1, C2, C3, C4):
import cn.hutool.core.util.HexUtil;
import org.bouncycastle.math.ec.ECPoint;
import java.math.BigInteger;
public class Capsule {
private ECPoint C1;
private BigInteger C2;
private BigInteger C3;
public Capsule(ECPoint C1, BigInteger C2, BigInteger C3){
this.C1 = C1;
this.C2 = C2;
this.C3 = C3;
}
public ECPoint getC1() {
return C1;
}
public void setC1(ECPoint c1) {
C1 = c1;
}
public BigInteger getC2() {
return C2;
}
public void setC2(BigInteger c2) {
C2 = c2;
}
public BigInteger getC3() {
return C3;
}
public void setC3(BigInteger c3) {
C3 = c3;
}
public String toString(){
String C1Hex = HexUtil.encodeHexStr(C1.getEncoded(false));
String C2Hex = HexUtil.encodeHexStr(C2.toByteArray());
return C1Hex + C2Hex;
}
}
数据发送者、接收者、以及代理服务器:
Sender:
import cn.hutool.core.util.HexUtil;
import cn.hutool.crypto.SmUtil;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.math.ec.ECPoint;
import java.math.BigInteger;
@Slf4j
public class Sender {
private BigInteger sk;
private ECPoint pk;
private SM2KeyPair keyPair;
public Sender(){
keyPair = KeyGenerator.generateKeyPair();
sk = keyPair.getPrivateKey();
pk = keyPair.getPublicKey();
}
public ECPoint getPk() {
return pk;
}
/**
* 一重加密
* */
public Capsule enCapsulate(byte[] src){
String rHex = SmUtil.sm3(keyPair.getPriHexInSoft()); //r = hash(skA)
log.debug("r=hash(skA) : {}", rHex);
BigInteger r = new BigInteger(rHex.getBytes());
SM2Curve sm2 = SM2Curve.Instance();
ECPoint P = sm2.ecc_point_g;
ECPoint C1 = P.multiply(r); //C1 = rP
log.debug("C1 = r*P : {}", HexUtil.encodeHexStr(C1.getEncoded(false)));
ECPoint rpkA = pk.multiply(r); //rpkA = (xA, yA)
log.debug("rpkA = (xA,yA) : {}", HexUtil.encodeHexStr(rpkA.getEncoded(false)));
//byte[] xA_yA = ArrayUtil.addAll(rpkA.getXCoord().getEncoded(), rpkA.getYCoord().getEncoded());
String tHex = SmUtil.sm3(HexUtil.encodeHexStr(rpkA.getEncoded(false))); // t = H1(rpkA)
log.debug("t = H1(r*pkA) : " + tHex);
BigInteger C2 = new BigInteger(src).xor(new BigInteger(tHex.getBytes())); // C2 = M xor t
log.debug("C2 = M xor t : {}", HexUtil.encodeHexStr(C2.toByteArray()));
return new Capsule(C1,C2, null);
}
/**
* 一重加密解密
* */
public byte[] deCapsulate(Capsule capsule){
ECPoint C1 = capsule.getC1();
BigInteger C2 = capsule.getC2();
ECPoint S = C1.multiply(sk); //S = skA*C1 = skA*r*P = r*pkA = (xA, yA)
log.debug("r*pkA = (xA, yA) : {}", HexUtil.encodeHexStr(S.getEncoded(false)));
//byte[] tmp = ArrayUtil.addAll(S.getXCoord().getEncoded(), S.getYCoord().getEncoded()); // xA||yA
String tHex2 = SmUtil.sm3(HexUtil.encodeHexStr(S.getEncoded(false)));
log.debug("t = H1(skA*C1) : {}", tHex2);
BigInteger M = C2.xor(new BigInteger(tHex2.getBytes()));
return M.toByteArray();
}
/**
* 生成重加密密钥
* */
public BigInteger generateReKey(ECPoint pkB){
String alpha = "sign";
String rHex = SmUtil.sm3(keyPair.getPriHexInSoft()); //r = hash(skA)
BigInteger r = new BigInteger(rHex.getBytes());
ECPoint rpkA = pk.multiply(r); //rpkA = (xA, yA)
ECPoint rpkB = pkB.multiply(r);
String h1_rpkA = SmUtil.sm3(HexUtil.encodeHexStr(rpkA.getEncoded(false))); // t = H1(rpkA)
String h1_rpkB = SmUtil.sm3(HexUtil.encodeHexStr(rpkB.getEncoded(false))); //H1(rpkB || alpha)
//rkAB = H1(rpkA) xor H1(rpkB||alpha) , alpha貌似没用
BigInteger rkAB = (new BigInteger(h1_rpkA.getBytes())).xor( new BigInteger(h1_rpkB.getBytes()) );
return rkAB;
}
}
Receiver:
import cn.hutool.core.util.HexUtil;
import cn.hutool.crypto.SmUtil;
import org.bouncycastle.math.ec.ECPoint;
import java.math.BigInteger;
public class Receiver {
private BigInteger sk;
private ECPoint pk;
private SM2KeyPair keyPair;
public Receiver(){
keyPair = KeyGenerator.generateKeyPair();
sk = keyPair.getPrivateKey();
pk = keyPair.getPublicKey();
}
public ECPoint getPk() {
return pk;
}
public byte[] deCapsulate(Capsule reCapsule){
ECPoint C1s = reCapsule.getC1();
BigInteger C2s = reCapsule.getC2();
ECPoint skBC1s = C1s.multiply(sk); //skB*C1'
String h1_skBC1s = SmUtil.sm3(HexUtil.encodeHexStr(skBC1s.getEncoded(false))); // H1(skB*C1'||alpha)
BigInteger Ms = C2s.xor(new BigInteger(h1_skBC1s.getBytes()));
//System.out.println(new String(Ms.toByteArray()));
return Ms.toByteArray();
}
}
Proxy:
import cn.hutool.core.util.HexUtil;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.math.ec.ECPoint;
import java.math.BigInteger;
@Slf4j
public class Proxy {
private Capsule capsule; //一重加密密文
private BigInteger rk;
private Capsule reCapsule; //二重加密密文
public void setCapsule(Capsule capsule) {
this.capsule = capsule;
}
public void setRk(BigInteger rk) {
this.rk = rk;
}
public Capsule getReCapsule() {
return reCapsule;
}
/**
* 代理服务器端二重加密
* */
public void reEnCapsulate(){
ECPoint C1s = capsule.getC1(); //C1' = C1
BigInteger C2 = capsule.getC2();
BigInteger C2s = rk.xor(C2); //C2' = rkAB xor C2
log.debug("C2' : {}" , HexUtil.encodeHexStr(C2s.toByteArray()));
reCapsule = new Capsule(C1s, C2s, null);
log.info("二重加密Capsule : {}", reCapsule);
}
}
测试类:
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.symmetric.SM4;
import lombok.extern.slf4j.Slf4j;
import java.math.BigInteger;
import java.security.SecureRandom;
@Slf4j
public class Test {
public static void main(String[] args) throws Exception{
//0、初始化
Sender sender = new Sender();
Receiver receiver = new Receiver();
Proxy proxy = new Proxy();
//1、sender初始加密,并将密文存放在proxy
//String src = "LoveLoveLoveLove"; //128bit
byte[] srcbyte = new byte[16];
SecureRandom random = new SecureRandom(); //密码学安全的128bit伪随机数
random.nextBytes(srcbyte);
//String src = new String(srcbyte, "utf-8");
log.info("对称密钥原文hex : {}", HexUtil.encodeHexStr(srcbyte));
Capsule capsule = sender.enCapsulate(srcbyte);
proxy.setCapsule(capsule);
log.info("一重加密Capsule : {}", capsule.toString());
//一重加密sender解密,测试
byte[] deSrc = sender.deCapsulate(capsule);
log.info("一重加密解密:{}", HexUtil.encodeHexStr(deSrc));
//2、sender生成重加密密钥, 给到服务端
BigInteger rkAB = sender.generateReKey(receiver.getPk());
proxy.setRk(rkAB);
log.info("重加密密钥rk:{}", HexUtil.encodeHexStr(rkAB.toByteArray()));
//3、proxy二重加密
proxy.reEnCapsulate();
//4、二重加密解密
byte[] text = receiver.deCapsulate(proxy.getReCapsule());
log.info("接收方用sk解密,得到对称密钥:{}", HexUtil.encodeHexStr(text));
symmetricCryptoTest(text);
}
public static void symmetricCryptoTest(byte[] key){
String message = "闪电贷是一种关于DeFi无抵押贷款的新思路,所有操作都在一笔交易(一个区块)中完成,它允许借款人无需抵押资产即可实现借贷(但需支付额外较少费用)。因为代码保证在一定时间内(以太坊大约是13秒)偿还借款,如果资金没有返还,那么交易会被还原,即撤消之前执行的所有操作,从而确保协议和资金的安全。";
SM4 sm4 = SmUtil.sm4(key);
String encryptHex = sm4.encryptHex(message, "utf-8");
log.info("sm4算法对称加密明文,得到16进制密文:{}", encryptHex);
String decryptStr = sm4.decryptStr(encryptHex, CharsetUtil.charset("utf-8"));
log.info("sm4解密16进制密文,得到明文:{}", decryptStr);
}
}
输出结果:
"C:\Program Files\Java\jdk1.8.0_202\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2022.3.3\lib\idea_rt.jar=60115:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2022.3.3\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_202\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\rt.jar;C:\Users\linyang\IdeaProjects\sm\target\classes;C:\Users\linyang\.m2\repository\org\bouncycastle\bcpkix-jdk15on\1.57\bcpkix-jdk15on-1.57.jar;C:\Users\linyang\.m2\repository\org\bouncycastle\bcprov-jdk15on\1.57\bcprov-jdk15on-1.57.jar;C:\Users\linyang\.m2\repository\org\bouncycastle\bcprov-jdk15to18\1.69\bcprov-jdk15to18-1.69.jar;C:\Users\linyang\.m2\repository\cn\hutool\hutool-all\5.8.16\hutool-all-5.8.16.jar;C:\Users\linyang\.m2\repository\org\slf4j\slf4j-api\1.7.28\slf4j-api-1.7.28.jar;C:\Users\linyang\.m2\repository\org\slf4j\slf4j-simple\1.7.28\slf4j-simple-1.7.28.jar" org.example.Test
[main] INFO org.example.Test - 对称密钥原文hex : e48c1582d860fcc7a9059917ca39f4ec
[main] INFO org.example.Test - 一重加密Capsule : 047428bbce0f2fe5860bdaa961712abe41762ca76b76fd61881aa8cb7c2420d4df7f3538fdd6d43b2800a13f0de0b11fd00359df8d0540f932c2b2c50bd464501bcccb9ace9ec99ac99e9ccecd9dcdc6ca9ec69a9c99caccccca9aca9ccdce9bcccacf9e9ec69dc99c9ec6c89bc79a9bcad6b827b0ea0198f19b66a125ac01c1dc
[main] INFO org.example.Test - 一重加密解密:e48c1582d860fcc7a9059917ca39f4ec
[main] INFO org.example.Test - 重加密密钥rk:020255525250070f0756040a03565a03595f5d5b00500305545d0d5403555403025659500053545052580f075c0702020604050003055c0005570b06560b0c54
[main] INFO org.example.Proxy - 二重加密Capsule : 047428bbce0f2fe5860bdaa961712abe41762ca76b76fd61881aa8cb7c2420d4df7f3538fdd6d43b2800a13f0de0b11fd00359df8d0540f932c2b2c50bd464501bcec9cf9ccc999dc699cacac79e9b9cc9c799c7c7999acfc99ec7c7c8ce9bcfcfc899c7cec6ce9dcccc9ec79c9b9d99c8d0bc22b0e904c4f19e31aa23fa0acd88
[main] INFO org.example.Test - 接收方用sk解密,得到对称密钥:e48c1582d860fcc7a9059917ca39f4ec
[main] INFO org.example.Test - sm4算法对称加密明文,得到16进制密文:785f107930e062ab914216a155cf455d33d3a0f58bedb647e18336c6b2d5e78da8d4b07e8a8f8c84872f5fb86af51ce7dc6ad74396e1ab99be3cc1a329a07ba6a1a7b137e58c1733be66ff758561c73c5846464a17d1b8dadc61670570d1c9813bcb50f80ce6a5bd68ba94437073dcb27cd753be64ec9155c3b734c1acbc4bf509eb7e4a397ffb2371bec0c319f1d5ac49179b2ac884bb9ad95dc3340d1ae92352233611716e5ce9add5a1178d33cfe7d30f94b83fb22af4f3add26a43130a2426a52af414cd9baf9f5415e789b8ffaa81b3ac9950954234c5383121e4cd4bc484e67f87bcd32c3973891927377a9ea58ac4dbb3f4f404ab2559402c64a2d334dc5f932c85aeb71573fb75eabe6ab2993c8b533cef944bf73f17334d3cc7713cda2d2926e6d858efe2ae7996564ebe186f7d08ae0da3b9614a2225add56c2924ff83ae585d47bc7e533b1172e4a4048e77701a28c049bbb650df119ba7a5c45fdff8c83c6da89f2dd1d36097c92690bf6a1d488deb077d7d88b1a0e9ff16a3bbb88dde95c5966d5444d4c1225787df90cead9bf25979eff1e2f0226a175719ccd0f9c77a0c9e4759380a04f4686b95b9
[main] INFO org.example.Test - sm4解密16进制密文,得到明文:闪电贷是一种关于DeFi无抵押贷款的新思路,所有操作都在一笔交易(一个区块)中完成,它允许借款人无需抵押资产即可实现借贷(但需支付额外较少费用)。因为代码保证在一定时间内(以太坊大约是13秒)偿还借款,如果资金没有返还,那么交易会被还原,即撤消之前执行的所有操作,从而确保协议和资金的安全。
Process finished with exit code 0
SM2 - Web Encrypt SM2推荐参数
package org.example.test;
import cn.hutool.core.util.HexUtil;
import java.math.BigInteger;
public class Hex2Integer {
public static void main(String[] args) {
String p = "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF";
String a = "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC";
String b = "28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93";
String n = "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123";
String Gx = "32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7";
String Gy = "BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0";
System.out.println(hex2int(p).toString());
System.out.println(hex2int(a).toString());
System.out.println(hex2int(b).toString());
System.out.println(hex2int(n).toString());
System.out.println(hex2int(Gx).toString());
System.out.println(hex2int(Gy).toString());
}
public static BigInteger hex2int(String hex){
// byte[] bytes = HexUtil.decodeHex(hex);
return new BigInteger(hex, 16);
}
}
115792089210356248756420345214020892766250353991924191454421193933289684991999
115792089210356248756420345214020892766250353991924191454421193933289684991996
18505919022281880113072981827955639221458448578012075254857346196103069175443
115792089210356248756420345214020892766061623724957744567843809356293439045923
22963146547237050559479531362550074578802567295341616970375194840604139615431
85132369209828568825618990617112496413088388631904505083283536607588877201568