英文:
Encrypting a string with AES and Base64
问题
我正在尝试在程序启动期间加密数据库中的一些文本,并在加载时解密。
我尝试了几种方法,包括使用第三方库https://github.com/richard-lyman/lithcrypt,但都没有成功。使用以下方法可以加密/解密8/10个项目,但似乎在加密/解密过程中留下了一些填充残留物。目前我的代码如下:
package client
import (
"encoding/base64"
"crypto/aes"
"crypto/cipher"
"fmt"
)
var iv = []byte{34, 35, 35, 57, 68, 4, 35, 36, 7, 8, 35, 23, 35, 86, 35, 23}
func encodeBase64(b []byte) string {
return base64.StdEncoding.EncodeToString(b)
}
func decodeBase64(s string) []byte {
data, err := base64.StdEncoding.DecodeString(s)
if err != nil { panic(err) }
return data
}
func Encrypt(key, text string) string {
block, err := aes.NewCipher([]byte(key))
if err != nil { panic(err) }
plaintext := []byte(text)
cfb := cipher.NewCFBEncrypter(block, iv)
ciphertext := make([]byte, len(plaintext))
cfb.XORKeyStream(ciphertext, plaintext)
return encodeBase64(ciphertext)
}
func Decrypt(key, text string) string {
block, err := aes.NewCipher([]byte(key))
if err != nil { panic(err) }
ciphertext := decodeBase64(text)
cfb := cipher.NewCFBEncrypter(block, iv)
plaintext := make([]byte, len(ciphertext))
cfb.XORKeyStream(plaintext, ciphertext)
}
有人告诉我可能需要对字符串进行填充,但我觉得对流密码进行填充似乎有些奇怪。
以下是此错误的示例:http://play.golang.org/p/4FQBAeHgRs
英文:
I'm trying to encrypt some text inside a database to be loaded and decrypted during program startup.
I have tried a few methods, including a third party library https://github.com/richard-lyman/lithcrypt to no avail. Using the following method encrypts/decrypts 8/10 items, but it seems that some padding residue is left behind at some point in the encrypt/decrypt. As it stands my code is like this:
package client
import (
"encoding/base64"
"crypto/aes"
"crypto/cipher"
"fmt"
)
var iv = []byte{34, 35, 35, 57, 68, 4, 35, 36, 7, 8, 35, 23, 35, 86, 35, 23}
func encodeBase64(b []byte) string {
return base64.StdEncoding.EncodeToString(b)
}
func decodeBase64(s string) []byte {
data, err := base64.StdEncoding.DecodeString(s)
if err != nil { panic(err) }
return data
}
func Encrypt(key, text string) string {
block, err := aes.NewCipher([]byte(key))
if err != nil { panic(err) }
plaintext := []byte(text)
cfb := cipher.NewCFBEncrypter(block, iv)
ciphertext := make([]byte, len(plaintext))
cfb.XORKeyStream(ciphertext, plaintext)
return encodeBase64(ciphertext)
}
func Decrypt(key, text string) string {
block, err := aes.NewCipher([]byte(key))
if err != nil { panic(err) }
ciphertext := decodeBase64(text)
cfb := cipher.NewCFBEncrypter(block, iv)
plaintext := make([]byte, len(ciphertext))
cfb.XORKeyStream(plaintext, ciphertext)
}
It was mentioned to me that I might need to pad the string, but it seems strange that I would have to pad a stream cipher.
Below is an example of this error: http://play.golang.org/p/4FQBAeHgRs
答案1
得分: 65
这是基于NewCFBEncrypter / NewCFBDecrypter示例的代码,看起来可以满足你的需求:
编辑:根据Kluyg的评论,关于IV创建的问题,我修改了示例代码,使用了与链接示例相同的方法,从密文中创建IV。(在生产代码中,IV应该每次单独生成。感谢RoundSparrow hilltx指出这一点。)
我认为你遇到的问题可能是由于无效的密钥长度,但我不能百分之百确定。
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/base64"
"errors"
"fmt"
"io"
"log"
)
func main() {
key := []byte("a very very very very secret key") // 32 bytes
plaintext := []byte("some really really really long plaintext")
fmt.Printf("%s\n", plaintext)
ciphertext, err := encrypt(key, plaintext)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%0x\n", ciphertext)
result, err := decrypt(key, ciphertext)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n", result)
}
// See alternate IV creation from ciphertext below
//var iv = []byte{35, 46, 57, 24, 85, 35, 24, 74, 87, 35, 88, 98, 66, 32, 14, 05}
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(b))
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(b))
return ciphertext, nil
}
func decrypt(key, text []byte) ([]byte, error) {
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)
data, err := base64.StdEncoding.DecodeString(string(text))
if err != nil {
return nil, err
}
return data, nil
}
输出结果:
some really really really long plaintext
54618bd6bb10612a7b590c53192df214501e01b685540b012581a0ed9ff3ddaa1f4177cc6186b501fb8cce0c2eb764daff475aab724d4d33e614d7d89cf556d8512fd920018c090f
some really really really long plaintext
英文:
This is based on the NewCFBEncrypter / NewCFBDecrypter examples and seems to do what you require:
EDIT: Based on Kluyg's comment regarding IV creation I've modified the example code to use the <s>recommended method of creating the IV from the ciphertext</s> same method as the linked example to create the IV from the ciphertext. (In production code the IV should be generated seperately each time. Thanks to RoundSparrow hilltx for pointing this out.)
I think the problem you're encountering is due to an invalid key length, but I'm not 100% sure.
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/base64"
"errors"
"fmt"
"io"
"log"
)
func main() {
key := []byte("a very very very very secret key") // 32 bytes
plaintext := []byte("some really really really long plaintext")
fmt.Printf("%s\n", plaintext)
ciphertext, err := encrypt(key, plaintext)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%0x\n", ciphertext)
result, err := decrypt(key, ciphertext)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n", result)
}
// See alternate IV creation from ciphertext below
//var iv = []byte{35, 46, 57, 24, 85, 35, 24, 74, 87, 35, 88, 98, 66, 32, 14, 05}
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(b))
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(b))
return ciphertext, nil
}
func decrypt(key, text []byte) ([]byte, error) {
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)
data, err := base64.StdEncoding.DecodeString(string(text))
if err != nil {
return nil, err
}
return data, nil
}
Produces:
>some really really really long plaintext
54618bd6bb10612a7b590c53192df214501e01b685540b012581a0ed9ff3ddaa1f4177cc6186b501fb8cce0c2eb764daff475aab724d4d33e614d7d89cf556d8512fd920018c090f
some really really really long plaintext
答案2
得分: 12
加密是一项复杂的任务,而Go语言的库可能不够高级,因此很容易出错。
对于那些想要看一个领域专家(CoreOS的安全开发人员)正确实现的示例的人来说,这个链接提供了一个很好的AES加密示例(以及其他常见的加密用法)。
https://github.com/gtank/cryptopasta
英文:
Crypto is hard and the go libraries are perhaps not high level enough so it's easy to make mistakes.
For anyone looking for an example of doing it right by an expert in the field (a security developer at CoreOS), this gives a good example of AES encryption (along with other common uses of crypto).
答案3
得分: 4
这是我刚刚完成的工作演示,它主要使用了Go文档中的代码示例,但经过了定制,以满足大多数应用程序和我的使用场景对加密方法的期望。
它使用AES加密。
将字符串加密为Base64字符串。在URL和数据库上使用非常方便。
将上述创建的Base64字符串解密为原始文本。
到处都是简单的文本转换。
这是一个简单的Go文件,可以直接运行。
英文:
Here is the working demo i just finished writing, it mostly uses code samples from the go document but it is tailored to do what most apps including my use case expects out of encryption methods.
It use AES encryption.
encrypt from string to base64 string. Easy to use on URL and dbs.
decrypt from base64 string created above to original text.
Simple text conversions everywhere.
GIST: Here is the gist, please let me know if there are any need for the improvements.
It's a simple go file, ready to be run.
答案4
得分: 4
似乎你的操作顺序有些颠倒了。以下是你目前的操作顺序:
ct = encrypt(encode(pt))
pt = decode(decrypt(ct))
正确的操作顺序应该是:
ct = encode(encrypt(pt))
pt = decrypt(decode(ct))
以下是我修改后的代码示例:
func Encrypt(key, text []byte) string {
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
ciphertext := make([]byte, aes.BlockSize+len(text))
iv := ciphertext[:aes.BlockSize]
if _, err := io.ReadFull(crand.Reader, iv); err != nil {
panic(err)
}
cfb := cipher.NewCFBEncrypter(block, iv)
cfb.XORKeyStream(ciphertext[aes.BlockSize:], text)
return encodeBase64(ciphertext)
}
func Decrypt(key []byte, b64 string) string {
text := decodeBase64(b64)
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
if len(text) < aes.BlockSize {
panic("ciphertext too short")
}
iv := text[:aes.BlockSize]
text = text[aes.BlockSize:]
cfb := cipher.NewCFBDecrypter(block, iv)
cfb.XORKeyStream(text, text)
return string(text)
}
希望对你有帮助!
英文:
It appears your order of operations are a bit backwards. Here is what you appear to be doing:
ct = encrypt(encode(pt))
pt = decode(decrypt(ct))
It should look more like:
ct = encode(encrypt(pt))
pt = decrypt(decode(ct))
The following works for me
func Encrypt(key, text []byte) string {
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
ciphertext := make([]byte, aes.BlockSize+len(text))
iv := ciphertext[:aes.BlockSize]
if _, err := io.ReadFull(crand.Reader, iv); err != nil {
panic(err)
}
cfb := cipher.NewCFBEncrypter(block, iv)
cfb.XORKeyStream(ciphertext[aes.BlockSize:], text)
return encodeBase64(ciphertext)
}
func Decrypt(key []byte, b64 string) string {
text := decodeBase64(b64)
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
if len(text) < aes.BlockSize {
panic("ciphertext too short")
}
iv := text[:aes.BlockSize]
text = text[aes.BlockSize:]
cfb := cipher.NewCFBDecrypter(block, iv)
cfb.XORKeyStream(text, text)
return string(text)
}
答案5
得分: 1
许多人已经提供了很好的答案。但正如@PiersyP在@Intermernet的答案评论中指出的那样,没有必要对文本进行base64编码。所以在这里,如果有人急于使用,就不需要进行base64编码。
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"errors"
"fmt"
"io"
"log"
)
func main() {
key := []byte("a very very very very secret key") // 32 bytes
plaintext := []byte("some really really really long plaintext")
fmt.Printf("%s\n", plaintext)
ciphertext, err := encrypt(key, plaintext)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%0x\n", ciphertext)
result, err := decrypt(key, ciphertext)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n", result)
}
// See alternate IV creation from ciphertext below
//var iv = []byte{35, 46, 57, 24, 85, 35, 24, 74, 87, 35, 88, 98, 66, 32, 14, 05}
func encrypt(key, text []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
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:], text)
return ciphertext, nil
}
func decrypt(key, text []byte) ([]byte, error) {
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)
return text, nil
}
英文:
Many have already provided nice answers. But as @PiersyP has pointed in the comments on @Intermernet 's answer, there's no need to base64 the text. So here it is without base64ing in case someone is in rush
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"errors"
"fmt"
"io"
"log"
)
func main() {
key := []byte("a very very very very secret key") // 32 bytes
plaintext := []byte("some really really really long plaintext")
fmt.Printf("%s\n", plaintext)
ciphertext, err := encrypt(key, plaintext)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%0x\n", ciphertext)
result, err := decrypt(key, ciphertext)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n", result)
}
// See alternate IV creation from ciphertext below
//var iv = []byte{35, 46, 57, 24, 85, 35, 24, 74, 87, 35, 88, 98, 66, 32, 14, 05}
func encrypt(key, text []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
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:], text)
return ciphertext, nil
}
func decrypt(key, text []byte) ([]byte, error) {
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)
return text, nil
}
答案6
得分: 0
这是一个完整的工作示例,用于帮助从搜索引擎中来的人。
这个示例是在 gtank/cryptopasta 的基础上进行修改的,添加了一个“hash password”功能。你可以使用任何密码,而不用担心其大小。
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"errors"
"fmt"
"io"
)
func main() {
key := []byte("secret")
ct, err := Encrypt([]byte("Plain text"), key)
if err != nil {
panic(err)
}
fmt.Println("Encrypted:", base64.StdEncoding.EncodeToString(ct))
pt, err := Decrypt(ct, key)
if err != nil {
panic(err)
}
fmt.Println("Decrypted:", string(pt))
}
// Encrypt 使用256位AES-GCM加密数据。这既隐藏了数据的内容,又提供了检查数据是否被篡改的功能。输出的格式为nonce|ciphertext|tag,其中'|'表示连接。
func Encrypt(plaintext []byte, key []byte) (ciphertext []byte, err error) {
k := sha256.Sum256(key)
block, err := aes.NewCipher(k[:])
if err != nil {
return nil, err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
nonce := make([]byte, gcm.NonceSize())
_, err = io.ReadFull(rand.Reader, nonce)
if err != nil {
return nil, err
}
return gcm.Seal(nonce, nonce, plaintext, nil), nil
}
// Decrypt 使用256位AES-GCM解密数据。这既隐藏了数据的内容,又提供了检查数据是否被篡改的功能。输入的格式为nonce|ciphertext|tag,其中'|'表示连接。
func Decrypt(ciphertext []byte, key []byte) (plaintext []byte, err error) {
k := sha256.Sum256(key)
block, err := aes.NewCipher(k[:])
if err != nil {
return nil, err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
if len(ciphertext) < gcm.NonceSize() {
return nil, errors.New("malformed ciphertext")
}
return gcm.Open(nil,
ciphertext[:gcm.NonceSize()],
ciphertext[gcm.NonceSize():],
nil,
)
}
英文:
Not actually answer to the question. But I put a full working example here for someone who comes from search engines.
Twisted from gtank/cryptopasta by adding a hash password
. You can use any password without worrying about its size.
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"errors"
"fmt"
"io"
)
func main() {
key := []byte("secret")
ct, err := Encrypt([]byte("Plain text"), key)
if err != nil {
panic(err)
}
fmt.Println("Encrypted:", base64.StdEncoding.EncodeToString(ct))
pt, err := Decrypt(ct, key)
if err != nil {
panic(err)
}
fmt.Println("Decrypted:", string(pt))
}
// Encrypt encrypts data using 256-bit AES-GCM. This both hides the content of
// the data and provides a check that it hasn't been altered. Output takes the
// form nonce|ciphertext|tag where '|' indicates concatenation.
func Encrypt(plaintext []byte, key []byte) (ciphertext []byte, err error) {
k := sha256.Sum256(key)
block, err := aes.NewCipher(k[:])
if err != nil {
return nil, err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
nonce := make([]byte, gcm.NonceSize())
_, err = io.ReadFull(rand.Reader, nonce)
if err != nil {
return nil, err
}
return gcm.Seal(nonce, nonce, plaintext, nil), nil
}
// Decrypt decrypts data using 256-bit AES-GCM. This both hides the content of
// the data and provides a check that it hasn't been altered. Expects input
// form nonce|ciphertext|tag where '|' indicates concatenation.
func Decrypt(ciphertext []byte, key []byte) (plaintext []byte, err error) {
k := sha256.Sum256(key)
block, err := aes.NewCipher(k[:])
if err != nil {
return nil, err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
if len(ciphertext) < gcm.NonceSize() {
return nil, errors.New("malformed ciphertext")
}
return gcm.Open(nil,
ciphertext[:gcm.NonceSize()],
ciphertext[gcm.NonceSize():],
nil,
)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论