个人笔记
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

19 KiB

#Java安全 ##为什么要掌握Java安全?

  • 提升逼格,现在市场上的大部分程序员都对Java安全、数据加密等等一无所知,而现在的面试中很多面试官为了凸显自己的高逼格,总爱问你一些安全相关的问题。只有掌握了Java安全相关知识,才能跟面试官站在同一逼格线,进行愉快滴交流。
  • 项目中到处需要。如:账号密码的加密,订单支付的加密,个人资料的加密等等。
  • 初级工程师向中高级工程师进阶的必经之路。 ##什么是加密?
  • 古老的加密方法:《潜伏》中的余则成,收到密码:12 25 13 15 29 19;《模仿游戏》,二战时期,德国的密码机及图灵为了破解德军密码而研制出第一代计算机(图灵机)。
  • 对称加密和非对称加密等等。
  • MD5,属于消息摘要(Message Digest)。

学习目标

  1. 复习字节与字符、字符编码、进制转换、java里的io
  2. 一些基本的安全知识
  3. 三大风险:窃听风险、篡改风险、冒充风险
  4. 重放攻击(wpe)
  5. 常用加密算法(对称/非对称)
  6. 消息摘要
  7. 数字签名
  8. 数字证书
  9. Keytool工具的使用
  10. SSL/TLS的工作原理
  11. Https双向认证原理

#凯撒密码 ##第一个案例,对字符进行简单的加密。

  • 对字符数组中所有的字符做+1处理(后面可以转换成+num这个数由通讯双方来协定) ###代码

    /**

    • @author Camille *对字符简单的加密 */ public class SimpleEncryptionDemo01 {

      public static void main(String[] args) { // 确定加密的内容 String content = "i love you tonight 404 see you z"; //加密 String encryptData = encrypt(content); System.out.println("加密后:" + encryptData); //解密 String decryptData = decrypt(encryptData); System.out.println("解密后:" + decryptData); }

      /**

      • 加密方法
      • @param content
      • @return */ public static String encrypt(String content) { // 将内容转换成字符数组 char[] charArray = content.toCharArray(); // 遍历出所有字符 for (int i = 0; i < charArray.length; i++) { // 对所有字符进行 + 1操作 charArray[i] = (char) (charArray[i] + 1); } return new String(charArray); }

      /**

      • 解密方法
      • @param encryptData
      • @return */ public static String decrypt(String encryptData) { // 将内容转换成字符数组 char[] charArray = encryptData.toCharArray(); System.out.println(new String(charArray)); // 解密 for (int i = 0; i < charArray.length; i++) { charArray[i] = (char) (charArray[i] - 1); } return new String(charArray); } }

##第二个案例,对字节进行简单的加密。

字节和字符的区别?

  • UTF-8编码下,一个英文或数字字符就是一个字节
  • UTF-8编码下,一个汉字代表三个字节。
  • 为什么new String(bytes)的时候回发生乱码,就是因为加密后得到的字节找不到对应的字符。
  • Base64的原理

###代码

/**
 * @author Camille
 *对字节进行加密
 */
public class SimpleEncryptionDemo2 {

	public static void main(String[] args) {
        // 测试字节字符
        String content = "中文";
        int key = 80;
        String encryptData = encrypt(content,key);
        System.out.println("+密后:" + encryptData);
        String decryptData = decrypt(encryptData,key);
        System.out.println("解密后:" + decryptData);
	}

	/**
     * 加密
     * 
     * @param content
     * @return
     */
	public static String encrypt(String content,int key) {
        // 第一步,转换成字节数组
        byte[] bytes = content.getBytes();
        System.out.println("打印未加密的字节数组:");
        Util.printBytes(bytes);
        System.out.println("_________________________________________");
        // 第二步,遍历出所有字节,并做+1处理
        for (int i = 0; i < bytes.length; i++) {
            bytes[i] = (byte) (bytes[i] + key);
        }
        System.out.println("打印加密的字节数组:");
        Util.printBytes(bytes);
        System.out.println("_________________________________________");
//		return new String(bytes);
        return Base64.getEncoder().encodeToString(bytes);
	}

	/**
     * 解密
     * @param encryptData
     * @return
     */
	public static String decrypt(String encryptData,int key) {
        // 第一步,转换成字节数组,加密时使用Base64编码,那解密时也要使用Base64解码
        byte[] bytes = Base64.getDecoder().decode(encryptData);
//		byte[] bytes = encryptData.getBytes();
        System.out.println("打印经过两次转换后的字节数组:");
        Util.printBytes(bytes);
        System.out.println("_________________________________________");
        // 解密
        for (int i = 0; i < bytes.length; i++) {
            bytes[i] = (byte) (bytes[i] - key);
        }
        System.out.println("打印解密后的字节数组:");
        Util.printBytes(bytes);
        System.out.println("_________________________________________");
        return new String(bytes);
	}
}

##对称加密与非对称加密

  • 对称加密与非对称加密的区别
    • 对称加密。同一把钥匙进行加密和解密
    • 非对称加密,公钥加密,私钥解密。

##对称加密的案例 常见算法:AES、DES、3DES、TDEA、Blowfish、RC2、RC4、RC5、IDEA、SKIPJACK
AES:高级加密标准(Advanced Encryption Standard)
DES:数据加密标准(Data Encryption Standard) ###对称加密第一个案例,代码,。 public class SymmetricalEncryptionDemo01 {

    public static void main(String[] args) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
            IllegalBlockSizeException, BadPaddingException {
        // 声明要加密的内容
        String content = "今晚404,不见不散";
        //生成key
        SecretKey key = createKey();
        //加密
        String encryptData = encrypt(content, key);
        System.out.println(encryptData);
        //解密
        String decryptData = decrypt(encryptData, key);
        System.out.println(decryptData);
	}

	/**
     * 生成key
     * @return
     * @throws NoSuchAlgorithmException
     */
	public static SecretKey createKey() throws NoSuchAlgorithmException {
        // 生成key
        KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
        SecretKey secretKey = keyGenerator.generateKey();
        return secretKey;
	}

	/**
     * 加密方法
     * 
     * @param content
     * @return
     * @throws InvalidKeyException
     * @throws NoSuchPaddingException
     * @throws NoSuchAlgorithmException
     * @throws BadPaddingException
     * @throws IllegalBlockSizeException
     */
	public static String encrypt(String content, SecretKey key) throws InvalidKeyException, NoSuchAlgorithmException,
            NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
        // 获取cipher单例对象
        Cipher cipher = Cipher.getInstance("AES");
        // 初始化cipher,设置为加密模式
        cipher.init(Cipher.ENCRYPT_MODE, key);
        // 开始加密
        byte[] encryptBytes = cipher.doFinal(content.getBytes());
        return Base64.getEncoder().encodeToString(encryptBytes);
	}

	/**
     * @param encryptData
     * @return
     */
	public static String decrypt(String encryptData, SecretKey key) throws InvalidKeyException,
            NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
        byte[] encryptBytes = Base64.getDecoder().decode(encryptData);
        // 获取cipher单例对象
        Cipher cipher = Cipher.getInstance("AES");
        // 解密
        cipher.init(Cipher.DECRYPT_MODE, key);
        byte[] decryptBytes = cipher.doFinal(encryptBytes);
        return new String(decryptBytes);
	}
}

###对称加密的第二个案例,将生成的key保存到本地,然后解密时和下一次加密时就只需要到本地读取即可。。

/**
 * @author Camille
 *保存key到本地
 */
public class SymmetricalEncryptionDemo02 {

	public static void main(String[] args) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
            IllegalBlockSizeException, BadPaddingException, IOException, ClassNotFoundException {
        // 声明要加密的内容
        String content = "今晚404,不见不散";
        //第一次生成key,并序列化到本地
//		SecretKey key = createKey();
//		SerializableUtil.saveObject2File("heima.key", key);
        //从文件中读取key
        SecretKey key = (SecretKey) SerializableUtil.readObjectFromFile("heima.key");
        System.out.println(key);
        //加密
        String encryptData = encrypt(content, key);
        System.out.println(encryptData);
        //解密
        String decryptData = decrypt(encryptData, key);
        System.out.println(decryptData);
	}

	/**
     * 生成key
     * @return
     * @throws NoSuchAlgorithmException
     */
	public static SecretKey createKey() throws NoSuchAlgorithmException {
        // 生成key
        KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
        SecretKey secretKey = keyGenerator.generateKey();
        return secretKey;
	}

	/**
     * 加密方法
     * 
     * @param content
     * @return
     * @throws InvalidKeyException
     * @throws NoSuchPaddingException
     * @throws NoSuchAlgorithmException
     * @throws BadPaddingException
     * @throws IllegalBlockSizeException
     */
	public static String encrypt(String content, SecretKey key) throws InvalidKeyException, NoSuchAlgorithmException,
            NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
        // 获取cipher单例对象
        Cipher cipher = Cipher.getInstance("AES");
        // 初始化cipher,设置为加密模式
        cipher.init(Cipher.ENCRYPT_MODE, key);
        // 开始加密
        byte[] encryptBytes = cipher.doFinal(content.getBytes());
        return Base64.getEncoder().encodeToString(encryptBytes);
	}

	/**
     * @param encryptData
     * @return
     */
	public static String decrypt(String encryptData, SecretKey key) throws InvalidKeyException,
            NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
        byte[] encryptBytes = Base64.getDecoder().decode(encryptData);
        // 获取cipher单例对象
        Cipher cipher = Cipher.getInstance("AES");
        // 解密
        cipher.init(Cipher.DECRYPT_MODE, key);
        byte[] decryptBytes = cipher.doFinal(encryptBytes);
        return new String(decryptBytes);
	}
}

保存对象到文件

/**
 * 保存对象到本地
 * 
 * @param obj
 * @throws IOException
 */
public static void saveObject2File(String fileName, Object obj) throws IOException {
	FileOutputStream fos = new FileOutputStream(new File(fileName));
	ObjectOutputStream oos = new ObjectOutputStream(fos);
	oos.writeObject(obj);
}

从文件中读取对象

/**
 * 从文件中读取对象
 * @param fileName
 * @return
 * @throws IOException
 * @throws ClassNotFoundException
 */
public static Object readObjectFromFile(String fileName) throws IOException, ClassNotFoundException {
	FileInputStream fis = new FileInputStream(new File(fileName));
	ObjectInputStream ois = new ObjectInputStream(fis);
	Object object = ois.readObject();
	return object;
}

###第三个案例——自定义秘钥,代码

/**
     * 生成key
     * @return
     * @throws NoSuchAlgorithmException
     */
	public static SecretKey createKey(String keyword) throws NoSuchAlgorithmException {
        // 生成key
        KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
        keyGenerator.init(new SecureRandom(keyword.getBytes()));
        SecretKey secretKey = keyGenerator.generateKey();
        return secretKey;
	}

###应用场景

  • 登录,post请求{username=lisi,pw=加密}

##非对称加密的案例 常见算法:RSA、Elgamal、背包算法、Rabin、D-H、ECC(椭圆曲线加密算法)等 应用场景:银行和电商网站,他就采用非对称加密,将公钥给所有人,你们就用这个公钥加密,私钥我自己留着,谁也不知道,所以除了我,谁也解密不了。 ###第一个案例(数据量不大的情况下可用,加密数据小于117个字节,解密数据小于128个字节) public class AysmmetricEncryptionDemo {

    public static void main(String[] args) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
            IllegalBlockSizeException, BadPaddingException {
        String content = "今晚小树林,等你哦";
        //生成密钥对
        KeyPair keyPair = createKeyypair();
        //获取公钥私钥
        PublicKey publicKey = keyPair.getPublic();
        PrivateKey privateKey = keyPair.getPrivate();
        //加密
        String encryptData = encrypt(content, publicKey);
        System.out.println("加密后:" + encryptData);
        //解密
        String decryptData = decrypt(encryptData, privateKey);
        System.out.println("解密后:" + decryptData);
	}
	/**
     * 生成秘钥对
     * @return
     * @throws NoSuchAlgorithmException
     */
	public static KeyPair createKeyypair() throws NoSuchAlgorithmException{
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        return keyPair;
	}
	/**
     * 加密
     * 
     * @param content
     * @return
     * @throws NoSuchPaddingException
     * @throws NoSuchAlgorithmException
     * @throws BadPaddingException
     * @throws IllegalBlockSizeException
     * @throws InvalidKeyException
     */
	public static String encrypt(String content, PublicKey publicKey) throws NoSuchAlgorithmException,
            NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException {
        // 获取cipher对象
        Cipher cipher = Cipher.getInstance("RSA");
        // 初始化cipher,指定模式为加密,传入公钥
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] encryptBytes = cipher.doFinal(content.getBytes());
        return Base64.getEncoder().encodeToString(encryptBytes);
	}

	/**
     * 解密方法
     * 
     * @param encryptData
     * @param privateKey
     * @return
     * @throws NoSuchPaddingException 
     * @throws NoSuchAlgorithmException 
     * @throws InvalidKeyException 
     * @throws BadPaddingException 
     * @throws IllegalBlockSizeException 
     * @throws Exception
     */
	public static String decrypt(String encryptData, PrivateKey privateKey) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException{
        byte[] encryptBytes = Base64.getDecoder().decode(encryptData);
        // 获取cipher对象
        Cipher cipher = Cipher.getInstance("RSA");
        // 小芝解密
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] decryptBytes = cipher.doFinal(encryptBytes);
        return new String(decryptBytes);
	}
}

###如果数据量大的话,则要对要加密、解密的数据进行分块处理,代码如下:

    /**
         * 处理大量数据时,分块加密/解密
         * @param content
         * @param cipher
         * @param max
         * @return
         * @throws IllegalBlockSizeException
         * @throws BadPaddingException
         * @throws IOException
         */
        private static byte[] doFinalWithBlock(byte[] bytes, Cipher cipher,int max)
                throws IllegalBlockSizeException, BadPaddingException, IOException {
            int len = bytes.length;//3000
            //加密数据长度不能超过117个byte
            int inputOffset = 0;//2700
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            while (len > inputOffset) {
                //特殊情况,最后一次剩下的数据不足117
                if ((len - inputOffset) >= max) {
                    byte[] encryptBytes = cipher.doFinal(bytes, inputOffset, max);
                    baos.write(encryptBytes);
                    inputOffset += max;
                }else {
                    byte[] encryptBytes = cipher.doFinal(bytes, inputOffset, len - inputOffset);
                    baos.write(encryptBytes);
                    inputOffset = len;
                }
            }
            byte[] byteArray = baos.toByteArray();
            return byteArray;
        }

##消息摘要 消息摘要是一个不可逆的过程。常用来防篡改。

常见算法:MD5、SHA、CRC等

###byte数组和16进制字符串的互转

/**
     * byte数组转16进制字符串
     * @param bytes
     * @return
     */
	public static String bytes2Hex(byte[] bytes){
        StringBuffer sBuffer = new StringBuffer();
        for (int i = 0; i < bytes.length; i++) {
            //取高位
            int high = (bytes[i] & 0xf0) >> 4;
            //取低位
            int low = bytes[i] & 0x0f;
            sBuffer.append(HEXSTR[high]).append(HEXSTR[low]);
        }
        return sBuffer.toString();
	}

/**
     * 16进制字符串转字节数组
     * @param hex
     * @return
     */
	public static byte[] hex2Bytes(String hex){
        int len = hex.length()/2;
        //声明一个字节数组用于接收转换后的字节
        byte[] bytes = new byte[len];
        for (int i = 0; i < len; i++) {
            //首先取高位,取偶数位
            String highStr = hex.substring(2 * i, 2 * i + 1);
            String lowStr = hex.substring(2 * i + 1, 2 * i + 2);
            int high = Integer.parseInt(highStr, 16) << 4;
            int low = Integer.parseInt(lowStr, 16);
            bytes[i] = (byte) (high + low);
        }
        return bytes;
	}

##数字签名

签名过程

        String content = "404房卡";
        Signature signature = Signature.getInstance("MD5withRSA");
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        PrivateKey privateKey = keyPair.getPrivate();
        PublicKey publicKey = keyPair.getPublic();
        signature.initSign(privateKey);
        signature.update(content.getBytes());
        byte[] sign = signature.sign();

###数字签名的流程(画图理解) ###Signature**

//小芝认证
        signature.initVerify(publicKey);
        //我们要对房卡进行认证
//		String content2 = "505的房卡";
        signature.update(content.getBytes());
        boolean verify = signature.verify(sign);
        Util.printBytes(sign);
        System.out.println(verify);

##keytool的使用。

  • 生成keyPair keytool -genkeypair
  • 修改别名 keytool -changealias -alias mykey -destalias heima1
  • 导出证书 keytool -exportcert
  • 导入证书 keytool -importcert

获取文件中的证书

    // 获取证书工厂的实例
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            FileInputStream inStream = new FileInputStream(new File("heima.cer"));
            X509Certificate certificate = (X509Certificate) cf.generateCertificate(inStream);

##SSl 双向认证的原理 代码访问12306

    public class HttpsDemo {
	
        public static void main(String[] args) throws IOException, NoSuchAlgorithmException, KeyManagementException, KeyStoreException, CertificateException {
            //第一种方式,我所有的证书都不检查
            SSLContext sslContext = SSLContext.getInstance("TLS");//TLS 是transport Layer safe
            //获取信任管理者工厂
            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            //获取keyStore
            KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
            ks.load(null);
            //从文件中读取证书
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            FileInputStream inStream = new FileInputStream(new File("srca.cer"));
            X509Certificate cert = (X509Certificate) cf.generateCertificate(inStream);
            //给keyStore设置证书
            ks.setCertificateEntry("12306", cert);
            trustManagerFactory.init(ks);
            TrustManager[] tm = trustManagerFactory.getTrustManagers();
            sslContext.init(null, tm, null);
            SSLSocketFactory socketFactory = sslContext.getSocketFactory();
            HttpsURLConnection.setDefaultSSLSocketFactory(socketFactory);
            // TODO Auto-generated method stub
            URL url = new URL("https://kyfw.12306.cn/otn/");
            //第二步,获取coon
            HttpsURLConnection coon = (HttpsURLConnection) url.openConnection();
            InputStream inputStream = coon.getInputStream();
            String response = Util.inputStream2String(inputStream);
            System.out.println(response);
        }
	}