(Go)对字符串进行加密/解密会导致字符丢失。

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

(Go) Encrypting / decrypting string results in lost characters

问题

我正在尝试在存储之前对敏感数据进行加密。首先,我生成一个用于加密过程的密钥:

import (
	"crypto/aes"
	CR "crypto/rand"
	"encoding/hex"
	"errors"
	"log"
	"os"
)

// []byte key used to encrypt tokens before saving to local file system
var key = make([]byte, 32)

func createKey(key *[]byte) {
	_, err = CR.Read(*key)
	if err != nil {
		log.Println("Error creating key from crypto/rand package:", err)
	}
}

接下来,我创建了分别用于加密和解密字符串的函数:

func encryptToken(t token) string {
	original := t.ID // ID is string member of token

	cipher, err := aes.NewCipher(key)
	if err != nil {
		log.Println("Error creating cipher during encrypt:", err)
	}

	out := make([]byte, len(original))
	cipher.Encrypt(out, []byte(original))

	return hex.EncodeToString(out) // this will be written to a csv file 

    // appears in file as: cec35df876e1b77diefg9023366c5f2f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
}

func decryptToken(s string) string {
	ciphertext, err := hex.DecodeString(s) // s is read from csv file
	if err != nil {
		log.Println("Error decoding string from hex:", err)
	}

	cipher, err := aes.NewCipher(key)
	if err != nil {
		log.Println("Error creating cipher during decrypt:", err)
	}

	original := make([]byte, len(ciphertext))
	cipher.Decrypt(original, ciphertext)

	originalAsString := string(original[:])

	return originalAsString  // returns: 6f928e728f485403

    // original token was: 6f928e728f485403e254049f684ea5ec853adcfa9553cdfc956fr45671447c57
}

考虑到encryptToken()返回的十六进制字符串中有这么多的零,我确信问题出在这里。我尝试调整key的长度,但是在var key = make([]byte, 32)中使用除32以外的值将导致恐慌,涉及到无效的内存地址或空指针解引用。为什么会这样呢?

英文:

I am attempting to encrypt sensitive data before storing it. First, I generate a key to be used for the encryption process:

import (
	"crypto/aes"
	CR "crypto/rand"
	"encoding/hex"
	"errors"
	"log"
	"os"
)

// []byte key used to encrypt tokens before saving to local file system
var key = make([]byte, 32)

func createKey(key *[]byte) {
    _, err = CR.Read(*key)
	if err != nil {
		log.Println("Error creating key from crypto/rand package:", err)
	}
}

Next I create functions that encrypt and decrypt a string respectively:

func encryptToken(t token) string {
	original := t.ID // ID is string member of token

	cipher, err := aes.NewCipher(key)
	if err != nil {
		log.Println("Error creating cipher during encrypt:", err)
	}

	out := make([]byte, len(original))
	cipher.Encrypt(out, []byte(original))

	return hex.EncodeToString(out) // this will be written to a csv file 

    // appears in file as: cec35df876e1b77diefg9023366c5f2f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
}

func decryptToken(s string) string {
	ciphertext, err := hex.DecodeString(s) // s is read from csv file
	if err != nil {
		log.Println("Error decoding string from hex:", err)
	}

	cipher, err := aes.NewCipher(key)
	if err != nil {
		log.Println("Error creating cipher during decrypt:", err)
	}

	original := make([]byte, len(ciphertext))
	cipher.Decrypt(original, ciphertext)

	originalAsString := string(original[:])

	return originalAsString  // returns: 6f928e728f485403

    // original token was: 6f928e728f485403e254049f684ea5ec853adcfa9553cdfc956fr45671447c57
}

Considering that encryptToken() returns a hex string with so many zeros, I am certain this is where my problem is. I've experimented adjusting the length of key, but using a value other than 32 in var key = make([]byte, 32) will result in a panic involving invalid memory address or nil pointer dereference. Why is this?

答案1

得分: 3

最短的答案可能是:正如@GradyPlayer所建议的那样,你正在错误地使用密码。你必须将其交给一个加密器,它会将其应用于所有的字节。模式如下所示:

ciphertext = make([]byte, len(plaintext))
cbc := cipher.NewCBCEncrypter(cipher, iv)
cbc.CryptBlocks(ciphertext, plaintext)

也就是说,在这里,你仍然需要将初始化向量放在密文之前(至少这是一个通常的位置),解密器可能期望它位于此处,并且你仍然需要考虑填充(如果你像我一样使用CBC)。

因此,虽然它并没有直接回答你的问题,但我认为它会帮助你获得更多的代码以更好地理解。这里是我刚刚创建的一个代码片段。免责声明:我主要是从其他地方组装代码片段。所以我只是将这里看到的片段放在一起,并在过程中获得了一些理解。

英文:

The shortest answer is probably: As @GradyPlayer suggested, you're using your cipher the wrong way. You have to hand it over to an encrypter instead which applies it to all the bytes. The pattern looks like that:

ciphertext = make([]byte, len(plaintext))
cbc := cipher.NewCBCEncrypter(cipher, iv)
cbc.CryptBlocks(ciphertext, plaintext)

That said, here you would still have to put the initialization vector in front of the ciphertext (that's a usual place to put it at least, and the decrypter might expect it to be located here), and you still have to think about padding (if you're using CBC as I did).

So while it's not directly addressing the question, I think it would help you do get more code for a better understanding. Here is a gist I've just created. Disclaimer: I mostly assembled code fragments from other places. So I merely put the pieces together you can see here, and gained some understanding on the road.

答案2

得分: 2

我对这个库不太熟悉,但它可能只是执行一个AES块操作,AES是一个16字节的块密码,你似乎得到的就是一个16字节的块...
要对一个任意大小的数据块进行AES加密,你需要将其填充到一个"完整大小模16=0"字节(参考PKCS5/7),然后执行一个块操作...然后,使用CTR或CBC模式(IV将来自上一个块的位置或输出),然后处理下一个块,以此类推...所以我认为你正在使用AES块操作,而不是整个数据块(除了密钥外,还需要块模式和IV)。

英文:

I am not familiar with the library, but it might just be doing one block of AES, AES is a 16 byte block cypher, which seems to be what you get back, one 16 byte block...
to do a whole arbitrary chunk of AES, you have to pad it to a full size mod 16 = 0 bytes (look up PKCS5/7), then do one block... then, either CTR or CBC (the IV will come from either the position or the output of the last block), then the next block and so on... so I think you are using the AES block primitive rather than a whole blob sort of thing, (which in addition to a key requires a block mode and an iv)

答案3

得分: 0

接受的答案,尤其是Topaco的评论,帮助我得出了这个解决方案,我在这里发布给可能遇到此问题的任何人:

var key = make([]byte, 32)

func encryptToken(t token) string {
    original := t.ID // ID是token的字符串成员
    var nonce = make([]byte, 12)

    // 读取随机字节到nonce中
    _, err := rand.Read(nonce)
    if err != nil {
        log.Println("读取随机字节到nonce时出错:", err)
    }

    block, err := aes.NewCipher(key)
    if err != nil {
        log.Println("在加密过程中创建密码时出错:", err)
    }

    aesgcm, err := cipher.NewGCM(block)
    if err != nil {
        log.Println("在加密过程中创建GCM时出错:", err)
    }

    ciphertext := aesgcm.Seal(nil, nonce, []byte(original), nil)

    // 在密文前添加nonce
    out := append(nonce, ciphertext...)

    return hex.EncodeToString(out)
}

func decryptToken(s string) string {
    // 读取描述nonce和密文的十六进制字符串
    enc, err := hex.DecodeString(s)
    if err != nil {
        log.Println("从十六进制解码字符串时出错:", err)
    }

    // 将密文和nonce分开
    nonce := enc[0:12]
    ciphertext := enc[12:]

    block, err := aes.NewCipher(key)
    if err != nil {
        log.Println("在解密过程中创建密码时出错:", err)
    }

    aesgcm, err := cipher.NewGCM(block)
    if err != nil {
        log.Println("在解密过程中创建GCM时出错:", err)
    }

    original, err := aesgcm.Open(nil, nonce, ciphertext, nil)
    if err != nil {
        log.Println("解密为字符串时出错:", err)
    }
    originalAsString := string(original)

    return originalAsString
}

参考:https://pkg.go.dev/crypto/cipher#example-NewGCM-Encrypt

英文:

EDITED per comments below (in order to correctly use a nonce):

The accepted answer, and especially the comment from Topaco, helped me arrive to this solution, I am posting here for anyone that may come across this:

var key = make([]byte, 32)
func encryptToken(t token) string {
original := t.ID // ID is string member of token
var nonce = make([]byte, 12)
// read random bytes into nonce
_, err := rand.Read(nonce)
if err != nil {
log.Println("Error reading random bytes into nonce:", err)
}
block, err := aes.NewCipher(key)
if err != nil {
log.Println("Error creating cipher during encrypt:", err)
}
aesgcm, err := cipher.NewGCM(block)
if err != nil {
log.Println("Error creating GCM during encrypt:", err)
}
ciphertext := aesgcm.Seal(nil, nonce, []byte(original), nil)
// prepend the ciphertext with the nonce
out := append(nonce, ciphertext...)
return hex.EncodeToString(out)
}
func decryptToken(s string) string {
// read hex string describing nonce and ciphertext
enc, err := hex.DecodeString(s)
if err != nil {
log.Println("Error decoding string from hex:", err)
}
// separate ciphertext from nonce
nonce := enc[0:12]
ciphertext := enc[12:]
block, err := aes.NewCipher(key)
if err != nil {
log.Println("Error creating cipher during decrypt:", err)
}
aesgcm, err := cipher.NewGCM(block)
if err != nil {
log.Println("Error creating GCM during decrypt:", err)
}
original, err := aesgcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
log.Println("Error decrypting to string:", err)
}
originalAsString := string(original)
return originalAsString
}

Reference: https://pkg.go.dev/crypto/cipher#example-NewGCM-Encrypt

huangapple
  • 本文由 发表于 2022年11月13日 11:44:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/74418323.html
匿名

发表评论

匿名网友

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

确定