WIP, storing here until I can work on this (:import java.util.Base64 java.security.Key java.security.MessageDigest java.security.spec.AlgorithmParameterSpec javax.crypto.Cipher javax.crypto.AEADBadTagException javax.crypto.spec.SecretKeySpec javax.crypto.spec.GCMParameterSpec) (defn secret->mon-key [s] (SecretKeySpec. (.digest (MessageDigest/getInstance "SHA-256") (bytes-from s)) "AES")) (defn- encrypt [key iv plainbytes] ;; Precisely what happens if I.V. is not correct length? (let [params (GCMParameterSpec. 128 iv) cipher (doto (Cipher/getInstance "AES/GCM/NoPadding") (.init Cipher/ENCRYPT_MODE ^Key key ^AlgorithmParameterSpec params))] ;; The tag is *appended to* the cipherbytes, so drop last 16 bytes ;; (we store tag separately) (let [output (.doFinal cipher plainbytes) k (- (count output) 16)] {:ciphertext (byte-array (take k output)) :tag (byte-array (drop k output))}))) (defn- decrypt [key iv tag cipherbytes] (let [params (GCMParameterSpec. 128 iv) cipher (doto (Cipher/getInstance "AES/GCM/NoPadding") (.init Cipher/DECRYPT_MODE ^Key key ^AlgorithmParameterSpec params))] ;; Working with "our" cipherbytes, the `tag` is not included -- let's do so (.doFinal cipher (byte-array (concat cipherbytes tag))))) Perl: use Bytes::Random::Secure 'random_bytes'; use Crypt::AuthEnc::GCM 'gcm_encrypt_authenticate'; use Digest::SHA 'sha256'; my $iv = random_bytes(16); my $aeskey = <32 random bytes, fx sha256('something-long-and-complex')>; my ($ciphertext, $tag) = gcm_encrypt_authenticate(AES => $aeskey, $iv, undef, $plaintext);