使用 Groovy 进行加密和解密
作者:Paul King
发布时间:2022-09-19 02:34PM
受最近一篇 博客文章 的启发,以下是一个使用 Groovy 进行加密和解密的示例。
使用 JDK 加密类
首先,我们需要一些要加密的文本。我们将使用上面提到的博客文章中的摘录
var text = 'Contrary to popular belief, Lorem Ipsum is not simply random text. It has \
roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old.'
接下来,我们将为我们的密码实例创建一个工厂,生成一个密钥,并设置一个初始化向量。
首先,是密码工厂
var factory = { Cipher.getInstance('AES/CBC/PKCS5Padding') }
接下来我们生成我们的密钥。我们的密钥就是我们的密码。只有拥有密码的人才能解密加密的消息。我们可以使用任何随机位作为我们的密钥,但像密码一样,我们希望选择一个强密钥而不是弱密钥。加密库提供类来生成此类密钥。我们只需要提供密钥大小。AES 支持 128 位、192 位和 256 位密钥。这里我们将选择 192 位
var key = generateKey('AES', 192)
我们的代码使用此辅助方法
def generateKey(String algorithm, Integer size) {
var generator = KeyGenerator.getInstance(algorithm)
generator.init(size)
generator.generateKey()
}
接下来,我们生成一个初始化向量
var ivParameterSpec = randomParameterSpec(factory)
它使用此辅助方法(我们使用算法块大小作为我们的初始化向量大小)
def randomParameterSpec(Closure<Cipher> factory) {
var block = new byte[factory().blockSize]
SecureRandom.instanceStrong.nextBytes(block)
new IvParameterSpec(block)
}
一个 初始化向量 用于在输入中引入一些额外的随机性,以避免重复模式导致加密字节中的重复模式。
有了这些,我们几乎可以开始加密或解密了,但首先,让我们定义两个辅助方法
def encrypt(byte[] bytes, Key key, IvParameterSpec spec, Closure<Cipher> factory) {
var cipher = factory()
cipher.init(ENCRYPT_MODE, key, spec)
cipher.doFinal(bytes)
}
def decrypt(byte[] bytes, Key key, IvParameterSpec spec, Closure<Cipher> factory) {
var cipher = factory()
cipher.init(DECRYPT_MODE, key, spec)
cipher.doFinal(bytes)
}
以下是我们如何加密和解密
var encrypted = encrypt(text.bytes, key, ivParameterSpec, factory)
println "Encrypted bytes : $encrypted"
println "Encrypted text : ${new String(encrypted)}"
var decrypted = decrypt(encrypted, key, ivParameterSpec, factory)
println "Decrypted bytes : $decrypted"
println "Decrypted text : ${new String(decrypted)}"
其输出如下
Encrypted bytes : [-117, 36, 18, 69, -101, -8, 35, 93, -102, -49, -12, …, -19, -100]
Encrypted text : ‹$E›ø#]šÏôæ”Á˜çp^µ³=L(Ö^_ŒC>CIË„ö,1É8ÆŸ.Š?vßG,Èw‰å¼zÜf>?µ›D¹éÆk€ °˜2êÔ}í©àhl$>?¹¡Kå3ÔO?±&…êî¶Ê–¾°®q®à—0ú‘ÔhO<H¦ç®Ç”ÈhAëjó QPyƒy6Ĥ*´un¼ï¯m¨´ÙjeJtëº\ó6ƪKªœíœ
Decrypted bytes : [67, 111, 110, 116, 114, 97, 114, 121, 32, 116, 111, 32, …, 100, 46]
Decrypted text : Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old.
我们可以看到,一切按预期工作,因为最终输出与我们原始的输入文本匹配。
使用 Bouncy Castle 库
import org.bouncycastle.jce.provider.BouncyCastleProvider
var bc = new BouncyCastleProvider()
factory = { Cipher.getInstance('CAST5', bc) }
key = generateKey('HmacSHA1', 128)
ivParameterSpec = randomParameterSpec(factory)
CAST5 是某些版本的 GPG 和 PGP 中使用的默认算法。它不是 JDK 中默认包含的,因此我们将使用 Bouncy Castle 库。
注意
我们现在像以前一样加密和解密
encrypted = encrypt(text.bytes, key, ivParameterSpec, factory)
println "Encrypted text : ${new String(encrypted)}"
decrypted = decrypt(encrypted, key, ivParameterSpec, factory)
println "Decrypted text : ${new String(decrypted)}"
其输出如下
Encrypted text : Mªá?r?v9£÷~4µT'›ÙÝÁl¿Þg¾0ñŽ¡?Ü=³9Q¬»3«ÖÁ¡µ ¾@4÷`FñÙŠfø7¥#›v¤Í–‰¼Ü¢ƒE6ôŽTÙlæÏz>o?àL›¡¢z1nÖo9]šOÔ¼SÔOÍ#Ý7LœÀî}ó5m%q•»l%/AWT´¢zH#t솱l¶£—Œ«©wˆÃ®>®Ü6ër-E
Decrypted text : Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old.
其他有用功能
传递像我们的密钥或加密数据这样的二进制数据,本身也存在问题。Groovy 提供扩展方法来对这种数据进行 `encode`(以及相应的 `decode` 方法)。例如,我们可以用不同的方式对我们的密钥进行编码
var keyBytes = key.encoded
println keyBytes.encodeHex()
println keyBytes.encodeBase64()
println keyBytes.encodeBase64Url()
其输出如下(密钥是随机的,因此每次运行的输出都会有所不同)
85a0d3f0ce0cbe6402dc9579fbffcf1d
haDT8M4MvmQC3JV5+//PHQ==
haDT8M4MvmQC3JV5-__PHQ
Groovy 还为各种校验和提供了扩展方法(但在安全敏感场景中,你可能想看看更强的校验和算法)
println "SHA256 : ${text.sha256()}"
println "MD5 : ${text.md5()}"
其输出如下
SHA256 : ccb184e35e4c32bafc730d84ec924ea2980035ea5fadb012e3b2b31abf4323c9
MD5 : 46c61a174c2dc99204521ca89f09f63c
如果你要加密和解密整个文件,JDK 也有专门的类,也很容易从 Groovy 中使用。目前就到这里。
参考资料
-
加密库比较(维基百科)
-
Bouncy castle 在 Maven 中央仓库
-
Java 中的 AES 加密和解密(Baeldung)
-
Java 中加密的初始化向量(Baeldung)
-
Java 中的 3DES(Baeldung)
结论
我们简要地介绍了使用 Apache Groovy 进行加密和解密。