如何复制openssl_encrypt?

时间:2015-03-21 02:59:24

标签: php encryption go

我希望有人已经在golang中实现了这一点,因为我甚至不擅长加密。然而,在将项目从php移植到golang时,我遇到了移植找到here的openssl_encrypt方法的问题。我也挖了source code一点点但没有用。

这是golang中implemented的方法。这给了我输出

lvb7JwaI4OCYUrdJMm8Q9uDd9rIILnvbZKJb/ozFbwCmLKkxoJN5Zf/ODOJ/RGq5

这是使用php时我需要的输出。

lvb7JwaI4OCYUrdJMm8Q9uDd9rIILnvbZKJb/ozFbwDV98XaJjvzEjBQp7jc+2DH

这是我用php生成它的函数。

$data = "This is some text I want to encrypt";
$method = "aes-256-cbc";
$password = "This is a really long key and su";
$options = 0;
$iv = "MMMMMMMMMMMMMMMM";

echo openssl_encrypt($data, $method, $password, $options, $iv);

对我来说,它看起来非常接近,我必须遗漏一些明显的东西。

2 个答案:

答案 0 :(得分:4)

你非常接近,但你的填充错了。根据{{​​3}}(以及PHP文档),PHP使用默认的OpenSSL填充行为,即使用所需数量的填充字节作为填充字节值。

我做的唯一改变是:

copy(plaintextblock[length:], bytes.Repeat([]byte{uint8(extendBlock)}, extendBlock))

您可以看到完整更新的代码this answer

答案 1 :(得分:1)

其他人在我玩它的时候打败了我,但是我有一个“更好”的固定版本的示例代码,也考虑到总是需要填充(至少模仿php代码的作用)

它还会显示您用于执行相同操作的openssl命令行,如果可用,则运行它(当然操场不会)。

package main

import (
    "crypto/aes"
    "crypto/cipher"
    "encoding/base64"
    "fmt"
    "log"
    "os/exec"
    "strings"
)

func main() {
    const input = "This is some text I want to encrypt"
    fmt.Println(opensslCommand(input))
    fmt.Println(aesCBCenctypt(input))
}

func aesCBCenctypt(input string) string {
    // Of course real IVs should be from crypto/rand
    iv := []byte("MMMMMMMMMMMMMMMM")
    // And real keys should be from something like PBKDF2, RFC 2898.
    // E.g. use golang.org/x/crypto/pbkdf2 to turn a
    // "passphrase" into a key.
    key := []byte("This is a really long key and su")

    // Make sure the block size is a multiple of aes.BlockSize
    // Pad to aes.BlockSize using the pad length as the padding
    // byte. If we would otherwise need no padding we instead
    // pad an entire extra block.
    pad := (aes.BlockSize - len(input)%aes.BlockSize)
    if pad == 0 {
        pad = aes.BlockSize
    }
    data := make([]byte, len(input)+pad)
    copy(data, input)
    for i := len(input); i < len(input)+pad; i++ {
        data[i] = byte(pad)
    }

    cb, err := aes.NewCipher(key)
    if err != nil {
        log.Fatalln("error NewCipher():", err)
    }

    mode := cipher.NewCBCEncrypter(cb, iv)
    mode.CryptBlocks(data, data)
    return base64.StdEncoding.EncodeToString(data)
}

// Just for comparison, don't do this for real!
func opensslCommand(input string) string {
    iv := []byte("MMMMMMMMMMMMMMMM")
    key := []byte("This is a really long key and su")

    args := []string{"enc", "-aes-256-cbc", "-base64"}
    // "-nosalt", "-nopad"
    args = append(args, "-iv", fmt.Sprintf("%X", iv))
    args = append(args, "-K", fmt.Sprintf("%X", key))

    cmd := exec.Command("openssl", args...)
    // Show how you could do this via the command line:
    fmt.Println("Command:", strings.Join(cmd.Args, " "))

    cmd.Stdin = strings.NewReader(input)
    result, err := cmd.CombinedOutput()
    if err != nil {
        if e, ok := err.(*exec.Error); ok && e.Err == exec.ErrNotFound {
            // openssl not available
            return err.Error() // XXX
        }
        // some other error, show it and the (error?) output and die
        fmt.Println("cmd error:", err)
        log.Fatalf("result %q", result)
    }
    // Strip trailing '\n' and return it.
    if n := len(result) - 1; result[n] == '\n' {
        result = result[:n]
    }
    return string(result)
}

Playground