兼容于golang语言版本:https://github.com/eliben/code-for-blog/blob/master/2019/aes-encrypt-file/aes-file.go
使用aes-256-cbc加密,加密文件前8字节为原始文件大小数字,跟着是16字节随机字节用作iv,再跟着是先补全的明文加密内容。
<?php
function decrypt($key, $infilename, $outfilename = "") {
if (!$outfilename) {
$outfilename = $infilename.'.dec';
}
$cipher_str = file_get_contents($infilename);
if (!$cipher_str) {
die("empty file".PHP_EOL);
}
$file_size = substr($cipher_str, 0, 8);
$file_size = unpack("P", $file_size);
$iv = substr($cipher_str, 8, 16);
$cipher_str = substr($cipher_str, 24);
$plain_str = openssl_decrypt($cipher_str, "aes-256-cbc", $key, 3, $iv);
$plain_str = substr($plain_str, 0, $file_size[1]);
file_put_contents($outfilename, $plain_str);
echo "decrypted output file ".$outfilename.PHP_EOL;
}
function encrypt($key, $infilename, $outfilename) {
if (!$outfilename) {
$outfilename = $infilename.'.enc';
}
$plain_str = file_get_contents($infilename);
if (!$plain_str) {
die("empty file".PHP_EOL);
}
$file_size = strlen($plain_str);
$file_size = pack("P", $file_size);
$iv = random_bytes(16);
if (strlen($plain_str) % 16 != 0) {
$bytesToPad = 16 - (strlen($plain_str) % 16);
$plain_str = $plain_str . random_bytes($bytesToPad);
}
$cipher_str = openssl_encrypt($plain_str, "aes-256-cbc", $key, 3, $iv);
$cipher_str = $file_size.$iv.$cipher_str;
file_put_contents($outfilename, $cipher_str);
echo "encrypted output file ".$outfilename.PHP_EOL;
}
$arg = getopt('e::d::k:i:o:');
if (isset($arg['i']) && !empty($arg['i'])) {
$infilename = $arg['i'];
} else {
die("please input filename".PHP_EOL);
}
if (isset($arg['o']) && !empty($arg['o'])) {
$outfilename = $arg['o'];
} else {
$outfilename = "";
}
if (isset($arg['k']) && !empty($arg['k'])) {
$key = $arg['k'];
} else {
$key = "pass";
}
$key = hash('sha256', $key, true);
if (isset($arg['d'])) {
decrypt($key, $infilename, $outfilename);
} elseif (isset($arg['e'])) {
encrypt($key, $infilename, $outfilename);
}
golang cbc使用pkcs7补全版:
package main
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/sha256"
//"encoding/binary"
"flag"
"fmt"
"io/ioutil"
"log"
"os"
)
func pkcs7strip(data *[]byte, blockSize int) (int, error) {
length := len(*data)
if length == 0 {
return 0, fmt.Errorf("pkcs7: Data is empty")
}
if length%blockSize != 0 {
return 0, fmt.Errorf("pkcs7: Data is not block-aligned")
}
padLen := int((*data)[length-1])
ref := bytes.Repeat([]byte{byte(padLen)}, padLen)
if padLen > blockSize || padLen == 0 || !bytes.HasSuffix(*data, ref) {
return 0, fmt.Errorf("pkcs7: Invalid padding")
}
return length-padLen, nil
}
func pkcs7pad(data *[]byte, blockSize int) error {
if blockSize < 0 || blockSize > 256 {
return fmt.Errorf("pkcs7: Invalid block size %d", blockSize)
} else {
padLen := blockSize - len(*data) % blockSize
padding := bytes.Repeat([]byte{byte(padLen)}, padLen)
*data = append(*data, padding...)
return nil
}
}
func encryptFile(key []byte, filename string, outFilename string) (string, error) {
if len(outFilename) == 0 {
outFilename = filename + ".enc"
}
plaintext, err := ioutil.ReadFile(filename)
if err != nil {
return "", err
}
of, err := os.Create(outFilename)
if err != nil {
return "", err
}
defer of.Close()
if err := pkcs7pad(&plaintext, aes.BlockSize) ; err != nil {
return "", err
}
iv := make([]byte, aes.BlockSize)
if _, err := rand.Read(iv); err != nil {
return "", err
}
if _, err = of.Write(iv); err != nil {
return "", err
}
ciphertext := make([]byte, len(plaintext))
block, err := aes.NewCipher(key)
if err != nil {
return "", err
}
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(ciphertext, plaintext)
if _, err = of.Write(ciphertext); err != nil {
return "", err
}
return outFilename, nil
}
func decryptFile(key []byte, filename string, outFilename string) (string, error) {
if len(outFilename) == 0 {
outFilename = filename + ".dec"
}
ciphertext, err := ioutil.ReadFile(filename)
if err != nil {
return "", err
}
of, err := os.Create(outFilename)
if err != nil {
return "", err
}
defer of.Close()
buf := bytes.NewReader(ciphertext)
iv := make([]byte, aes.BlockSize)
if _, err = buf.Read(iv); err != nil {
return "", err
}
paddedSize := len(ciphertext) - aes.BlockSize
if paddedSize%aes.BlockSize != 0 {
return "", fmt.Errorf("want padded plaintext size to be aligned to block size")
}
plaintext := make([]byte, paddedSize)
block, err := aes.NewCipher(key)
if err != nil {
return "", err
}
mode := cipher.NewCBCDecrypter(block, iv)
mode.CryptBlocks(plaintext, ciphertext[aes.BlockSize:])
origSize, err := pkcs7strip(&plaintext, aes.BlockSize)
if err != nil {
return "", err
}
if _, err := of.Write(plaintext[:origSize]); err != nil {
return "", err
}
return outFilename, nil
}
func main() {
var keyFlag string
encFlag := flag.Bool("e", false, "encrypt")
decFlag := flag.Bool("d", false, "decrypt")
flag.StringVar(&keyFlag, "k", "password", "encrypt password")
flag.Parse()
filename := flag.Arg(0)
key := sha256.Sum256([]byte(keyFlag))
fmt.Println("use password", keyFlag)
//fmt.Println(key)
if *encFlag {
outFilename, err := encryptFile(key[:], filename, "")
if err != nil {
log.Fatal(err)
}
fmt.Println("Encrypted output file:", outFilename)
} else if *decFlag {
outFilename, err := decryptFile(key[:], filename, "")
if err != nil {
log.Fatal(err)
}
fmt.Println("Decrypted output file:", outFilename)
} else {
fmt.Println(flag.Usage)
os.Exit(1)
}
}
golang使用aes cfb:
package main
import (
//"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/sha256"
//"encoding/binary"
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"os"
)
func encryptFile(key []byte, filename string, outFilename string) (string, error) {
if len(outFilename) == 0 {
outFilename = filename + ".enc"
}
plaintext, err := ioutil.ReadFile(filename)
if err != nil {
return "", err
}
of, err := os.Create(outFilename)
if err != nil {
return "", err
}
defer of.Close()
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
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)
fmt.Printf("iv:\t%x\n", iv)
fmt.Printf("cipher text:\t%x\n", ciphertext)
if _, err = of.Write(ciphertext); err != nil {
return "", err
}
return outFilename, nil
}
func decryptFile(key []byte, filename string, outFilename string) (string, error) {
if len(outFilename) == 0 {
outFilename = filename + ".dec"
}
ciphertext, err := ioutil.ReadFile(filename)
fmt.Printf("cipher text:\t%x\n", ciphertext)
if err != nil {
return "", err
}
of, err := os.Create(outFilename)
if err != nil {
return "", err
}
defer of.Close()
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
iv := ciphertext[:aes.BlockSize]
ciphertext = ciphertext[aes.BlockSize:]
fmt.Printf("cipher2 text:\t%x\n", ciphertext)
fmt.Printf("iv:\t%x\n", iv)
stream := cipher.NewCFBDecrypter(block, iv)
stream.XORKeyStream(ciphertext, ciphertext)
if _, err := of.Write(ciphertext); err != nil {
return "", err
}
return outFilename, nil
}
func main() {
var keyFlag string
encFlag := flag.Bool("e", false, "encrypt")
decFlag := flag.Bool("d", false, "decrypt")
flag.StringVar(&keyFlag, "k", "password", "encrypt password")
flag.Parse()
filename := flag.Arg(0)
key := sha256.Sum256([]byte(keyFlag))
fmt.Println("use password", keyFlag)
//fmt.Println(key)
if *encFlag {
outFilename, err := encryptFile(key[:], filename, "")
if err != nil {
log.Fatal(err)
}
fmt.Println("Encrypted output file:", outFilename)
} else if *decFlag {
outFilename, err := decryptFile(key[:], filename, "")
if err != nil {
log.Fatal(err)
}
fmt.Println("Decrypted output file:", outFilename)
} else {
fmt.Println(flag.Usage)
os.Exit(1)
}
}