Last active
August 28, 2017 15:16
-
-
Save postspectacular/a5497ed6b03268dac273 to your computer and use it in GitHub Desktop.
GPG encrypt/decrypt files w/ Clojure (requires Bouncycastle & unlimited JCE). Based on: http://fastpicket.com/blog/2012/05/14/easy-pgp-in-java-bouncy-castle/ & http://sloanseaman.com/wordpress/2012/05/13/revisited-pgp-encryptiondecryption-in-java/#comment-1432
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;; add lein dep: | |
;; [org.bouncycastle/bcpg-jdk15on "1.51"] | |
;; export gpg public & private keys to armored files: | |
;; gpg -ao pub.asc --export XXXXXXXX | |
;; gpg -ao pk.asc --export-secret-keys XXXXXXXX | |
;; usage: | |
;; (encrypt "foo.txt" "foo.gpg" (pub-key "pub.asc")) | |
;; (decrypt "foo.gpg" (io/output-stream "foo.txt") (secret-key "ks-pk.asc") "passphrase") | |
(import | |
'[org.bouncycastle.openpgp | |
PGPObjectFactory | |
PGPPublicKeyRingCollection PGPSecretKeyRingCollection | |
PGPPublicKey PGPSecretKey | |
PGPPublicKeyRing PGPSecretKeyRing | |
PGPUtil | |
PGPCompressedData | |
PGPEncryptedData | |
PGPLiteralData | |
PGPEncryptedDataList | |
PGPCompressedDataGenerator | |
PGPEncryptedDataGenerator] | |
'[org.bouncycastle.jce.provider BouncyCastleProvider] | |
'[org.bouncycastle.openpgp.operator.bc | |
BcPGPDataEncryptorBuilder | |
BcPGPDigestCalculatorProvider | |
BcPBESecretKeyDecryptorBuilder | |
BcPublicKeyDataDecryptorFactory | |
BcPublicKeyKeyEncryptionMethodGenerator] | |
'[java.io InputStream OutputStream ByteArrayOutputStream] | |
'[java.security SecureRandom Security]) | |
(require '[clojure.java.io :as io]) | |
(Security/addProvider (BouncyCastleProvider.)) | |
(defn pub-key [path] | |
(with-open [ks (io/input-stream path)] | |
(let [rcoll (-> ks (PGPUtil/getDecoderStream) (PGPPublicKeyRingCollection.))] | |
(->> (for [ring (-> rcoll (.getKeyRings) (iterator-seq)) | |
key (-> ring (.getPublicKeys) (iterator-seq))] | |
key) | |
(some #(if (.isEncryptionKey ^PGPPublicKey %) %)))))) | |
(defn secret-key [path] | |
(with-open [ks (io/input-stream path)] | |
(let [rcoll (-> ks (PGPUtil/getDecoderStream) (PGPSecretKeyRingCollection.))] | |
(->> (for [ring (-> rcoll (.getKeyRings) (iterator-seq)) | |
key (-> ring (.getSecretKeys) (iterator-seq))] | |
key) | |
(some | |
#(if (and (.isSigningKey %) (not (.. % (getPublicKey) (isRevoked)))) %)))))) | |
(defn file->zipped-bytes [src] | |
(with-open [bytes (ByteArrayOutputStream.) | |
com (PGPCompressedDataGenerator. PGPCompressedData/ZIP)] | |
(PGPUtil/writeFileToLiteralData (.open com bytes) PGPLiteralData/BINARY (io/file src)) | |
(.close com) | |
(.toByteArray bytes))) | |
(defn encrypt | |
[src dest ^PGPPublicKey pub-key] | |
(let [bytes (file->zipped-bytes src) | |
enc (doto (BcPGPDataEncryptorBuilder. PGPEncryptedData/AES_256) | |
(.setWithIntegrityPacket true) | |
(.setSecureRandom (SecureRandom.))) | |
gen (doto (PGPEncryptedDataGenerator. enc) | |
(.addMethod (BcPublicKeyKeyEncryptionMethodGenerator. pub-key)))] | |
(with-open [out (.open gen (io/output-stream dest) (long (alength bytes)))] | |
(.write out bytes)))) | |
(defn extract-private-key | |
[^PGPSecretKey key ^chars pass] | |
(.extractPrivateKey | |
key (-> (BcPGPDigestCalculatorProvider.) | |
(BcPBESecretKeyDecryptorBuilder.) | |
(.build pass)))) | |
(defn decrypt | |
[src ^OutputStream out sec-key pass] | |
(with-open [in (io/input-stream src) | |
out out] | |
(let [pk (extract-private-key sec-key (char-array pass)) | |
in (-> in (PGPUtil/getDecoderStream) (PGPObjectFactory.)) | |
enc (.nextObject in) | |
enc (if (instance? PGPEncryptedDataList enc) enc (.nextObject in)) | |
pbe (-> enc (.getEncryptedDataObjects) (.next)) | |
msg (-> (.getDataStream pbe (BcPublicKeyDataDecryptorFactory. pk)) | |
(PGPObjectFactory.) | |
(.nextObject)) | |
msg (if (instance? PGPCompressedData msg) | |
(-> msg (.getDataStream) (PGPObjectFactory.) (.nextObject)) | |
msg)] | |
(if (instance? PGPLiteralData msg) | |
(with-open [ld (.getInputStream ^PGPLiteralData msg)] | |
(loop [] | |
(let [c (.read ^InputStream ld)] | |
(when (>= c 0) | |
(.write out c) | |
(recur))))))))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment