黑马程序员技术交流社区

标题: [黑马程序员杭州]Java中的数据传输安全策略 [打印本页]

作者: 小江哥    时间: 2017-11-12 23:17
标题: [黑马程序员杭州]Java中的数据传输安全策略
本帖最后由 小江哥 于 2017-11-12 23:17 编辑

1. 引言
1.0  在开发中面临的数据安全问题:
1.数据存储安全
2.数据传输安全(本节我们探究的内容)

1.1  密码学中的常见概念:
ü  明文:指待加密信息
ü  密文:指经过加密后的明文
ü  加签:主要用来验证数据有没有被篡改
ü  加盐:在明文中加入一些无关字符后再进行不可逆加密(例如MD5)
附:MD5加密后的数据是不可逆的,目前的解密策略都是基于碰撞机制进行,本质上并不算是真正的解密

1.2  加解密策略:
l  对称加密:加密和解密使用同一个秘钥
n  常见的对称加密算法:DES3DES、TDEA、Blowfish、RC2、RC4、RC5IDEA、SKIPJACK、AES
l  非对称加密:加密使用公钥(public key),解密使用私钥(private key),公钥和私钥是一对
n  常见的非对称加密算法:RSAElgamal、背包算法、Rabin、D-H、ECC(椭圆曲线加密算法)等
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无限制权限策略文件下载地址:
http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html
http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html

解锁限制:解压下载好的UnlimitedJCEPolicy文件,里面有两个jar包:
将这两个jar包拷贝到jdk/jre/lib/security文件夹下,覆盖原有文件即可

期待与同学们下次再见~

作者: fanbuer    时间: 2017-11-12 23:20
这更新速度,都赶上我的学习速度了……点赞
作者: gy_yt    时间: 2017-11-12 23:21
啸哥666,每日一学
作者: 小江哥    时间: 2017-11-12 23:21
fanbuer 发表于 2017-11-12 23:20
这更新速度,都赶上我的学习速度了……点赞

不进则退,紧跟时代步伐
作者: 咸鱼666    时间: 2017-11-12 23:22
牛,真是6
作者: 猫先森    时间: 2017-11-12 23:27
66666666666
作者: 许言    时间: 2017-11-12 23:35
顶顶顶顶顶
作者: wxz_hm    时间: 2017-11-12 23:37
厉害了。顶顶顶

作者: nhm    时间: 2017-11-13 15:32
大佬666666666
作者: 从此学好    时间: 2017-11-13 15:34
强无敌,每次都能学到东西,666




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2