打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
SM2非对称算法的原理及实现 Java SM2的代码案例 | 一生孤注掷温柔 | 小奋斗

SM2椭圆曲线公钥密码算法:我国自主知识产权的商用密码算法,是ECC(Elliptic Curve Cryptosystem)算法的一种,基于椭圆曲线离散对数问题,计算复杂度是指数级,求解难度较大,同等安全程度要求下,椭圆曲线密码较其他公钥算法所需密钥长度小很多。

ECC算法描述:

1、用户A选定一条适合加密的椭圆曲线Ep(a,b)(如:y2=x3+ax+b),并取椭圆曲线上一点,作为基点G。

2、用户A选择一个私有密钥k,并生成公开密钥(公钥PB)K=kG。

3、用户A将Ep(a,b)和点(公钥)K,G传给用户B。

4、用户B接到信息后 ,将待传输的明文(M)编码到Ep(a,b)上一点M,并产生一个随机整数r(r<n)。加密开始

5、用户B计算点C1=M+rK;C2=rG。

6、用户B将C1、C2传给用户A。

7、用户A接到信息后,计算C1-kC2,结果就是点M。因为C1-kC2=M+rK-k(rG)=M+rK-r(kG)=M

再对点M进行解码就可以得到明文。

密码学中,描述一条Fp上的椭圆曲线,常用到六个参量:

T=(p,a,b,G,n,h)。

(p 、a 、b 用来确定一条椭圆曲线,G为基点,n为点G的阶,h 是椭圆曲线上所有点的个数m与n相除的整数部分)

这几个参量取值的选择,直接影响了加密的安全性。参量值一般要求满足以下几个条件:

1、p 当然越大越安全,但越大,计算速度会变慢,200位左右可以满足一般安全要求;

2、p≠n×h;

3、pt≠1 (mod n),1≤t<20;

4、4a3+27b2≠0 (mod p);

5、n 为素数;

6、h≤4。

轻量级密码术包(BouncyCastle):

一种用于 Java 平台的开放源码的轻量级密码术包;它支持大量的密码术算法,并提供JCE 1.2.1的实现。

地址:http://www.bouncycastle.org/latest_releases.html

bcprov-ext-jdk15on-160.jar

This release adds support for SHA-3 signatures to CMS, support for the Unified Model of Diffie-Hellman, and a parser for the GNU keybox format. PGP EC operations now support a wider range of curves, the BCJSSE now supports SNI, support has been added for generating ECGOST key transport messages and key wrapping ciphers can now also be used for wrapping general data. Initial low-level support has also been added for EdDSA. In terms of bugs fixed: RSA key pair generation with adjusted certainty values now conforms fully with FIPS PUB 186-4, the CRMF EncryptedValue field now encodes the encrypted value correctly without including the wrapping structure from the encryption, XMSS now validates any private key BDS data before construction, the SM2 signature now fully resets on a sign() call, and PGP secret keys with a null encryptor now calculate the correct checksum. An off by one error in the EST JsseDefaultHostnameAuthorizer valid name matcher has also been fixed. Further details on other additions and bug fixes can be found in the release notes file accompanying the release.

pom.xml

1
2
3
4
5
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15on</artifactId>
    <version>1.60</version>
</dependency>

代码案例:

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
package com.what21.demo.bouncycastle;
import java.math.BigInteger;
import org.bouncycastle.math.ec.ECPoint;
public class SM2KeyPair {
     
    /** 公钥 */
    private  ECPoint publicKey;
     
    /** 私钥 */
    private BigInteger privateKey;
    SM2KeyPair(ECPoint publicKey, BigInteger privateKey) {
        this.publicKey = publicKey;
        this.privateKey = privateKey;
    }
    public ECPoint getPublicKey() {
        return publicKey;
    }
    public BigInteger getPrivateKey() {
        return privateKey;
    }
     
}
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
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
package com.what21.demo.bouncycastle;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.Arrays;
  
import org.bouncycastle.crypto.DerivationFunction;
import org.bouncycastle.crypto.digests.SHA256Digest;
import org.bouncycastle.crypto.digests.ShortenedDigest;
import org.bouncycastle.crypto.generators.KDF1BytesGenerator;
import org.bouncycastle.crypto.params.ISO18033KDFParameters;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECPoint;
  
public class SM2Utils {
  
    /** 素数p */
    private static final BigInteger p = new BigInteger("FFFFFFFE" "FFFFFFFF"
        "FFFFFFFF" "FFFFFFFF" "FFFFFFFF" "00000000" "FFFFFFFF"
        "FFFFFFFF"16);
     
    /** 系数a */
    private static final BigInteger a = new BigInteger("FFFFFFFE" "FFFFFFFF"
        "FFFFFFFF" "FFFFFFFF" "FFFFFFFF" "00000000" "FFFFFFFF"
        "FFFFFFFC"16);
     
    /** 系数b */
    private static final BigInteger b = new BigInteger("28E9FA9E" "9D9F5E34"
        "4D5A9E4B" "CF6509A7" "F39789F5" "15AB8F92" "DDBCBD41"
        "4D940E93"16);
     
    /** 坐标x */
    private static final BigInteger xg = new BigInteger("32C4AE2C" "1F198119"
        "5F990446" "6A39C994" "8FE30BBF" "F2660BE1" "715A4589"
        "334C74C7"16);
     
    /** 坐标y */
    private static final BigInteger yg = new BigInteger("BC3736A2" "F4F6779C"
        "59BDCEE3" "6B692153" "D0A9877C" "C62A4740" "02DF32E5"
        "2139F0A0"16);
     
    /** 基点G, G=(xg,yg),其介记为n */
    private static final BigInteger n = new BigInteger("FFFFFFFE" "FFFFFFFF"
            "FFFFFFFF" "FFFFFFFF" "7203DF6B" "21C6052B" "53BBF409"
            "39D54123"16);
     
    private static SecureRandom random = new SecureRandom();
    private ECCurve.Fp curve;
    private ECPoint G;
  
    @SuppressWarnings("deprecation")
    public SM2Utils() {
        curve = new ECCurve.Fp(p, // q
        a, // a
        b); // b
        G = curve.createPoint(xg, yg);
    }
     
    public static String printHexString(byte[] b) {
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < b.length; i++) {
            String hex = Integer.toHexString(b[i] & 0xFF);
            if (hex.length() == 1) {
                builder.append('0'+hex);
            hex = '0' + hex;
        }
            //System.out.print(hex.toUpperCase());
            //System.out.print(hex.toUpperCase());
            builder.append(hex);
        }
        //System.out.println();
        return builder.toString();
    }
  
    public BigInteger random(BigInteger max) {
        BigInteger r = new BigInteger(256, random);
        // int count = 1;
        while (r.compareTo(max) >= 0) {
            r = new BigInteger(128, random);
            // count++;
        }
        // System.out.println("count: " + count);
        return r;
    }
  
    private boolean allZero(byte[] buffer) {
        for (int i = 0; i < buffer.length; i++) {
            if (buffer[i] != 0)
            return false;
        }
        return true;
    }
  
    /**
     * 加密
     * @param input 待加密消息M
     * @param publicKey 公钥
     * @return byte[] 加密后的字节数组
     */
    public byte[] encrypt(String input, ECPoint publicKey) {
         
        //System.out.println("publicKey is: "+publicKey);
     
        byte[] inputBuffer = input.getBytes();
        //printHexString(inputBuffer);
     
        /* 1 产生随机数k,k属于[1, n-1] */
        BigInteger k = random(n);
        //System.out.print("k: ");
        printHexString(k.toByteArray());
     
        /* 2 计算椭圆曲线点C1 = [k]G = (x1, y1) */
        ECPoint C1 = G.multiply(k);
        byte[] C1Buffer = C1.getEncoded(false);
        //System.out.print("C1: ");
        printHexString(C1Buffer);
         
        // 3 计算椭圆曲线点 S = [h]Pb * curve没有指定余因子,h为空
          
    //           BigInteger h = curve.getCofactor(); System.out.print("h: ");
    //           printHexString(h.toByteArray()); if (publicKey != null) { ECPoint
    //           result = publicKey.multiply(h); if (!result.isInfinity()) {
    //           System.out.println("pass"); } else {
    //          System.err.println("计算椭圆曲线点 S = [h]Pb失败"); return null; } }
     
        /* 4 计算 [k]PB = (x2, y2) */
        ECPoint kpb = publicKey.multiply(k).normalize();
     
        /* 5 计算 t = KDF(x2||y2, klen) */
        byte[] kpbBytes = kpb.getEncoded(false);
        DerivationFunction kdf = new KDF1BytesGenerator(new ShortenedDigest(
        new SHA256Digest(), 20));
        byte[] t = new byte[inputBuffer.length];
        kdf.init(new ISO18033KDFParameters(kpbBytes));
        kdf.generateBytes(t, 0, t.length);
     
        if (allZero(t)) {
        //System.err.println("all zero");
        }
     
        /* 6 计算C2=M^t */
        byte[] C2 = new byte[inputBuffer.length];
        for (int i = 0; i < inputBuffer.length; i++) {
        C2[i] = (byte) (inputBuffer[i] ^ t[i]);
        }
     
        /* 7 计算C3 = Hash(x2 || M || y2) */
        byte[] C3 = calculateHash(kpb.getXCoord().toBigInteger(), inputBuffer,
        kpb.getYCoord().toBigInteger());
     
        /* 8 输出密文 C=C1 || C2 || C3 */
        byte[] encryptResult = new byte[C1Buffer.length + C2.length + C3.length];
        System.arraycopy(C1Buffer, 0, encryptResult, 0, C1Buffer.length);
        System.arraycopy(C2, 0, encryptResult, C1Buffer.length, C2.length);
        System.arraycopy(C3, 0, encryptResult, C1Buffer.length + C2.length,
        C3.length);
     
        //System.out.print("密文: ");
        // printHexString(encryptResult);
  
        return encryptResult;
    }
  
    public byte[] decrypt(byte[] encryptData, BigInteger privateKey) {
        //System.out.println("privateKey is: "+privateKey);
        //System.out.println("encryptData length: " + encryptData.length);
     
        byte[] C1Byte = new byte[65];
        System.arraycopy(encryptData, 0, C1Byte, 0, C1Byte.length);
     
        ECPoint C1 = curve.decodePoint(C1Byte).normalize();
     
        /* 计算[dB]C1 = (x2, y2) */
        ECPoint dBC1 = C1.multiply(privateKey).normalize();
     
        /* 计算t = KDF(x2 || y2, klen) */
        byte[] dBC1Bytes = dBC1.getEncoded(false);
        DerivationFunction kdf = new KDF1BytesGenerator(new ShortenedDigest(
        new SHA256Digest(), 20));
     
        int klen = encryptData.length - 65 - 20;
        //System.out.println("klen = " + klen);
     
        byte[] t = new byte[klen];
        kdf.init(new ISO18033KDFParameters(dBC1Bytes));
        kdf.generateBytes(t, 0, t.length);
     
        if (allZero(t)) {
            //System.err.println("all zero");
        }
     
        /* 5 计算M'=C2^t */
        byte[] M = new byte[klen];
        for (int i = 0; i < M.length; i++) {
            M[i] = (byte) (encryptData[C1Byte.length + i] ^ t[i]);
        }
     
        /* 6 计算 u = Hash(x2 || M' || y2) 判断 u == C3是否成立 */
        byte[] C3 = new byte[20];
        System.arraycopy(encryptData, encryptData.length - 20, C3, 0, 20);
        byte[] u = calculateHash(dBC1.getXCoord().toBigInteger(), M, dBC1.getYCoord().toBigInteger());
        if (Arrays.equals(u, C3)) {
            //System.out.println("解密成功");
            //System.out.println("M' = " + new String(M));
            return M;
        } else {
            //System.out.print("u = ");
            //printHexString(u);
            //System.out.print("C3 = ");
            //printHexString(C3);
            //System.err.println("解密验证失败");
        }
        return null;
    }
  
    private byte[] calculateHash(BigInteger x2, byte[] M, BigInteger y2) {
        ShortenedDigest digest = new ShortenedDigest(new SHA256Digest(), 20);
        byte[] buf = x2.toByteArray();
        digest.update(buf, 0, buf.length);
        digest.update(M, 0, M.length);
        buf = y2.toByteArray();
        digest.update(buf, 0, buf.length);
     
        buf = new byte[20];
        digest.doFinal(buf, 0);
        return buf;
    }
  
    private boolean between(BigInteger param, BigInteger min, BigInteger max) {
        if (param.compareTo(min) >= 0 && param.compareTo(max) < 0) {
            return true;
        } else {
            return false;
        }
    }
     
    /**
     * 公钥校验
     * @param publicKey 公钥
     * @return boolean true或false
     */
    private boolean checkPublicKey(ECPoint publicKey) {
        if (!publicKey.isInfinity()) {
            BigInteger x = publicKey.getXCoord().toBigInteger();
            BigInteger y = publicKey.getYCoord().toBigInteger();
            if (between(x, new BigInteger("0"), p) && between(y, new BigInteger("0"), p)) {
                BigInteger xResult = x.pow(3).add(a.multiply(x)).add(b).mod(p);
                //System.out.println("xResult: " + xResult.toString());
                BigInteger yResult = y.pow(2).mod(p);
                //System.out.println("yResult: " + yResult.toString());
                if (yResult.equals(xResult) && publicKey.multiply(n).isInfinity()) {
                    return true;
                }
            }
            return false;
        } else {
            return false;
        }
    }
     
    /**
     * 获得公私钥对
     * @return 
     */
    public SM2KeyPair generateKeyPair() {
        BigInteger d = random(n.subtract(new BigInteger("1")));
        SM2KeyPair keyPair = new SM2KeyPair(G.multiply(d).normalize(), d);
        if (checkPublicKey(keyPair.getPublicKey())) {
            //System.out.println("generate key successfully");
            return keyPair;
        else {
            //System.err.println("generate key failed");
            return null;
        }
    }
  
     
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.what21.demo.bouncycastle;
import java.util.Arrays;
public class App {
     
    /**
     * @param args
     */
    public static void main(String[] args) {
        SM2Utils sm2 = new SM2Utils();
        SM2KeyPair keyPair = sm2.generateKeyPair();
        String text = "小奋斗{www.what21.com}";
        System.out.println("文本为:" + text);
        byte[] data = sm2.encrypt(text,keyPair.getPublicKey());
        System.out.println("加密为:"+Arrays.toString(data));
        byte[] data2 = sm2.decrypt(data, keyPair.getPrivateKey());
        System.out.println("解密为:" new String(data2));
    }
     
}

程序输入:

1
2
3
文本为:小奋斗{www.what21.com}
加密为:[4, 66, 87, 4, -112, -103, 73, -79, -26, -2, 50, 110, -45, 119, -63, -94, 70, -24, 111, 93, 79, 47, 2, 92, -118, -65, -87, -20, 12, 43, -121, 20, -39, -35, -27, 4, 20, 103, 26, 89, 48, -95, -64, -6, 93, -35, 69, 63, -64, -119, -14, 65, 11, 24, 43, -119, 50, 89, 60, 15, 61, 83, 33, -90, 5, -44, -107, -118, 98, 0, -12, 10, 0, -73, 41, -91, 69, 106, 100, 109, 117, 99, 31, 97, 127, -120, 64, -76, 52, 98, -52, -79, 96, 19, -71, -100, -111, -93, 113, 45, 92, 105, 46, 124, -19, -66, -102, 106, 2, 75]
解密为:小奋斗{www.what21.com}
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
C# RSA和Java RSA互通
Java基于BC生成X509v3证书,以及部分扩展Extension的使用
C# 通过java生成的RSA公钥加密和解密
Android开发资料
程序员必备基础:加签验签
java RSA加密解密实现
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服