Last active
January 10, 2025 18:00
-
-
Save willshiao/f4b03650e5a82561a460b4a15789cfa1 to your computer and use it in GitHub Desktop.
AES 256-CFB in Node.js, Go, and Python
This file contains hidden or 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
package main | |
import ( | |
"crypto/aes" | |
"crypto/cipher" | |
"crypto/rand" | |
"crypto/sha256" | |
"encoding/base64" | |
"fmt" | |
"io" | |
) | |
func keyEncrypt(keyStr string, cryptoText string) string { | |
keyBytes := sha256.Sum256([]byte(keyStr)) | |
return encrypt(keyBytes[:], cryptoText) | |
} | |
// encrypt string to base64 crypto using AES | |
func encrypt(key []byte, text string) string { | |
plaintext := []byte(text) | |
block, err := aes.NewCipher(key) | |
if err != nil { | |
panic(err) | |
} | |
// The IV needs to be unique, but not secure. Therefore it's common to | |
// include it at the beginning of the ciphertext. | |
ciphertext := make([]byte, aes.BlockSize+len(plaintext)) | |
iv := ciphertext[:aes.BlockSize] | |
if _, err := io.ReadFull(rand.Reader, iv); err != nil { | |
panic(err) | |
} | |
stream := cipher.NewCFBEncrypter(block, iv) | |
stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext) | |
return base64.StdEncoding.EncodeToString(ciphertext) | |
} | |
func keyDecrypt(keyStr string, cryptoText string) string { | |
keyBytes := sha256.Sum256([]byte(keyStr)) | |
return decrypt(keyBytes[:], cryptoText) | |
} | |
// decrypt from base64 to decrypted string | |
func decrypt(key []byte, cryptoText string) string { | |
ciphertext, err := base64.StdEncoding.DecodeString(cryptoText) | |
if err != nil { | |
panic(err) | |
} | |
block, err := aes.NewCipher(key) | |
if err != nil { | |
panic(err) | |
} | |
// The IV needs to be unique, but not secure. Therefore it's common to | |
// include it at the beginning of the ciphertext. | |
if len(ciphertext) < aes.BlockSize { | |
panic("ciphertext too short") | |
} | |
iv := ciphertext[:aes.BlockSize] | |
ciphertext = ciphertext[aes.BlockSize:] | |
stream := cipher.NewCFBDecrypter(block, iv) | |
// XORKeyStream can work in-place if the two arguments are the same. | |
stream.XORKeyStream(ciphertext, ciphertext) | |
return fmt.Sprintf("%s", ciphertext) | |
} | |
func main() { | |
encrypted := "lIR3JIHpomC5Zm8sjy29D/xFcXUX0c/4vQ==" | |
fmt.Println(encrypted) | |
fmt.Println(keyDecrypt("SecretKey", encrypted)) | |
} |
This file contains hidden or 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
'use strict'; | |
const crypto = require('crypto'); | |
const algorithm = 'aes-256-cfb'; | |
function encryptText(keyStr, text) { | |
const hash = crypto.createHash('sha256'); | |
hash.update(keyStr); | |
const keyBytes = hash.digest(); | |
const iv = crypto.randomBytes(16); | |
const cipher = crypto.createCipheriv(algorithm, keyBytes, iv); | |
console.log('IV:', iv); | |
let enc = [iv, cipher.update(text, 'utf8')]; | |
enc.push(cipher.final()); | |
return Buffer.concat(enc).toString('base64'); | |
} | |
function decryptText(keyStr, text) { | |
const hash = crypto.createHash('sha256'); | |
hash.update(keyStr); | |
const keyBytes = hash.digest(); | |
const contents = Buffer.from(text, 'base64'); | |
const iv = contents.slice(0, 16); | |
const textBytes = contents.slice(16); | |
const decipher = crypto.createDecipheriv(algorithm, keyBytes, iv); | |
let res = decipher.update(textBytes, '', 'utf8'); | |
res += decipher.final('utf8'); | |
return res; | |
} | |
const encrypted = encryptText('SecretKey', 'It works!'); | |
console.log('Encrypted: ', encrypted); | |
const decrypted = decryptText('SecretKey', encrypted); | |
console.log('Decrypted: ', decrypted); |
This file contains hidden or 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
import base64 | |
import hashlib | |
from Crypto.Cipher import AES | |
from Crypto import Random | |
def encrypt(keyStr, text): | |
private_key = hashlib.sha256(keyStr.encode()).digest() | |
rem = len(text) % 16 | |
padded = str.encode(text) + (b'\0' * (16 - rem)) if rem > 0 else str.encode(text) | |
iv = Random.new().read(AES.block_size) | |
cipher = AES.new(private_key, AES.MODE_CFB, iv, segment_size=128) | |
enc = cipher.encrypt(padded)[:len(text)] | |
return base64.b64encode(iv + enc).decode() | |
def decrypt(keyStr, text): | |
private_key = hashlib.sha256(keyStr.encode()).digest() | |
text = base64.b64decode(text) | |
iv, value = text[:16], text[16:] | |
rem = len(value) % 16 | |
padded = value + (b'\0' * (16 - rem)) if rem > 0 else value | |
cipher = AES.new(private_key, AES.MODE_CFB, iv, segment_size=128) | |
return (cipher.decrypt(padded)[:len(value)]).decode() | |
def main(): | |
encrypted = 'lIR3JIHpomC5Zm8sjy29D/xFcXUX0c/4vQ==' | |
print(encrypted) | |
print(decrypt('SecretKey', encrypted)) | |
if __name__== '__main__': | |
main() |
Thanks a lot! I had to do both encryption and decryption with js and python, and this code was very helpful.
Only one point needed to be fixed in the py code.
enc = cipher.encrypt(padded)[:len(text)]
change to
enc = cipher.encrypt(padded)[:len(str.encode(text)]
By changing this, multi-byte characters can be properly encrypted and decrypted.
this is how to do it with crypto-js
. Very painful for me to find it our
import CryptoJS from 'crypto-js';
export function encryptText(keyStr, text) {
const private_key = CryptoJS.SHA256(keyStr).toString(CryptoJS.enc.Latin1);
const rem = text.length % 16;
const padded = CryptoJS.enc.Latin1.parse(text.padEnd(text.length + (16 - rem), '\0'));
const iv = CryptoJS.lib.WordArray.random(16);
const cipher = CryptoJS.AES.encrypt(padded, CryptoJS.enc.Latin1.parse(private_key), {
iv: iv,
mode: CryptoJS.mode.CFB,
padding: CryptoJS.pad.NoPadding,
segmentSize: 128,
});
const ciphertext = iv.concat(cipher.ciphertext);
return ciphertext.toString(CryptoJS.enc.Base64);
}
export const decryptText = (keyStr, text) => {
const private_key = CryptoJS.SHA256(keyStr).toString(CryptoJS.enc.Latin1);
const encrypted = CryptoJS.enc.Base64.parse(text);
const iv = encrypted.clone().words.slice(0, 4);
const ciphertext = encrypted.clone().words.slice(4);
const cipherParams = {
ciphertext: CryptoJS.lib.WordArray.create(ciphertext),
};
const decrypted = CryptoJS.AES.decrypt(cipherParams, CryptoJS.enc.Latin1.parse(private_key), {
iv: CryptoJS.lib.WordArray.create(iv),
mode: CryptoJS.mode.CFB,
padding: CryptoJS.pad.ZeroPadding,
segmentSize: 128,
});
const decryptedText = CryptoJS.enc.Utf8.stringify(decrypted);
return decryptedText;
};
How can i write this in python?
function encryptAES(plaintext, key, iv) {
var keyUtf8 = CryptoJS.enc.Utf8.parse(key);
var encrypted = CryptoJS.AES.encrypt(plaintext, keyUtf8, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
return iv.concat(encrypted.ciphertext).toString(CryptoJS.enc.Base64);
};
var iv1 = CryptoJS.lib.WordArray.random(16);
var iv2 = CryptoJS.lib.WordArray.random(16);
iv1.words[0] &= 0xFF00FFFF;
iv2.words[0] &= 0xFF00FFFF;
loginDetails.username = encryptAES(email, captchaKey, iv1);
loginDetails.password = encryptAES(password, captchaKey, iv2);
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
with JAVA
i use the lib :
implementation('commons-codec:commons-codec:1.15')
other,You can change hex encoding to base64 encoding