第一次写文章,不知道怎么写,写的很差或者不对的地方请你们指出,本人确定会虚心接纳。前端
对于数据加密和为何须要数据加密,网上的文章一搜就一大片,以博主那三脚猫的知识就不和你们解释啦(😂🤣)。
进入正题,博主所在公司开发的项目,不管是app端仍是h5网页,和后端都以json数据交互,也就是常说的一套接口给全部前端使用,因此完全解放了博主写接口的压力,哈哈😆😆。 java
既然涉及数据交互,确定不能避免数据安全的问题,天然而然咱们就会想到对数据进行加密和解密。如今博主接触到的数加密和解密算法是对称算法(AES)和非对称算法(RSA),固然还有其余算法,但咱们也不须要研究太多,够用就行,开发不就是这样嘛😂😂。咱们传递的数据被非法用户窃取,若是使用AES算法,这个算法的优点就是加解密速度快,因为秘钥也保存在前端(若是是保存app,还须要费劲的破解app;但若是是保存在网页,这个博主不说你们也知道)基本没有安全保障,若是使用RSA算法,那基本安全无忧,但RSA算法对数据加密和解密很是的耗时,固然这个也受限于服务器哈,若是服务器性能很强,直接使用RSA对数据加密解密就行,若是服务器性能有限,能够继续看博主接下来的方式。web
若是咱们的AES秘钥不能或者很难被获取到,是否是就解决安全性的问题啦。那咱们如何来保证AES秘钥很难被获取呢?没错,用RSA来保证,首先AES秘钥都是随机的,而后咱们使用RSA算法,对AES的秘钥进行加密解密,这个其实就是数字信封技术,博主小小的卖弄一波哈😋😋。redis
画个图来解释咱们这个加密解密过程,帮助你们理解。算法
1:首先客户端存储着RSA公钥,就算非法用户拿到RSA公钥,他也很难根据公钥和加密数据进行解密,这个算法已经保证了,支付宝 也是使用RSA来对数据加密,因此你们能够放心使用RSA算法。 2:客户端随机生成AES的秘钥,这个秘钥长度必须是16的倍数,这个是通过项目检验获得验证,而后使用RSA公钥对AES产生的秘钥 进行加密发送给服务端。 3:服务端使用RSA秘钥对已通过RSA公钥加密的AES秘钥进行解密,获得AES的秘钥,保存起来;接下来比较重要的就是产生临时会 话号,为何须要这个会话号呢?由于咱们的场景下多是不须要用户登陆的,为了标识这个用户,咱们得产生一个会话号,这个 必须是保证惟一性,由于咱们得根据这个会话号去标识当前的请求用户,还有一个做用就是根据这个会话号来对应不一样 的AES秘钥。博主是将会话号保存在redis中,固然也有不少方案,这个看项目需求。 4:接下来就开始进行客户端和服务端数据交互,固然若是咱们在未登陆状况下,注意客户端得传递临时会话号,不然服务端找不到 AES秘钥就没办法对数据解密和加密 说了那么多理论的东西,是否是有些急不可待看看怎么取实现了呐。废话很少说,上代码。
RSAHelper.javaspring
import java.io.IOException; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import org.apache.commons.codec.binary.Base64; import org.apache.commons.io.output.ByteArrayOutputStream; import org.junit.Test; import org.springframework.util.Base64Utils; public class RSAHelper { public static final String PUBLIC_KEY_VALUES = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCgl0qiFQP8P/Wv5ZXRe78wxU3h7f/5xkh0ChiJ0f4Md0KZv0cSOzBGptNC41tL6cg1qKNPcEuJH296jet/T0Q+cD2tuofHtnB4ghhhCVHT8gkxhV+DJ61BquUIRrhJwj8jU3pw/klY+gIiiOfJFz2Hpar6Lyp+KCTZTyljg0vKPQIDAQAB"; public static final String PRIVATE_KEY_VALUES = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAKCXSqIVA/w/9a/lldF7vzDFTeHt//nGSHQKGInR/gx3Qpm/RxI7MEam00LjW0vpyDWoo09wS4kfb3qN639PRD5wPa26h8e2cHiCGGEJUdPyCTGFX4MnrUGq5QhGuEnCPyNTenD+SVj6AiKI58kXPYelqvovKn4oJNlPKWODS8o9AgMBAAECgYEAgmW2vVtxK/9HYPd8Kmhf+5sKPX0Cz+8YX9jeyfIQZlDkbHErpXsYHRZTDsoMFN0Uq7VuPg/B1esHmyzn3y0fDIU1MYcrxBGtKWm1AxwpTcCafsQ3VakkMRkTD8QNZXqcyctB4aRIVWq3p3556zHEIwyxUMCr4UDUKjilJBi/q2ECQQDf1N4Gi1eWnkFuVjD6mJ6bfGJ04l8DajIdt/N/a/J4O1TlPMnaBons5cg/evatsiZvRHYsXeoWygaJhCcoap8JAkEAt6uzQXxfM/oNSZj5voIgZc2xhMWej7bAYYS6Q9/n5KNkybtWSHYe8lbJc4Q4/eYzinDwMYRxVNFR0WuUwafqlQJAPjlkG7ei+tk14WGOriu9dAYpLMs9lKpyEjbwN00gE/KSkEPM7ZKBx1y9xX/+kZ0D+Ey0+XKGQB2boaEebaruWQJAXHeivVtCCsbenajYQuL8MISH1JIxK6UT4YSSyc0Vz/O6sB0SaVSea97peLCeiKS2WgJVynglHlBrYoVI1N4WqQJBAJt92JHPMlvsKcmeVgQS5LmobsYc5EYUQ0sHUpvQJ26/hh2pDBKxYmyhl1TNuEVfsjuvDbdfPzn9MMqDt5RPbcc="; /** * RSA密钥长度必须是64的倍数,在512~65536之间。默认是1024 */ public static final int KEY_SIZE = 1024; /** * 生成公钥、私钥对(keysize=1024) */ public static RSAHelper.KeyPairInfo getKeyPair() { return getKeyPair(KEY_SIZE); } /** * 生成公钥、私钥对 * * @param keySize * @return */ public static RSAHelper.KeyPairInfo getKeyPair(int keySize) { try { KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA"); keyPairGen.initialize(keySize); // 生成一个密钥对,保存在keyPair中 KeyPair keyPair = keyPairGen.generateKeyPair(); // 获得私钥 RSAPrivateKey oraprivateKey = (RSAPrivateKey) keyPair.getPrivate(); // 获得公钥 RSAPublicKey orapublicKey = (RSAPublicKey) keyPair.getPublic(); RSAHelper.KeyPairInfo pairInfo = new RSAHelper.KeyPairInfo(keySize); //公钥 byte[] publicKeybyte = orapublicKey.getEncoded(); String publicKeyString = Base64.encodeBase64String(publicKeybyte);//Base64.encode(publicKeybyte); pairInfo.setPublicKey(publicKeyString); //私钥 byte[] privateKeybyte = oraprivateKey.getEncoded(); String privateKeyString = Base64.encodeBase64String(privateKeybyte);//Base64.encode(privateKeybyte); pairInfo.setPrivateKey(privateKeyString); return pairInfo; } catch (Exception e) { e.printStackTrace(); return null; } } /** * 获取公钥对象 * * @param publicKeyBase64 * @return * @throws InvalidKeySpecException * @throws NoSuchAlgorithmException */ public static PublicKey getPublicKey(String publicKeyBase64) throws InvalidKeySpecException, NoSuchAlgorithmException { KeyFactory keyFactory = KeyFactory.getInstance("RSA"); X509EncodedKeySpec publicpkcs8KeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyBase64));//Base64.decode(publicKeyBase64) PublicKey publicKey = keyFactory.generatePublic(publicpkcs8KeySpec); return publicKey; } /** * 获取私钥对象 * * @param privateKeyBase64 * @return * @throws NoSuchAlgorithmException * @throws InvalidKeySpecException */ public static PrivateKey getPrivateKey(String privateKeyBase64) throws NoSuchAlgorithmException, InvalidKeySpecException { KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PKCS8EncodedKeySpec privatekcs8KeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyBase64));//Base64.decode(privateKeyBase64) PrivateKey privateKey = keyFactory.generatePrivate(privatekcs8KeySpec); return privateKey; } /** * 使用公钥加密 * * @param content 待加密内容 * @param publicKeyBase64 公钥 base64 编码 * @return 通过 base64 编码后的字符串 */ public static String encipher(String content, String publicKeyBase64) { return encipher(content, publicKeyBase64, KEY_SIZE / 8 - 11); } /** * 使用公司钥加密(分段加密) * * @param content 待加密内容 * @param publicKeyBase64 公钥 base64 编码 * @param segmentSize 分段大小,通常小于 keySize/8(段小于等于0时,将不使用分段加密) * @return 通过 base64 编码后的字符串 */ public static String encipher(String content, String publicKeyBase64, int segmentSize) { try { PublicKey publicKey = getPublicKey(publicKeyBase64); return encipher(content, publicKey, segmentSize); } catch (Exception e) { e.printStackTrace(); return null; } } /** * 分段加密 * * @param ciphertext 密文 * @param key 加密秘钥 * @param segmentSize 分段大小,<=0 不分段 * @return */ public static String encipher(String ciphertext, java.security.Key key, int segmentSize) { try { // 用公钥加密 byte[] srcBytes = ciphertext.getBytes(); // Cipher负责完成加密或解密工做,基于RSA Cipher cipher = Cipher.getInstance("RSA"); // 根据公钥,对Cipher对象进行初始化 cipher.init(Cipher.ENCRYPT_MODE, key); byte[] resultBytes = null; if (segmentSize > 0) resultBytes = cipherDoFinal(cipher, srcBytes, segmentSize); //分段加密 else resultBytes = cipher.doFinal(srcBytes); String base64Str = Base64Utils.encodeToString(resultBytes); return base64Str; } catch (Exception e) { e.printStackTrace(); return null; } } /** * 分段大小 * * @param cipher * @param srcBytes * @param segmentSize * @return * @throws IllegalBlockSizeException * @throws BadPaddingException * @throws IOException */ public static byte[] cipherDoFinal(Cipher cipher, byte[] srcBytes, int segmentSize) throws IllegalBlockSizeException, BadPaddingException, IOException { if (segmentSize <= 0) throw new RuntimeException("分段大小必须大于0"); ByteArrayOutputStream out = new ByteArrayOutputStream(); int inputLen = srcBytes.length; int offSet = 0; byte[] cache; int i = 0; // 对数据分段解密 while (inputLen - offSet > 0) { if (inputLen - offSet > segmentSize) { cache = cipher.doFinal(srcBytes, offSet, segmentSize); } else { cache = cipher.doFinal(srcBytes, offSet, inputLen - offSet); } out.write(cache, 0, cache.length); i++; offSet = i * segmentSize; } byte[] data = out.toByteArray(); out.close(); return data; } /** * 使用私钥解密 * * @param contentBase64 待加密内容,base64 编码 * @param privateKeyBase64 私钥 base64 编码 * @return * @segmentSize 分段大小 */ public static String decipher(String contentBase64, String privateKeyBase64) { return decipher(contentBase64, privateKeyBase64, KEY_SIZE / 8); } /** * 使用私钥解密(分段解密) * * @param contentBase64 待加密内容,base64 编码 * @param privateKeyBase64 私钥 base64 编码 * @return * @segmentSize 分段大小 */ public static String decipher(String contentBase64, String privateKeyBase64, int segmentSize) { try { PrivateKey privateKey = getPrivateKey(privateKeyBase64); return decipher(contentBase64, privateKey, segmentSize); } catch (Exception e) { e.printStackTrace(); return null; } } /** * 分段解密 * * @param contentBase64 密文 * @param key 解密秘钥 * @param segmentSize 分段大小(小于等于0不分段) * @return */ public static String decipher(String contentBase64, java.security.Key key, int segmentSize) { try { // 用私钥解密 byte[] srcBytes = Base64Utils.decodeFromString(contentBase64); // Cipher负责完成加密或解密工做,基于RSA Cipher deCipher = Cipher.getInstance("RSA"); // 根据公钥,对Cipher对象进行初始化 deCipher.init(Cipher.DECRYPT_MODE, key); byte[] decBytes = null;//deCipher.doFinal(srcBytes); if (segmentSize > 0) decBytes = cipherDoFinal(deCipher, srcBytes, segmentSize); //分段加密 else decBytes = deCipher.doFinal(srcBytes); String decrytStr = new String(decBytes); return decrytStr; } catch (Exception e) { e.printStackTrace(); return null; } } /** * 秘钥对 */ public static class KeyPairInfo { String privateKey; String publicKey; int keySize = 0; public KeyPairInfo(int keySize) { setKeySize(keySize); } public KeyPairInfo(String publicKey, String privateKey) { setPrivateKey(privateKey); setPublicKey(publicKey); } public String getPrivateKey() { return privateKey; } public void setPrivateKey(String privateKey) { this.privateKey = privateKey; } public String getPublicKey() { return publicKey; } public void setPublicKey(String publicKey) { this.publicKey = publicKey; } public int getKeySize() { return keySize; } public void setKeySize(int keySize) { this.keySize = keySize; } } public static void main(String[] args) { System.out.println("第一步:客户端和服务端进行密码握手通讯,确保密码的安全性"); System.out.println(" 1.客户端请求RSA公钥"); System.out.println(" 2.服务端开始颁发公钥给客户端"); KeyPairInfo keyPairInfo = RSAHelper.getKeyPair(); System.out.println(" 3.服务端返回给客户端RSA公钥:"+keyPairInfo.getPublicKey()); System.out.println(" 4.服务端本身保存RSA公钥和RSA私钥:公钥:"+keyPairInfo.getPublicKey()+";私钥"+keyPairInfo.getPrivateKey()); String AESPassword = "1234567890123456"; System.out.println(" 5.客户端产生RES的密码:"+AESPassword); String AESPasswordEncipher = RSAHelper.encipher(AESPassword, keyPairInfo.getPublicKey()); System.out.println(" 6.客户端使用RSA的公钥加密第5步中的产生的RES密码:"+AESPasswordEncipher); System.out.println(" 7.客户端发送使用RSA公钥加密的AES密码,服务端接收到密码:"+AESPasswordEncipher); String AESPasswordDecipher = RSAHelper.decipher(AESPasswordEncipher, keyPairInfo.getPrivateKey()); System.out.println(" 8.服务端使用RSA私钥解密密码(已经通过RSA加密的AES的密码):"+AESPasswordDecipher); System.out.println("第二步:开始数据交互"); String inputStr = "{\"token\":\"ippay_bf0eb479e1b6cfe7cfe609ebf52c5328\",\"info\":[{\"an\":\"2017114256712\",\"sqr\":[\"横琴国际知识产权交易中心有限公司\"],\"ti\":\"一种视频编码方法及装置\",\"jiaofei\":[{\"name\":\"印花税\",\"amount\":\"5\"}]}],\"linkMan\":{\"userName\":\"毛雨田\",\"userTel\":\"13718881828\",\"userEmail\":\"layn@live.com\"},\"reimburseInfo\":{\"isReimburse\":\"2\",\"type\":\"1\",\"title\":\"北京航空航天大学\",\"taxNumber\":\"\",\"mailType\":\"1\",\"mailUserName\":\"毛雨田\",\"mailUserTel\":\"13718881828\",\"mailAddr\":\"北京\",\"deliveryType\":\"1\"},\"device\":\"1\"}"; System.out.println(" 1.未使用AES加密前的数据:"+inputStr); try { String inputStrEncrypt = AESUtil.aesEncrypt(inputStr, AESPassword); System.out.println(" 1.客户端使用第一步中产生的AES密码对数据加密并发送给服务端:"+inputStrEncrypt); System.out.println(" 2.服务端接收到客户端发送过来的加密数据:"+inputStrEncrypt); String inputStrDecrypt = AESUtil.aesDecrypt(inputStrEncrypt, AESPassword); System.out.println(" 3.服务端使用第一步中解密后的密码解密数据:"+inputStrDecrypt); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
AESUtil.javaapache
import java.math.BigInteger; import java.util.Map; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.spec.SecretKeySpec; import org.apache.commons.codec.binary.Base64; import org.apache.commons.lang3.StringUtils; import org.apache.log4j.Logger; import org.springframework.util.Base64Utils; import com.fasterxml.jackson.databind.ObjectMapper; public class AESUtil { public static String AES_KEY = "MTIzNDU2Nzg5MDk4NzY1NA=="; // 算法 private static final String ALGORITHMSTR = "AES/ECB/PKCS5Padding"; private static ObjectMapper objectMapper = new ObjectMapper(); public static Logger logger = Logger.getLogger(AESUtil.class); /** * aes解密 * * @param encrypt * 内容 * @return * @throws Exception */ public static String aesDecrypt(String encrypt) { try { return aesDecrypt(encrypt, AES_KEY); } catch (Exception e) { e.printStackTrace(); return ""; } } /** * aes加密 * * @param content * @return * @throws Exception */ public static String aesEncrypt(String content) { try { return aesEncrypt(content, AES_KEY); } catch (Exception e) { e.printStackTrace(); return ""; } } /** * 将byte[]转为各类进制的字符串 * * @param bytes * byte[] * @param radix * 能够转换进制的范围,从Character.MIN_RADIX到Character.MAX_RADIX,超出范围后变为10进制 * @return 转换后的字符串 */ public static String binary(byte[] bytes, int radix) { return new BigInteger(1, bytes).toString(radix);// 这里的1表明正数 } /** * base 64 encode * * @param bytes * 待编码的byte[] * @return 编码后的base 64 code */ public static String base64Encode(byte[] bytes) { return Base64.encodeBase64String(bytes); } /** * base 64 decode * * @param base64Code * 待解码的base 64 code * @return 解码后的byte[] * @throws Exception */ public static byte[] base64Decode(String base64Code) throws Exception { return StringUtils.isEmpty(base64Code) ? null : Base64Utils.decodeFromString(base64Code); } /** * AES加密 * * @param content * 待加密的内容 * @param encryptKey * 加密密钥 * @return 加密后的byte[] * @throws Exception */ public static byte[] aesEncryptToBytes(String content, String encryptKey) throws Exception { KeyGenerator kgen = KeyGenerator.getInstance("AES"); kgen.init(128); Cipher cipher = Cipher.getInstance(ALGORITHMSTR); cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(encryptKey.getBytes(), "AES")); return cipher.doFinal(content.getBytes("utf-8")); } /** * AES加密为base 64 code * * @param content * 待加密的内容 * @param encryptKey * 加密密钥 * @return 加密后的base 64 code * @throws Exception */ public static String aesEncrypt(String content, String encryptKey) throws Exception { return base64Encode(aesEncryptToBytes(content, encryptKey)); } /** * AES解密 * * @param encryptBytes * 待解密的byte[] * @param decryptKey * 解密密钥 * @return 解密后的String * @throws Exception */ public static String aesDecryptByBytes(byte[] encryptBytes, String decryptKey) throws Exception { KeyGenerator kgen = KeyGenerator.getInstance("AES"); kgen.init(128); Cipher cipher = Cipher.getInstance(ALGORITHMSTR); cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(decryptKey.getBytes(), "AES")); byte[] decryptBytes = cipher.doFinal(encryptBytes); return new String(decryptBytes); } /** * 将base 64 code AES解密 * * @param encryptStr * 待解密的base 64 code * @param decryptKey * 解密密钥 * @return 解密后的string * @throws Exception */ public static String aesDecrypt(String encryptStr, String decryptKey) throws Exception { return StringUtils.isEmpty(encryptStr) ? null : aesDecryptByBytes(base64Decode(encryptStr), decryptKey); } @SuppressWarnings("unchecked") public static Map<String, Object> aesDecrypt2Map(String encryptStr, String decryptKey){ if(StringUtils.isBlank(decryptKey) || decryptKey.length() != 16){ return null; } try { String json = aesDecrypt(encryptStr, decryptKey); return objectMapper.readValue(json, Map.class); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } public static void main(String[] args) throws Exception { String content = "kkkkkkk"; System.out.println("加密前:" + content); System.out.println("加密密钥和解密密钥:" + AES_KEY); String encrypt = aesEncrypt(content, AES_KEY); System.out.println("加密后:" + encrypt); String decrypt = aesDecrypt(encrypt, AES_KEY); System.out.println("解密后:" + decrypt); // js加密后的字符串: lkqsgKHH7OkhIa0tISMtuQ== //String jsData = aesDecrypt("lkqsgKHH7OkhIa0tISMtuQ==", AES_KEY); //System.out.println("前端数据解密后的值:" + jsData); } }
EncryptController.javajson
import java.util.HashMap; import java.util.Map; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import com.smartsport.common.cache.CacheService; import com.smartsport.common.cache.constant.CacheConstant; import com.smartsport.common.model.ReturnFormatModel; import com.smartsport.common.utils.RSAHelper; import com.smartsport.common.utils.RandomStringUtil; @Controller @RequestMapping({ "/encrypt" }) public class EncryptController { @Autowired @Qualifier("RedisCacheServiceImpl") private CacheService cacheService; @RequestMapping("/disposable") @ResponseBody public Map<String, Object> disposable(@RequestBody Map<String, String> jsonMap){ String aesPasswordEncrypt = jsonMap.get("disposable"); System.out.println(aesPasswordEncrypt); String aesPasswordDecipher = RSAHelper.decipher(aesPasswordEncrypt, RSAHelper.PRIVATE_KEY_VALUES); String signal = RandomStringUtil.generateString(16) + cacheService.generate(CacheConstant.prefix+"aes_disposable"); System.out.println("signal:"+signal); cacheService.put(CacheConstant.prefix+"aes_" + signal, aesPasswordDecipher, CacheConstant.AES_DISPOSABLE_TIMEOUT, CacheConstant.AES_DISPOSABLE_UNIT); Map<String, Object> resultMap= new HashMap<String, Object>(); resultMap.put("signal", signal); return ReturnFormatModel.retParam("1", resultMap); } }