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=e−1 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
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析