首先。我在这里如履薄冰!
我有一个从 php 获得的加密文件。我正在尝试用 golang 解密它。
php 应用程序使用公共(public) RSA key 来加密用于使用 aes-256-cbc 加密的 key 。
我已经创建了一些概念验证代码,但我做不对。尽管 key 和 iv 在两边看起来都是正确的,但也有不正确的地方。结果只是垃圾。我怀疑某些编码不匹配(期望 base64,获取字符串字节......某事)或者我误解了一些概念。
加密:
<?php
$cipher = "AES-256-CBC";
$ivLength = openssl_cipher_iv_length($cipher="AES-256-CBC");
echo "iv len: " . $ivLength . "\n";
$iv = openssl_random_pseudo_bytes($ivLength);
$key = "1234567890abcdef";
$ciphertext = openssl_encrypt("hello world", $cipher, $key, 0, $iv);
$publicKey = openssl_pkey_get_public(file_get_contents("some-public-key.pub"));
if (!$publicKey) {
die("OpenSSL: Unable to get public key for encryption. Is the location correct? Does this key require a password?");
}
$ok = openssl_public_encrypt($key, $encryptedKey, $publicKey);
if (!$ok) {
die("Encryption failed. Ensure you are using a PUBLIC key.");
}
echo "key unencrypted: " . $key . "\n";
echo "iv: " . base64_encode($iv) . "\n";
echo "ciphertext: " . $ciphertext . "\n";
echo "ciphertext binary: " . (base64_decode($ciphertext)) . "\n";
echo "combined: " . ($iv . $ciphertext) . "\n";
file_put_contents("key.enc", $encryptedKey);
file_put_contents("content.enc", $iv . $ciphertext);
file_put_contents("content.dec", openssl_decrypt($ciphertext, $cipher, $key, 0, $iv));
openssl_free_key($publicKey);
?>
解密:
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/hex"
"encoding/pem"
"fmt"
"io"
"io/ioutil"
"log"
)
func main() {
// Read the input file
in, err := ioutil.ReadFile("key.enc")
if err != nil {
log.Fatalf("input file: %s", err)
}
// Read the private key
pemData, err := ioutil.ReadFile("some-private-key")
if err != nil {
log.Fatalf("read key file: %s", err)
}
// Extract the PEM-encoded data block
block, _ := pem.Decode(pemData)
if block == nil {
log.Fatalf("bad key data: %s", "not PEM-encoded")
}
if got, want := block.Type, "RSA PRIVATE KEY"; got != want {
log.Fatalf("unknown key type %q, want %q", got, want)
}
// Decode the RSA private key
priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
log.Fatalf("bad private key: %s", err)
}
// Decrypt the data
cipherKey, err := rsa.DecryptPKCS1v15(rand.Reader, priv, in)
if err != nil {
log.Fatalf("decrypt: %s", err)
}
fmt.Println("Key decrypted:", string(cipherKey))
// Read encrypted content file
content, err := ioutil.ReadFile("content.enc")
if err != nil {
log.Fatalf("input file: %s", err)
}
fmt.Println("Cipherkey: ", string(cipherKey))
cipherText := content
cipherBlock, err := aes.NewCipher(cipherKey)
if err != nil {
panic(err)
}
iv := cipherText[:aes.BlockSize]
fmt.Println("iv:", base64.StdEncoding.EncodeToString(iv))
fmt.Println("ciphertext:", string(cipherText[aes.BlockSize:]))
cipherText, _ = base64.StdEncoding.DecodeString(string(cipherText[aes.BlockSize:]))
fmt.Println("ciphertext binary: ", string(cipherText))
// CBC mode always works in whole blocks.
if len(cipherText)%aes.BlockSize != 0 {
panic(fmt.Sprintf("ciphertext (len=%d) is not a multiple of the block size (%d)", len(cipherText), aes.BlockSize))
}
mode := cipher.NewCBCDecrypter(cipherBlock, iv)
mode.CryptBlocks(cipherText, cipherText)
fmt.Printf("The result: %s\n", cipherText)
}
这是执行此操作的一些示例输出(首先是 php,然后是 go):
iv len: 16
key unencrypted: 1234567890abcdef
iv: QffXbVRuwyopwwvQXQ8N6g==
ciphertext: Wk8Gv1xQWikp1YryQiywgQ==
ciphertext binary: ZO�\PZ))Պ�B,��
combined: A��mTn�*)�
�Wk8Gv1xQWikp1YryQiywgQ==
-----
Key decrypted: 1234567890abcdef
Cipherkey: 1234567890abcdef
iv: QffXbVRuwyopwwvQXQ8N6g==
ciphertext: Wk8Gv1xQWikp1YryQiywgQ==
ciphertext binary: ZO�\PZ))Պ�B,��
The result: ��2��J���~A�D
最佳答案
让我们退一步简化一下:
// encrypt.php
<?php
$iv = base64_decode("AJf3QItKM7+Lkh/BZT2xNg==");
$key = "1234567890abcdef";
echo openssl_encrypt("hello world", "AES-256-CBC", $key, OPENSSL_RAW_DATA, $iv);
// decrypt.go
package main
import (
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"fmt"
"io/ioutil"
"log"
"os"
)
func main() {
iv, _ := base64.StdEncoding.DecodeString("AJf3QItKM7+Lkh/BZT2xNg==")
key := []byte("1234567890abcdef")
text, _ := ioutil.ReadAll(os.Stdin)
cipherBlock, err := aes.NewCipher(key)
if err != nil {
log.Fatal(err)
}
cipher.NewCBCDecrypter(cipherBlock, iv).CryptBlocks(text, text)
fmt.Println(string(text))
}
如果我们运行这个,低看,我们得到垃圾:
$ php encrypt.php | go run decrypt.go
7v>r
请注意 Go 代码中明显缺少字符串 256
。您知道,不需要您指定 key 大小,而是查看 key 的大小。在这种情况下,您定义了一个 16 字节/128 位 key 。
如果您指定 AES-256,但随后将 128 位 key 传递给 openssl,openssl 会用零填充 key ,直到它达到 256 位长。
以下是可能的修复方法(按照我个人喜好的顺序):
使用 256 位 key :
--- encrypt.php.orig 2018-04-13 10:55:10.988913605 +0200
+++ encrypt.php.fix-key 2018-04-13 10:57:13.565673205 +0200
@@ -3,3 +3,3 @@
$iv = base64_decode("AJf3QItKM7+Lkh/BZT2xNg==");
-$key = "1234567890abcdef";
+$key = "1234567890abcdef1234567890abcdef";
--- decrypt.go.orig 2018-04-13 10:55:17.083901651 +0200
+++ decrypt.go.fix-key 2018-04-13 10:55:49.467838139 +0200
@@ -14,3 +14,3 @@
iv, _ := base64.StdEncoding.DecodeString("AJf3QItKM7+Lkh/BZT2xNg==")
- key := []byte("1234567890abcdef")
+ key := []byte("1234567890abcdef1234567890abcdef")
在PHP中,选择与 key 匹配的加密方法:
--- encrypt.php.orig 2018-04-13 10:55:10.988913605 +0200
+++ encrypt.php.fix-method 2018-04-13 10:56:18.105781974 +0200
@@ -5,2 +5,2 @@
-echo openssl_encrypt("hello world", "AES-256-CBC", $key, OPENSSL_RAW_DATA, $iv);
+echo openssl_encrypt("hello world", "AES-128-CBC", $key, OPENSSL_RAW_DATA, $iv);
在 Go 中也做零填充:
--- decrypt.go.orig 2018-04-13 10:55:17.083901651 +0200
+++ decrypt.go.pad-key 2018-04-13 10:56:39.601739816 +0200
@@ -14,3 +14,4 @@
iv, _ := base64.StdEncoding.DecodeString("AJf3QItKM7+Lkh/BZT2xNg==")
- key := []byte("1234567890abcdef")
+ key := make([]byte, 32)
+ copy(key, []byte("1234567890abcdef"))
关于php - 使用golang解密用php openssl_encrypt加密的文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49810592/