Last active
November 6, 2024 08:13
-
-
Save willshiao/f4b03650e5a82561a460b4a15789cfa1 to your computer and use it in GitHub Desktop.
AES 256-CFB in Node.js, Go, and Python
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
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 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 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() |
but if we decrypt with invalid cipher in nodejs return look like this value. ,{!�ߺ`�KQ��mh�
So how do I make sure it's a invalid cipher text ? because both success and failed scenarios return a string
with JAVA
i use the lib : implementation('commons-codec:commons-codec:1.15')
other,You can change hex encoding to base64 encoding
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.Arrays;
public class AesUtil {
static String key = "SecretKey";
private static byte[] decrypt(byte[] payload, byte[] key) throws NoSuchPaddingException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, InvalidKeyException {
SecretKeySpec key_spec = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/CFB/NoPadding");
int block_size = cipher.getBlockSize();
IvParameterSpec iv = new IvParameterSpec(Arrays.copyOf(payload, block_size));
byte[] decryption_data = Arrays.copyOfRange(payload, block_size, payload.length);
cipher.init(Cipher.DECRYPT_MODE, key_spec, iv);
return cipher.doFinal(decryption_data);
}
public static String decrypt(String payload) {
try {
byte[] decrypt = new byte[0];
try {
decrypt = decrypt(Hex.decodeHex(payload), sha256(key));
} catch (DecoderException e) {
e.printStackTrace();
}
return new String(decrypt);
} catch (NoSuchPaddingException | NoSuchAlgorithmException | IllegalBlockSizeException | BadPaddingException | InvalidAlgorithmParameterException | InvalidKeyException e) {
e.printStackTrace();
}
return null;
}
private static byte[] encrypt(byte[] encoded_payload, byte[] key) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, ShortBufferException, IllegalBlockSizeException, BadPaddingException, ShortBufferException {
SecureRandom rand = new SecureRandom();
SecretKeySpec key_spec = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/CFB/NoPadding");
int block_size = cipher.getBlockSize();
byte[] buffer = new byte[block_size];
rand.nextBytes(buffer);
IvParameterSpec iv = new IvParameterSpec(buffer);
buffer = Arrays.copyOf(buffer, block_size + encoded_payload.length);
cipher.init(Cipher.ENCRYPT_MODE, key_spec, iv);
cipher.doFinal(encoded_payload, 0, encoded_payload.length, buffer, block_size);
return buffer;
}
public static String encrypt(String text) {
try {
byte[] encrypted = encrypt(text.getBytes(StandardCharsets.UTF_8), sha256(key));
return Hex.encodeHexString(encrypted);
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (ShortBufferException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
//String encrypted = encrypt("123345");
String decrypt = decrypt("25ae775850d949f1d6b1161180d23c10e4f5646e2635b8d7cd");
System.out.println(decrypt);
}
public static byte[] sha256(String value) {
if (value == null || value.length() == 0) {
return null;
}
MessageDigest messageDigest = null;
try {
messageDigest = MessageDigest.getInstance("sha-256");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
byte[] buffer = value.getBytes(StandardCharsets.UTF_8);
buffer = messageDigest.digest(buffer);
return buffer;
}
}
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
Thanks for this, you seems to be the only one knowing what you are doing on internet about crypting stuff