Flo*_*eck 4 base64 cryptography go
我正在尝试实现一种结构(CryptoService),以便从主程序流中隐藏加密/解密。我已经实现了“正常”功能和base64变体,应该将密码编码为与base64等效的密码,反之亦然。这样做是因为我们的内部网络协议使用换行符\n作为分隔符。
参见下面的实现代码
在编写下面的代码后,我开始对其进行测试。最初,它运行良好,并且加密和解密都能正常工作,但是很快我开始注意到解密过程中出现的“随机发生”错误:cipher: message authentication failed。现在重要的事实:该错误只从返回DecryptBase64FUNC。但是go中的base64用法非常简单,不必担心太多,因此我不知道问题出在哪里。
在下面,您会看到我的CryptoService实现代码和相关的测试文件。我试图在不删除上下文的情况下尽可能地清理代码(删除注释,其他输入检查等)。尽管如此,它的代码很多,因此感谢所有阅读它的人-非常感谢您的帮助!
cryptoservice.go
type CryptoService struct {
gcm cipher.AEAD
}
func NewCryptoService(key []byte) (cs *CryptoService, err error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
return &CryptoService{
gcm: gcm,
}, nil
}
func (cs CryptoService) Encrypt(plain []byte) (cipher []byte, err error) {
nonce := make([]byte, cs.gcm.NonceSize())
_, err = io.ReadFull(rand.Reader, nonce)
if err != nil {
return nil, err
}
cipher = cs.gcm.Seal(nil, nonce, plain, nil)
cipher = append(nonce, cipher...)
return cipher, nil
}
func (cs CryptoService) EncryptBase64(plain []byte) (base64Cipher []byte, err error) {
cipher, err := cs.Encrypt(plain)
if err != nil {
return nil, err
}
base64Cipher = make([]byte, base64.StdEncoding.EncodedLen(len(cipher)))
base64.StdEncoding.Encode(base64Cipher, cipher)
return
}
func (cs CryptoService) Decrypt(cipher []byte) (plain []byte, err error) {
nonce := cipher[0:cs.gcm.NonceSize()]
ciphertext := cipher[cs.gcm.NonceSize():]
plain, err = cs.gcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
return nil, err
}
return
}
func (cs CryptoService) DecryptBase64(base64Cipher []byte) (plain []byte, err error) {
cipher := make([]byte, base64.StdEncoding.DecodedLen(len(base64Cipher)))
_, err = base64.StdEncoding.Decode(cipher, base64Cipher)
if err != nil {
return nil, err
}
return cs.Decrypt(cipher)
}
Run Code Online (Sandbox Code Playgroud)
cryptoservice_test.go
TestCryptoService_EncryptDecryptRoundtrip 工作正常TestCryptoService_EncryptBase64DecryptBase64Roundtrip 有时“失败”(也请注意,它并不总是在相同的测试用例上失败)附加信息:FastRandomString(n int)Dynamic-Test-Case创建中的func实际上只是以下位置的已接受答案的复制和粘贴:如何在golang中生成固定长度的随机字符串?
func TestCryptoService_EncryptDecryptRoundtrip(t *testing.T) {
tests := []struct {
name string
aeskey string
text string
}{
{"Simple 1", "c4cc0dfc4ae0e45c045727f84ffd373127453bc232230bf1386972ac692436c1", "Text"},
{"Simple 2", "c4cc0dfc4ae0e45c045727f84ffd373127453bc232230bf1386972ac692436c1", "Some random content"},
{"Simple 3", "c4cc0dfc4ae0e45c045727f84ffd373127453bc232230bf1386972ac692436c1", "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua."},
{"Dynamic 1", "c4cc0dfc4ae0e45c045727f84ffd373127453bc232230bf1386972ac692436c1", FastRandomString(32)},
{"Dynamic 2", "c4cc0dfc4ae0e45c045727f84ffd373127453bc232230bf1386972ac692436c1", FastRandomString(1024)},
{"Dynamic 3", "c4cc0dfc4ae0e45c045727f84ffd373127453bc232230bf1386972ac692436c1", FastRandomString(1024 * 64)},
{"Dynamic 4", "c4cc0dfc4ae0e45c045727f84ffd373127453bc232230bf1386972ac692436c1", FastRandomString(1024 * 256)},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
for i := 0; i < 1000; i++ {
key, _ := hex.DecodeString(tt.aeskey)
cs, _ := NewCryptoService(key)
cipher, err := cs.Encrypt([]byte(tt.text))
if err != nil {
t.Errorf("CryptoService.Encrypt() error = %v", err)
return
}
plain, err := cs.Decrypt(cipher)
if err != nil {
t.Errorf("CryptoService.Decrypt() error = %v", err)
return
}
plainStr := string(plain)
if plainStr != tt.text {
t.Errorf("CryptoService.Decrypt() plain = %v, want = %v", plainStr, tt.text)
return
}
}
})
}
}
func TestCryptoService_EncryptBase64DecryptBase64Roundtrip(t *testing.T) {
tests := []struct {
name string
aeskey string
text string
}{
{"Simple 1", "c4cc0dfc4ae0e45c045727f84ffd373127453bc232230bf1386972ac692436c1", "Text"},
{"Simple 2", "c4cc0dfc4ae0e45c045727f84ffd373127453bc232230bf1386972ac692436c1", "Some random content"},
{"Simple 3", "c4cc0dfc4ae0e45c045727f84ffd373127453bc232230bf1386972ac692436c1", "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua."},
{"Dynamic 1", "c4cc0dfc4ae0e45c045727f84ffd373127453bc232230bf1386972ac692436c1", FastRandomString(32)},
{"Dynamic 2", "c4cc0dfc4ae0e45c045727f84ffd373127453bc232230bf1386972ac692436c1", FastRandomString(1024)},
{"Dynamic 3", "c4cc0dfc4ae0e45c045727f84ffd373127453bc232230bf1386972ac692436c1", FastRandomString(1024 * 64)},
{"Dynamic 4", "c4cc0dfc4ae0e45c045727f84ffd373127453bc232230bf1386972ac692436c1", FastRandomString(1024 * 256)},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
for i := 0; i < 1000; i++ {
key, _ := hex.DecodeString(tt.aeskey)
cs, _ := NewCryptoService(key)
cipher, err := cs.EncryptBase64([]byte(tt.text))
if err != nil {
t.Errorf("CryptoService.EncryptBase64() error = %v", err)
return
}
plain, err := cs.DecryptBase64(cipher)
if err != nil {
t.Errorf("CryptoService.DecryptBase64() error = %v", err)
return
}
plainStr := string(plain)
if plainStr != tt.text {
t.Errorf("CryptoService.DecryptBase64() plain = %v, want = %v", plainStr, tt.text)
return
}
}
})
}
}
Run Code Online (Sandbox Code Playgroud)
GopherSlack社区的某人提出了解决方案:
StdEncoding填充结果,在这种情况下(在加密过程中)需要填充输出时,会导致解密问题。因此,您应该RawStdEncoding在此示例中使用。
谢谢您的帮助!:)
| 归档时间: |
|
| 查看次数: |
1737 次 |
| 最近记录: |