Skip to content

Instantly share code, notes, and snippets.

@sunary
Last active August 4, 2023 09:04
Show Gist options
  • Save sunary/a9edeea337450040769253de1e4eae08 to your computer and use it in GitHub Desktop.
Save sunary/a9edeea337450040769253de1e4eae08 to your computer and use it in GitHub Desktop.
aes256 CBC & CFB
package main
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"errors"
"fmt"
"io"
"strings"
"golang.org/x/crypto/pbkdf2"
)
const BlockSize = 32
func deriveKey(passphrase string, salt []byte) []byte {
// http://www.ietf.org/rfc/rfc2898.txt
if salt == nil {
salt = make([]byte, 8)
// rand.Read(salt)
}
return pbkdf2.Key([]byte(passphrase), salt, 1000, BlockSize, sha256.New)
}
func addBase64Padding(value string) string {
m := len(value) % 4
if m != 0 {
value += strings.Repeat("=", 4-m)
}
return value
}
func removeBase64Padding(value string) string {
return strings.Replace(value, "=", "", -1)
}
func Pad(src []byte) []byte {
padding := aes.BlockSize - len(src)%aes.BlockSize
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
return append(src, padtext...)
}
func Unpad(src []byte) ([]byte, error) {
length := len(src)
unpadding := int(src[length-1])
if unpadding > length {
return nil, errors.New("unpad error. This could happen when incorrect encryption key is used")
}
return src[:(length - unpadding)], nil
}
func encrypt(key []byte, text string) (string, error) {
block, err := aes.NewCipher(key)
if err != nil {
return "", err
}
msg := Pad([]byte(text))
ciphertext := make([]byte, aes.BlockSize+len(msg))
iv := ciphertext[:aes.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
return "", err
}
// cfb := cipher.NewCFBEncrypter(block, iv)
// cfb.XORKeyStream(ciphertext[aes.BlockSize:], []byte(msg))
cbc := cipher.NewCBCEncrypter(block, iv)
cbc.CryptBlocks(ciphertext[aes.BlockSize:], []byte(msg))
finalMsg := removeBase64Padding(base64.URLEncoding.EncodeToString(ciphertext))
return finalMsg, nil
}
func decrypt(key []byte, text string) (string, error) {
block, err := aes.NewCipher(key)
if err != nil {
return "", err
}
decodedMsg, err := base64.URLEncoding.DecodeString(addBase64Padding(text))
if err != nil {
return "", err
}
if (len(decodedMsg) % aes.BlockSize) != 0 {
return "", errors.New("blocksize must be multipe of decoded message length")
}
iv := decodedMsg[:aes.BlockSize]
msg := decodedMsg[aes.BlockSize:]
// cfb := cipher.NewCFBDecrypter(block, iv)
// cfb.XORKeyStream(msg, msg)
cbc := cipher.NewCBCDecrypter(block, iv)
cbc.CryptBlocks(msg, msg)
unpadMsg, err := Unpad(msg)
if err != nil {
return "", err
}
return string(unpadMsg), nil
}
func main() {
key := deriveKey("password", []byte("salt"))
encryptMsg, _ := encrypt(key, "Hello World")
fmt.Println(encryptMsg)
msg, _ := decrypt(key, encryptMsg)
fmt.Println(msg)
}
using System;
using System.IO;
using System.Text;
using System.Security.Cryptography;
public class rfc2898test
{
const string saltStr = "pjico@)!^";
const string password = "pjico@2020";
public static string EncryptString(string text)
{
try
{
byte[] baPwd = Encoding.UTF8.GetBytes(password);
// Hash the password with SHA256
byte[] baPwdHash = SHA256Managed.Create().ComputeHash(baPwd);
byte[] baText = Encoding.UTF8.GetBytes(text);
var baEncrypted = AES_Encrypt(baText, baPwdHash);
string result = Convert.ToBase64String(baEncrypted);
return result;
}
catch (Exception ex)
{
return string.Empty;
}
}
private static byte[] AES_Encrypt(byte[] bytesToBeEncrypted, byte[] passwordBytes)
{
byte[] encryptedBytes = null;
// Set your salt here, change it to meet your flavor:
// The salt bytes must be at least 8 bytes.
byte[] saltBytes = Encoding.UTF8.GetBytes(saltStr);
using (MemoryStream ms = new MemoryStream())
{
using (RijndaelManaged AES = new RijndaelManaged())
{
AES.KeySize = 256;
AES.BlockSize = 128;
var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Mode = CipherMode.CBC;
using (var cs = new CryptoStream(ms, AES.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(bytesToBeEncrypted, 0, bytesToBeEncrypted.Length);
cs.Close();
}
encryptedBytes = ms.ToArray();
}
}
return encryptedBytes;
}
public static string DecryptString(string text)
{
try
{
byte[] baPwd = Encoding.UTF8.GetBytes(password);
// Hash the password with SHA256
byte[] baPwdHash = SHA256Managed.Create().ComputeHash(baPwd);
byte[] baText = Convert.FromBase64String(text);
byte[] baDecrypted = AES_Decrypt(baText, baPwdHash);
string result = Encoding.UTF8.GetString(baDecrypted);
return result;
}
catch (Exception ex)
{
return string.Empty;
}
}
private static byte[] AES_Decrypt(byte[] bytesToBeDecrypted, byte[] passwordBytes)
{
byte[] decryptedBytes = null;
// Set your salt here, change it to meet your flavor:
// The salt bytes must be at least 8 bytes.
byte[] saltBytes = Encoding.UTF8.GetBytes(saltStr);
using (MemoryStream ms = new MemoryStream())
{
using (RijndaelManaged AES = new RijndaelManaged())
{
AES.KeySize = 256;
AES.BlockSize = 128;
var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
Console.WriteLine("key: {0}", Convert.ToBase64String(AES.Key));
Console.WriteLine("IV: {0}", Convert.ToBase64String(AES.IV));
AES.Padding = PaddingMode.None;
//AES.Padding = PaddingMode.Zeros;
AES.Mode = CipherMode.CBC;
using (var cs = new CryptoStream(ms, AES.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(bytesToBeDecrypted, 0, bytesToBeDecrypted.Length);
cs.Close();
}
decryptedBytes = ms.ToArray();
}
}
return decryptedBytes;
}
public static void Main()
{
string enc = EncryptString("Hello World");
Console.WriteLine(enc);
Console.WriteLine(DecryptString(enc));
}
}
// run at: https://dotnetfiddle.net/
package main
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/sha256"
"encoding/base64"
"errors"
"fmt"
"strings"
"golang.org/x/crypto/pbkdf2"
)
const (
pw = "pjico@2020"
st = "pjico@)!^"
)
func deriveKey(passphrase string, salt []byte, l int) []byte {
// http://www.ietf.org/rfc/rfc2898.txt
if salt == nil {
salt = make([]byte, 8)
// rand.Read(salt)
}
return pbkdf2.Key([]byte(passphrase), salt, 1000, l, sha256.New)
}
func addBase64Padding(value string) string {
m := len(value) % 4
if m != 0 {
value += strings.Repeat("=", 4-m)
}
return value
}
func Pad(src []byte) []byte {
padding := aes.BlockSize - len(src)%aes.BlockSize
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
return append(src, padtext...)
}
func Unpad(src []byte) ([]byte, error) {
length := len(src)
if length == 0 {
return nil, errors.New("Emtpy src")
}
unpadding := int(src[length-1])
if unpadding > length {
return nil, errors.New("unpad error. This could happen when incorrect encryption key is used")
}
return src[:(length - unpadding)], nil
}
func encrypt(text string) (string, error) {
key := deriveKey(pw, []byte(st), 32)
iv := deriveKey(pw, []byte(st), 16)
block, err := aes.NewCipher(key)
if err != nil {
return "", err
}
msg := Pad([]byte(text))
ciphertext := make([]byte, len(msg))
// cfb := cipher.NewCFBEncrypter(block, iv)
// cfb.XORKeyStream(ciphertext[aes.BlockSize:], []byte(msg))
cbc := cipher.NewCBCEncrypter(block, iv)
cbc.CryptBlocks(ciphertext, []byte(msg))
finalMsg := base64.StdEncoding.EncodeToString(ciphertext)
return finalMsg, nil
}
func decrypt(text string) (string, error) {
key := deriveKey(pw, []byte(st), 32)
iv := deriveKey(pw, []byte(st), 16)
block, err := aes.NewCipher(key)
if err != nil {
return "", err
}
decodedMsg, err := base64.StdEncoding.DecodeString(addBase64Padding(text))
if err != nil {
return "", err
}
if (len(decodedMsg) % aes.BlockSize) != 0 {
return "", errors.New("blocksize must be multipe of decoded message length")
}
msg := decodedMsg
// cfb := cipher.NewCFBDecrypter(block, iv)
// cfb.XORKeyStream(msg, msg)
cbc := cipher.NewCBCDecrypter(block, iv)
cbc.CryptBlocks(msg, msg)
unpadMsg, err := Unpad(msg)
if err != nil {
return "", err
}
return string(unpadMsg), nil
}
func decryptWithKey(text, kk, ii string) (string, error) {
key, _ := base64.StdEncoding.DecodeString(addBase64Padding(kk))
iv, _ := base64.StdEncoding.DecodeString(addBase64Padding(ii))
block, err := aes.NewCipher(key)
if err != nil {
return "", err
}
decodedMsg, err := base64.StdEncoding.DecodeString(addBase64Padding(text))
if err != nil {
return "", err
}
if (len(decodedMsg) % aes.BlockSize) != 0 {
return "", errors.New("blocksize must be multipe of decoded message length")
}
msg := decodedMsg
// cfb := cipher.NewCFBDecrypter(block, iv)
// cfb.XORKeyStream(msg, msg)
cbc := cipher.NewCBCDecrypter(block, iv)
cbc.CryptBlocks(msg, msg)
unpadMsg, err := Unpad(msg)
if err != nil {
return "", err
}
return string(unpadMsg), nil
}
func main() {
encryptMsg, _ := encrypt("Hello World")
fmt.Println(encryptMsg)
msg, err := decrypt(encryptMsg)
if err != nil {
fmt.Printf("1 decrypt err: %s\n", err.Error())
}
fmt.Println(msg)
sampleKey := "5/3J1iJsIaTtqJ1OrNYf4I9lBNqn4eL8+CKbFK2DkuQ="
sampleIV := "YPfPbCE2hV1iTZJLqq3H+w=="
desMsg := "S0UyKrmw4YO+zhGs4b//nA=="
fmt.Println(desMsg)
msg, err = decryptWithKey(desMsg, sampleKey, sampleIV)
if err != nil {
fmt.Printf("2 decrypt err: %s\n", err.Error())
}
fmt.Println(msg)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment