From Python Django to Clojure, authentication and hashers algorithm

Project AES

Repository: https://github.com/ego/AES

This id old story, about that how we rewrite whole system from Python Django stack to Clojure and do not break authentication. Users login easily to the new system with old cookies with no problems 😄.

Motivation

Django has its own authentication system and cryptography for that. Clojure web frameworks also has own. Question, how to make users happy during new login?

Copy authentication system algorithm 😄

But in a new language in a new system.

The algorithm

In that smooth time for Django it was AES/ECB/PKCS5Padding 128/256 bits So we just need Python/Clojure encrypt/decrypt code.

Python version
File: aes_ecb_pkcs5padding_128_256.py

 1#####################################################################
 2# Python encrypt/decrypt code for AES/ECB/PKCS5Padding 128/256 bits #
 3#                                                                   #
 4# 128bits == 16Bytes == 16 Chars.                                   #
 5# 256bits == 32Bytes == 32 Chars.                                   #
 6#####################################################################
 7
 8
 9import base64
10from Crypto.Cipher import AES
11
12
13CRYPT_KEY = '12345678912345678912345678912345'
14_AES_CIPHER = AES.new(CRYPT_KEY)
15_BLOCK_SIZE = _AES_CIPHER.block_size
16
17
18def pad(s):
19    len_pad = _BLOCK_SIZE - (len(s) % _BLOCK_SIZE)
20    return s + (len_pad * chr(len_pad))
21
22
23def unpad(s):
24    return s[:-ord(s[-1])]
25
26
27def encrypt(s):
28    if not isinstance(s, basestring) or not s:
29        return s
30    pad_text = pad(s.encode('utf-8'))
31    crypt_text = _AES_CIPHER.encrypt(pad_text)
32    return base64.b64encode(crypt_text)
33
34
35def decrypt(s):
36    if not isinstance(s, basestring) or not s:
37        return s
38    try:
39        crypt_text = base64.b64decode(s)
40        pad_text = _AES_CIPHER.decrypt(crypt_text)
41        text = unpad(pad_text).decode('utf-8')
42    except (ValueError, TypeError, UnicodeEncodeError, UnicodeEncodeError):
43        return s
44    return text

Clojure version
File: aes_ecb_pkcs5padding_128_256.clj

 1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 2;; Python/Clojure encrypt/decrypt code for AES/ECB/PKCS5Padding 128/256 bits          ;;
 3;;                                                                                    ;;
 4;; Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy           ;;
 5;; http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html ;;
 6;;                                                                                    ;;
 7;; Copy local_policy.jar and US_export_policy.jar to the $JAVA_HOME/jre/lib/security  ;;
 8;; (Note: these jars will be already there so you have to overwrite them)             ;;
 9;;                                                                                    ;;
10;; Ubuntu:                                                                            ;;
11;; 1. Download and unzip                                                              ;;
12;; 2. sudo cp -r UnlimitedJCEPolicy/* /usr/lib/jvm/java-8-oracle/jre/lib/security     ;;
13;;                                                                                    ;;
14;; AES/ECB/PKCS5Padding 128/256 bits                                                  ;;
15;;                                                                                    ;;
16;; 128bits == 16Bytes == 16 Chars.                                                    ;;
17;; 256bits == 32Bytes == 32 Chars.                                                    ;;
18;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
19
20
21(import (java.security Key)
22        (javax.crypto Cipher)
23        (javax.crypto.spec SecretKeySpec)
24        (org.apache.commons.codec.binary Base64))
25
26(defn- to-bytes [s]
27  (cond
28    (string? s)     (.getBytes s "UTF-8")
29    (sequential? s) (into-array Byte/TYPE (.getBytes s "UTF-8"))
30    :else s))
31
32(defn debase64 [s] (Base64/decodeBase64 (to-bytes s)))
33(defn base64 [s] (Base64/encodeBase64String s))
34
35(defn- ^Key secret-spec
36  [^String s]
37  (SecretKeySpec. (to-bytes s) "AES"))
38
39(defn ^bytes encrypt
40  [^String s ^String key]
41  (let [cipher (doto (Cipher/getInstance "AES/ECB/PKCS5Padding")
42                 (.init Cipher/ENCRYPT_MODE (secret-spec key)))]
43    (.doFinal cipher (to-bytes s))))
44
45(defn ^String decrypt
46  [^bytes buf ^String key]
47  (let [cipher (doto (Cipher/getInstance "AES/ECB/PKCS5Padding")
48                 (.init Cipher/DECRYPT_MODE (secret-spec key)))]
49    (String. (.doFinal cipher buf) "UTF-8")))
50
51(defn encrypt->str [s key] (-> (encrypt s key) base64))
52(defn decrypt->str [s key] (-> (decrypt (debase64 s) key)))

Have fun!