1、公钥和私钥
公钥和私钥其实就是为了保证双方通信数据传输安全而采取的一种加密策略;公钥(Public Key)与私钥(Private Key)是通过一种算法得到的一个密钥对(即一个公钥和一个私钥),公钥是密钥对中公开的部分,私钥则是非公开的部分
2、公钥和私钥的使用
例如:甲->乙发送消息:
1)、甲给乙发送的加密消息,只有乙才解密,别人就是窃取,没没法解密,保证数据安全(公钥加密,私钥解密)
2)、乙接收到的消息必须保证是甲发送的,并且是没有被篡改,保证数据的完整性(签名,签名验证)。
那么整个流程应该这样的:
1)、甲用乙的公钥进行信息加密,
2)、甲用自己的私钥对消息进行数字签名
3)、乙收到消息后,用自己的私钥进行解密
4)、乙对接收到的消息进行签名验证
其中的签名,可以对整个消息进行签名;也可以对先对消息生成摘要,然后对摘要进行签名,这样签名运算的效率高。
3、代码:
RAS工具类:
package com.rsa; import java.io.ByteArrayOutputStream; import java.security.Key; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.PrivateKey; import java.security.PublicKey; import java.security.Signature; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.HashMap; import java.util.Map; import javax.crypto.Cipher; /** *@author yunlong.liu *@date 2017年7月31日 *@time 下午6:01:50 *@description */ public class RSAUtils { /** * 加密算法RSA */ public static final String KEY_ALGORITHM = "RSA"; /** * 签名算法 */ public static final String SIGNATURE_ALGORITHM = "MD5withRSA"; /** * 获取公钥的key */ private static final String PUBLIC_KEY = "RSAPublicKey"; /** * 获取私钥的key */ private static final String PRIVATE_KEY = "RSAPrivateKey"; /** * RSA最大加密明文大小 */ private static final int MAX_ENCRYPT_BLOCK = 117; /** * RSA最大解密密文大小 */ private static final int MAX_DECRYPT_BLOCK = 128; /** ** 生成密钥对(公钥和私钥) *
* * @return * @throws Exception */ public static MapgenKeyPair() throws Exception { KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM); keyPairGen.initialize(1024); KeyPair keyPair = keyPairGen.generateKeyPair(); RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); Map keyMap = new HashMap (2); keyMap.put(PUBLIC_KEY, publicKey); keyMap.put(PRIVATE_KEY, privateKey); return keyMap; } /** * * 用私钥对信息生成数字签名 *
* * @param data 已加密数据 * @param privateKey 私钥(BASE64编码) * * @return * @throws Exception */ public static String sign(byte[] data, String privateKey) throws Exception { byte[] keyBytes = Base64Utils.decode(privateKey); PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec); Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); signature.initSign(privateK); signature.update(data); return Base64Utils.encode(signature.sign()); } /** ** 校验数字签名 *
* * @param data 已加密数据 * @param publicKey 公钥(BASE64编码) * @param sign 数字签名 * * @return * @throws Exception * */ public static boolean verify(byte[] data, String publicKey, String sign) throws Exception { byte[] keyBytes = Base64Utils.decode(publicKey); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); PublicKey publicK = keyFactory.generatePublic(keySpec); Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); signature.initVerify(publicK); signature.update(data); return signature.verify(Base64Utils.decode(sign)); } /** ** 私钥解密 *
* * @param encryptedData 已加密数据 * @param privateKey 私钥(BASE64编码) * @return * @throws Exception */ public static byte[] decryptByPrivateKey(byte[] encryptedData, String privateKey) throws Exception { byte[] keyBytes = Base64Utils.decode(privateKey); PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); Key privateK = keyFactory.generatePrivate(pkcs8KeySpec); Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); cipher.init(Cipher.DECRYPT_MODE, privateK); int inputLen = encryptedData.length; ByteArrayOutputStream out = new ByteArrayOutputStream(); int offSet = 0; byte[] cache; int i = 0; // 对数据分段解密 while (inputLen - offSet > 0) { if (inputLen - offSet > MAX_DECRYPT_BLOCK) { cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK); } else { cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet); } out.write(cache, 0, cache.length); i++; offSet = i * MAX_DECRYPT_BLOCK; } byte[] decryptedData = out.toByteArray(); out.close(); return decryptedData; } /** ** 公钥解密 *
* * @param encryptedData 已加密数据 * @param publicKey 公钥(BASE64编码) * @return * @throws Exception */ public static byte[] decryptByPublicKey(byte[] encryptedData, String publicKey) throws Exception { byte[] keyBytes = Base64Utils.decode(publicKey); X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); Key publicK = keyFactory.generatePublic(x509KeySpec); Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); cipher.init(Cipher.DECRYPT_MODE, publicK); int inputLen = encryptedData.length; ByteArrayOutputStream out = new ByteArrayOutputStream(); int offSet = 0; byte[] cache; int i = 0; // 对数据分段解密 while (inputLen - offSet > 0) { if (inputLen - offSet > MAX_DECRYPT_BLOCK) { cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK); } else { cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet); } out.write(cache, 0, cache.length); i++; offSet = i * MAX_DECRYPT_BLOCK; } byte[] decryptedData = out.toByteArray(); out.close(); return decryptedData; } /** ** 公钥加密 *
* * @param data 源数据 * @param publicKey 公钥(BASE64编码) * @return * @throws Exception */ public static byte[] encryptByPublicKey(byte[] data, String publicKey) throws Exception { byte[] keyBytes = Base64Utils.decode(publicKey); X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); Key publicK = keyFactory.generatePublic(x509KeySpec); // 对数据加密 Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); cipher.init(Cipher.ENCRYPT_MODE, publicK); int inputLen = data.length; ByteArrayOutputStream out = new ByteArrayOutputStream(); int offSet = 0; byte[] cache; int i = 0; // 对数据分段加密 while (inputLen - offSet > 0) { if (inputLen - offSet > MAX_ENCRYPT_BLOCK) { cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK); } else { cache = cipher.doFinal(data, offSet, inputLen - offSet); } out.write(cache, 0, cache.length); i++; offSet = i * MAX_ENCRYPT_BLOCK; } byte[] encryptedData = out.toByteArray(); out.close(); return encryptedData; } /** ** 私钥加密 *
* * @param data 源数据 * @param privateKey 私钥(BASE64编码) * @return * @throws Exception */ public static byte[] encryptByPrivateKey(byte[] data, String privateKey) throws Exception { byte[] keyBytes = Base64Utils.decode(privateKey); PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); Key privateK = keyFactory.generatePrivate(pkcs8KeySpec); Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); cipher.init(Cipher.ENCRYPT_MODE, privateK); int inputLen = data.length; ByteArrayOutputStream out = new ByteArrayOutputStream(); int offSet = 0; byte[] cache; int i = 0; // 对数据分段加密 while (inputLen - offSet > 0) { if (inputLen - offSet > MAX_ENCRYPT_BLOCK) { cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK); } else { cache = cipher.doFinal(data, offSet, inputLen - offSet); } out.write(cache, 0, cache.length); i++; offSet = i * MAX_ENCRYPT_BLOCK; } byte[] encryptedData = out.toByteArray(); out.close(); return encryptedData; } /** ** 获取私钥 *
* * @param keyMap 密钥对 * @return * @throws Exception */ public static String getPrivateKey(MapkeyMap) throws Exception { Key key = (Key) keyMap.get(PRIVATE_KEY); return Base64Utils.encode(key.getEncoded()); } /** * * 获取公钥 *
* * @param keyMap 密钥对 * @return * @throws Exception */ public static String getPublicKey(MapkeyMap) throws Exception { Key key = (Key) keyMap.get(PUBLIC_KEY); return Base64Utils.encode(key.getEncoded()); } }
Base64工具类:
package com.rsa; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.util.Base64; /** *@author yunlong.liu *@date 2017年7月31日 *@time 下午6:03:11 *@description */ public class Base64Utils { /** * 文件读取缓冲区大小 */ private static final int CACHE_SIZE = 1024; /** ** BASE64字符串解码为二进制数据 *
* * @param base64 * @return * @throws Exception */ public static byte[] decode(String base64) throws Exception { return Base64.getDecoder().decode(base64.getBytes()); } /** ** 二进制数据编码为BASE64字符串 *
* * @param bytes * @return * @throws Exception */ public static String encode(byte[] bytes) throws Exception { return new String(Base64.getEncoder().encode(bytes)); } /** ** 将文件编码为BASE64字符串 *
** 大文件慎用,可能会导致内存溢出 *
* * @param filePath * 文件绝对路径 * @return * @throws Exception */ public static String encodeFile(String filePath) throws Exception { byte[] bytes = fileToByte(filePath); return encode(bytes); } /** ** BASE64字符串转回文件 *
* * @param filePath * 文件绝对路径 * @param base64 * 编码字符串 * @throws Exception */ public static void decodeToFile(String filePath, String base64) throws Exception { byte[] bytes = decode(base64); byteArrayToFile(bytes, filePath); } /** ** 文件转换为二进制数组 *
* * @param filePath * 文件路径 * @return * @throws Exception */ public static byte[] fileToByte(String filePath) throws Exception { byte[] data = new byte[0]; File file = new File(filePath); if (file.exists()) { FileInputStream in = new FileInputStream(file); ByteArrayOutputStream out = new ByteArrayOutputStream(2048); byte[] cache = new byte[CACHE_SIZE]; int nRead = 0; while ((nRead = in.read(cache)) != -1) { out.write(cache, 0, nRead); out.flush(); } out.close(); in.close(); data = out.toByteArray(); } return data; } /** ** 二进制数据写文件 *
* * @param bytes * 二进制数据 * @param filePath * 文件生成目录 */ public static void byteArrayToFile(byte[] bytes, String filePath) throws Exception { InputStream in = new ByteArrayInputStream(bytes); File destFile = new File(filePath); if (!destFile.getParentFile().exists()) { destFile.getParentFile().mkdirs(); } destFile.createNewFile(); OutputStream out = new FileOutputStream(destFile); byte[] cache = new byte[CACHE_SIZE]; int nRead = 0; while ((nRead = in.read(cache)) != -1) { out.write(cache, 0, nRead); out.flush(); } out.close(); in.close(); } }
MD5 生成摘要工具类:
package com.rsa; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.util.Base64; /** *@author yunlong.liu *@date 2017年7月31日 *@time 下午6:03:11 *@description */ public class Base64Utils { /** * 文件读取缓冲区大小 */ private static final int CACHE_SIZE = 1024; /** ** BASE64字符串解码为二进制数据 *
* * @param base64 * @return * @throws Exception */ public static byte[] decode(String base64) throws Exception { return Base64.getDecoder().decode(base64.getBytes()); } /** ** 二进制数据编码为BASE64字符串 *
* * @param bytes * @return * @throws Exception */ public static String encode(byte[] bytes) throws Exception { return new String(Base64.getEncoder().encode(bytes)); } /** ** 将文件编码为BASE64字符串 *
** 大文件慎用,可能会导致内存溢出 *
* * @param filePath * 文件绝对路径 * @return * @throws Exception */ public static String encodeFile(String filePath) throws Exception { byte[] bytes = fileToByte(filePath); return encode(bytes); } /** ** BASE64字符串转回文件 *
* * @param filePath * 文件绝对路径 * @param base64 * 编码字符串 * @throws Exception */ public static void decodeToFile(String filePath, String base64) throws Exception { byte[] bytes = decode(base64); byteArrayToFile(bytes, filePath); } /** ** 文件转换为二进制数组 *
* * @param filePath * 文件路径 * @return * @throws Exception */ public static byte[] fileToByte(String filePath) throws Exception { byte[] data = new byte[0]; File file = new File(filePath); if (file.exists()) { FileInputStream in = new FileInputStream(file); ByteArrayOutputStream out = new ByteArrayOutputStream(2048); byte[] cache = new byte[CACHE_SIZE]; int nRead = 0; while ((nRead = in.read(cache)) != -1) { out.write(cache, 0, nRead); out.flush(); } out.close(); in.close(); data = out.toByteArray(); } return data; } /** ** 二进制数据写文件 *
* * @param bytes * 二进制数据 * @param filePath * 文件生成目录 */ public static void byteArrayToFile(byte[] bytes, String filePath) throws Exception { InputStream in = new ByteArrayInputStream(bytes); File destFile = new File(filePath); if (!destFile.getParentFile().exists()) { destFile.getParentFile().mkdirs(); } destFile.createNewFile(); OutputStream out = new FileOutputStream(destFile); byte[] cache = new byte[CACHE_SIZE]; int nRead = 0; while ((nRead = in.read(cache)) != -1) { out.write(cache, 0, nRead); out.flush(); } out.close(); in.close(); } }
测试类:
package com.rsa; /** *@author yunlong.liu *@date 2017年7月31日 *@time 下午6:04:26 *@description */ public class RSATester1 { static String publicKey="MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCRRnY9EvF3nUhQ/FU4zauaEKCcO65Vpy5vHPCxFYnCXYaTQ97vuZ/9LPEAjGo8kJQJwOYmq4bbiPpe+ecMMqwhMg+K2cZuxVY6nHW9bGT3O9hW96OW8Ra2Kcdd56j6sR1AoTsX0AaopJLzvWzmOPbBH3E2BE7ByCiXfKEgRfRY9QIDAQAB"; //自己是公钥 static String privateKey="MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAJFGdj0S8XedSFD8VTjNq5oQoJw7rlWnLm8c8LEVicJdhpND3u+5n/0s8QCMajyQlAnA5iarhtuI+l755wwyrCEyD4rZxm7FVjqcdb1sZPc72Fb3o5bxFrYpx13nqPqxHUChOxfQBqikkvO9bOY49sEfcTYETsHIKJd8oSBF9Fj1AgMBAAECgYBT43BbjBjLjZM40L0VdA5nhLp5/SyKYWO63edPGrTV+9O+Li8KFxJ/y/fO7UZbetFBYJaGNf5Fvy90sLHupUQnd3NaBtRLcYdj6iJaxi8FwBWkz0ynL136TzalpAw4Xs7RjbouyxR7U33Ew7U9TPH2i8MG5BvTqbnId1Vg9Ao48QJBANMULcbhbAt8rrcPKd0ab5qdUqlasFiKjXHhwRakvKlHSmUnoOvdZbkZD5XjXdN0DgX0MSeyxnubgvF+gmIBlWMCQQCwMToZeytvxYpe2cj/HI7VWWW8pXNvW06lb8RpbJqaqRHGx4juQkYxr3cZUWggyXzquICo9qEFg0wdLyuBN7PHAkBrjqXA6Q84T0FsMILhkt35yF7dw0OG6psmw6WzlyA3lgc3wljCt59lEXsaorAl1sZqjEMb+oIfbF/uFMv1it8HAkA6wX/NhYybrnWRYfEsWfqr3S8XNNmE9h8I/neybHw2PVFnR507TzCXWxtNfx9JZ8fXgoWtUumpq/pS9Sr94XR7AkAtgAvwNCm2r1OR0IItJEg/tuaOd85NXdOXkMv+mUGOWPe783hMpEnULLSQQiyArmtFZEWoS1ucwWlJyNnRcaeS"; //自己的私钥 static String publicKey2="MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCsk2b7UaLMLrUqPvv3+8/gJ6PH/zRq4FJcDMlXEOQubDF+zJ58zNc0UeRqLY/A7IxBpHagLgjdjFSshtIdAudOm3mjSrTTZP9a7EFHRhnq3rMFDG6gBfshuz8gfihT1IMJrY5zXfDu57KqR2tWFiB60PR38QRfMM2o/s/TYLfldQIDAQAB"; //对方的公钥 static String privateKey2="MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAKyTZvtRoswutSo++/f7z+Ano8f/NGrgUlwMyVcQ5C5sMX7MnnzM1zRR5Gotj8DsjEGkdqAuCN2MVKyG0h0C506beaNKtNNk/1rsQUdGGereswUMbqAF+yG7PyB+KFPUgwmtjnNd8O7nsqpHa1YWIHrQ9HfxBF8wzaj+z9Ngt+V1AgMBAAECgYBzlOGTDtD++YVifv6YzvEhysq5pyX/MrBWcEXk5y48HUBlETxQj4zOwM4/grZV2tfxGupMS6iqYVNQnZdZ0NkDQ3paD4XxVXuQL00di7VYkBz7bNeJk8mobg5jo1JrKVQaJhtf8wiM6WFoBnfxSeoe9g+HFovDzRT+dYz47nooIQJBAPGeIXPYTs3SWVFkZHxhsV6U8hTXfujRGWUyVEPBdERsSWgvsN8O1WzzGWYlhna7lA4xoL8azONwUzrYqrJvipkCQQC22S7JgtcfMWfjTg1ahTgQIgT7byDoa7Wlgt02sq33RIhHcAXxzpotRm/gFsNDwmySWYBbQMfkD/uoeZpPZTc9AkAhSo6Fbbp99YvFATYvRS6xP4iTgpXD8nuzS9n8c/1XKCx9JIUUVvDlUCGhx23dkj72LbReYIz2kZWGODBc4a2RAkAOv3m8mycKxwtgFB2TnYcmFfhc51u6I2XlUabSEJtjhE2RpYF1PXrGCBZh9rQNNRNYwotN0O3OgVOFBTnjCYDpAkAY6EaladZS8Nbgg1ELGz7H4ZDb3ILb47OikzsniacQy8La7ICARz/VotLkO5lJe1ClF8G6+32FLQpcY2PqJ8q4"; //对方的私钥 static String param = "body={\"businessId\":\"12345678942222222\",\"departureDistrict\":\"310100\",\"departureAddress\":\"徐家汇\",\"shipperName\":\"张晓龙\",\"shipperMobile\":\"13878993432\",\"arrivalDistrict\":\"320100\",\"arrivalAddress\":\"玄武湖\",\"consigneeName\":\"李四\",\"consigneeMobile\":\"13956625877\",\"cargoName\":\"钢材\",\"weight\":\"30\",\"volume\":\"20\",\"cargoType\":\"设备\",\"loadingType\":\"1\",\"truckModel\":\"平板车\",\"truckLength\":\"6.2\",\"cargoLength\":\"6.2\",\"estimateLoadingDate\":\"20170607\",\"estimateLoadingTime\":\"4\",\"freight\":\"2000.00\",\"requiringBail\":\"0\",\"remarks\":\"给承运商的留言\",\"contact\":\"王五\",\"contactMobile\":\"13634358766\",\"expiringTime\":\"72\"}&business=PUBLISHCARGO&id=7283978804797681&key=8TIxhQTA6NsjAUCdrMNckUjfDUbeSb4v"; static void testHttpSign1() throws Exception { System.out.println("原始数据:" + param); byte[] encodedData = RSAUtils.encryptByPublicKey(param.getBytes(), publicKey2); System.out.println("甲方用乙方的公钥加密后:" + encodedData); String base64Str= Base64Utils.encode(encodedData); System.out.println("甲方传送的加密后再进行Base64编码数据:"+base64Str); long start=System.currentTimeMillis(); String sign = RSAUtils.sign(encodedData, privateKey); System.out.println("甲方用自己的私钥签名后sign:" + sign); long end=System.currentTimeMillis(); System.out.println("签名耗时:"+(end-start)+"ms"); byte[] encodedData1 = Base64Utils.decode(base64Str); boolean status = RSAUtils.verify(encodedData1, publicKey, sign); System.out.println("乙方用甲方的公钥进行签名验证结果:" + status); byte [] decodeData1=RSAUtils.decryptByPrivateKey(encodedData1, privateKey2); System.out.println("乙方用自己的私钥解密后:" + new String(decodeData1)); } static void testHttpSign2() throws Exception { System.out.println("原始数据:" + param); byte[] encodedData = RSAUtils.encryptByPublicKey(param.getBytes("utf-8"), publicKey2); //System.out.println("甲方用乙方的公钥加密后:" + encodedData); //对传入的数据进行64进制转换,以字符串的形式进行传输 String base64Str= Base64Utils.encode(encodedData); System.out.println("甲方传送的数据:"+base64Str); //对传输的消息取摘要 String md5Str=MD5Util.MD5(param); //对消息取摘要 long start=System.currentTimeMillis(); //根据摘要生成签名;本来也是可以直接对加密后的消息生成签名,不过因为消息的的字节数过大生成签名耗时较长,2、消息可能被篡改 String sign = RSAUtils.sign(md5Str.getBytes("utf-8"), privateKey); System.out.println("甲方用自己的私钥签名后sign:" + sign); long end=System.currentTimeMillis(); System.out.println("签名耗时:"+(end-start)+"ms"); //乙方用自己的密钥进行解密 byte[] decodedData = RSAUtils.decryptByPrivateKey(Base64Utils.decode(base64Str), privateKey2); // System.out.println("乙方用自己的私钥解密后:" + decodedData); //获得接收到的字符串 String param1=new String(decodedData); System.out.println("乙方收到的数据:" + param1); //根据接收到的消息生成摘要 String md5Str1=MD5Util.MD5(param1); //根据摘要摘要进行签名验证 boolean status = RSAUtils.verify(md5Str1.getBytes("utf-8"), publicKey, sign); System.out.println("乙方用甲方的公钥进行签名验证结果:" + status); } public static void main(String[] args) { try { testHttpSign1(); testHttpSign2(); } catch (Exception e) { e.printStackTrace(); } } }
源代码下载