0

首先,让我说我对 clojure 很陌生,并且已经有一段时间没有对 java 或 jvm 做了很多工作了。

当我尝试解密由同一程序加密的文件时,我收到了 javax.crypto.BadPaddingException。以下是代码:

(ns clojure-crypt-file.core
(:use [clojure.tools.cli :only [cli]])
(:require [clojure.java.io :as io]
      [me.raynes.fs :as fs])
(:import (org.apache.commons.codec.binary Base64)
     (javax.crypto Cipher KeyGenerator SecretKey)
     (javax.crypto.spec SecretKeySpec)
     (java.security SecureRandom))
(:gen-class))

(defn str-to-bytes [s] (.getBytes s "UTF-8"))
(defn bytes-to-str [bs] (apply str (map (comp char byte) bs)))

(defn base64 [b]
  (Base64/encodeBase64String b))

(defn debase64 [s]
  (Base64/decodeBase64 (str-to-bytes s)))

(defn fetch-b64-key [filename]
(let [encoded-key (slurp filename)
     size (count encoded-key)
     ekey-no-newline (apply str (take (dec size) encoded-key))]
     (bytes-to-str (debase64 ekey-no-newline))))

(defn get-raw-key [seed]
  (let [keygen (KeyGenerator/getInstance "AES")
    sr (SecureRandom/getInstance "SHA1PRNG")]
    (.setSeed sr (str-to-bytes seed))
    (.init keygen (count seed) sr)
    (.. keygen generateKey getEncoded)))

(defn get-cipher [mode seed]
  (let [key-spec (SecretKeySpec. (get-raw-key seed) "AES")
    cipher (Cipher/getInstance "AES")]
    (.init cipher mode key-spec) cipher))

(defn encrypt [ba key]
  (let [cipher (get-cipher Cipher/ENCRYPT_MODE key)]
    (.doFinal cipher ba)))


(defn decrypt [enc-buffer key]
  (let [cipher (get-cipher Cipher/DECRYPT_MODE key)]
  (str-to-bytes (String. (.doFinal cipher enc-buffer)))))


(defn encrypt-file [src-file dest-file key-text]
  (let  [in     (new java.io.FileInputStream src-file)
     out    (java.io.BufferedOutputStream. 
             (java.io.FileOutputStream. dest-file))
     buffer (make-array Byte/TYPE 16)
     encbuf (atom nil)]
    (loop [g (.read in buffer) r 0]
  (if-not (= g -1)
    (do
      (reset! encbuf (encrypt buffer key-text));(println r "/" size)
      (.write out (deref encbuf) 0 (count (deref encbuf)))
      (recur (.read in buffer) (+ r g)))))
(.close in)
(.close out)) nil)

(defn decrypt-file [src-file dest-file key-text]
  (let  [in     (new java.io.FileInputStream src-file)
     out    (java.io.BufferedOutputStream. 
             (java.io.FileOutputStream. dest-file))
     buffer (make-array Byte/TYPE 16)
     decbuf (atom nil)]
  (loop [g (.read in buffer) r 0]
    (if-not (= g -1)
      (do
        (reset! decbuf (decrypt buffer key-text));(println r "/" size)
        (.write out (deref decbuf) 0 (count (deref decbuf)))
        (recur (.read in buffer) (+ r g)))))
  (.close in)
  (.close out)) nil)

(defn -main
  "I don't do a whole lot ... yet."
  [& args]
  ;; work around dangerous default behaviour in Clojure
  (alter-var-root #'*read-eval* (constantly false))

 (def ret-val
      (let [[opts extra banner] 
      (cli args
          ["-e" "--encrypt" "Encrypt source file" :flag true :default false]
          ["-d" "--decrypt" "Decrypt source file" :flag true :default false] 
          ["-k" "--keyfile" "Path to keyfile" :default "./keyfile"]
          ["-h" "--help" "Help" :flag true :default false] 
          )]

    ;(println opts extra)
    (if (true? (:help opts)) banner
        (let [
            sfile (first extra) 
            dfile (if (= (count extra) 2) 
              (second extra)
              (if (true? (:encrypt opts))
                  (str (first extra) ".encrypted")
                  (str (first extra) ".decrypted"))) 
            ktext (slurp (:keyfile opts))
              ]

              (if (true? (:encrypt opts)) (encrypt-file sfile dfile ktext)
                                  (decrypt-file sfile dfile ktext))))))

(if (nil? ret-val) (println "Finished.") (println ret-val)))

以下是完整的错误列表:

Exception in thread "main" javax.crypto.BadPaddingException: Given final block not properly padded
  at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:811)
  at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:676)
  at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:317)
  at javax.crypto.Cipher.doFinal(Cipher.java:1813)
  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  at java.lang.reflect.Method.invoke(Method.java:616)
  at clojure.lang.Reflector.invokeMatchingMethod(Reflector.java:93)
  at clojure.lang.Reflector.invokeInstanceMethod(Reflector.java:28)
  at clojure_crypt_file.core$decrypt.invoke(core.clj:54)
  at clojure_crypt_file.core$decrypt_file.invoke(core.clj:83)
  at clojure_crypt_file.core$_main.doInvoke(core.clj:117)
  at clojure.lang.RestFn.applyTo(RestFn.java:137)
  at clojure_crypt_file.core.main(Unknown Source)

在之前的一篇文章中,关于一个实际的 java 程序,有人建议这可能是最后几个字节的数据没有填充缓冲区的问题。我不确定这是否完全正确。不管是什么原因,我不知道如何解决它。在此先感谢您的帮助。

4

1 回答 1

0

BadPaddingException 有两个主要原因:

  1. 您加密/解密不正确,因此填充应该是随机垃圾。

  2. 您在加密时添加了一种填充(或没有),并期望在解密时使用不同类型的填充,因此两者不匹配。

检查的方法是暂时将您的解密方法设置为 expect NoPadding。该设置不会引发错误,因此允许程序继续。 在调试时执行此操作。这将允许您查看解密消息的外观。

如果整个消息是随机垃圾,那么您要么加密要么解密不正确。对于字节级别的加密和解密,校验密钥、IV 模式等都是相同的。

如果消息很清楚,但末尾添加了一些字节,那么这些额外的字节就是填充。检查各种填充类型并将您的解密方法设置为期望该类型的填充。或者将加密和解密方法都设置为使用 PKCS7 填充。

如果没有可见的填充,只有消息,那么您应该在加密之前添加一个完整的填充块 - AES 为 16 个字节。这可能意味着更改加密方法的设置。

恐怕我不知道clojure,所以您必须在手册中查找如何执行此操作的详细信息。

于 2013-05-28T13:47:57.813 回答