Uncategorized

AES Between Python, Go, Javascript(NodeJS) and PHP

AES

Wikipedia: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard. AES needs Key, IV and PKCS.

  • Use 256 bits key length, Python/Go can auto choice by key length your passed. Named `MCRYPT_RIJNDAEL_128` in PHP5, Nodejs/PHP7.1 is aes-256-cbc.
  • Use CBC mode due to ECB no IV.
  • Key is `AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA` (256 bits),IV is `AAAAAAAAAAAAAAAA`(128bits).
  • Python use pycrypto(pip install pycrypto),NodeJS require crypto(npm install crypto),PHP5 need `mcrypt`.

Python

from Crypto.Cipher import AES
import base64


def _pad(s): return s + (AES.block_size - len(s) % AES.block_size) * chr(AES.block_size - len(s) % AES.block_size) 
def _cipher():
    key = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
    iv = 'AAAAAAAAAAAAAAAA'
    return AES.new(key=key, mode=AES.MODE_CBC, IV=iv)

def encrypt_token(data):
    return _cipher().encrypt(_pad(data))
    
def decrypt_token(data):
    return _cipher().decrypt(data)

if __name__ == '__main__':
    print('Python encrypt: ' + base64.b64encode(encrypt_token('dmyz.org')))
    print('Python decrypt: ' + decrypt_token(base64.b64decode('FSfhJ/gk3iEJOPVLyFVc2Q==')))

Go

package main

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"encoding/base64"
	"fmt"
)

var (
	key   = []byte("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
	iv    = []byte("AAAAAAAAAAAAAAAA")
	block cipher.Block
)

func init() {
	block, _ = aes.NewCipher(key)
}

func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
	padding := blockSize - len(ciphertext)%blockSize
	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
	return append(ciphertext, padtext...)
}

func EncryptToken(key, iv, data []byte) string {
	paddingData := PKCS5Padding(data, block.BlockSize())

	cipherData := make([]byte, len(paddingData))
	mode := cipher.NewCBCEncrypter(block, iv)
	mode.CryptBlocks(cipherData, paddingData)
	encoded := base64.StdEncoding.EncodeToString(cipherData)
	return encoded
}
func DecryptToken(key []byte, iv []byte, data string) string {
	decoded, err := base64.StdEncoding.DecodeString(data)
	if err != nil {
		panic(err)
	}
	mode := cipher.NewCBCDecrypter(block, iv)
	mode.CryptBlocks(decoded, decoded)
	if err != nil {
		panic(err)
	}
	return string(decoded)
}

func main() {
	fmt.Printf("Go encrypt: %s\n", EncryptToken(key, iv, []byte("dmyz.org")))
	fmt.Printf("Go decrypt: %s\n", DecryptToken(key, iv, "FSfhJ/gk3iEJOPVLyFVc2Q=="))
}

Javascript(NodeJS)

var crypto = require('crypto'),
  key = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
  iv = 'AAAAAAAAAAAAAAAA';

function encrypt_token(data) {
  var cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
  cipher.update(data, 'binary', 'base64');
  return cipher.final('base64');
}

function decrypt_token(data) {
  var decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
  decipher.update(data, 'base64', 'binary');
  return decipher.final('binary');
}

console.log('NodeJS encrypt: ', encrypt_token('dmyz.org'));
console.log('NodeJS decrypt: ', decrypt_token('FSfhJ/gk3iEJOPVLyFVc2Q=='));
// NodeJS V0.8+ https://github.com/joyent/node/wiki/Api-changes-between-v0.8-and-v0.10

var crypto = require('crypto'),
  key = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
  iv = 'AAAAAAAAAAAAAAAA';

function encrypt_token(data) {
  var encipher = crypto.createCipheriv('aes-256-cbc', key, iv),
    buffer = Buffer.concat([
      encipher.update(data),
      encipher.final()
    ]);
  return buffer.toString('base64');
}

function decrypt_token(data) {
  var decipher = crypto.createDecipheriv('aes-256-cbc', key, iv),
    buffer = Buffer.concat([
      decipher.update(Buffer.from(data, 'base64')),
      decipher.final()
    ]);
  return buffer.toString();
}

console.log('NodeJS encrypt: ', encrypt_token('dmyz.org'));
console.log('NodeJS decrypt: ', decrypt_token('FSfhJ/gk3iEJOPVLyFVc2Q=='));

PHP

key, OPENSSL_RAW_DATA, $this->iv);
        $padding = 16 - (strlen($data) % 16);
        $data .= str_repeat(chr($padding), $padding);
        return mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $this->key, $data, MCRYPT_MODE_CBC, $this->iv);
    }

    function decryptToken($data)
    {
        // Mcrypt library has been DEPRECATED since PHP 7.1, use openssl:
        // return openssl_decrypt(base64_decode($data), 'aes-256-cbc', $this->key, OPENSSL_RAW_DATA, $this->iv);
        $data = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $this->key, base64_decode($data), MCRYPT_MODE_CBC, $this->iv);
        $padding = ord($data[strlen($data) - 1]);
        return substr($data, 0, -$padding);
    }
}

if (php_sapi_name() === 'cli')
{
    $aes = new AES();
    echo ('PHP encrypt: '.base64_encode($aes->encryptToken('dmyz.org')))."\n";
    echo ('PHP decrypt: '.$aes->decryptToken('FSfhJ/gk3iEJOPVLyFVc2Q=='))."\n";
}

Testing

Environment:

  • Ubuntu 14.04.2
  • Go 1.5
  • Python 2.7.6
  • NodeJS v0.10.25
  • PHP 5.5.9
python test.py && go run main.go && nodejs test.js && php test.php

# Python encrypt: FSfhJ/gk3iEJOPVLyFVc2Q==
# Python decrypt: dmyz.org
# Go encrypt: FSfhJ/gk3iEJOPVLyFVc2Q==
# Go decrypt: dmyz.org
# Nodejs encrypt:  FSfhJ/gk3iEJOPVLyFVc2Q==
# Nodejs decrypt:  dmyz.org
# PHP encrypt: FSfhJ/gk3iEJOPVLyFVc2Q==
# PHP decrypt: dmyz.org
avatar

This site uses Akismet to reduce spam. Learn how your comment data is processed.

  Subscribe  
newest oldest most voted
Notify of
Saran Kumar P
Guest
Saran Kumar P

Excellent post, I was searched 4 days for this solution. I have found one drawback though. unpad is missing in the python function.

def unpad(s): return s[:-ord(s[len(s) – 1:])]

Small modification in the main function.

if __name__ == ‘__main__’:
encrypted = base64.b64encode(encrypt_token(‘TestingPython’))
encrypted = encrypted.decode(‘utf-8’)
print(‘Python encrypt: ‘ , encrypted )
print(‘Python decrypt: ‘ , unpad(decrypt_token(base64.b64decode(encrypted)).decode(‘utf-8’)))