PRE数学原理以及Java实现研究

参考

解决对接JAVA SM2加密遇到的坑

《基于代理重加密的区块链数据受控共享方案》贵州大学 郭庆

《基于国密算法的秘密文件的加、解密方法及保护系统》 北京航空航天大学 伍前红

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个步骤:

  1. 双方各自生成PKA, SKA, PKB, SKB
  2. 发送方根据PKA生成对称密钥K、以及Capsule(封装的参数集),然后把Capsule给到代理Server。
  3. 生成重加密密钥rk,把rk和Capsule给到代理。(跟rk一起给到代理的还有一个随机出来的公钥XA,下面会介绍)
  4. Server重加密Capsule,得到新的reCapsule,然后把reCapsule给到接收方。
  5. 接收方用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算法步骤:
SM2算法步骤.png

1、KDF密钥导出函数

2、明文长度klen在解密阶段如何获得
解密阶段的klen对应的是密文的长度。

PRE步骤:

1、初始化和生成密钥对

pre1.png

这里H1-H4是4个哈希函数,可以统一用SM3

基点P、有限域Fp上的椭圆曲线E(p是有限域Fp的规模)、q阶循环群G ,这些都是算法规则公开的部分。

2、初始加密和解密

pre2.png

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、重加密密钥生成

pre3.png

生成重加密密钥参量、重加密密钥本体

数据拥有者自身定义的授权参数α其实可以用他的数字签名,签名内容α = 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、二重加密和解密

pre4.png

二重加密实质上是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推荐参数

SM2推荐参数.png

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
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容