使用Go和PHP进行AES加密

huangapple go评论79阅读模式
英文:

AES encryption with Go and PHP

问题

我正在使用Go和PHP进行AES加密。但是这两种语言无法互相加密/解密对方的密文。以下是我在PHP中尝试的代码:

class Crypto {
    private $encryptKey = "keyforencryption";
    private $iv = 'ivusedforencrypt';
    private $blocksize = 16;
    public function encrypt($toEncrypt){
        $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CFB);
        //$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
        return base64_encode($this->iv . mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $this->encryptKey, $toEncrypt, MCRYPT_MODE_CFB, $this->iv));
    }

    public function decrypt($toDecrypt){
       $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CFB);//$this->blocksize;
       $toDecrypt = base64_decode($toDecrypt);
       return rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $this->encryptKey, substr($toDecrypt, $iv_size), MCRYPT_MODE_CFB, substr($toDecrypt, 0, $iv_size)));
    }
}

$c = new Crypto();
echo "Encrypted : ".$e =  $c->encrypt("test");
echo "<br/>Decrypted : ".$c->decrypt($e);

输出结果为:aXZ1c2VkZm9yZW5jcnlwdDpdZEinU2rB

以下是使用AES在Go中的代码:

package main

import (
    "crypto/aes"
    "crypto/cipher"
    "crypto/rand"
    "encoding/base64"
    "errors"
    "fmt"
    "io"
    "log"
)

func main() {
    key := []byte("keyforencryption")
    plaintext := []byte("test")
    fmt.Printf("%s\n", plaintext)
    ciphertext, err := encrypt(key, plaintext)
    if err != nil {
        log.Fatal(err)
    }
    b := base64.StdEncoding.EncodeToString(ciphertext)
    fmt.Printf("Encrypted text : %s\n", b)
    result, err := decrypt(key, b)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Decrypted Text : %s\n", result)
}

func encrypt(key, text []byte) ([]byte, error) {
    block, err := aes.NewCipher(key)
    if err != nil {
        return nil, err
    }
    //b := base64.StdEncoding.EncodeToString(text)
    ciphertext := make([]byte, aes.BlockSize+len(text))
    iv := ciphertext[:aes.BlockSize]
    if _, err := io.ReadFull(rand.Reader, iv); err != nil {
        return nil, err
    }
    cfb := cipher.NewCFBEncrypter(block, iv)
    cfb.XORKeyStream(ciphertext[aes.BlockSize:], []byte(text))
    return ciphertext, nil
}

func decrypt(key []byte, text1 string) ([]byte, error) {
    text, _ := base64.StdEncoding.DecodeString(string(text1))
    block, err := aes.NewCipher(key)
    if err != nil {
        return nil, err
    }
    if len(text) < aes.BlockSize {
        return nil, errors.New("ciphertext too short")
    }
    iv := text[:aes.BlockSize]
    text = text[aes.BlockSize:]
    cfb := cipher.NewCFBDecrypter(block, iv)
    cfb.XORKeyStream(text, text)
    b := base64.StdEncoding.EncodeToString(text)
    data, err := base64.StdEncoding.DecodeString(string(b))
    if err != nil {
        return nil, err
    }
    return data, nil
}

输出结果为:ZVnhCXjIvtGKBdqvjwHRZKcVy34=

希望对你有所帮助。

英文:

I am using AES encryption in Go and PHP. But both the languages does not encrypt/decrypt each other ciphertext. Following i have tried in php

       class Crypto {
private $encryptKey = &quot;keyforencryption&quot;;
private $iv = &#39;ivusedforencrypt&#39;;
private $blocksize = 16;
public function encrypt($toEncrypt){
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CFB);
//$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
return base64_encode($this-&gt;iv . mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $this-&gt;encryptKey, $toEncrypt, MCRYPT_MODE_CFB, $this-&gt;iv));
}
public function decrypt($toDecrypt){
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CFB);//$this-&gt;blocksize;
$toDecrypt = base64_decode($toDecrypt);
return rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $this-&gt;encryptKey, substr($toDecrypt, $iv_size), MCRYPT_MODE_CFB, substr($toDecrypt, 0, $iv_size)));
}
}
$c = new Crypto();
echo &quot;Encrypted : &quot;.$e =  $c-&gt;encrypt(&quot;test&quot;);
echo &quot;&lt;br/&gt;Decrypted : &quot;.$c-&gt;decrypt($e);

output : aXZ1c2VkZm9yZW5jcnlwdDpdZEinU2rB

and this one in Go with AES

    package main
import (
&quot;crypto/aes&quot;
&quot;crypto/cipher&quot;
&quot;crypto/rand&quot;
&quot;encoding/base64&quot;
&quot;errors&quot;
&quot;fmt&quot;
&quot;io&quot;
&quot;log&quot;
)
func main() {
key := []byte(&quot;keyforencryption&quot;)
plaintext := []byte(&quot;test&quot;)
fmt.Printf(&quot;%s\n&quot;, plaintext)
ciphertext, err := encrypt(key, plaintext)
if err != nil {
log.Fatal(err)
}
b := base64.StdEncoding.EncodeToString(ciphertext)
fmt.Printf(&quot;Encrypted text : %s\n&quot;, b)
result, err := decrypt(key, b)
if err != nil {
log.Fatal(err)
}
fmt.Printf(&quot;Decrypted Text : %s\n&quot;, result)
}
func encrypt(key, text []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
//b := base64.StdEncoding.EncodeToString(text)
ciphertext := make([]byte, aes.BlockSize+len(text))
iv := ciphertext[:aes.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
return nil, err
}
cfb := cipher.NewCFBEncrypter(block, iv)
cfb.XORKeyStream(ciphertext[aes.BlockSize:], []byte(text))
return ciphertext, nil
}
func decrypt(key []byte, text1 string) ([]byte, error) {
text, _ := base64.StdEncoding.DecodeString(string(text1))
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
if len(text) &lt; aes.BlockSize {
return nil, errors.New(&quot;ciphertext too short&quot;)
}
iv := text[:aes.BlockSize]
text = text[aes.BlockSize:]
cfb := cipher.NewCFBDecrypter(block, iv)
cfb.XORKeyStream(text, text)
b := base64.StdEncoding.EncodeToString(text)
data, err := base64.StdEncoding.DecodeString(string(b))
if err != nil {
return nil, err
}
return data, nil
}

output : ZVnhCXjIvtGKBdqvjwHRZKcVy34=

any help would be appreciable.

答案1

得分: 4

CFB模式存在问题,在CBC模式下可以正常工作。

class Crypto {
    private $encryptKey = "keyforencryption";
    public function encrypt($toEncrypt){
        $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
        $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
        return base64_encode($iv . mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $this->encryptKey, $toEncrypt, MCRYPT_MODE_CBC, $iv));
    }

    public function decrypt($toDecrypt){
       $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
       echo "<br/>".$toDecrypt = base64_decode($toDecrypt);
       return rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $this->encryptKey, substr($toDecrypt, $iv_size), MCRYPT_MODE_CBC, substr($toDecrypt, 0, $iv_size)));
    }
}

$c = new Crypto();
echo "Encrypted : ".$e =  $c->encrypt("test123");
echo "<br/>Decrypted : ".$c->decrypt($e);
package main

import (
    "crypto/aes"
    "crypto/cipher"
    "crypto/rand"
    "encoding/base64"
    "fmt"
    "io"
    "bytes"
)

func main() {
    e:= cbcEncrypt()
    fmt.Printf("Encrypted String : %s\n", e)

    d:= cbcDecrypt(e)
    fmt.Printf("Decrypted String : %s\n", d)
}

func cbcDecrypt(text1 string) []byte{
    key := []byte("keyforencryption")
    ciphertext, _ := base64.StdEncoding.DecodeString(string(text1))
    block, err := aes.NewCipher(key)
    if err != nil {
        panic(err)
    }

    // include it at the beginning of the ciphertext.
    if len(ciphertext) < aes.BlockSize {
        panic("ciphertext too short")
    }
    iv := ciphertext[:aes.BlockSize]
    ciphertext = ciphertext[aes.BlockSize:]

    // CBC mode always works in whole blocks.
    if len(ciphertext)%aes.BlockSize != 0 {
        panic("ciphertext is not a multiple of the block size")
    }

    mode := cipher.NewCBCDecrypter(block, iv)

    // CryptBlocks can work in-place if the two arguments are the same.
    mode.CryptBlocks(ciphertext, ciphertext)
    ciphertext = PKCS5UnPadding(ciphertext)
    return ciphertext
}

func cbcEncrypt() string{
    key := []byte("keyforencryption")
    plaintext := []byte("testssssss")
    plaintext = PKCS5Padding(plaintext, 16)
    // CBC mode works on blocks so plaintexts may need to be padded to the
    // next whole block. For an example of such padding, see
    // https://tools.ietf.org/html/rfc5246#section-6.2.3.2. Here we'll
    // assume that the plaintext is already of the correct length.
    if len(plaintext)%aes.BlockSize != 0 {
        panic("plaintext is not a multiple of the block size")
    }

    block, err := aes.NewCipher(key)
    if err != nil {
        panic(err)
    }

    // The IV needs to be unique, but not secure. Therefore it's common to
    // include it at the beginning of the ciphertext.
    ciphertext := make([]byte, aes.BlockSize+len(plaintext))
    iv := ciphertext[:aes.BlockSize]
    if _, err := io.ReadFull(rand.Reader, iv); err != nil {
        panic(err)
    }

    mode := cipher.NewCBCEncrypter(block, iv)
    mode.CryptBlocks(ciphertext[aes.BlockSize:], plaintext)

    // It's important to remember that ciphertexts must be authenticated
    // (i.e. by using crypto/hmac) as well as being encrypted in order to
    // be secure.

    return base64.StdEncoding.EncodeToString(ciphertext)
}

func PKCS5Padding(src []byte, blockSize int) []byte {
    padding := blockSize - len(src)%blockSize
    padtext := bytes.Repeat([]byte{byte(padding)}, padding)
    return append(src, padtext...)
}

func PKCS5UnPadding(src []byte) []byte {
    length := len(src)
    unpadding := int(src[length-1])
    return src[:(length - unpadding)]
}

这样应该可以工作。

英文:

CFB mode has an issue, this will work in CBC mode

class Crypto {
private $encryptKey = &quot;keyforencryption&quot;;
public function encrypt($toEncrypt){
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
return base64_encode($iv . mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $this-&gt;encryptKey, $toEncrypt, MCRYPT_MODE_CBC, $iv));
}
public function decrypt($toDecrypt){
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
echo &quot;&lt;br/&gt;&quot;.$toDecrypt = base64_decode($toDecrypt);
return rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $this-&gt;encryptKey, substr($toDecrypt, $iv_size), MCRYPT_MODE_CBC, substr($toDecrypt, 0, $iv_size)));
}
}
$c = new Crypto();
echo &quot;Encrypted : &quot;.$e =  $c-&gt;encrypt(&quot;test123&quot;);
echo &quot;&lt;br/&gt;Decrypted : &quot;.$c-&gt;decrypt($e);

and this one in golang

package main
import (
&quot;crypto/aes&quot;
&quot;crypto/cipher&quot;
&quot;crypto/rand&quot;
&quot;encoding/base64&quot;
&quot;fmt&quot;
&quot;io&quot;
&quot;bytes&quot;
)
func main() {
e:= cbcEncrypt()
fmt.Printf(&quot;Encrypted String : %s\n&quot;, e)
d:= cbcDecrypt(e)
fmt.Printf(&quot;Decrypted String : %s\n&quot;, d)
}
func cbcDecrypt(text1 string) []byte{
key := []byte(&quot;keyforencryption&quot;)
ciphertext, _ := base64.StdEncoding.DecodeString(string(text1))
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
// include it at the beginning of the ciphertext.
if len(ciphertext) &lt; aes.BlockSize {
panic(&quot;ciphertext too short&quot;)
}
iv := ciphertext[:aes.BlockSize]
ciphertext = ciphertext[aes.BlockSize:]
// CBC mode always works in whole blocks.
if len(ciphertext)%aes.BlockSize != 0 {
panic(&quot;ciphertext is not a multiple of the block size&quot;)
}
mode := cipher.NewCBCDecrypter(block, iv)
// CryptBlocks can work in-place if the two arguments are the same.
mode.CryptBlocks(ciphertext, ciphertext)
ciphertext = PKCS5UnPadding(ciphertext)
return ciphertext
}
func cbcEncrypt() string{
key := []byte(&quot;keyforencryption&quot;)
plaintext := []byte(&quot;testssssss&quot;)
plaintext = PKCS5Padding(plaintext, 16)
// CBC mode works on blocks so plaintexts may need to be padded to the
// next whole block. For an example of such padding, see
// https://tools.ietf.org/html/rfc5246#section-6.2.3.2. Here we&#39;ll
// assume that the plaintext is already of the correct length.
if len(plaintext)%aes.BlockSize != 0 {
panic(&quot;plaintext is not a multiple of the block size&quot;)
}
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
// The IV needs to be unique, but not secure. Therefore it&#39;s common to
// include it at the beginning of the ciphertext.
ciphertext := make([]byte, aes.BlockSize+len(plaintext))
iv := ciphertext[:aes.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic(err)
}
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(ciphertext[aes.BlockSize:], plaintext)
// It&#39;s important to remember that ciphertexts must be authenticated
// (i.e. by using crypto/hmac) as well as being encrypted in order to
// be secure.
return base64.StdEncoding.EncodeToString(ciphertext)
}
func PKCS5Padding(src []byte, blockSize int) []byte {
padding := blockSize - len(src)%blockSize
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
return append(src, padtext...)
}
func PKCS5UnPadding(src []byte) []byte {
length := len(src)
unpadding := int(src[length-1])
return src[:(length - unpadding)]
}

this should work

答案2

得分: 1

在PHP中,编码时使用填充(padding),解码时使用去填充(unpadding)。

以下是用于填充的函数:

function pkcs5_pad($text)
{
    $blocksize = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
    $pad = $blocksize - (strlen($text) % $blocksize);
    return $text . str_repeat(chr($pad), $pad);
}

以下是用于去填充的函数:

function pkcs5_unpad($text)
{
    $block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
    $pad = ord($text[($len = strlen($text)) - 1]);
    $len = strlen($text);
    $pad = ord($text[$len-1]);
    return substr($text, 0, strlen($text) - $pad);
}

这些函数使用了MCrypt扩展中的mcrypt_get_block_size函数来获取加密算法的块大小,并根据块大小进行填充或去填充操作。

英文:

Also use padding for encoding and unpadding for decode in php.

function pkcs5_pad($text)
{
$blocksize = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$pad = $blocksize - (strlen($text) % $blocksize);
return $text . str_repeat(chr($pad), $pad);
}
function pkcs5_unpad($text)
{
$block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$pad = ord($text[($len = strlen($text)) - 1]);
$len = strlen($text);
$pad = ord($text[$len-1]);
return substr($text, 0, strlen($text) - $pad);
}

huangapple
  • 本文由 发表于 2016年3月31日 22:42:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/36336629.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定