尝试使用Clojure和一点点AES进行AES加密


我在Clojure中编写了用于AES加密的代码。
这并不困难,因为可以使用Java标准库来完成。

aes.clj

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
(import '[java.security SecureRandom Key]
        '[javax.crypto KeyGenerator Cipher]
        '[javax.crypto.spec IvParameterSpec]
        '[java.security.spec AlgorithmParameterSpec])

(defn ^bytes rand-bytes [n]
  (let [b (byte-array n)]
    (.. (SecureRandom.) (nextBytes b))
    b))

(defn ^Key aes-genkey [^long bits]
  {:pre[(#{128 192 256} bits)]}
  (let [kg (KeyGenerator/getInstance "AES")]
    (.init kg bits)
    (.generateKey kg)))

(def ^:dynamic *aes-mode* "AES/CBC/PKCS5Padding")

(defn- ^bytes aes-*code [^long mode ^bytes bytes ^Key key ^AlgorithmParameterSpec spec]
  (let [c (Cipher/getInstance *aes-mode*)]
    (if spec
      (.init c mode key spec)
      (.init c mode key))
    (.doFinal c bytes)))

(defn ^bytes aes-encode [^bytes bytes ^Key key & [^AlgorithmParameterSpec spec]]
  (aes-*code Cipher/ENCRYPT_MODE bytes key spec))

(defn ^bytes aes-decode [^bytes bytes ^Key key & [^AlgorithmParameterSpec spec]]
  (aes-*code Cipher/DECRYPT_MODE bytes key spec))

如何使用

1
2
3
4
5
6
7
8
(def k (aes-genkey 128))
(def iv (rand-bytes 16))
(def encoded (aes-encode (byte-array 2) k (IvParameterSpec. iv)))
(def decoded (aes-decode encoded k (IvParameterSpec. iv)))

(binding [*aes-mode* "AES/ECB/NoPadding"]
  (let [e (aes-encode (byte-array 16) k)]
    (seq (aes-decode e k))))

似乎可以为

*aes-mode*指定以下内容。

  • AES / CBC /无填充
  • AES / CBC / PKCS5填充
  • AES / ECB /无填充
  • AES / ECB / PKCS5填充

CBC是一种方法,即使AES密钥相同,每次执行加密时,都通过更改IV(初始化矢量)来生成相同的明文但不同的密文。每次解密时,都必须教导解密器IV。
在ECB中,如果密钥相同,则以相同的明文输出相同的密文,因此该密文较弱。

仅当确定明文是16字节AES块长度的倍数时,才可以使用NoPadding。如果不是16的倍数,则将引发异常。
当明文不总是16字节的倍数时,使用PKCS5Padding。 (作为一种方法,将字节数除以16,然后在余数为1时添加15字节的0x0f,在余数为2时添加14字节的0x0e,在余数为15时添加1字节的0x01,在余数为它是0,加上16个字节的0x10)

*官方名称似乎是PKCS#7 Padding。 PKCS#5填充是DES时块长度为8字节时的名称。我认为这是Bouncy Castle中的PKCS7名称。
*顺便说一句,如果在解密过程中发生与Padding相关的异常,则在大多数情况下,这不是Padding错误,而是解密失败(密钥,IV错误)。

在Oracle JDK中,默认情况下AES密钥长度只能为128位。如果要以192、256位使用它,则需要下载Java密码学扩展(JCE)无限强度管辖权策略文件,并将其放置(覆盖)在jre / lib / security中。
如果不这样做,则会出现类似java.security.InvalidKeyException: Illegal key size的异常。