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

36次阅读

共计 5100 个字符,预计需要花费 13 分钟才能阅读完成。

最近在应用 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 \\phi(n)$,这样就省事多了。

2.Cipher 类

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

3. 代码

RSACrtUtil.java

package com.zhantianzuo.alg;

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 RSACrtUtil {
    
    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 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);
    }
    
    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);
        //
    }

}

Test.java

package com.zhantianzuo.alg;

import java.math.BigInteger;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.interfaces.RSAPublicKey;


public class Test {

    private static final String ALGORITHM = "RSA";
    private static final int key_len = 128;
    
    public static void main(String[] args) {
        //
        byte[] pubkey = new byte[128];
        byte[] prikey = new byte[128];
        try {RSACrtUtil.generateKeyPair(pubkey, prikey);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();}
        //
        int i;
        byte[] plaintext = null;
        byte[] ciphertext = null;
        byte[] data = new byte[key_len];
        for(i=0; i<key_len; i++){data[i] = (byte)i;
        }
        //
        try {ciphertext = RSACrtUtil.pubkey_encrypt(data, pubkey);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();}
        //
        
        try {plaintext = RSACrtUtil.prikey_crt_decrypt(ciphertext, prikey);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();}
        
    }

}

正文完
 0