clojure文件加密程序中的javax.crypto.BadPaddingException

clojure文件加密程序中的javax.crypto.BadPaddingException,java,encryption,file-io,clojure,aes,Java,Encryption,File Io,Clojure,Aes,首先,让我说我对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 (

首先,让我说我对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程序,有人认为这可能是最后几个字节的数据没有填充缓冲区的问题。我不确定这到底是不是真的。不管是什么原因,我都不知道该怎么解决。提前感谢您的帮助。

导致BadPaddingException的主要原因有两个:

  • 您加密/解密不正确,因此应该填充的内容是随机垃圾

  • 您在加密时添加了一种填充(或无填充),而在解密时需要另一种填充,因此这两种填充不匹配

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

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

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

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


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

    我想补充一点,上面的异常是在运行时生成的,而不是编译时生成的。我现在有了相关的工作代码,我将试着在某个时候发布它。我刚刚完成了对加密和解密函数的彻底测试,使用的字符串(随机生成)小到足以放入内存(高达18MB)。所有测试均成功。我确信我没有正确加密/解密文件I/O。我的问题是我不确定如何从文件中读取块,转换该块(在本例中为加密/解密),然后将转换后的块写入另一个文件,重复这些步骤,直到到达源文件的结尾。设置一个小文本文件作为测试,然后尝试从磁盘对其进行加密/解密。在Java中,处理未知长度数据的方法是使用流。我不确定clojure是否允许访问Java流。