RSA非对称加密算法

非对称加密

  1. 具体算法看这位大神详解 https://blog.csdn.net/jijianshuai/article/details/80582187
  2. 常用用途

api接口数据传输等

  1. 加密解密用法

公钥暴露在网络上,私钥在本地保存,凡是能拿到公钥的人都可以加密信息,但是使用公钥加密的数据,无法再次使用公钥解开,所以一般采用公钥加密私钥解密的方式

  1. 签名/验签的用法

一般采用私钥进行签名,然后把签名和明文数据一起传送给拥有公钥的一方,拥有公钥的一方,可以使用公钥验证此签名是否为对应私钥签名,以防止数据篡改

代码


import javax.crypto.Cipher;
import java.io.*;
import java.security.*;
import java.security.interfaces.RSAKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.EncodedKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

/**
 * <p>
 *      Utils for RSA Encrypt
 *      <ul>
 *           <li>Use private key to decrypt {@link #decrypt(Key, byte[])} data;</li>
 *           <li>Use public key to encrypt {@link #encrypt(Key, byte[])} data;</li>
 *           <li>Use sign {@link #sign(byte[], PrivateKey)} to generate a signature;</li>
 *           <li>Use the verify {@link #verify(byte[], PublicKey, String)} to valuation if has be hijacked;</li>
 *      </ul>
 * </p>
 *
 * @author Wilton Jia
 * @date 2020-09-29
 * @since 1.0
 */
public class RSAEncrypt {
    /**
     * array of byte data, for transform strings
     */
    private static final char[] HEX_CHAR = { '0', '1', '2', '3', '4', '5', '6',
            '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
    /**
     * Algorithm of Encrypt
     */
    private final static String KEY_RSA = "RSA";
    /**
     * Algorithm of Signature
     */
    private final static String KEY_RSA_SIGNATURE = "MD5withRSA";
    /**
     * Public key name
     */
    private final static String publicKeyName = "publicKey";
    /**
     * Private key name
     */
    private final static String privateKeyName = "privateKey";


    /**
     * Generate a pair of random keys
     * @param filePath location of key files
     */
    public static Map<String, String> genKeyPair(String filePath) throws Exception {
        // get KeyGenerator
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
        // Init the KeyGenerator, size of key :96-1024 bit
        keyPairGen.initialize(2048);

        // key pair
        KeyPair keyPair = keyPairGen.generateKeyPair();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();

        // key string
        Base64.Encoder base64Encoder = Base64.getEncoder();
        String publicKeyString = base64Encoder.encodeToString(publicKey.getEncoded());
        String privateKeyString = base64Encoder.encodeToString(privateKey.getEncoded());

        String uuid = UUIDGenerator.generateWithout_();
        // Result for file path
        Map<String, String> pairs = new HashMap<>(2);
        String publicKeyFilePath = filePath + File.separator + uuid+ "_publicKey.pem";
        String privateKeyFilePath = filePath + File.separator + uuid+ "_privateKey.pem";
        pairs.put(publicKeyName,publicKeyFilePath);
        pairs.put(privateKeyName,privateKeyFilePath);

        // Write files to local
        FileWriter publicKeyWriter = new FileWriter(publicKeyFilePath);
        FileWriter privateKeyWriter = new FileWriter(privateKeyFilePath);
        BufferedWriter publicKeyBufferWriter = new BufferedWriter(publicKeyWriter);
        BufferedWriter privateKeyBufferWriter = new BufferedWriter(privateKeyWriter);

        //Close streams
        publicKeyBufferWriter.write(publicKeyString);
        privateKeyBufferWriter.write(privateKeyString);
        publicKeyBufferWriter.flush();
        publicKeyBufferWriter.close();
        publicKeyWriter.close();
        privateKeyBufferWriter.flush();
        privateKeyBufferWriter.close();
        privateKeyWriter.close();
        return pairs;
    }


    /**
     * Load a key from local file, first step is get the file byte array,
     * then transform the byte array to a RSAKey,
     * @param path the pem file path or
     * @param type Implements of RSAKey, RSAPublicKey or RSAPrivateKey
     * @throws IOException
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     * @return {@code RSAPublicKey, RSAPrivateKey}
     */
    public static RSAKey loadKeyFromFile(String path, Class<? extends RSAKey> type) throws IOException,
            NoSuchAlgorithmException, InvalidKeySpecException {
        //load the key string from file
        File file = new File(path);
        FileInputStream fileInputStream = new FileInputStream(path);
        DataInputStream dis = new DataInputStream(fileInputStream);
        byte[] keyBytes = new byte[(int)file.length()];
        dis.readFully(keyBytes);
        dis.close();
        fileInputStream.close();
        // parse the key string to a key
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_RSA);
        EncodedKeySpec keySpec = null;

        // while need a public key
        keyBytes = Base64.getDecoder().decode(keyBytes);
        if(type.getName().equals(RSAPublicKey.class.getName())){
            keySpec = new X509EncodedKeySpec(keyBytes);
            return (RSAPublicKey)keyFactory.generatePublic(keySpec);
        }else if(type.getName().equals(RSAPrivateKey.class.getName())){
            keySpec = new PKCS8EncodedKeySpec(keyBytes);
            return (RSAPrivateKey)keyFactory.generatePrivate(keySpec);
        }
        return null;
    }

    /**
     * Use the key to encrypt some data
     * @param key publicKey or privateKey
     * @param plainTextData clear data array
     * @return encrypted data bytes
     * @throws Exception
     */
    public static byte[] encrypt(Key key, byte[] plainTextData) throws Exception {
        if (key == null) {
            throw new Exception("key could not be null");
        }
        Cipher cipher = Cipher.getInstance(KEY_RSA);
        cipher.init(Cipher.ENCRYPT_MODE, key);
        return cipher.doFinal(plainTextData);
    }


    /**
     * Use key to decrypt data
     * @param key public key or private key
     * @param cipherData encrypt data
     * @return clear data array
     * @throws Exception
     */
    public static byte[] decrypt(Key key, byte[] cipherData) throws Exception {
        if (key == null) {
            throw new Exception("private key could not be empty or null");
        }
        Cipher cipher = Cipher.getInstance(KEY_RSA);;
        cipher.init(Cipher.DECRYPT_MODE, key);
        return cipher.doFinal(cipherData);
    }

    /**
     * Use Private key to sign
     * <p>
     *     could be a public key,  but not suggest, cause you should write your sign by your self
     *     and this method provide EncodedKeySpec was a PKCS8EncodedKeySpec, if you want to use a public key to sign,
     *     you should change {@code PKCS8EncodedKeySpec pkcs = new PKCS8EncodedKeySpec(bytes)}
     *     to {@code X509EncodedKeySpec pkcs = new X509EncodedKeySpec(bytes)}
     * </p>
     * @param data clear data
     * @param privateKey a key encoded by Base64
     * @throws Exception
     */
    public static String sign(byte[] data, PrivateKey privateKey) throws Exception {
        // create the real Signature Object
        Signature signature = Signature.getInstance(KEY_RSA_SIGNATURE);
        signature.initSign(privateKey);
        signature.update(data);
        return encryptBase64(signature.sign());
    }

    /**
     * Verify the Sign context
     *
     * @param data encrypted data
     * @param publicKey  a base64 key
     * @param sign
     * @return if success return {@code true} else return {@code false}
     */
    public static boolean verify(byte[] data, PublicKey publicKey, String sign) {
        boolean flag = false;
        try {
            // to verify
            Signature signature = Signature.getInstance(KEY_RSA_SIGNATURE);
            signature.initVerify(publicKey);
            signature.update(data);
            flag = signature.verify(decryptBase64(sign));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return flag;
    }

    /**
     * Parse a byte array to a hex String
     * @param data input byte array
     * @return hex String
     */
    public static String byteArrayToString(byte[] data) {
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < data.length; i++) {
            // get high four bit as a index ,then parse to hex, be careful to forget no sign left move
            stringBuilder.append(HEX_CHAR[(data[i] & 0xf0) >>> 4]);
            // get the lower four bit as a index ,get hex sign
            stringBuilder.append(HEX_CHAR[(data[i] & 0x0f)]);
            if (i < data.length - 1) {
                stringBuilder.append(' ');
            }
        }
        return stringBuilder.toString();
    }

    /**
     * BASE64 decode
     * @param key base64 string needs to decode
     * @return clear data array
     */
    public static byte[] decryptBase64(String key) throws Exception {
        return Base64.getDecoder().decode(key);
    }

    /**
     * BASE64 encode
     * @param key data array need to encode
     * @return a string
     */
    public static String encryptBase64(byte[] key) throws Exception {
        return Base64.getEncoder().encodeToString(key);
    }

}


测试

  1. 生成密钥
@Test
public void test1() throws Exception {
    Map<String, String> keyPair = RSAEncrypt.genKeyPair(path);
    for (String s : keyPair.keySet()) {
        System.out.printf("%s: %s", s, keyPair.get(s));
        //4bfda50f21e540f19ab394218e62ccb7_privateKey.pem
        //4bfda50f21e540f19ab394218e62ccb7_publicKey.pem
    }
}
  1. 公钥加密/私钥解密
@Test
public void test2() throws Exception {
    String publicKey = "4bfda50f21e540f19ab394218e62ccb7_publicKey.pem";
    RSAPublicKey rsaPublicKey = (RSAPublicKey)RSAEncrypt.loadKeyFromFile(path + publicKey, RSAPublicKey.class);
    byte[] encrypt = RSAEncrypt.encrypt(rsaPublicKey, "123456ABc".getBytes());
    System.out.println(new String(Base64Utils.encode(encrypt)));
    String privateKey = "4bfda50f21e540f19ab394218e62ccb7_privateKey.pem";
    RSAPrivateKey key = (RSAPrivateKey)RSAEncrypt.loadKeyFromFile(path + privateKey, RSAPrivateKey.class);
    byte[] decrypt = RSAEncrypt.decrypt(key, encrypt);
    System.out.println(new String(decrypt));
}
  1. 私钥签名/公钥验签
@Test
public void test4() throws Exception {
    String privateKey = "4bfda50f21e540f19ab394218e62ccb7_privateKey.pem";
    RSAPrivateKey key = (RSAPrivateKey)RSAEncrypt.loadKeyFromFile(path + privateKey, RSAPrivateKey.class);
    String sign = RSAEncrypt.sign("123456AbCd".getBytes(), key);
    System.out.println(sign);
    String publicKey = "4bfda50f21e540f19ab394218e62ccb7_publicKey.pem";
    RSAPublicKey rsaPublicKey = (RSAPublicKey)RSAEncrypt.loadKeyFromFile(path + publicKey, RSAPublicKey.class);
    boolean verify = RSAEncrypt.verify("123456AbCd1".getBytes(), rsaPublicKey, sign);
    System.out.println(verify);
}

本人自己整理的工具类集合

geeking-common-utils
欢迎各位大神将自己的工具类贡献,省去每次找一个工具类的烦恼

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345