使用golang解密用php openssl_encrypt加密的文件

时间:2018-04-13 06:29:15

标签: php go encryption openssl

首先。我在这里很薄!

我有一个加密文件,我从PHP获得。我试图用golang解密这个。

php应用程序使用公共RSA密钥加密用于使用aes-256-cbc加密的密钥。

我已经创建了一些概念代码证明,但我无法做到正确。即使键和副本看起来都是正确的,但有些东西却没有。结果就是垃圾。我怀疑某些编码不匹配(期望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

1 个答案:

答案 0 :(得分:2)

让我们退后一步,简化:

// 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。您知道,它不需要您只指定密钥大小,而是查看密钥的大小。在这种情况下,您定义了一个16字节/ 128位密钥。

如果指定AES-256但是然后将128位密钥传递给openssl,openssl会用零填充密钥,直到它为256位长。

以下是可能的修复程序(按照个人喜好的顺序):

使用256位密钥:

--- 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中,选择与密钥匹配的密码方法:

--- 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"))