如何将 rijndaelManaged 函数从 C# 重写为 Go?
How to rewrite rijndaelManaged function from C# to Go?
可能只是我犯了一个我看不到的小错误。也许其他查看此内容的人可以弄清楚我做错了什么。
这是C#
中的函数,我试图在Go
中重写,objective是在调用函数时输出相同的值。
public static string NewEncrypt(string Input)
{
RijndaelManaged rijndaelManaged = new RijndaelManaged();
rijndaelManaged.KeySize = 256;
rijndaelManaged.BlockSize = 256;
rijndaelManaged.Padding = PaddingMode.PKCS7;
rijndaelManaged.Key = Convert.FromBase64String(Convert.ToBase64String(Encoding.UTF8.GetBytes("095fc90fe8b18e8f243e4b07a9c0d170")));
rijndaelManaged.IV = Convert.FromBase64String(Convert.ToBase64String(Encoding.UTF8.GetBytes("8bef55a546d27958ead1fdddba4d36ea")));
ICryptoTransform transform = rijndaelManaged.CreateEncryptor(rijndaelManaged.Key, rijndaelManaged.IV);
byte[] myArray = null;
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Write))
{
byte[] bytes = Encoding.UTF8.GetBytes(Input);
cryptoStream.Write(bytes, 0, bytes.Length);
}
myArray = memoryStream.ToArray();
}
return Convert.ToBase64String(myArray);
}
您可以使用以下方式调用它:
NewEncrypt("{\"randJsonList\":[ \"abc\" ], \"name\":\"baron\", \"support\":\"king\"}")
我们有这个 return 输出 (myArray):
DdSUyoYRYW/zDNSVaA1JZ39WqJt06qp0FiJUlCW5BbZWEt41GzsmtgVnGZuHigZNs7qKhI+kHAKMXL8EPnK1vg==
现在开始我的 Go 实现(我试图利用 GitHub 资源:https://gist.github.com/huyinghuan/7bf174017bf54efb91ece04a48589b22):
您可能会注意到的第一件事是我不知道在哪里可以使用全局 IV 变量,每次您 运行 这段代码都会显示不同的输出。我想输出和C#一样的结果,除非修改了输入字符串
package main
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/base64"
"errors"
"fmt"
"io"
)
var KEY = []byte("095fc90fe8b18e8f243e4b07a9c0d170")
var IV = []byte("8bef55a546d27958ead1fdddba4d36ea")
func encrypt(myInput string) []byte {
plaintext := []byte(myInput)
// Create the AES cipher
block, err := aes.NewCipher(KEY)
if err != nil {
panic(err)
}
plaintext, _ = pkcs7Pad(plaintext, block.BlockSize())
// 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)
}
bm := cipher.NewCBCEncrypter(block, iv)
bm.CryptBlocks(ciphertext[aes.BlockSize:], plaintext)
//stream := cipher.NewCFBEncrypter(block, iv)
//stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)
return ciphertext
}
// pkcs7Pad right-pads the given byte slice with 1 to n bytes, where
// n is the block size. The size of the result is x times n, where x
// is at least 1.
func pkcs7Pad(b []byte, blocksize int) ([]byte, error) {
if blocksize <= 0 {
return nil, errors.New("invalid blocksize")
}
if b == nil || len(b) == 0 {
return nil, errors.New("invalid PKCS7 data (empty or not padded)")
}
n := blocksize - (len(b) % blocksize)
pb := make([]byte, len(b)+n)
copy(pb, b)
copy(pb[len(b):], bytes.Repeat([]byte{byte(n)}, n))
return pb, nil
}
func main() {
plainText := "{\"randJsonList\":[ \"abc\" ], \"name\":\"baron\", \"support\":\"king\"}"
x := encrypt(plainText)
outputString := base64.StdEncoding.EncodeToString(x)
fmt.Println(outputString)
}
示例输出(与 C# 不同):
PS D:\Software\Git\repositories\tet> go run .\main.go
N+hm5TItq367eXAz+WbtKXhhhMAy4woEKSngTf6rGUt8GZce7LsUxaqNtheceGDZ2dK8Bx187x87NeRPC1UQ6lUokjy7t1MLU8NcCtjODCM=
PS D:\Software\Git\repositories\tet> go run .\main.go
OT/CngTVs2O4BR4czjvR3MLVPoKFH2dUtW8LsIDUgLXfikJrRKsvKGaf0JFe39Cwf1/00HP7mvmCure7+IO+vupzAtdLX6nTQt1KZGsNp4o=
PS D:\Software\Git\repositories\tet> go run .\main.go
yDRHxWTvjX4HnSW8jbao+0Mhf77zgRj9tKXA3MNtAoF1I3bRou5Sv4Ds+r0HRuiA7NkoBR57m4aCYcU6quYzQA3R0GCGB8TGUfrWS5PvMNU=
C# 代码使用块大小为 256 位的 Rijndael(参见注释)、静态 IV(参见注释)和 returns 仅密文(即没有前置 IV)。
根据定义,Go 代码应用 AES,块大小为 128 位,随机生成的 IV(代码中的静态 IV 被忽略)和 returns IV 和密文的串联。
AES 是 Rijndael 的子集。 Rijndael 以 32 位步长定义了 128 位和 256 位之间的不同块大小和密钥大小,请参阅 here。对于 AES,仅定义了 128 位的块大小和 128、192 和 256 位的密钥大小。请注意,标准是 AES 而不是 Rijndael,因此 AES 应该优于 Rijndael(许多库甚至不实现 Rijndael,而是实现 AES)。
静态 IV 是不安全的。出于安全原因,Key/IV 对不得重复。因此,一般情况下,使用固定密钥,每次加密都会生成一个随机IV。 IV 不是秘密的,它与密文一起传递到解密端,通常按 IV|ciphertext.
的顺序连接
因此,当前的 Go 代码是一个安全的实现(更安全的是经过身份验证的加密,例如通过 GCM),而 C# 代码不是。因此,将 C# 代码修改为在功能上等同于 Go 代码会更有意义。
然而,由于C#代码似乎是参考,因此需要在Go代码中进行以下更改,使其在功能上与C#代码相同:
- 必须应用 Rijndael 而不是 AES。在以下示例中,使用了 pkg.go.dev/github.com/azihsoyn/rijndael256。为此,导入
"github.com/azihsoyn/rijndael256"
并正式将 aes
替换为 rijndael256
。您当然可以应用其他实现。
- 将应用静态 IV:
bm := cipher.NewCBCEncrypter(block, IV)
。
iv
及其填充物将与相关导入一起删除。
enecrypt()
方法只返回密文:return ciphertext[rijndael256.BlockSize:]
.
以下Go代码给出了C#代码的结果:
package main
import (
"bytes"
"github.com/azihsoyn/rijndael256"
"crypto/cipher"
"encoding/base64"
"errors"
"fmt"
)
var KEY = []byte("095fc90fe8b18e8f243e4b07a9c0d170")
var IV = []byte("8bef55a546d27958ead1fdddba4d36ea")
func encrypt(myInput string) []byte {
plaintext := []byte(myInput)
// Create the AES cipher
block, err := rijndael256.NewCipher(KEY)
if err != nil {
panic(err)
}
plaintext, _ = pkcs7Pad(plaintext, block.BlockSize())
// 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, rijndael256.BlockSize+len(plaintext))
bm := cipher.NewCBCEncrypter(block, IV)
bm.CryptBlocks(ciphertext[rijndael256.BlockSize:], plaintext)
return ciphertext[rijndael256.BlockSize:]
}
// pkcs7Pad right-pads the given byte slice with 1 to n bytes, where
// n is the block size. The size of the result is x times n, where x
// is at least 1.
func pkcs7Pad(b []byte, blocksize int) ([]byte, error) {
if blocksize <= 0 {
return nil, errors.New("invalid blocksize")
}
if b == nil || len(b) == 0 {
return nil, errors.New("invalid PKCS7 data (empty or not padded)")
}
n := blocksize - (len(b) % blocksize)
pb := make([]byte, len(b)+n)
copy(pb, b)
copy(pb[len(b):], bytes.Repeat([]byte{byte(n)}, n))
return pb, nil
}
func main() {
plainText := "{\"randJsonList\":[ \"abc\" ], \"name\":\"baron\", \"support\":\"king\"}"
x := encrypt(plainText)
outputString := base64.StdEncoding.EncodeToString(x)
fmt.Println(outputString)
}
输出:
DdSUyoYRYW/zDNSVaA1JZ39WqJt06qp0FiJUlCW5BbZWEt41GzsmtgVnGZuHigZNs7qKhI+kHAKMXL8EPnK1vg==
等同于C#代码。
可能只是我犯了一个我看不到的小错误。也许其他查看此内容的人可以弄清楚我做错了什么。
这是C#
中的函数,我试图在Go
中重写,objective是在调用函数时输出相同的值。
public static string NewEncrypt(string Input)
{
RijndaelManaged rijndaelManaged = new RijndaelManaged();
rijndaelManaged.KeySize = 256;
rijndaelManaged.BlockSize = 256;
rijndaelManaged.Padding = PaddingMode.PKCS7;
rijndaelManaged.Key = Convert.FromBase64String(Convert.ToBase64String(Encoding.UTF8.GetBytes("095fc90fe8b18e8f243e4b07a9c0d170")));
rijndaelManaged.IV = Convert.FromBase64String(Convert.ToBase64String(Encoding.UTF8.GetBytes("8bef55a546d27958ead1fdddba4d36ea")));
ICryptoTransform transform = rijndaelManaged.CreateEncryptor(rijndaelManaged.Key, rijndaelManaged.IV);
byte[] myArray = null;
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Write))
{
byte[] bytes = Encoding.UTF8.GetBytes(Input);
cryptoStream.Write(bytes, 0, bytes.Length);
}
myArray = memoryStream.ToArray();
}
return Convert.ToBase64String(myArray);
}
您可以使用以下方式调用它:
NewEncrypt("{\"randJsonList\":[ \"abc\" ], \"name\":\"baron\", \"support\":\"king\"}")
我们有这个 return 输出 (myArray):
DdSUyoYRYW/zDNSVaA1JZ39WqJt06qp0FiJUlCW5BbZWEt41GzsmtgVnGZuHigZNs7qKhI+kHAKMXL8EPnK1vg==
现在开始我的 Go 实现(我试图利用 GitHub 资源:https://gist.github.com/huyinghuan/7bf174017bf54efb91ece04a48589b22):
您可能会注意到的第一件事是我不知道在哪里可以使用全局 IV 变量,每次您 运行 这段代码都会显示不同的输出。我想输出和C#一样的结果,除非修改了输入字符串
package main
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/base64"
"errors"
"fmt"
"io"
)
var KEY = []byte("095fc90fe8b18e8f243e4b07a9c0d170")
var IV = []byte("8bef55a546d27958ead1fdddba4d36ea")
func encrypt(myInput string) []byte {
plaintext := []byte(myInput)
// Create the AES cipher
block, err := aes.NewCipher(KEY)
if err != nil {
panic(err)
}
plaintext, _ = pkcs7Pad(plaintext, block.BlockSize())
// 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)
}
bm := cipher.NewCBCEncrypter(block, iv)
bm.CryptBlocks(ciphertext[aes.BlockSize:], plaintext)
//stream := cipher.NewCFBEncrypter(block, iv)
//stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)
return ciphertext
}
// pkcs7Pad right-pads the given byte slice with 1 to n bytes, where
// n is the block size. The size of the result is x times n, where x
// is at least 1.
func pkcs7Pad(b []byte, blocksize int) ([]byte, error) {
if blocksize <= 0 {
return nil, errors.New("invalid blocksize")
}
if b == nil || len(b) == 0 {
return nil, errors.New("invalid PKCS7 data (empty or not padded)")
}
n := blocksize - (len(b) % blocksize)
pb := make([]byte, len(b)+n)
copy(pb, b)
copy(pb[len(b):], bytes.Repeat([]byte{byte(n)}, n))
return pb, nil
}
func main() {
plainText := "{\"randJsonList\":[ \"abc\" ], \"name\":\"baron\", \"support\":\"king\"}"
x := encrypt(plainText)
outputString := base64.StdEncoding.EncodeToString(x)
fmt.Println(outputString)
}
示例输出(与 C# 不同):
PS D:\Software\Git\repositories\tet> go run .\main.go
N+hm5TItq367eXAz+WbtKXhhhMAy4woEKSngTf6rGUt8GZce7LsUxaqNtheceGDZ2dK8Bx187x87NeRPC1UQ6lUokjy7t1MLU8NcCtjODCM=
PS D:\Software\Git\repositories\tet> go run .\main.go
OT/CngTVs2O4BR4czjvR3MLVPoKFH2dUtW8LsIDUgLXfikJrRKsvKGaf0JFe39Cwf1/00HP7mvmCure7+IO+vupzAtdLX6nTQt1KZGsNp4o=
PS D:\Software\Git\repositories\tet> go run .\main.go
yDRHxWTvjX4HnSW8jbao+0Mhf77zgRj9tKXA3MNtAoF1I3bRou5Sv4Ds+r0HRuiA7NkoBR57m4aCYcU6quYzQA3R0GCGB8TGUfrWS5PvMNU=
C# 代码使用块大小为 256 位的 Rijndael(参见注释)、静态 IV(参见注释)和 returns 仅密文(即没有前置 IV)。
根据定义,Go 代码应用 AES,块大小为 128 位,随机生成的 IV(代码中的静态 IV 被忽略)和 returns IV 和密文的串联。
AES 是 Rijndael 的子集。 Rijndael 以 32 位步长定义了 128 位和 256 位之间的不同块大小和密钥大小,请参阅 here。对于 AES,仅定义了 128 位的块大小和 128、192 和 256 位的密钥大小。请注意,标准是 AES 而不是 Rijndael,因此 AES 应该优于 Rijndael(许多库甚至不实现 Rijndael,而是实现 AES)。
静态 IV 是不安全的。出于安全原因,Key/IV 对不得重复。因此,一般情况下,使用固定密钥,每次加密都会生成一个随机IV。 IV 不是秘密的,它与密文一起传递到解密端,通常按 IV|ciphertext.
的顺序连接因此,当前的 Go 代码是一个安全的实现(更安全的是经过身份验证的加密,例如通过 GCM),而 C# 代码不是。因此,将 C# 代码修改为在功能上等同于 Go 代码会更有意义。
然而,由于C#代码似乎是参考,因此需要在Go代码中进行以下更改,使其在功能上与C#代码相同:
- 必须应用 Rijndael 而不是 AES。在以下示例中,使用了 pkg.go.dev/github.com/azihsoyn/rijndael256。为此,导入
"github.com/azihsoyn/rijndael256"
并正式将aes
替换为rijndael256
。您当然可以应用其他实现。 - 将应用静态 IV:
bm := cipher.NewCBCEncrypter(block, IV)
。iv
及其填充物将与相关导入一起删除。 enecrypt()
方法只返回密文:return ciphertext[rijndael256.BlockSize:]
.
以下Go代码给出了C#代码的结果:
package main
import (
"bytes"
"github.com/azihsoyn/rijndael256"
"crypto/cipher"
"encoding/base64"
"errors"
"fmt"
)
var KEY = []byte("095fc90fe8b18e8f243e4b07a9c0d170")
var IV = []byte("8bef55a546d27958ead1fdddba4d36ea")
func encrypt(myInput string) []byte {
plaintext := []byte(myInput)
// Create the AES cipher
block, err := rijndael256.NewCipher(KEY)
if err != nil {
panic(err)
}
plaintext, _ = pkcs7Pad(plaintext, block.BlockSize())
// 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, rijndael256.BlockSize+len(plaintext))
bm := cipher.NewCBCEncrypter(block, IV)
bm.CryptBlocks(ciphertext[rijndael256.BlockSize:], plaintext)
return ciphertext[rijndael256.BlockSize:]
}
// pkcs7Pad right-pads the given byte slice with 1 to n bytes, where
// n is the block size. The size of the result is x times n, where x
// is at least 1.
func pkcs7Pad(b []byte, blocksize int) ([]byte, error) {
if blocksize <= 0 {
return nil, errors.New("invalid blocksize")
}
if b == nil || len(b) == 0 {
return nil, errors.New("invalid PKCS7 data (empty or not padded)")
}
n := blocksize - (len(b) % blocksize)
pb := make([]byte, len(b)+n)
copy(pb, b)
copy(pb[len(b):], bytes.Repeat([]byte{byte(n)}, n))
return pb, nil
}
func main() {
plainText := "{\"randJsonList\":[ \"abc\" ], \"name\":\"baron\", \"support\":\"king\"}"
x := encrypt(plainText)
outputString := base64.StdEncoding.EncodeToString(x)
fmt.Println(outputString)
}
输出:
DdSUyoYRYW/zDNSVaA1JZ39WqJt06qp0FiJUlCW5BbZWEt41GzsmtgVnGZuHigZNs7qKhI+kHAKMXL8EPnK1vg==
等同于C#代码。