Я надеялся, что кто-то уже реализовал это в golang, так как я далек от криптографии. Однако при переносе проекта с php на golang у меня возникла проблема с портированием найденного метода openssl_encrypt. Вот. Я также копался в исходный код немного безрезультатно.
Вот метод у меня есть реализованы в Голанге. что дает мне вывод
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);
Мне кажется, что это очень близко, и я, должно быть, упускаю что-то очевидное.
Вы были очень близки, но вы ошиблись. В соответствии с этот ответ (и документы PHP), PHP использует стандартное поведение заполнения OpenSSL, которое использует требуемое количество байтов заполнения в качестве значения байта заполнения.
Единственное изменение, которое я сделал, было:
copy(plaintextblock[length:], bytes.Repeat([]byte{uint8(extendBlock)}, extendBlock))
Вы можете увидеть полный обновленный код Вот.
Другие побеждали меня за ответ, пока я играл с ним, но у меня есть «лучшая» исправленная версия вашего примера кода, которая также учитывает, что заполнение всегда требуется (по крайней мере, для эмуляции того, что делает код 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)
}