#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的原理 ![](Base64yuanli.png) ###代码 /** * @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); } }