BouncyCastle算法库,我暂时没有找到可用于加密大文件的SM2实现,有发现的可以以邮件方式同步一下,谢谢。

所以我就自己参照SM2Engine实现了一个即可用于处理小数据,又可用于处理大文件的类:

import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.CryptoServicesRegistrar;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.crypto.params.*;
import org.bouncycastle.crypto.signers.DSAKCalculator;
import org.bouncycastle.crypto.signers.RandomDSAKCalculator;
import org.bouncycastle.math.ec.ECFieldElement;
import org.bouncycastle.math.ec.ECMultiplier;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.math.ec.FixedPointCombMultiplier;
import org.bouncycastle.util.BigIntegers;
import org.bouncycastle.util.Memoable;
import org.bouncycastle.util.Pack;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.SecureRandom;

public class YiSM2Engine {
    private final Digest digest;
    private final Digest kdfDigest;

    private ECKeyParameters ecKey;
    private ECDomainParameters ecParams;
    private int curveLength;
    private ECPoint kPB;

    private ByteArrayOutputStream buffer;
    private int currentRound = 1;
    private boolean forEncryption;

    public YiSM2Engine() {
        this(new SM3Digest());
    }

    public YiSM2Engine(Digest digest) {
        this.digest = digest;
        this.buffer = new ByteArrayOutputStream(32);
        if(digest instanceof Memoable && digest.getDigestSize() == 32) {
            this.kdfDigest = (Digest) ((Memoable) digest).copy();
        }else {
            throw new IllegalArgumentException("digest must implements Memoable");
        }
    }


    /**
     * 返回加密后的数据长度
     * @param inputLen 输入数据长度
     * @return 返回加密后数据长度
     */
    public int getOutputSize(int inputLen) {
        return (1 + 2 * curveLength) + inputLen + digest.getDigestSize();
    }

    /**
     * 加密初始
     * @param param 公钥参数
     * @return 返回C1
     */
    public byte[] initForEncryption(CipherParameters param) {
        SecureRandom random;

        if (param instanceof ParametersWithRandom) {
            ParametersWithRandom rParam = (ParametersWithRandom)param;
            random = rParam.getRandom();
            param = rParam.getParameters();
        }else {
            random = CryptoServicesRegistrar.getSecureRandom();
        }

        ecKey = (ECKeyParameters)param;
        ecParams = ecKey.getParameters();

        ECPoint s = ((ECPublicKeyParameters)ecKey).getQ().multiply(ecParams.getH());
        if (s.isInfinity()) {
            throw new IllegalArgumentException("invalid key: [h]Q at infinity");
        }

        curveLength = (ecParams.getCurve().getFieldSize() + 7) / 8;

        ECMultiplier multiplier = createBasePointMultiplier();

        DSAKCalculator kCalculator = new RandomDSAKCalculator();
        kCalculator.init(ecParams.getN(), random);

        BigInteger k = kCalculator.nextK();

        ECPoint c1P = multiplier.multiply(ecParams.getG(), k).normalize();

        byte[] c1 = c1P.getEncoded(false);

        kPB = ((ECPublicKeyParameters)ecKey).getQ().multiply(k).normalize();

        addFieldElement(this.digest, kPB.getAffineXCoord());

        this.forEncryption = true;

        return c1;
    }

    /**
     * 解密初始
     * @param param 私钥参数
     * @param c1 C1
     */
    public void initForDecryption(CipherParameters param, byte[] c1) {
        ecKey = (ECKeyParameters)param;
        ecParams = ecKey.getParameters();
        curveLength = (ecParams.getCurve().getFieldSize() + 7) / 8;

        ECPoint c1P = ecParams.getCurve().decodePoint(c1);

        ECPoint s = c1P.multiply(ecParams.getH());
        if (s.isInfinity()) {
            throw new IllegalArgumentException("[h]C1 at infinity");
        }

        kPB = c1P.multiply(((ECPrivateKeyParameters)ecKey).getD()).normalize();

        addFieldElement(this.digest, kPB.getAffineXCoord());

        this.forEncryption = false;
    }

    private byte[] oneRound(byte[] data, int len) {
        int digestSize = digest.getDigestSize();
        byte[] kdfBuf = new byte[digestSize];

        this.kdfDigest.reset();

        addFieldElement(this.kdfDigest, kPB.getAffineXCoord());
        addFieldElement(this.kdfDigest, kPB.getAffineYCoord());
        Pack.intToBigEndian(this.currentRound++, kdfBuf, 0);
        this.kdfDigest.update(kdfBuf, 0, 4);
        this.kdfDigest.doFinal(kdfBuf, 0);

        if(this.forEncryption) {
            this.digest.update(data, 0, len);
        }

        for(int i = 0; i < len; i++) {
            data[i] ^= kdfBuf[i];
        }

        if(!this.forEncryption) {
            this.digest.update(data, 0, len);
        }

        return data;
    }

    /**
     * 添加待计算数据,每满一轮计算一轮
     * @param input 待计算数据
     * @return 已处理数据,不满一轮时,无输出则返回null
     * @throws IOException 计算异常
     */
    public byte[] update(byte[] input) throws IOException {
        return this.update(input, 0, input.length);
    }

    /**
     * 添加待计算数据,每满一轮计算一轮
     * @param input 待计算数据
     * @param off 数据偏移
     * @param len 数据长度
     * @return 已处理数据,不满一轮时,无输出则返回null
     * @throws IOException 计算异常
     */
    public byte[] update(byte[] input, int off, int len) throws IOException {
        int curLen = buffer.size();
        int wLen = 0;
        ByteArrayOutputStream retBuffer = new ByteArrayOutputStream(curLen + len);

        while (wLen < len) {
            int curWriteLen = 32 - curLen; // 本次预写入的数据长度
            if(curWriteLen > (len - wLen)) {
                // 如果本次预写入长度超过待写入数据的长度
                curWriteLen = len - wLen;
            }
            buffer.write(input, off + wLen, curWriteLen);
            wLen += curWriteLen;
            if (buffer.size() == 32) {
                retBuffer.write(oneRound(buffer.toByteArray(), 32), 0, 32);
                buffer.reset();
            }
            curLen = buffer.size();
        }
        if(retBuffer.size() > 0) return retBuffer.toByteArray();
        return null;
    }

    /**
     * 结束数据处理,解密时要自行用这里输出的C3与密文中的C3进行比较
     * @param c3 用于存储输出C3的缓冲区
     * @param c3Off 缓冲区偏移
     * @return 已处理数据,不满一轮时,无输出则返回null
     */
    public byte[] doFinal(byte[] c3, int c3Off) {
        byte[] ret = null;
        if(buffer.size() > 0) {
            ret = oneRound(buffer.toByteArray(), buffer.size());
        }
        addFieldElement(this.digest, kPB.getAffineYCoord());
        this.digest.doFinal(c3, c3Off);
        return ret;
    }

    protected ECMultiplier createBasePointMultiplier() {
        return new FixedPointCombMultiplier();
    }

    private void addFieldElement(Digest digest, ECFieldElement v) {
        byte[] p = BigIntegers.asUnsignedByteArray(curveLength, v.toBigInteger());

        digest.update(p, 0, p.length);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221

以下为单元测试案例:

public class SM2CryptTest {

    /**
     * 单元测试需要,固定每次签名生成的随机K,实际生产可千万不能这么用
     */
    private class FixedRandom extends SecureRandom {
        byte[] fixedK = Hex.decode("b8b08eae2876ef4e24bc7b3e95373b39246cdcce58aaf6cdaf42874369ba1ff3");
        @Override
        public void nextBytes(byte[] bytes) {
            System.arraycopy(fixedK, 0, bytes, 0, 32);
        }
    }

    /**
     * 测试公钥加密
     */
    @Test
    public void testEncrypt() {
        // 获取国密曲线
        X9ECParameters gmParameters = GMNamedCurves.getByName("sm2p256v1");
        // 构造Domain参数
        ECDomainParameters gmDomainParameters = new ECDomainParameters(gmParameters.getCurve(),
                gmParameters.getG(), gmParameters.getN());

        try {
            // 从压缩公钥中创建点
            ECPoint sm2Q = gmDomainParameters.getCurve().decodePoint(
                    Hex.decode("02a9036e0289d9fa6d566cd0500807e3cba1ce14ba9b58bfbbef00b4b8d502ed72"));

            // 跟私钥一样,在创建ECPublicKeyParameters实例的时候,会去校验点是否符合SM2曲线要求
            ECPublicKeyParameters ecpub = new ECPublicKeyParameters(sm2Q, gmDomainParameters);

            // 生产时,请勿这样使用
            ParametersWithRandom fixedRandomParameters = new ParametersWithRandom(ecpub, new FixedRandom());

            YiSM2Engine yiSM2Engine = new YiSM2Engine();
            byte[] c1 = yiSM2Engine.initForEncryption(fixedRandomParameters); // 注意生产时应直接用ecpub代替fixedRandomParameters
            yiSM2Engine.update(new byte[]{0x61, 0x62, 0x63}, 0, 3); // 因为数据只有3字节,不满一轮,所以这里是不会输出结果的
            byte[] c3 = new byte[32];
            byte[] c2 = yiSM2Engine.doFinal(c3, 0);

            SM2Engine sm2Engine = new SM2Engine();
            sm2Engine.init(true, fixedRandomParameters);
            byte[] c1c2c3 = sm2Engine.processBlock(new byte[]{0x61, 0x62, 0x63}, 0, 3);

            // 这里处理一下,更方便比较
            ByteArrayOutputStream aout = new ByteArrayOutputStream(100);
            aout.write(c1);
            aout.write(c2);
            aout.write(c3);

            byte[] myC1C2C3 = aout.toByteArray();

            // 与预期结果比较
            Assert.assertArrayEquals(myC1C2C3,
                    Hex.decodeStrict("04a9036e0289d9fa6d566cd0500807e3cba1ce14ba9b58bfbbef00b4b8d502ed724a1411c7fef1c108a2e71dc421c2d18fd0ef183e97f600ab06e84dfdc6c55f5278134241f03ef890e33026fcdaf822a2dc48959eea26348c9a699a1217825a7417f25f"));

            // 与BC算法比较
            Assert.assertArrayEquals(myC1C2C3, c1c2c3);
        }catch (Exception ex) {
            Assert.fail(ex.getMessage());
        }
    }

    /**
     * 测试私钥解密
     */
    @Test
    public void testDecrypt() {
        // 获取国密曲线
        X9ECParameters gmParameters = GMNamedCurves.getByName("sm2p256v1");
        // 构造Domain参数
        ECDomainParameters gmDomainParameters = new ECDomainParameters(gmParameters.getCurve(),
                gmParameters.getG(), gmParameters.getN());

        try {
            // 创建无符号大数
            BigInteger sm2D = new BigInteger(1,
                    Hex.decode("b8b08eae2876ef4e24bc7b3e95373b39246cdcce58aaf6cdaf42874369ba1ff3"));

            // 创建SM2私钥,ECPrivateKeyParameters实例创建时,会去校验大数是否符合SM2曲线的要求
            ECPrivateKeyParameters ecpriv = new ECPrivateKeyParameters(sm2D, gmDomainParameters);

            YiSM2Engine yiSM2Engine = new YiSM2Engine();
            yiSM2Engine.initForDecryption(ecpriv,
                    Hex.decodeStrict("04a9036e0289d9fa6d566cd0500807e3cba1ce14ba9b58bfbbef00b4b8d502ed724a1411c7fef1c108a2e71dc421c2d18fd0ef183e97f600ab06e84dfdc6c55f52"));
            yiSM2Engine.update(new byte[]{0x78, 0x13, 0x42}, 0, 3); // 因为数据只有3字节,不满一轮,所以这里是不会输出结果的
            byte[] c3 = new byte[32];
            byte[] c2 = yiSM2Engine.doFinal(c3, 0);

            SM2Engine sm2Engine = new SM2Engine();
            sm2Engine.init(false, ecpriv);
            byte[] plainBytes = sm2Engine.processBlock(
                    Hex.decodeStrict("04a9036e0289d9fa6d566cd0500807e3cba1ce14ba9b58bfbbef00b4b8d502ed724a1411c7fef1c108a2e71dc421c2d18fd0ef183e97f600ab06e84dfdc6c55f5278134241f03ef890e33026fcdaf822a2dc48959eea26348c9a699a1217825a7417f25f"), 0, 100);

            // 比较解密计算的C3与预期是否一样
            Assert.assertArrayEquals(c3, Hex.decodeStrict("41f03ef890e33026fcdaf822a2dc48959eea26348c9a699a1217825a7417f25f"));

            // 比较解密的C2与预期的明文是否一样
            Assert.assertArrayEquals(c2, new byte[]{0x61, 0x62, 0x63});

            // 与BC计算结果相比较
            Assert.assertArrayEquals(c2, plainBytes);
        }catch (Exception ex) {
            Assert.fail(ex.getMessage());
        }
    }

    /**
     * 测试算法的正确性,多样本覆盖
     */
    @Test
    public void testAlg() {
        // 获取国密曲线
        X9ECParameters gmParameters = GMNamedCurves.getByName("sm2p256v1");
        // 构造Domain参数
        ECDomainParameters gmDomainParameters = new ECDomainParameters(gmParameters.getCurve(),
                gmParameters.getG(), gmParameters.getN());

        byte[] plainBytes = Hex.decodeStrict("3187bc006d750266e579d12acb0a67ea8057a35c4d0357df7034115ba9249d1315210962527f8e66d9d77bc7572fde210f8179201e91caebe7abe4b70965cf2fd0f67b54e1a162cbff2e68026b789569c3e7744996703472586deb3fa7c72a37feeaca");

        try {
            int [][] rounds = new int[][] {
                    // 第一轮数据小于32
                    {3, 0},  // 只加一轮数据
                    {3, 6},  // 两轮数据总和小于32
                    {3, 29}, // 两轮数据正好等于32
                    {3, 32}, // 第二轮数据正好等于32
                    {3, 33}, // 第二轮数据大于32
                    {3, 61}, // 两轮数据正好是32的两倍
                    {3, 64}, // 两轮数据大于32的两倍

                    // 第二轮数据等于32
                    {32, 0},
                    {32, 6},
                    {32, 29},
                    {32, 32},
                    {32, 33},
                    {32, 61},
                    {32, 64},

                    // 第二轮数据大于32
                    {35, 0},
                    {35, 6},
                    {35, 29},
                    {35, 32},
                    {35, 33},
                    {35, 61},
                    {35, 64},
            };

            String[] expects = new String[] {
                    "04a9036e0289d9fa6d566cd0500807e3cba1ce14ba9b58bfbbef00b4b8d502ed724a1411c7fef1c108a2e71dc421c2d18fd0ef183e97f600ab06e84dfdc6c55f5228f69d27be058de611ed7196e2a2edada9a401cdb2969fdadce19ed7a945a2bf0dc472",
                    "04a9036e0289d9fa6d566cd0500807e3cba1ce14ba9b58bfbbef00b4b8d502ed724a1411c7fef1c108a2e71dc421c2d18fd0ef183e97f600ab06e84dfdc6c55f5228f69d1d6a80c3da49871fe7c904bd823f84856ddebb49bc551979ae3651e6c2f93efa7130b835a2a1",
                    "04a9036e0289d9fa6d566cd0500807e3cba1ce14ba9b58bfbbef00b4b8d502ed724a1411c7fef1c108a2e71dc421c2d18fd0ef183e97f600ab06e84dfdc6c55f5228f69d1d6a80c3da493e4e14ea01f8861409f30dfb054b92811676abe7fd33da3e4eb57cb0bd1c47e99a0f1dcb741f8cc23edddb82d0fef90b254c8505c72ce6",
                    "04a9036e0289d9fa6d566cd0500807e3cba1ce14ba9b58bfbbef00b4b8d502ed724a1411c7fef1c108a2e71dc421c2d18fd0ef183e97f600ab06e84dfdc6c55f5228f69d1d6a80c3da493e4e14ea01f8861409f30dfb054b92811676abe7fd33da6992937c4728113ccbac3d1eefb2a38095e1a4897a7743bb5ffa45dac288e3afac8b54",
                    "04a9036e0289d9fa6d566cd0500807e3cba1ce14ba9b58bfbbef00b4b8d502ed724a1411c7fef1c108a2e71dc421c2d18fd0ef183e97f600ab06e84dfdc6c55f5228f69d1d6a80c3da493e4e14ea01f8861409f30dfb054b92811676abe7fd33da6992938ecfd6ba15dc9b837bba45a7de554a619095bc0163bd5cc30b51819bd805c45e17",
                    "04a9036e0289d9fa6d566cd0500807e3cba1ce14ba9b58bfbbef00b4b8d502ed724a1411c7fef1c108a2e71dc421c2d18fd0ef183e97f600ab06e84dfdc6c55f5228f69d1d6a80c3da493e4e14ea01f8861409f30dfb054b92811676abe7fd33da6992938ec9991bd8ebec35c1169e054522afaf2882c7070019bd54fb3ea5574986212f0e6f3c860cc4c2845271b7ea085afc96ee537304a482ed63c0e9e7dd4e",
                    "04a9036e0289d9fa6d566cd0500807e3cba1ce14ba9b58bfbbef00b4b8d502ed724a1411c7fef1c108a2e71dc421c2d18fd0ef183e97f600ab06e84dfdc6c55f5228f69d1d6a80c3da493e4e14ea01f8861409f30dfb054b92811676abe7fd33da6992938ec9991bd8ebec35c1169e054522afaf2882c7070019bd54fb3ea557496914da9a6e10c637d24731edc6cbb5bb450a45517604240cb49e3d9d5d68cd2d0b7347",
                    "04a9036e0289d9fa6d566cd0500807e3cba1ce14ba9b58bfbbef00b4b8d502ed724a1411c7fef1c108a2e71dc421c2d18fd0ef183e97f600ab06e84dfdc6c55f5228f69d1d6a80c3da493e4e14ea01f8861409f30dfb054b92811676abe7fd33da3e4eb57cb0bd1c47e99a0f1dcb741f8cc23edddb82d0fef90b254c8505c72ce6",
                    "04a9036e0289d9fa6d566cd0500807e3cba1ce14ba9b58bfbbef00b4b8d502ed724a1411c7fef1c108a2e71dc421c2d18fd0ef183e97f600ab06e84dfdc6c55f5228f69d1d6a80c3da493e4e14ea01f8861409f30dfb054b92811676abe7fd33da6992938ec999ae24e250fc8d268cb8a0d6e47a14c0e9be626d6849e752c81db607d0b5cbda0b",
                    "04a9036e0289d9fa6d566cd0500807e3cba1ce14ba9b58bfbbef00b4b8d502ed724a1411c7fef1c108a2e71dc421c2d18fd0ef183e97f600ab06e84dfdc6c55f5228f69d1d6a80c3da493e4e14ea01f8861409f30dfb054b92811676abe7fd33da6992938ec9991bd8ebec35c1169e054522afaf2882c7070019bd54fb3e57824ac49f9526b84b39fa4e6560bc2432fe656a1416249f6750e0c113ce120f",
                    "04a9036e0289d9fa6d566cd0500807e3cba1ce14ba9b58bfbbef00b4b8d502ed724a1411c7fef1c108a2e71dc421c2d18fd0ef183e97f600ab06e84dfdc6c55f5228f69d1d6a80c3da493e4e14ea01f8861409f30dfb054b92811676abe7fd33da6992938ec9991bd8ebec35c1169e054522afaf2882c7070019bd54fb3ea5574986212f0e6f3c860cc4c2845271b7ea085afc96ee537304a482ed63c0e9e7dd4e",
                    "04a9036e0289d9fa6d566cd0500807e3cba1ce14ba9b58bfbbef00b4b8d502ed724a1411c7fef1c108a2e71dc421c2d18fd0ef183e97f600ab06e84dfdc6c55f5228f69d1d6a80c3da493e4e14ea01f8861409f30dfb054b92811676abe7fd33da6992938ec9991bd8ebec35c1169e054522afaf2882c7070019bd54fb3ea5574969ae31daf7c9f02be0f4942d00eab0b27b4ed5ffd2e27f4fea3163c128bc3bc6cb",
                    "04a9036e0289d9fa6d566cd0500807e3cba1ce14ba9b58bfbbef00b4b8d502ed724a1411c7fef1c108a2e71dc421c2d18fd0ef183e97f600ab06e84dfdc6c55f5228f69d1d6a80c3da493e4e14ea01f8861409f30dfb054b92811676abe7fd33da6992938ec9991bd8ebec35c1169e054522afaf2882c7070019bd54fb3ea557496914dab88c6c8f29537f89d2f721f9975e57ac4bd03d9755e11d366352a57d01d6ac3cddb518765eeea674fe42a3f322588d96c4afa67f11cc3e804f32",
                    "04a9036e0289d9fa6d566cd0500807e3cba1ce14ba9b58bfbbef00b4b8d502ed724a1411c7fef1c108a2e71dc421c2d18fd0ef183e97f600ab06e84dfdc6c55f5228f69d1d6a80c3da493e4e14ea01f8861409f30dfb054b92811676abe7fd33da6992938ec9991bd8ebec35c1169e054522afaf2882c7070019bd54fb3ea557496914dab88c6c8f29537f89d2f721f9975e57ac4bd03d9755e11d3663522c6d6d7d0a2d622cf3977304331515f3ecd240d1905f9fe7bc0461bcf4b6c224b23d90",
                    "04a9036e0289d9fa6d566cd0500807e3cba1ce14ba9b58bfbbef00b4b8d502ed724a1411c7fef1c108a2e71dc421c2d18fd0ef183e97f600ab06e84dfdc6c55f5228f69d1d6a80c3da493e4e14ea01f8861409f30dfb054b92811676abe7fd33da6992937c4728113ccbac3d1eefb2a38095e1a4897a7743bb5ffa45dac288e3afac8b54",
                    "04a9036e0289d9fa6d566cd0500807e3cba1ce14ba9b58bfbbef00b4b8d502ed724a1411c7fef1c108a2e71dc421c2d18fd0ef183e97f600ab06e84dfdc6c55f5228f69d1d6a80c3da493e4e14ea01f8861409f30dfb054b92811676abe7fd33da6992938ec9991bd8ebc41849913154cb9778412c21e81ffda7f6a9282b2d711466190a3a867957493f",
                    "04a9036e0289d9fa6d566cd0500807e3cba1ce14ba9b58bfbbef00b4b8d502ed724a1411c7fef1c108a2e71dc421c2d18fd0ef183e97f600ab06e84dfdc6c55f5228f69d1d6a80c3da493e4e14ea01f8861409f30dfb054b92811676abe7fd33da6992938ec9991bd8ebec35c1169e054522afaf2882c7070019bd54fb3ea5574986212f0e6f3c860cc4c2845271b7ea085afc96ee537304a482ed63c0e9e7dd4e",
                    "04a9036e0289d9fa6d566cd0500807e3cba1ce14ba9b58bfbbef00b4b8d502ed724a1411c7fef1c108a2e71dc421c2d18fd0ef183e97f600ab06e84dfdc6c55f5228f69d1d6a80c3da493e4e14ea01f8861409f30dfb054b92811676abe7fd33da6992938ec9991bd8ebec35c1169e054522afaf2882c7070019bd54fb3ea557496914da9a6e10c637d24731edc6cbb5bb450a45517604240cb49e3d9d5d68cd2d0b7347",
                    "04a9036e0289d9fa6d566cd0500807e3cba1ce14ba9b58bfbbef00b4b8d502ed724a1411c7fef1c108a2e71dc421c2d18fd0ef183e97f600ab06e84dfdc6c55f5228f69d1d6a80c3da493e4e14ea01f8861409f30dfb054b92811676abe7fd33da6992938ec9991bd8ebec35c1169e054522afaf2882c7070019bd54fb3ea557496914dab8cc9e93a554c4afe69d713eb5d5257395d438642f6d850140ef6d06930f413ce3",
                    "04a9036e0289d9fa6d566cd0500807e3cba1ce14ba9b58bfbbef00b4b8d502ed724a1411c7fef1c108a2e71dc421c2d18fd0ef183e97f600ab06e84dfdc6c55f5228f69d1d6a80c3da493e4e14ea01f8861409f30dfb054b92811676abe7fd33da6992938ec9991bd8ebec35c1169e054522afaf2882c7070019bd54fb3ea557496914dab88c6c8f29537f89d2f721f9975e57ac4bd03d9755e11d3663522c6d6d7d0a2d622cf3977304331515f3ecd240d1905f9fe7bc0461bcf4b6c224b23d90",
                    "04a9036e0289d9fa6d566cd0500807e3cba1ce14ba9b58bfbbef00b4b8d502ed724a1411c7fef1c108a2e71dc421c2d18fd0ef183e97f600ab06e84dfdc6c55f5228f69d1d6a80c3da493e4e14ea01f8861409f30dfb054b92811676abe7fd33da6992938ec9991bd8ebec35c1169e054522afaf2882c7070019bd54fb3ea557496914dab88c6c8f29537f89d2f721f9975e57ac4bd03d9755e11d3663522c6d6d7af11b5a03a2dde8b2e114681ee289955879ef605e1b68f1a0189240dae7ababed2cd3"
            };

            // 设BC的算法是正确的

            // 从压缩公钥中创建点
            ECPoint sm2Q = gmDomainParameters.getCurve().decodePoint(
                    Hex.decode("02a9036e0289d9fa6d566cd0500807e3cba1ce14ba9b58bfbbef00b4b8d502ed72"));

            // 跟私钥一样,在创建ECPublicKeyParameters实例的时候,会去校验点是否符合SM2曲线要求
            ECPublicKeyParameters ecpub = new ECPublicKeyParameters(sm2Q, gmDomainParameters);

            // 生产时,请勿这样使用
            ParametersWithRandom fixedRandomParameters = new ParametersWithRandom(ecpub, new FixedRandom());

            // 创建无符号大数
            BigInteger sm2D = new BigInteger(1,
                    Hex.decode("b8b08eae2876ef4e24bc7b3e95373b39246cdcce58aaf6cdaf42874369ba1ff3"));

            // 创建SM2私钥,ECPrivateKeyParameters实例创建时,会去校验大数是否符合SM2曲线的要求
            ECPrivateKeyParameters ecpriv = new ECPrivateKeyParameters(sm2D, gmDomainParameters);

            for(int i = 0; i < rounds.length; i++) {
                // 加密测试
                ByteArrayOutputStream bout = new ByteArrayOutputStream(rounds[i][0] + rounds[i][1] + 97);

                YiSM2Engine yiSM2Engine = new YiSM2Engine();
                byte[] c1 = yiSM2Engine.initForEncryption(fixedRandomParameters);
                bout.write(c1);

                int off = 0;
                for(int j = 0; j < rounds[i].length; j++) {
                    byte[] c2 = yiSM2Engine.update(plainBytes, off, rounds[i][j]);
                    if (c2 != null) {
                        bout.write(c2);
                    }
                    off += rounds[i][j];
                }

                byte[] c3 = new byte[32];
                byte[] c2 = yiSM2Engine.doFinal(c3, 0);
                if(c2 != null) {
                    bout.write(c2);
                }
                bout.write(c3);

                byte[] myC1C2C3 = bout.toByteArray();

                SM2Engine sm2Engine = new SM2Engine();
                sm2Engine.init(true, fixedRandomParameters);
                byte[] c1c2c3 = sm2Engine.processBlock(plainBytes, 0, rounds[i][0] + rounds[i][1]);

                Assert.assertArrayEquals(myC1C2C3, c1c2c3);

                Assert.assertArrayEquals(myC1C2C3, Hex.decodeStrict(expects[i]));


                // 解密测试
                yiSM2Engine = new YiSM2Engine();
                yiSM2Engine.initForDecryption(ecpriv, Hex.decodeStrict(expects[i], 0, 130));
                bout = new ByteArrayOutputStream(rounds[i][0] + rounds[i][1]);
                off = 130;
                for(int j = 0; j < rounds[i].length; j++) {
                    c2 = yiSM2Engine.update(Hex.decodeStrict(expects[i], off, rounds[i][j] * 2));
                    if(c2 != null) {
                        bout.write(c2);
                    }
                    off += rounds[i][j] * 2;
                }

                c2 = yiSM2Engine.doFinal(c3, 0);
                if(c2 != null) {
                    bout.write(c2);
                }

                // 比较C3是否相同
                Assert.assertArrayEquals(c3, Hex.decodeStrict(expects[i], off, 64));
                // 比较原文是否相同
                byte[] exceptPlainBytes = new byte[(off - 130) / 2];
                System.arraycopy(plainBytes, 0, exceptPlainBytes, 0, exceptPlainBytes.length);
                Assert.assertArrayEquals(bout.toByteArray(), exceptPlainBytes);
            }
        }catch (Exception ex) {
            Assert.fail(ex.getMessage());
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259