JAVA中使用P和Q分量计算N和D进行RSA运算

最近在使用Java中需要使用PQ形式的私钥进行RSA加解密运算,本来以为Java中应该很多类似的例子,发现所有的例子都是从ND形式的私钥,竟然没有人用分量P和Q计算N和D进行运算。对Java使用RSA运算不太熟,只能自己一点一点搞了。身边的Java 的仙们,好像身边都没人中国剩余定理,所以也不会遇到P和Q?不管他们了,开工了。

1.BigInteger类

Java中有现成的大数运算的BigInteger类,直接使用这个类进行运算即可,总结一下使用中遇到的坑。Java的大数多1bit表示符号,所以如果1024byte的N在BigInteger中是1025bit,最高位多了1bit符号位,所以如果用BigInteger中的toByteArray()可以获得大数的二进制补码,如果需要导出BigInteger中的数据,需要忽略符号位,从第二字节开始拷贝,如果从第一字节就拷贝,那么会丢失最后一字节,把符号位存下来。 
BigInteger类提供modInverse方法,可以直接求d=e1  mod φ(n),这样就省事多了。

2.Cipher类

javax.crypto.Cipher类有个getInstance()方法,参数是“算法/模式/填充方式”,因为我只有一块定长128字节数据进行RSA运算,自己进行填充和去填充,按照sun的文档中的说明,填写”RSA/None/NoPadding”,但是编译的时候报错,提示不支持,网上搜了搜,都说默认的Crypt Provider不支持NoPadding,必须是PKCS#1的填充,感觉很不靠谱啊,后来发现是在Jdk1.7还是哪个版本之后,不支持None的模式,用ECB模式就行了,之前的版本是不是支持None也没去验证。

3.代码

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
package net.sms.datatool.algorithm;
 
import java.math.BigInteger;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
 
import javax.crypto.Cipher;
 
/**
*
* RSACrtUtil RSA加解密
* 使用中国剩余定理类型的密钥
* 私钥是P和Q,公钥是N,E固定0x10001
*
* @Date 2016.9.6
*
* @version v1.0
*
* @author 赵洋 cnrgc@163.com
*/
public class RSACrtUtils {
 
    public static final int RSA_MODULUS_LEN = 128;
    public static final int RSA_P_LEN = RSA_MODULUS_LEN/2;
    public static final int RSA_Q_LEN = RSA_MODULUS_LEN/2;
    public static final int publicExponent = 65537;
    public static final String KEY_ALGORITHM_MODE_PADDING = "RSA/ECB/NoPadding"; //不填充
    public static final String KEY_ALGORITHM = "RSA"; //不填充
 
     
    /**
     * prikey_crt_decrypt 使用PQ的RSA私钥解密
     * 私钥格式前半部分是P,后半部分是Q
     *
     * */
    public static void prikey_crt_test(byte[] prikey) throws Exception{
 
 
        byte[] buf_p = new byte[RSA_P_LEN];
        byte[] buf_q = new byte[RSA_Q_LEN];
        //buf_p[0] = (byte)0x00;
        //buf_q[0] = (byte)0x00;
        System.arraycopy(prikey, 0, buf_p, 0, RSA_P_LEN);
        System.arraycopy(prikey, RSA_P_LEN, buf_q, 0, RSA_Q_LEN);
        //
        /**
         *  1.p,q计算n
         * */
        BigInteger p = new BigInteger(1, buf_p);
        BigInteger q = new BigInteger(1, buf_q);
        BigInteger n = p.multiply(q); //n = p * q
        
        /**
         *  2. 计算d = (p-1) * (q-1) mod e
         * */
        BigInteger p1 = p.subtract(BigInteger.valueOf(1));
        BigInteger q1 = q.subtract(BigInteger.valueOf(1));
        BigInteger h = p1.multiply(q1);// h = (p-1) * (q-1)
        BigInteger e = BigInteger.valueOf(publicExponent);
        BigInteger d = e.modInverse(h);
 
        BigInteger qinV;
         
        qinV = q.modInverse(p);
         
        String strQinV = qinV.toString(16);//QinV的十六进制形式
         
         
         
        /**
         *  3. 创建 RSA私钥
         * */
        RSAPrivateKeySpec keyspec = new RSAPrivateKeySpec(n, d);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);  
        Key privateKey = keyFactory.generatePrivate(keyspec);
        /**
         *  4. 数据解密
         * */
        Cipher cipher = Cipher.getInstance(KEY_ALGORITHM_MODE_PADDING);  
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
         
         
        /**
         *  5. 返回结果
         * */
        return
    }
     
     
    /**
     * prikey_crt_decrypt 使用PQ的RSA私钥解密
     * 私钥格式前半部分是P,后半部分是Q
     *
     * */
    public static byte[] prikey_crt_decrypt(byte[] data, byte[] prikey) throws Exception{
 
 
        byte[] buf_p = new byte[RSA_P_LEN];
        byte[] buf_q = new byte[RSA_Q_LEN];
        //buf_p[0] = (byte)0x00;
        //buf_q[0] = (byte)0x00;
        System.arraycopy(prikey, 0, buf_p, 0, RSA_P_LEN);
        System.arraycopy(prikey, RSA_P_LEN, buf_q, 0, RSA_Q_LEN);
        //
        /**
         *  1.p,q计算n
         * */
        BigInteger p = new BigInteger(1, buf_p);
        BigInteger q = new BigInteger(1, buf_q);
        BigInteger n = p.multiply(q); //n = p * q
        /**
         *  2. 计算d = (p-1) * (q-1) mod e
         * */
        BigInteger p1 = p.subtract(BigInteger.valueOf(1));
        BigInteger q1 = q.subtract(BigInteger.valueOf(1));
        BigInteger h = p1.multiply(q1);// h = (p-1) * (q-1)
        BigInteger e = BigInteger.valueOf(publicExponent);
        //BigInteger d = h.mod(e);
        BigInteger d = e.modInverse(h);
 
        /**
         *  3. 创建 RSA私钥
         * */
        RSAPrivateKeySpec keyspec = new RSAPrivateKeySpec(n, d);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);  
        Key privateKey = keyFactory.generatePrivate(keyspec);
        /**
         *  4. 数据解密
         * */
        Cipher cipher = Cipher.getInstance(KEY_ALGORITHM_MODE_PADDING);  
        cipher.init(Cipher.DECRYPT_MODE, privateKey);  
        /**
         *  5. 返回结果
         * */
        return cipher.doFinal(data); 
    }
    /**
     * pubkey_encrypt 公钥加密
     * 密钥是N
     * */
    public static byte[] pubkey_encrypt(byte[] data, byte[] pubkey) throws Exception{
 
        /**
         *  1.初始化大数模n和公钥指数e
         * */
        byte[] pubkey_buf = new byte[RSA_MODULUS_LEN+1];//多一字节符号位
        pubkey_buf[0] = (byte)0x00;
        System.arraycopy(pubkey, 0, pubkey_buf, 1, RSA_MODULUS_LEN);
        //
        BigInteger e = BigInteger.valueOf(publicExponent);
        BigInteger n = new BigInteger(pubkey_buf);
        /**
         *  2.创建RSA公钥
         * */
        //
        RSAPublicKeySpec keyspec = new RSAPublicKeySpec(n, e);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);  
        Key publicKey = keyFactory.generatePublic(keyspec);
        /**
         *  3.数据加密
         * */
        Cipher cipher = Cipher.getInstance(KEY_ALGORITHM_MODE_PADDING);
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);  
        /**
         *  5. 返回结果
         * */
        return cipher.doFinal(data);
    }
 
    /**
     * 产生RSA密钥对
     *
     * @param pubkey 公钥
     * @param prikey 私钥
     * @throws Exception
     */
    public static void generateKeyPair(byte[] pubkey, byte[] prikey) throws Exception{
 
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        keyPairGenerator.initialize(RSA_MODULUS_LEN*8);
 
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        RSAPublicKey publicKey = (RSAPublicKey)keyPair.getPublic();
        RSAPrivateCrtKey privateKey = (RSAPrivateCrtKey)keyPair.getPrivate();
        //
        BigInteger n = publicKey.getModulus();
        BigInteger p = privateKey.getPrimeP();
        BigInteger q = privateKey.getPrimeQ();
        /**
         *  BigInteger 里有一个bit的符号位,所以直接用toByteArray会包含符号位,
         *  在c的代码里没符号位,所以1024bit的n,java里BigInteger是1025bit长
         *  直接拷贝128byte出来,正数第一个字节是是0,后面会丢掉最后一字节
         * */
        System.arraycopy(n.toByteArray(), 1, pubkey, 0, 128);
        System.arraycopy(p.toByteArray(), 1, prikey, 0, 64);
        System.arraycopy(q.toByteArray(), 1, prikey, 64, 64);
        //
    }
     
     
     
     
}

  

参考文章: http://blog.csdn.net/youngbug/article/details/52709507?locationNum=8

 

posted @   jiftle  阅读(1682)  评论(0编辑  收藏  举报
编辑推荐:
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
点击右上角即可分享
微信分享提示