У меня есть модуль PHP, который шифрует электронную почту с помощью aes-256-cbc
с использованием openssl_encrypt
.
Зашифрованные тексты, сгенерированные этим модулем, также могут быть расшифрованы этим модулем.
Но если я попытаюсь расшифровать их с помощью реализации aes-256-cbc в Go с тем же IV иКлюч, я получаю плохую ошибку размера блока.Предполагается, что размер блока должен быть кратным 16, но зашифрованный текст, сгенерированный PHP, не кратен 16.
Вот код
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"fmt"
)
var (
IV = []byte("fg3Dk54f4340fKF2JTC9")
KEY = []byte("13GsJd6076v69^f4(fdB")
)
func main() {
h := sha256.New()
h.Write(KEY)
KEY = []byte(hex.EncodeToString(h.Sum(nil))[:32])
h.Reset()
h.Write(IV)
IV = []byte(hex.EncodeToString(h.Sum(nil))[:16])
fmt.Println("Key", string(KEY))
fmt.Println("IV", string(IV))
// This ciphertext was generated by the PHP module
de := "ZHRodkpCK3R5QXlCMnh3MFdudDh3Zz09"
q, err := decrypt(KEY, IV, []byte(de))
fmt.Println(string(q), err)
}
// Returns slice of the original data without padding.
func pkcs7Unpad(data []byte, blocklen int) ([]byte, error) {
if blocklen <= 0 {
return nil, fmt.Errorf("invalid blocklen %d", blocklen)
}
if len(data)%blocklen != 0 || len(data) == 0 {
return nil, fmt.Errorf("invalid data len %d", len(data))
}
padlen := int(data[len(data)-1])
if padlen > blocklen || padlen == 0 {
return nil, fmt.Errorf("invalid padding")
}
// check padding
pad := data[len(data)-padlen:]
for i := 0; i < padlen; i++ {
if pad[i] != byte(padlen) {
return nil, fmt.Errorf("invalid padding")
}
}
return data[:len(data)-padlen], nil
}
func decrypt(key, iv, data []byte) ([]byte, error) {
var err error
data, err = base64.StdEncoding.DecodeString(string(data))
if err != nil {
return nil, err
}
if len(data) == 0 || len(data)%aes.BlockSize != 0 {
return nil, fmt.Errorf("bad blocksize(%v), aes.BlockSize = %v\n", len(data), aes.BlockSize)
}
c, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
cbc := cipher.NewCBCDecrypter(c, iv)
cbc.CryptBlocks(data[aes.BlockSize:], data[aes.BlockSize:])
out, err := pkcs7Unpad(data[aes.BlockSize:], aes.BlockSize)
if err != nil {
return out, err
}
return out, nil
}
Я пробовал разные вещи, такие как использованиеOPENSSL_RAW_DATA
и OPENSSL_ZERO_PADDING
в модуле PHP, но абсолютно ничего не работает.
Зашифрованный текст, использованный в приведенной выше программе, был сгенерирован с полем options
в openssl_encrypt
, установленным на 0.
Я предполагаю, что PHP не дополняет ввод перед шифрованием, что генерирует зашифрованные тексты, которыене кратны 16.
Реализация PHP
<?php
class MBM_Encrypt_Decrypt {
const ENCRYPT_METHOD = 'AES-256-CBC'; // type of encryption
const SECRET_KEY = '13GsJd6076v69^f4(fdB'; // secret key
const SECRET_IV = 'fg3Dk54f4340fKF2JTC9'; // secret iv
public function encrypt($string) {
return $this->encrypt_decrypt('encrypt', $string);
}
public function decrypt($string) {
return $this->encrypt_decrypt('decrypt', $string);
}
private function encrypt_decrypt($action, $string)
{
$key = hash('sha256', self::SECRET_KEY);
$iv = substr(hash('sha256', self::SECRET_IV), 0, 16);
if ($action == 'encrypt') {
$output = openssl_encrypt($string, self::ENCRYPT_METHOD, $key, 0, $iv);
} else if ($action == 'decrypt') {
$output = openssl_decrypt(base64_decode($string), self::ENCRYPT_METHOD, $key, 0, $iv);
}
$output = (!empty($output)) ? $output : false;
return $output;
}
}
$class_encrypt = new MBM_Encrypt_Decrypt();
$plain_txt = "xyz@abc.com";
echo 'Plain Text: ' . $plain_txt . PHP_EOL;
$decrypted_txt = $class_encrypt->decrypt("ZHRodkpCK3R5QXlCMnh3MFdudDh3Zz09");
echo 'Decrypted Text: ' . $decrypted_txt . PHP_EOL;
if ($plain_txt === $decrypted_txt) echo 'SUCCESS' . PHP_EOL;
else echo 'FAILED' . PHP_EOL;
echo PHP_EOL . 'Length of Plain Text: ' . strlen($plain_txt);
echo PHP_EOL . 'Length of Encrypted Text: ' . strlen($encrypted_txt). PHP_EOL;