美文网首页随笔
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