首先应当区分加密与编码并不是一回事。
文章目录
一、简介
- 加密方式主要有3种:- 可逆- 【对称加密】:symmetric,例如 AES、DES 等。- 【非对称加密】:asymmetric,例如 RSA、DSA 等。- 不可逆- 【摘要加密】:Digest/Hash,例如 MD5、SHA-1、SHA-256、HMAC 等。
- 加解密思想:将一种排序好的二进制,转变为另一种排序的二进制。- 输入:二进制数据- 输出:二进制数据
- 注意:在加密后得到的是二进制数据,一般来说我们需要将其转变为更加容易阅读的十六进制范式,两种方式- Base64编码:源数据3个字节为一组, 转化为4个字符表示。- BigInteger转码:本质十六进制,源数据的1个字节为一组, 转为2个字符表示。
String en =Base64.getEncoder().encodeToString( endoce );``````// 默认的2进制转16进制。【1】表示整数,不加可能得负值(结果出错)String encodeHex =newBigInteger(1,msg).toString(16);// 16进制数据转2进制newBigInteger("1f6f",16).toString(2)
- 16进制一般针对无法显示的一些二进制进行显示,常用于:- 加密解密。- 编码转换。- 图片表现形式转换。
- 【英语翻译】:- Bin:Binary,二进制。- Oct:Octal,八进制。- Dec:Decimal,十进制。- Hex:Hexadecimal,十六进制。
- BouncyCastle库: 一个提供了很多哈希算法和加密算法的第三方库。它包含了Java原生中所缺失的一些加密方式(如国产算法),需要时再导入。
<!-- jdk15on 代表适用于 jdk1.5 以上的版本而不是 jdk15 --><dependency><groupId>org.bouncycastle</groupId><artifactId>bcprov-jdk15on</artifactId><version>1.70</version></dependency>
- 在接下来的章节中,为了达到快速开发的目的,我会结合着Java辅助开发框架【hutool】来讲解。
<!-- pom 导包 --><dependency><groupId>cn.hutool</groupId><artifactId>hutool-crypto</artifactId><version>5.8.5</version></dependency>
二、对称加密AES
- 简介- 对称加密也称私匙加密、传统密码算法,又分为两种类型: - 分组加密:也叫块加密(block cyphers),一次加密明文中的一个块。- 序列加密:也叫流加密(stream cyphers),一次加密明文中的一个位。- 加密和解密共用一套密匙、共用同一个密码,私匙一般不可泄露,双方需要在一开始的时候就相互协商密匙;常用的zip解压缩使用的就是对称加密算法。- 在加密的过程中存在三个概念:加密方式、工作模式、填充模式。
- 加密方式:- DES:Data Encryption Standard,“数据加密标准”,密匙过短,可以在短时间内被暴力破解。- AES:Advanced Encryption Standard,“高级加密标准”,又称Rijndael加密法,开发目的是为了取代DES,目前最流行的对称加密方式。
- 工作模式(6种):维基百科- ECB:Electronic codebook,电子密码本- CBC:Cipher-block chaining,密码块链接- PCBC:Propagating cipher-block chaining,填充密码块链接- CFB:Cipher feedback,密文反馈- OFB:Output feedback,输出反馈- CTR:Counter mode,计数器模式
- 填充模式(9种):- No Padding:不填充,在此填充下原始数据必须是分组大小的整数倍,非整数倍时无法使用该模式。- PKCS5 / PKCS7 Padding:两者基本相同,填充至符合块大小的整数倍。 - 原始:
FF FF FF FF FF FF FF FF FF
- 填充:FF FF FF FF FF FF FF FF FF 07 07 07 07 07 07 07
- ISO10126 Padding:填充至符合块大小的整数倍,填充值最后一个字节为填充的数量数,其他字节随机处理。 - 原始:FF FF FF FF FF FF FF FF FF
- 填充:FF FF FF FF FF FF FF FF FF 3F 7A B4 09 14 36 07
- ISO7816-4 Padding:填充至符合块大小的整数倍,填充值第一个字节为十六进制80,其他字节填 0。 - 原始:FF FF FF FF FF FF FF FF FF
- 填充:FF FF FF FF FF FF FF FF FF 80 00 00 00 00 00 00
- ZeroByte Padding:填充至符合块大小的整数倍,填充值为 0。 - 原始:FF FF FF FF FF FF FF FF FF
- 填充:FF FF FF FF FF FF FF FF FF 00 00 00 00 00 00 00
- X923 Padding:填充至符合块大小的整数倍,填充值最后一个字节为填充的数量数,其他字节填 0。 - 原始:FF FF FF FF FF FF FF FF FF
- 填充:FF FF FF FF FF FF FF FF FF 00 00 00 00 00 00 07
- TBC Padding(Trailing-Bit-Compliment):填充至符合块大小的整数倍,原文最后一位为“1”时填充 0x00,最后一位为“0”时填充“0xFF”。 - 原始:FF FF FF FF FF FF FF FF FF
- 填充:FF FF FF FF FF FF FF FF FF 00 00 00 00 00 00 00
- 原始:FF FF FF FF FF FF FF FF F0
- 填充:FF FF FF FF FF FF FF FF F0 FF FF FF FF FF FF FF
- PKCS1 Padding:RSA 加密时,需要将原文填充至密钥大小。 - 密匙长度: 以AES为例,密匙长度指的就是 key 的长度,有AES128、AES192、AES256之分,密匙长度越长、保护性就越强、所需要的计算量就越大。密钥长度根据指定密钥位数分别为16、24、32个字符,IV与密钥超过长度则截取,不足则在末尾填充’\0’补足,选择了对应长度的密匙加密方式,密匙本身长度也要跟上。
- AES,ECB模式加密- 简介:- ECB模式比较简单,每次固定的密匙总会生成固定的密文。- 使用Cipher进行加解密,SecretKey为相对应的密匙、需要在Cipher中初始化。- 步骤:1. 根据算法名称/工作模式/填充模式获取Cipher实例;2. 根据算法名称初始化一个SecretKey实例,密钥必须是指定长度;3. 使用SerectKey初始化Cipher实例,并设置加密或解密模式;4. 传入明文或密文,获得密文或明文。- 【Java原生-加密】
String message ="待加密内容";byte[] data = message.getBytes("UTF-8");// 128位 = 16 byte 的 key 值byte[] key ="1234567890abcdef".getBytes("UTF-8");Cipher cipher =Cipher.getInstance("AES/ECB/PKCS5Padding");SecretKey keySpec =newSecretKeySpec(key,"AES");cipher.init(Cipher.ENCRYPT_MODE, keySpec);// 初始化加密工具byte[] encrypted = cipher.doFinal(data);// 加密String en =Base64.getEncoder().encodeToString(encrypted);
- 【Java原生-解密】byte[] key ="1234567890abcdef".getBytes("UTF-8");byte[] decode =Base64.getDecoder().decode("AYpKX4YMIhmojH89B3Pd9Q==");Cipher cipher =Cipher.getInstance("AES/ECB/PKCS5Padding");SecretKey keySpec =newSecretKeySpec(key,"AES");cipher.init(Cipher.DECRYPT_MODE, keySpec);// 解密模式byte[] bytes = cipher.doFinal(decode);String msg =newString(bytes,"utf-8");System.out.println(msg);// 输出(正确):“待加密内容”
- 【Hutool-加解密】String content ="中文";//随机生成密钥,当然也可以自定义byte[] key =SecureUtil.generateKey(SymmetricAlgorithm.AES.getValue()).getEncoded();//构建【对称加密类(工具)】AES aes =SecureUtil.aes(key);// 1. 普通加密、解密byte[] encrypt = aes.encrypt(content);byte[] decrypt = aes.decrypt(encrypt);// 2. 加密、解密为16进制字符。String encryptHex = aes.encryptHex(content);String decryptStr = aes.decryptStr(encryptHex);
- AES,CBC模式加密- 简介: 需要一个随机数作为IV(Initialization Vector)初始化因子,这样对于同一份明文,每次生成的密文都会不同。IV参数不需要保密,在解密的时候作入参传入。IOS等移动端对AES加密有要求,必须为PKCS7Padding模式。- 【原生】简单复合案例
publicclassMain{publicstaticvoidmain(String[] args)throwsException{// 原文:String message ="Hello, world!";System.out.println("Message: "+ message);// 256位密钥 = 32 bytes Key:byte[] key ="1234567890abcdef1234567890abcdef".getBytes("UTF-8");// 加密:byte[] data = message.getBytes("UTF-8");byte[] encrypted =encrypt(key, data);System.out.println("Encrypted: "+Base64.getEncoder().encodeToString(encrypted));// 解密:byte[] decrypted =decrypt(key, encrypted);System.out.println("Decrypted: "+newString(decrypted,"UTF-8"));}// 加密:publicstaticbyte[]encrypt(byte[] key,byte[] input)throwsGeneralSecurityException{Cipher cipher =Cipher.getInstance("AES/CBC/PKCS5Padding");SecretKeySpec keySpec =newSecretKeySpec(key,"AES");// CBC模式需要生成一个16 bytes的initialization vector:SecureRandom sr =SecureRandom.getInstanceStrong();byte[] iv = sr.generateSeed(16);IvParameterSpec ivps =newIvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivps);byte[] data = cipher.doFinal(input);// IV不需要保密,把IV和密文一起返回:returnjoin(iv, data);}// 解密:publicstaticbyte[]decrypt(byte[] key,byte[] input)throwsGeneralSecurityException{// 把input分割成IV和密文:byte[] iv =newbyte[16];byte[] data =newbyte[input.length -16];System.arraycopy(input,0, iv,0,16);System.arraycopy(input,16, data,0, data.length);// 解密:Cipher cipher =Cipher.getInstance("AES/CBC/PKCS5Padding");SecretKeySpec keySpec =newSecretKeySpec(key,"AES");IvParameterSpec ivps =newIvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivps);return cipher.doFinal(data);}publicstaticbyte[]join(byte[] bs1,byte[] bs2){byte[] r =newbyte[bs1.length + bs2.length];System.arraycopy(bs1,0, r,0, bs1.length);System.arraycopy(bs2,0, r, bs1.length, bs2.length);return r;}}
- 【hutool】
AES aes =newAES(Mode.CBC,Padding.PKCS5Padding,"0123456789ABHAEQ".getBytes(),// 密匙key"DYgjCEIMVrj2W9xN".getBytes());// iv加盐// 加密、解密,16进制表示String encryptHex = aes.encryptHex(content);String decryptStr = aes.decryptStr(encryptHex);
三、非对称加密RSA
- 简介- 公匙、私匙,常用的非对称加密方式为: - RSA:由Ron Rivest,Adi Shamir,Leonard Adleman这三人共同发明,名字由为三人姓氏首字母大写。RSA加密算法基于一个十分简单的数论事实,即将两个大素数相乘十分容易,但想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥。- DSA:Digital Signature Algorithm,数字签名算法,常用于签名。- 某人可以向社会公布他的公匙,任何想发消息给他的人都得用公匙对消息进行加密,然后只有他可以用私匙解密对应消息。- 非对称加密和对称加密都不能防止【中间人攻击】,应用2种:加密、签名。
- 【必要性说明】 非对称加密相较于对称加密速度慢、效率低,实际开发中,非对称加密总是和对称加密一起使用。- 小明生成一个随机的AES口令,然后用小红的公钥通过RSA加密这个口令,并发给小红。- 小红用自己的RSA私钥解密得到AES口令。- 双方使用这个共享的AES口令用AES加密通信。
- RAS加密- 【原生】
publicclassMain{publicstaticvoidmain(String[] args)throwsException{// 明文:byte[] plain ="Hello, encrypt use RSA".getBytes("UTF-8");// 创建公钥/私钥对:Person alice =newPerson("Alice");// 用Alice的公钥加密:byte[] pk = alice.getPublicKey();System.out.println(String.format("public key: %x",newBigInteger(1, pk)));byte[] encrypted = alice.encrypt(plain);System.out.println(String.format("encrypted: %x",newBigInteger(1, encrypted)));// 用Alice的私钥解密:byte[] sk = alice.getPrivateKey();System.out.println(String.format("private key: %x",newBigInteger(1, sk)));byte[] decrypted = alice.decrypt(encrypted);System.out.println(newString(decrypted,"UTF-8"));}}classPerson{String name;// 私钥:PrivateKey sk;// 公钥:PublicKey pk;publicPerson(String name)throwsGeneralSecurityException{this.name = name;// 生成公钥/私钥对:KeyPairGenerator kpGen =KeyPairGenerator.getInstance("RSA");
kpGen.initialize(1024);KeyPair kp = kpGen.generateKeyPair();this.sk = kp.getPrivate();this.pk = kp.getPublic();}// 把私钥导出为字节publicbyte[]getPrivateKey(){returnthis.sk.getEncoded();}// 把公钥导出为字节publicbyte[]getPublicKey(){returnthis.pk.getEncoded();}// 用公钥加密:publicbyte[]encrypt(byte[] message)throwsGeneralSecurityException{Cipher cipher =Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE,this.pk);return cipher.doFinal(message);}// 用私钥解密:publicbyte[]decrypt(byte[] input)throwsGeneralSecurityException{Cipher cipher =Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE,this.sk);return cipher.doFinal(input);}}
- 【hutool】 - 使用AsymmetricCrypto类。- 当拥有密文和一份密匙,只需重新利用密匙构建AsymmetricCrypto即可,此时另一份密匙传入null。
// 构建 RSA 加密工具,可传参【公匙】与【私匙】。AsymmetricCrypto rsa =newAsymmetricCrypto("RSA");// 获得私钥,可存储于其他文件中PrivateKey sk = rsa.getPrivateKey();String sk64 = rsa.getPrivateKeyBase64();// 获得公钥,可存储于其他文件中PublicKey pk = rsa.getPublicKey();String pk64 = rsa.getPublicKeyBase64();// 公钥加密,私钥解密String encode = rsa.encryptBase64("中文",KeyType.PublicKey);String decode = rsa.decryptStr(encode,KeyType.PrivateKey);
- RAS签名- 私匙加密、公匙解密。 - 发布者在公布自己的消息时,需要公布两份东西,一份是数据,另一份就是签名。- 验证时,先将数据经过散列得到Hash1,再将签名公匙解密得到Hash2,两者进行比对。- 事先经过hash散列的好处就是缩短了要加密的数据和生成的签名数据,进一步减轻了带宽压力。- 常用的数字签名算法有3种,实际上就是指定某种哈希算法进行RSA签名的方式。 - MD5withRSA- SHA1withRSA- SHA256withRSA- 【原生】
// 生成RSA公钥、私钥KeyPairGenerator kpGen =KeyPairGenerator.getInstance("RSA");kpGen.initialize(1024);KeyPair kp = kpGen.generateKeyPair();PrivateKey sk = kp.getPrivate();PublicKey pk = kp.getPublic();// 待签名的消息byte[] message ="中文".getBytes(StandardCharsets.UTF_8);// 用私钥签名Signature signSk =Signature.getInstance("SHA1withRSA");signSk.initSign(sk);signSk.update(message);byte[] signed = signSk.sign();System.out.println(String.format("signature: %x",newBigInteger(1, signed)));// 用公钥验证,最后得到的是Boolean值,判断真假。Signature signPk =Signature.getInstance("SHA1withRSA");signPk.initVerify(pk);signPk.update(message);boolean valid = signPk.verify(signed);System.out.println("valid? "+ valid);
- 【hutool】byte[] data ="中文".getBytes();Sign sign =SecureUtil.sign(SignAlgorithm.MD5withRSA);//签名byte[] signed = sign.sign(data);//验证签名boolean verify = sign.verify(data, signed);System.out.println(verify);``````// 可以将本次生成的私匙与公匙保存到文件PublicKey pk = sign.getPublicKey();PrivateKey sk = sign.getPrivateKey();
四、摘要加密Hash
- 简介- 哈希严格来说只是一种摘要算法Digest而不是加密算法。- 特点: - 对于任意输入,都输出固定长度的值。- “不可逆”算法。- 应用领域:- 加密,MD5加盐散列。- 数据完整性验证。
- 加盐思想:为什么加盐能够防止黑客通过彩虹表破解? 因为在没有加盐时,黑客拥有一张通过大量时间计算得出来的“彩虹表1”,可以轻易的碰撞到原始密码。当我们加盐之后,由于盐值可以是随机的(比如盐值为用户名),此时黑客就需要重新计算大量数据,这种计算是极其耗费时间的。
- Java原生哈希算法:输入任意字符,输出固定4字节int型整数。
// 1. hash 后获得十进制数Integer code ="Java".hashCode();// 2. 将十进制数转为十六进制数String hashString =Integer.toHexString(code);// 3. 输出(0x)231e42,4字符 = 32位 = 长度为2+6的十六进制数。System.out.println(hashString);
- 常见Hash算法- MD5占内存128位,32字符。- MD5与SHA-1算法现已可破解,不推荐敏感程序使用(MD5更快、SHA-1更安全)。- SHA算法可分为3代,分别为: - SHA-1系列:SHA-1。- SHA-2系列:SHA-224、SHA-256、SHA-384、SHA-512、SHA-512/224、SHA-512/256。- SHA-3系列:SHA3-224、SHA3-256、SHA3-384、SHA3-512。
- hutool提供了一些哈希算法实现
- MD5加密:不加盐版- 【原生】
// 获取MessageDigest实例:MessageDigest md =MessageDigest.getInstance("MD5");// 反复调用update输入数据:md.update("中".getBytes("UTF-8"));md.update("文".getBytes("UTF-8"));// a7bac2239fcdcb3a067903d8077c4a07byte[] result = md.digest();System.out.println(newBigInteger(1, result).toString(16));
- 【hutool】String testStr ="中文";Digester md5 =newDigester(DigestAlgorithm.MD5);String digestHex = md5.digestHex(testStr);// a7bac2239fcdcb3a067903d8077c4a07System.out.println(digestHex);
- MD5加密:加盐版。- 加盐本质:拼接字符串再加密。- 【原生】:直接拼接字符串,将 key 与 原文拼接一起再 encry。- 【Hmac算法】:Hash-based Message Authentication Code,Java原生类,一种更安全的哈希算法,适用于任何可迭代的哈希算法,例如md5、SHA-1等。
KeyGenerator keyGen =KeyGenerator.getInstance("HmacMD5");SecretKey key = keyGen.generateKey();// 打印随机生成的key:byte[] skey = key.getEncoded();System.out.println(newBigInteger(1, skey).toString(16));Mac mac =Mac.getInstance("HmacMD5");mac.init(key);mac.update("HelloWorld".getBytes("UTF-8"));byte[] result = mac.doFinal();System.out.println(newBigInteger(1, result).toString(16));
- 【hutool】byte[] salt ="password".getBytes();HMac hmac =newHMac(HmacAlgorithm.HmacMD5, salt);// b977f4b13f93f549e06140971bded384String hmacHex = hmac.digestHex("中文".getBytes("UTF-8"));System.out.println(macHex);
五、国产加密SM
重要章节
- 简介:- 【hutool】框架工具类SmUtil。- 涉及到国产加密算法,需导入Bouncy Castle包。
<dependency><groupId>org.bouncycastle</groupId><artifactId>bcprov-jdk15on</artifactId><version>1.70</version></dependency>
- 国密加密主要分为:- 非对称加密和签名:SM2、SM9。- 摘要签名算法:SM3- 对称加密:SM1、SM4、SM7、祖冲之密码(ZUC)。
- SM1算法不公开,仅以IP核的方式存在于芯片中,例如智能门锁、智能IC卡等,成本高。
- 非对称加密SM2- 【加密】
String text ="中文";SM2 sm2 =SmUtil.sm2();// 随机公钥加密、私钥解密,自定义直接构造时传入或set__()即可String enStr = sm2.encryptBcd(text,KeyType.PublicKey);byte[] de = sm2.decryptFromBcd(encryptStr,KeyType.PrivateKey);String deStr =newString(decode);
- 【签名】 HexUtil是将字符串或byte数组与16进制表示转换的工具类,效果与使用BigInteger一样,其两者底层实现一样,可以互用。String content ="中文";finalSM2 sm2 =SmUtil.sm2();// 先转为十六进制数据Hex,然后再签名String sign = sm2.signHex(HexUtil.encodeHexStr(content));// trueboolean v = sm2.verifyHex(HexUtil.encodeHexStr(content), sign);
- 摘要加密SM3
String digestHex =SmUtil.sm3("中文");
- 对称加密SM4
String content ="test中文";SymmetricCrypto sm4 =SmUtil.sm4();String encryptHex = sm4.encryptHex(content);String decryptStr = sm4.decryptStr(encryptHex);
本文转载自: https://blog.csdn.net/qq_35760825/article/details/126356394
版权归原作者 ThinkStu 所有, 如有侵权,请联系我们删除。
版权归原作者 ThinkStu 所有, 如有侵权,请联系我们删除。