美文网首页随笔
RSA非对称加密算法

RSA非对称加密算法

作者: 朽木亦自雕 | 来源:发表于2020-10-09 16:37 被阅读0次

非对称加密

  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
欢迎各位大神将自己的工具类贡献,省去每次找一个工具类的烦恼

相关文章

网友评论

    本文标题:RSA非对称加密算法

    本文链接:https://www.haomeiwen.com/subject/shnaqktx.html