首页 » 通讯 » 安然系列之——手写 JAVA 加密、解密_密钥_字符串

安然系列之——手写 JAVA 加密、解密_密钥_字符串

萌界大人物 2024-12-19 05:14:15 0

扫一扫用手机浏览

文章目录 [+]



网络安全一样平常须要把稳以下几个关键点:

安然系列之——手写 JAVA 加密、解密_密钥_字符串 通讯



完全性(Integrity):确保信息在传输过程中,没有被修改。



私密性(Confidentiality):也便是通过加密,确保只有可信的实体可以看到这些信息。



源认证(Authenticity):确保是可信的源发送了这些信息,而不是伪装源发送的。
不可否认性(Nonrepudiation):不能事后否认发送过这条信息。



本日我们说的是数据传输的私密性。
加密技能是最常用的安全保密手段,利用技能手段把主要的数据变为乱码(加密)传送,到达目的地后再用相同或不同的手段还原(解密)。





加密技能包括两个元素:算法和密钥。



一、利用异或加密解密



先来看一个大略的加密解密的实例。

package com.wuxiaolong.EncrypteDecrypt;import org.apache.commons.codec.digest.DigestUtils;/ Description: @author 诸葛小猿 @date 2020-07-22 /public class Test1 { public static void main(String[] args) { String content = "我爱你"; Integer key = 1000; // 用户A发前,通过某种办法加密 String encryptStr = xor(content,key); System.out.println(encryptStr); // 密文通过网络传输 // 用户B收到密文后利用相同的办法解密 String decryptStr = xor(encryptStr,key); System.out.println(decryptStr); } / 加密算法及秘钥 @param content @return / public static String xor(String content, Integer key){ char[] chars = content.toCharArray(); for(int i=0; i<chars.length; i++){ chars[i] = (char) (chars[i]^key) ; } return new String(chars); }}

这里自定义了一个加解密方法xor,这个方法吸收两个参数,一个是原文content,一个秘钥key。
这样有加密算法,有秘钥,就可以加密了。



运行的结果:

懹燙䲈 // 加密结果我爱你 // 解密结果二、异或加密解密事理讲解



在发前,须要利用算法+秘钥对传输的(明文)进行加密,加密后利用密文在网络传输,传输到目的地时,再利用相同的算法+秘钥将密文翻译成明文。



在这个加密和解密的过程中,利用了相同的算法+秘钥。





这里可以看出,一个明文连续利用相同的算法+秘钥做两次加密,就可以得到原来的明文了。



这和打算机中的异或运算(^)很像,异或英文为exclusive OR,缩写成xor,异或运行有两个特点:



两个二进制数字相同为0,不同为1。



一个数字两次异或后,得到的是原数字本身。



下面利用一个字符a为例,和数字三进行两次异或运算,可以看出终极得到的结果还是a。





由于是位运算,参与运算的参数必须要转化成二进制后才能参与运算。
上面的算法public static String xor(String content, Integer key)中,首先要将content转化成一个字符数组,然后将数组的每一个字符,和整形的key做异或运算得到新的字符,终极,将新的字符数组转化成新的字符串。



为什么不能利用content字符串直接与key求异或运算?由于字符串不能直接与数字运算,但是字符可以与数字运算,以是字符串转换成字符数组。



下面先容的加密解密方法的底层,利用的都是二进制,以是加密解密的参数终极都会转换成二进制字节数组的形式进行处理。



上面这种方法中,加密和解密利用的是相同的密钥key,我们常日将这种办法称为对称加密。
如果加密和解密利用的是不同的秘钥,则称之为非对称加密。



三、对称加密



对付对称性加密,双方通讯之前,都要事先知道相同的密匙和算法,之后便是对数据进行加解密了。





这里先容几种常见的对称加密算法:DES,AES。



3.1 DES



DES(Data Encryption Standard) 算法是美国政府机关为了保护信息处理中的打算机数据而利用的一种加密办法,是一种常规密码系统编制的密码算法,目前已广泛利用。
该算法输入的是64比特的明文,在64比特密钥的掌握下产生64比特的密文;反之输入64比特的密文,输出64比特的明文。
64比特 的密钥中含有8个比特的奇偶校验位,以是实际有效密钥长度为56比特。
利用一个 56 位的密钥以及附加的 8 位奇偶校验位,产生最大 64 位的分组大小。
这是一个迭代的分组密码,利用称为 Feistel 的技能,个中将加密的文本块分成两半。
利用子密钥对个中一半运用循环功能,然后将输出与另一半进行"异或"运算;接着交流这两半,这一过程会连续下去,但末了一个循环不交流。
DES 利用 16 个循环,利用异或,置换,代换,移位操作四种基本运算。



下面是利用JDK进行加密的示例代码:

package com.wuxiaolong.EncrypteDecrypt;import javax.crypto.Cipher;import javax.crypto.SecretKeyFactory;import javax.crypto.spec.DESKeySpec;import javax.crypto.spec.IvParameterSpec;import java.security.Key;import java.util.Base64;/ Description: @author 诸葛小猿 @date 2020-07-23 /public class DESUtil { / 偏移变量,固定占8位字节 / private final static String IV_PARAMETER = "12345678"; / 加密算法 / private static final String ALGORITHM = "DES"; / 加密/解密算法-事情模式-添补模式 / private static final String CIPHER_ALGORITHM = "DES/CBC/PKCS5Padding"; / 默认编码 / private static final String CHARSET = "utf-8"; / 秘钥 / private static final String KEY = "key12345678"; public static void main(String[] args) { String content = "我爱你"; String encptStr = encrypt(KEY,content); System.out.println(encptStr); String decptStr = decrypt(KEY,encptStr); System.out.println(decptStr); } / 天生key @param password @return @throws Exception / private static Key generateKey(String password) throws Exception { DESKeySpec dks = new DESKeySpec(password.getBytes(CHARSET)); SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM); return keyFactory.generateSecret(dks); } / DES加密字符串 @param password 加密密码,长度不能够小于8位 @param data 待加密字符串 @return 加密后内容 / public static String encrypt(String password, String data) { if (password== null || password.length() < 8) { throw new RuntimeException("加密失落败,key不能小于8位"); } if (data == null) return null; try { Key secretKey = generateKey(password); Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); IvParameterSpec iv = new IvParameterSpec(IV_PARAMETER.getBytes(CHARSET)); cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv); // 加密 byte[] bytes = cipher.doFinal(data.getBytes(CHARSET)); // base64编码 JDK1.8及以上可直策应用Base64,JDK1.7及以下可以利用BASE64Encoder byte[] encode = Base64.getEncoder().encode(bytes); return new String(encode); } catch (Exception e) { e.printStackTrace(); return data; } } / DES解密字符串 @param password 解密密码,长度不能够小于8位 @param data 待解密字符串 @return 解密后内容 / public static String decrypt(String password, String data) { if (password== null || password.length() < 8) { throw new RuntimeException("加密失落败,key不能小于8位"); } if (data == null) return null; try { Key secretKey = generateKey(password); Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); IvParameterSpec iv = new IvParameterSpec(IV_PARAMETER.getBytes(CHARSET)); cipher.init(Cipher.DECRYPT_MODE, secretKey, iv); // base64解码 byte[] decode = Base64.getDecoder().decode(data.getBytes(CHARSET)); // 解密 byte[] decrypt = cipher.doFinal(decode); return new String(decrypt, CHARSET); } catch (Exception e) { e.printStackTrace(); return data; } }} 

上面代码运行的结果:

Xii999DE7LPx5io0awfOFw== // 加密结果我爱你 // 解密结果

DES是一种分组数据加密技能(先将数据分成固定长度的小数据块,之后进行加密),速率较快,适用于大量数据加密,比如文件加密。
文件加密的代码,可以关注我的公众号,输入关键字“java-summary”获取。



1997 年RSA数据安全公司发起了一项“DES 寻衅赛”的活动,志愿者四次分别用四个月、41天、56个小时和22个小时破解了其用56bit DES算法加密的密文。
即DES加密算法在打算机速率提升后的本日被认为是不屈安的。
以是针对保密级别特殊高的数据推举利用非对称加密算法。



3.2 AES



AES (Advanced Encryption Standard),高等加密标准,是一种区块加密标准。
这个标准用来替代原来的DES,已经被多方剖析且广为全天下所利用。



DES利用56位密钥,比较随意马虎被破解,而AES可以利用128、192、和256位密钥,并且用128位分组加密和解密数据,相对来说安全很多。
完善的加密算法在理论上是无法破解的,除非利用穷尽法。
利用穷尽法破解密钥长度在128位以上的加密数据是不现实的,仅存在理论上的可能性。
统计显示,纵然利用目前天下上运算速率最快的打算机,穷尽128位密钥也要花上几十亿年的韶光,更不用说去破解采取256位密钥长度的AES算法了。



下面是AES的源码展示:

package com.wuxiaolong.EncrypteDecrypt;import javax.crypto.Cipher;import javax.crypto.KeyGenerator;import javax.crypto.SecretKey;import javax.crypto.spec.SecretKeySpec;import java.security.SecureRandom;/ Description: @author 诸葛小猿 @date 2020-07-23 /public class AESUtil { / 加密算法 / private static final String ALGORITHM = "AES"; / 秘钥长度 / private static final Integer KEY_LENGTH = 128; / 默认编码 / private static final String CHARSET = "utf-8"; / 秘钥 / private static final String KEY = "key12345678"; public static void main(String[] args) throws Exception { String content = "我爱你"; String encrypt = encrypt(content, KEY); System.out.println(encrypt); String decrypt = decrypt(encrypt, KEY); System.out.println(decrypt); } / 天生key @param password @return @throws Exception / private static SecretKeySpec generateKey(String password) throws Exception { // 创建AES的Key生产者 KeyGenerator kgen = KeyGenerator.getInstance(ALGORITHM); // 利用用户密码作为随机数初始化出 // SecureRandom是天生安全随机数序列,password.getBytes()是种子,只要种子相同,序列就一样,以是解密只要有password就行 kgen.init(KEY_LENGTH, new SecureRandom(password.getBytes())); // 根据用户密码,天生一个密钥 SecretKey secretKey = kgen.generateKey(); // 返回基本编码格式的密钥,如果此密钥不支持编码,则返回 byte[] enCodeFormat = secretKey.getEncoded(); // 转换为AES专用密钥 SecretKeySpec key = new SecretKeySpec(enCodeFormat, ALGORITHM); return key; } / AES加密字符串 @param content 须要被加密的字符串 @param password 加密须要的密码 @return 密文 / public static String encrypt(String content, String password) { try { SecretKeySpec key = generateKey(password); // 创建密码器 Cipher cipher = Cipher.getInstance(ALGORITHM); byte[] byteContent = content.getBytes(CHARSET); // 初始化为加密模式的密码器 cipher.init(Cipher.ENCRYPT_MODE, key); // 加密 byte[] result = cipher.doFinal(byteContent); // 二进制转换成16进制字符串 String hexStr = parseByte2HexStr(result); return hexStr; } catch (Exception e) { e.printStackTrace(); } return null; } / 解密AES加密过的字符串 @param content AES加密过过的内容 @param password 加密时的密码 @return 明文 / public static String decrypt(String content, String password) { try { // 十六进制字符串转换成二进制字节数组 byte[] byteArr = parseHexStr2Byte(content); SecretKeySpec key = generateKey(password); Cipher cipher = Cipher.getInstance(ALGORITHM); cipher.init(Cipher.DECRYPT_MODE, key); // 解密 byte[] result = cipher.doFinal(byteArr); return new String(result,CHARSET); } catch (Exception e) { e.printStackTrace(); } return null; } /将二进制转换成16进制 @param buf @return / public static String parseByte2HexStr(byte buf[]) { StringBuffer sb = new StringBuffer(); for (int i = 0; i < buf.length; i++) { String hex = Integer.toHexString(buf[i] & 0xFF); if (hex.length() == 1) { hex = '0' + hex; } sb.append(hex.toUpperCase()); } return sb.toString(); } /将16进制转换为二进制 @param hexStr @return / public static byte[] parseHexStr2Byte(String hexStr) { if (hexStr.length() < 1) return null; byte[] result = new byte[hexStr.length()/2]; for (int i = 0;i< hexStr.length()/2; i++) { int high = Integer.parseInt(hexStr.substring(i2, i2+1), 16); int low = Integer.parseInt(hexStr.substring(i2+1, i2+2), 16); result[i] = (byte) (high 16 + low); } return result; }}

上面代码运行的结果:

765B2080D288F81355CB2A235AD7938C // 加密结果我爱你 // 解密结果

相较于DES而言,AES算法有着更高的速率和资源利用效率,安全级别也较之更高了,被称为下一代加密标准。
目前天下上还有组织在研究如何攻破AES这堵坚厚的墙,但是由于破解韶光太长,AES得到保障,但是所用的韶光不断缩小。
随着打算机打算速率的增快,新算法的涌现,AES遭到的攻击只会越来越剧烈,不会停滞的。



AES现在广泛用于金融财务、在线交易、无线通信、数字存储等领域,经受了最严格的磨练,但说不定哪天就会步DES的后尘。



3.3 对称加密的特点



优点:



速率快。
相对付非对称加密,对称加密的性能更好,加解密速率更快。
安全。
只能说相对比样安全的。
紧凑。
加密后内容的长度基本变革不大。



缺陷:



如果双方通讯时,通过明文传输共享密钥,随意马虎涌现中途挟制和窃听的问题。
随着通讯的参与者数量的增加,密钥数量急剧膨胀((n×(n-1))/2)。
由于密钥数量过多,对密钥的管理和存储是一个很大的问题(后面我会专门开一期说秘钥管理及系统设计)。
不支持数字署名和不可否认性。



四、非对称加密



对付非对称算法,双方通讯之前,都须要事师长西席成一对密匙(公钥、私钥),然后双方交流公钥。
常日非对称加密利用的便是RAS算法。





在利用非对称密钥技能之前,所有参与者,不管是用户还是路由器等网络设备,都须要预先利用非对称密钥算法(例如RSA)产生一对密钥,个中包括一个公钥和一个私钥。
公钥可以放在一个做事器上共享给属于这个密钥系统的所有用户与设备,而私钥须要由持有者严格保护,确保只有持有者才能唯一拥有。





非对称加密有很多优点,安全、公钥可以网络传输等。
但是加密速率很慢,不会用来加密上KB的数据。



关于RAS算法,可以说的内容太多太多,比如RSR的数学事理、公钥加密私钥解密、私钥署名公钥验签、公私钥天生、大文件如何加密、秘钥如何管理等问题。
这些我都会在这个安全系列的其他文章中详细解释的。
这一期以代码展示为主,就不过多在这里说了。



4.1 RSA



这里须要把稳的是,加密利用的是对方的公钥,解密利用的是对应的私钥。



package com.wuxiaolong.EncrypteDecrypt;import org.apache.commons.codec.binary.Base64;import javax.crypto.Cipher;import java.security.KeyFactory;import java.security.KeyPair;import java.security.KeyPairGenerator;import java.security.SecureRandom;import java.security.interfaces.RSAPrivateKey;import java.security.interfaces.RSAPublicKey;import java.security.spec.PKCS8EncodedKeySpec;import java.security.spec.X509EncodedKeySpec;import java.util.HashMap;import java.util.Map;/ Description: @author 诸葛小猿 @date 2020-07-24 /public class RSAEncrypt { //用于封装随机产生的公钥与私钥 private static Map<Integer, String> keyMap = new HashMap<Integer, String>(); public static void main(String[] args) throws Exception { //天生公钥和私钥 genKeyPair(); //加密字符串 String message = "我爱你"; System.out.println("随机天生的公钥为:" + keyMap.get(0)); System.out.println("随机天生的私钥为:" + keyMap.get(1)); String messageEn = encrypt(message,keyMap.get(0)); System.out.println(message + "\t加密后的字符串为:" + messageEn); String messageDe = decrypt(messageEn,keyMap.get(1)); System.out.println("还原后的字符串为:" + messageDe); } / 随机天生密钥对 @throws Exception / public static void genKeyPair() throws Exception { // KeyPairGenerator类用于天生公钥和私钥对,基于RSA算法天生工具 KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA"); // 初始化密钥对天生器,密钥大小为 keyPairGen.initialize(2048,new SecureRandom()); // 天生一个密钥对,保存在keyPair中 KeyPair keyPair = keyPairGen.generateKeyPair(); // 得到私钥、公钥 RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); // 得到私钥字符串 String publicKeyString = new String(Base64.encodeBase64(publicKey.getEncoded())); String privateKeyString = new String(Base64.encodeBase64((privateKey.getEncoded()))); // 将公钥和私钥保存到Map 0表示公钥 1表示私钥 keyMap.put(0,publicKeyString); keyMap.put(1,privateKeyString); } / RSA公钥加密 @param str 加密字符串 @param publicKey 公钥 @return 密文 @throws Exception 加密过程中的非常信息 / public static String encrypt( String str, String publicKey ) throws Exception{ //base64编码的公钥 byte[] decoded = Base64.decodeBase64(publicKey); RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded)); //RSA加密 Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.ENCRYPT_MODE, pubKey); String outStr = Base64.encodeBase64String(cipher.doFinal(str.getBytes("UTF-8"))); return outStr; } / RSA私钥解密 @param str 加密字符串 @param privateKey 私钥 @return 明文 @throws Exception 解密过程中的非常信息 / public static String decrypt(String str, String privateKey) throws Exception{ //64位解码加密后的字符串 byte[] inputByte = Base64.decodeBase64(str.getBytes("UTF-8")); //base64编码的私钥 byte[] decoded = Base64.decodeBase64(privateKey); RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded)); //RSA解密 Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.DECRYPT_MODE, priKey); String outStr = new String(cipher.doFinal(inputByte)); return outStr; }}

代码运行的结果:

随机天生的公钥为:MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgJW1vVhZWd8NDiTIrAJK5N4EipP5jWw8WIsWiX3SzZGpzRuz5duRjhubxS2kOoGP6GnOI8KMAHwcFCjtgQNLfvoufHG9OiRaKQEkPhypF1vsuEC0rzeOcWbzIAsWk7B6wboGd6Or4L2MAHsIrluISgICq6BU1cVb/XSPX9tbIOfrjRNsbX5DnNd39XZ4yUlqIDocCQtV3rmQlG4e4nlsJTw073/7/eNCkp7FMJVTch+rQspUPlks9V1ic9TmBhW7dszAGWz4BmIe1elar5bapivBtnVpRX+yEGZSQWkchTqSQTOeYfuh3GFDAYoWlGEx+OeUX8SsRK1V3zeiwv6lpQIDAQAB随机天生的私钥为:MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCAlbW9WFlZ3w0OJMisAkrk3gSKk/mNbDxYixaJfdLNkanNG7Pl25GOG5vFLaQ6gY/oac4jwowAfBwUKO2BA0t++i58cb06JFopASQ+HKkXW+y4QLSvN45xZvMgCxaTsHrBugZ3o6vgvYwAewiuW4hKAgKroFTVxVv9dI9f21sg5+uNE2xtfkOc13f1dnjJSWogOhwJC1XeuZCUbh7ieWwlPDTvf/v940KSnsUwlVNyH6tCylQ+WSz1XWJz1OYGFbt2zMAZbPgGYh7V6VqvltqmK8G2dWlFf7IQZlJBaRyFOpJBM55h+6HcYUMBihaUYTH455RfxKxErVXfN6LC/qWlAgMBAAECggEAe+FAFWpfsuDcsAqqNmWDCBoJoATOAP0M6nUdwlqxVBGI2K1e1Q2Dnrhki0Pcm+0k2tHMotEUlob7ekSwBIJLIssfLA9cMf7Byg1qgFiWY4XRevYD3WcV2ZVImE92cdUtfySchHjv53ZVwkTGaUyP8lUbg4PVF5qrdHTuiHhJxFmqLwB6DC7HM9X2jleYJaNSZ2UwiloqkHAMDhqAu9212qZ4ISjbOg/iHDeTRKG3wIX989rsxAVSsruQm3DMZ/jeXzx8MpNjiLaT448dDSOhjhb1tOycMI7CWhYsuT7fOSFmtCtb9E3L8AVarKD2eiOqMaXj28wiuoST7lxa26hHoQKBgQDSmLPYHLXgOfJPIjuYv3IrBv9wr6H9N5FnSKYfISo8gQZJj4YWVFsc9/9vQyOOk0VvtSd28z0dixo1P4axEFe84vwVUsbgo7797nFLZWFm6tZZLgDiSgcxLhz9/5cjnY7irZMdPqKWQKkcFMm4Km8JX8BipwKVwuDiZ74enE1viQKBgQCcTpi0IxOaQQXWaDjG50MBBUIU0F1vM69apsJKeIY8XgE9Vsz/4yqwW7enIZHY15yx4btmZ/1ieOTqRI3x6SwFcgRZQD8OiKf/6WpHp7hXmTE34QJd9CPq+c8MgImd9BAX4rsdKp9pOGsTjFcAvLkU1crk/JQdht+p6VErJUwCPQKBgHLcFjqobgn9kMrYQOjugbY1+tva7t7Mj+FlHjWcQQz+0g8M2HUVTRxfplForNv2NsjWZM/bmlmipIimPTAVWcULh7GZlB6xMFoO0nvsr1MSghXhoVnKRmHsZKOj4yrppCS9xp8MqmCIo7NNIzfu7OCP+L3VmPNVdpIFQwwu840BAoGAUJx8RpK8flZAc3YmqfYE6VbT4LYhKcOHEQJlu1BzMljs3LySVvnKP0/d5FT8yc9Q4bBgHI1O8WUV8ffPGM6/REOsGHd4zb8OnX28sR2/hXXdG0txFBFgIMQa0wDLeGmxjxAAnicoNXTCTD+Zcyjhbuxfij71CFFRsmhA0zaV/6UCgYEAua9INHaJQ5VIFEyAWMHhfwQOU00BynQP0MugNwPypH9qejEzzBJ2fq8j69fxfeG2EENIiDymZMPwHybpYcipKT1aksCPu95HF1+yR6lNrSFDzn/kQu06bgB3V61wsgsKeDuDh++z381m1BONfK3MS1+BlxR/pJ59ikBLyB15tiQ=我爱你加密后的字符串为:Qo2qkCPF27rfbfAV8k3uLH4ogVA8BlnhtKEvL77KH6wZQKmzrq3z794TuWU9PbJpkYLD2tALt6t0y+Rr9rpPHCqtAGXkzDddu7get/8eNs4N3i8yoOYW2ui7yny7v5cVd8ZsxSHcszmaSV+wb+5AJcMzQhOvWaRwx1WVEOkDp/qBENGFdfPJ8gecbL7YgnqzUtccaJQomQWIlXk88mAU8nAALHO/goEI2CK1HRtb+9qyQgjHU1zFhBsli5yA8NDcqiczFb/nV90yKLMhn5DpIm3gU/qquQltfnkflSk+KXe5BFThIVxcu2FBIovOaX+s82L4DJXtRZS+aCSrHOPfSg==还原后的字符串为:我爱你



加密解密利用的秘钥和署名验签的秘钥利用不一样,参考这个系列的其他文章。



4.2 非对称加密的特点



事情特点:



用一个密钥(公钥)加密的数据,只能用另一个密钥(私钥)来解密。
一个密钥(私钥)用来署名,一个密钥(公钥)用来验签。



优点:



由于不必担心交流的公钥被挟制,以是非对称密钥的分发更安全。
密钥数目和参与者数目相同。
在交流公钥之前,不须要预先建立某种信赖关系。
支持数字署名和不可否认性。



缺陷:



加密速率很慢。
要不用来加密上KB的数据,可以用来加密key,key的数据量较小。
加密后,密文会变长。



标签:

相关文章