github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/cry/aes.go (about) 1 package cry 2 3 import ( 4 "crypto/aes" 5 "crypto/cipher" 6 "crypto/rand" 7 "encoding/hex" 8 "fmt" 9 "io" 10 11 "github.com/pkg/errors" 12 ) 13 14 // AESOpt contains all aes session option 15 type AESOpt struct { 16 aesGCM cipher.AEAD 17 } 18 19 // NewAESOpt is function to create new configuration of aes algorithm option 20 // the secret must be hexa a-f & 0-9 21 func NewAESOpt(secret string) (*AESOpt, error) { 22 if len(secret) != 64 { 23 return nil, errors.New("Secret must be 64 character") 24 } 25 key, err := hex.DecodeString(secret) 26 if err != nil { 27 return nil, errors.Wrap(err, "NewAESOpt.hex.DecodeString") 28 } 29 30 // Create a new Cipher Block from the key 31 block, err := aes.NewCipher(key) 32 if err != nil { 33 return nil, errors.Wrap(err, "NewAESOpt.aes.NewCipher") 34 } 35 36 // AES本身有四种可能的模式,但是常用的只有两种:CBC和GCM。 37 // CBC模式就是一个一个块的计算每个块的编码结果,下一个块输入上一个块的计算结果,第一个块输入的是IV(初始向量)。如此所有的数据块形成一条链,逐个计算得到了最终的加密数据。 38 // GCM来自于AES的CTR模式,CTR是指计数器模式。GCM是利用GMAC(基于伽罗华域的MAC)和AES的CTR模式的组合。GMAC比普通的MAC算法快(毕竟冠以伽罗华之名), 39 // GCM模式与CBC的一个最大的区别是GCM模式不再把上一个数据块的计算结果输入到下一个数据块的计算,而是在分好的数据块中任意位置开始计算, 40 // 由一个计数器和一个不变的IV值(nounce)来控制每一次计算的随机性。由于下一次的计算并不依赖于上一次的结果,所以GCM模式可以实现大规模的并行化,并且Intel还专门推出了clmul指令用于加速GCM的运算速度,可见其应用之广。 41 // 推荐先后顺序:GCM > CTR > CBC http://nanhuacoder.top/2018/01/03/iOS-UseAES 42 // Create a new GCM - https://en.wikipedia.org/wiki/Galois/Counter_Mode 43 // https://golang.org/pkg/crypto/cipher/#NewGCM 44 aesGCM, err := cipher.NewGCM(block) 45 if err != nil { 46 return nil, errors.Wrap(err, "NewAESOpt.cipher.NewGCM") 47 } 48 49 return &AESOpt{ 50 aesGCM: aesGCM, 51 }, nil 52 } 53 54 // Encrypt is function to encrypt data using AES algorithm 55 func (aesOpt *AESOpt) Encrypt(plainText []byte) (string, error) { 56 // Create a nonce. Nonce should be from GCM 57 nonce := make([]byte, aesOpt.aesGCM.NonceSize()) 58 if _, err := io.ReadFull(rand.Reader, nonce); err != nil { 59 return "", errors.Wrap(err, "encryptAES.io.ReadFull") 60 } 61 62 // Encrypt the data using aesGCM.Seal 63 // Since we don't want to save the nonce somewhere else in this case, we add it as a prefix to the encrypted data. The first nonce argument in Seal is the prefix. 64 ciphertext := aesOpt.aesGCM.Seal(nonce, nonce, plainText, nil) 65 return fmt.Sprintf("%x", ciphertext), nil 66 } 67 68 // Decrypt is function to decypt data using AES algorithm 69 func (aesOpt *AESOpt) Decrypt(chiperText []byte) (string, error) { 70 enc, _ := hex.DecodeString(string(chiperText)) 71 72 // Get the nonce size 73 nonceSize := aesOpt.aesGCM.NonceSize() 74 if len(enc) < nonceSize { 75 return "", errors.New("The data can't be decrypted") 76 } 77 // Extract the nonce from the encrypted data 78 nonce, ciphertext := enc[:nonceSize], enc[nonceSize:] 79 80 // Decrypt the data 81 plainText, err := aesOpt.aesGCM.Open(nil, nonce, ciphertext, nil) 82 if err != nil { 83 return "", errors.Wrap(err, "decryptAES.aesGCM.Open") 84 } 85 86 return fmt.Sprintf("%s", plainText), nil 87 }