本帖最后由 小江哥 于 2017-11-12 23:17 编辑
1. 引言 1.0 在开发中面临的数据安全问题: 1.数据存储安全 2.数据传输安全(本节我们探究的内容)
1.1 密码学中的常见概念: ü 明文:指待加密信息 ü 密文:指经过加密后的明文 ü 加签:主要用来验证数据有没有被篡改 ü 加盐:在明文中加入一些无关字符后再进行不可逆加密(例如MD5) 附:MD5加密后的数据是不可逆的,目前的解密策略都是基于碰撞机制进行,本质上并不算是真正的解密
1.2 加解密策略: l 对称加密:加密和解密使用同一个秘钥 l 非对称加密:加密使用公钥(public key),解密使用私钥(private key),公钥和私钥是一对 2. 对称加密—AES 对称加密是最快速、最简单的一种加密方式,加密(encryption)与解密(decryption)用的是同样的密钥(secret key)。 对称加密通常使用的是相对较小的密钥,一般小于256 bit。因为密钥越大,加密越强,但加密与解密的过程越慢。如果你只用1 bit来做这个密钥,那黑客们可以先试着用0来解密,不行的话就再用1解;但如果你的密钥有1 MB大,黑客们可能永远也无法破解,但加密和解密的过程要花费很长的时间。 3. 非对称加密—RSA 非对称加密为数据的加密与解密提供了一个非常安全的方法,它使用了一对密钥,公钥(public key)和私钥(private key)。私钥只能由一方安全保管,不能外泄,而公钥则可以发给任何请求它的人。非对称加密使用这对密钥中的一个进行加密,而解密则需要另一个密钥。比如,你向银行请求公钥,银行将公钥发给你,你使用公钥对消息加密,那么只有私钥的持有人--银行才能对你的消息解密。与对称加密不同的是,银行不需要将私钥通过网络发送出去,因此安全性大大提高。 4. 总结 a. 对称加密与解密使用的是同样的密钥,所以速度快,但由于需要将密钥在网络传输,所以安全性不高。 b. 非对称加密使用了一对密钥,公钥与私钥,所以安全性高,但加密与解密速度慢。 c. 解决的办法是将对称加密的密钥使用非对称加密的公钥进行加密,然后发送出去,接收方使用私钥进行解密得到对称加密的密钥,然后双方可以使用对称加密来进行沟通。 5. 个人建议 Ø 信息量较少的数据使用RSA Ø 信息量较大的数据使用AES 6. 案例代码 Maven依赖: <dependencies> <!-- security --> <dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> <version>1.6</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.1</version> </dependency> <dependency> <groupId>org.cryptonode.jncryptor</groupId> <artifactId>jncryptor</artifactId> <version>1.2.0</version> </dependency> <!-- CryptoException--> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcmail-jdk14</artifactId> <version>1.51</version> </dependency> </dependencies> AES工具类: public class AESUtil { public static JNCryptor cryptor = newAES256JNCryptor(100); /** * 加密 * * @param plaintext * @param password * @return * @throwsCryptoException */ public static StringencryptData(String plaintext, String password) throwsCryptoException { byte[] ciphertext = null; try { ciphertext = cryptor.encryptData(plaintext.getBytes(),password.toCharArray()); } catch(CryptorException e) { throw newCryptoException("Failed to encrypt data by AES", e); } return Hex.encodeHexString(ciphertext); } /** * 解密 * * @param data * @param password * @return * @throwsCryptoException */ public static StringdecryptData(String data, String password) throwsCryptoException { if(StringUtils.isEmpty(data)) { throw newCryptoException("Decryption error", newIllegalArgumentException("Data is empty")); } if(StringUtils.isEmpty(password)) { throw newCryptoException("Decryption error", newIllegalArgumentException("Key is empty")); } byte[] plaintext = null; try { plaintext = cryptor.decryptData(Hex.decodeHex(data.toCharArray()),password.toCharArray()); } catch(DecoderException e) { throw newCryptoException("Decryption error", e); } catch(CryptorException e) { throw newCryptoException("Decryption error", e); } return new String(plaintext); } public static voidmain(String[] args) throwsDecoderException { // 1.定义原始数据 byte[] plaintext = ("欢迎来到黑马程序员!").getBytes(); // 2.定义key String password = "CdFzeUoOvGptXwkR"; try { // 3.加密数据 byte[] ciphertext = cryptor.encryptData(plaintext, password.toCharArray()); String cString = Hex.encodeHexString(ciphertext); // 4.打印加密后的数据 System.out.println(cString); // 5.解密数据 byte[] s = cryptor.decryptData(Hex.decodeHex(cString.toCharArray()),password.toCharArray()); // 6.打印解密后的数据 System.out.println(new String(s)); } catch(CryptorException e) { e.printStackTrace(); } } } RSA工具类: public class RSAUtil { // String to hold name of the encryption algorithm. public static final String ALGORITHM = "RSA"; // String to hold name of the public key file. private static String PUBLIC_KEY_FILE = "public.txt"; // String to hold name of the public key file. private static String PRIVATE_KEY_FILE = "private.txt"; public static PublicKey publicKey; public static PrivateKey privateKey; /** * Encrypt the plain text using public key. * * @param text * @param key * @return * @throws CryptoException */ public static byte[]encrypt(String text, PublicKey key) throws CryptoException { byte[] cipherText = null; try { // get an RSA cipher object final Cipher cipher = Cipher.getInstance(ALGORITHM); // encrypt the text using the public key cipher.init(Cipher.ENCRYPT_MODE, key); cipherText = cipher.doFinal(text.getBytes()); }catch (Exception e) { throw newCryptoException("Failed to encrypt data byRSA", e); } return cipherText; } /** * Decrypt text using private key. * * @param text * @param key * @return * @throws CryptoException */ public static Stringdecrypt(byte[] text, PrivateKey key) throws CryptoException { byte[] dectyptedText = null; try { // get an RSA cipher object final Cipher cipher = Cipher.getInstance(ALGORITHM); // decrypt the text using the private key cipher.init(Cipher.DECRYPT_MODE, key); dectyptedText = cipher.doFinal(text); }catch (Exception e) { throw newCryptoException("Failed to decrypt data byRSA", e); } return new String(dectyptedText); } public static void init() throws IOException { InputStreaminputStream01 = RSAUtil.class.getClassLoader().getResourceAsStream(PUBLIC_KEY_FILE); ObjectInputStreampublicIns = null; InputStreaminputStream02 = RSAUtil.class.getClassLoader().getResourceAsStream(PRIVATE_KEY_FILE); ObjectInputStreamprivateIns = null; try { publicIns = newObjectInputStream(inputStream01); privateIns = newObjectInputStream(inputStream02); publicKey =(PublicKey) publicIns.readObject(); privateKey =(PrivateKey) privateIns.readObject(); }catch (IOException e) { System.out.println(e.getMessage()); }catch (ClassNotFoundException f) { System.out.println(f.getMessage()); }finally { privateIns.close(); publicIns.close(); } } public static void main(String[] args) throws IOException { // 1.加载公钥,私钥文件 init(); // 2.定义原始内容 final String originalText = "woaiheimawoaijava"; byte[] encryptText = null; StringdecryptText = null; try { // 3.加密数据 encryptText = encrypt(originalText, publicKey); // 4.解密数据 decryptText = decrypt(encryptText, privateKey); }catch (CryptoException e) { e.printStackTrace(); } // 5.打印原始数据,加密后的数据,解密后的数据 System.out.println("Original: " + originalText); System.out.println("Encrypted: " + encryptText); System.out.println("Decrypted: " + decryptText); } } 注意: 在使用AES工具类的时候,会出现如下错误: org.cryptonode.jncryptor.CryptorException: Caught InvalidKeyException. Do you have unlimited strength jurisdictionfiles installed? atorg.cryptonode.jncryptor.AES256JNCryptor.encryptData(AES256JNCryptor.java:494) atorg.cryptonode.jncryptor.AES256JNCryptor.encryptData(AES256JNCryptor.java:345) atorg.cryptonode.jncryptor.AES256JNCryptor.encryptData(AES256JNCryptor.java:361) atcom.itheima.security.AESUtil.main(AESUtil.java:20) Caused by: java.security.InvalidKeyException: Illegal key size atjavax.crypto.Cipher.checkCryptoPerm(Cipher.java:1024) atjavax.crypto.Cipher.implInit(Cipher.java:790) atjavax.crypto.Cipher.chooseProvider(Cipher.java:849) atjavax.crypto.Cipher.init(Cipher.java:1348) atjavax.crypto.Cipher.init(Cipher.java:1282) atorg.cryptonode.jncryptor.AES256JNCryptor.encryptData(AES256JNCryptor.java:481) ...3 more 问题由来: 因为密钥长度是受限制的,不能超过128位,而我们使用的是256位,java运行时环境读到的是受限的policy文件,文件位于${java_home}/jre/lib/security下,这种限制是由于美国对出口软件的控制
解决方案: 在JDK官网下载JCE无限制权限策略文件,下列分别为JDK7和JDK8的JCE无限制权限策略文件下载地址:
解锁限制:解压下载好的UnlimitedJCEPolicy文件,里面有两个jar包: 将这两个jar包拷贝到jdk/jre/lib/security文件夹下,覆盖原有文件即可
期待与同学们下次再见~
|