Go中使用加密算法的方法
哈希算法
md5
128bit,16字節(jié)
如:md5 (“hello world!”) = fc3ff98e8c6a0d3087d515c0473f8677 // 32位16進(jìn)制數(shù)字
func Test(t *testing.T) {
//方法一
str := "hello world!"
has := md5.Sum([]byte(str))
md5str1 := fmt.Sprintf("%x", has) //將[]byte轉(zhuǎn)成16進(jìn)制
t.Log(md5str1)
//方法二
w := md5.New()
io.WriteString(w, str)
md5str2 := fmt.Sprintf("%x", w.Sum(nil))
t.Log(md5str2)
}
SHA1
160bit,20字節(jié)
如:SHA1 (“hello world!”) = 430ce34d020724ed75a196dfc2ad67c77772d169 // 40位16進(jìn)制數(shù)字
func Test(t *testing.T) {
str := "hello world!"
//產(chǎn)生一個(gè)散列值得方式是 sha1.New(),sha1.Write(bytes),然后 sha1.Sum([]byte{})。
h := sha1.New()
//寫入要處理的字節(jié)。
h.Write([]byte(str))
//SHA1 值經(jīng)常以 16 進(jìn)制輸出,例如在 git commit 中。
t.Log(hex.EncodeToString(h.Sum(nil)))
}
RIPEMD-160
160bit,20字節(jié)
如:RIPEMD-160 (“hello world!”) = dffd03137b3a333d5754813399a5f437acd694e5 // 40位16進(jìn)制數(shù)字
func Test(t *testing.T) {
str := "hello world!"
h := ripemd160.New()
h.Write([]byte(str))
t.Log(hex.EncodeToString(h.Sum(nil)))
}
SHA256
256bit,32字節(jié)
如:SHA256 (“hello world!”) = 7509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca9 // 64位16進(jìn)制數(shù)字
func Test(t *testing.T) {
str := "hello world!"
// 第一種調(diào)用方法
sum := sha256.Sum256([]byte(str))
t.Logf("%x\n", sum)
// 第二種調(diào)用方法
h := sha256.New()
io.WriteString(h,str)
t.Log(hex.EncodeToString(h.Sum(nil)))
}
SHA256實(shí)現(xiàn)原理
附加填充⽐特
SHA-256算法輸⼊報(bào)⽂的最⼤⻓度不超過2^64 bit,輸⼊按512bit分組進(jìn)⾏處理,產(chǎn)⽣的輸出是⼀個(gè)256bit的報(bào)⽂摘要。
SHA256算法包括以下⼏步:
對(duì)報(bào)⽂進(jìn)⾏填充,使報(bào)⽂⻓度與448 模512 同余(⻓度=448 mod512),填充的⽐特?cái)?shù)范圍是1 到512,填充⽐特串的最⾼位為1,其余位為0。就是先在報(bào)⽂后⾯加⼀個(gè) 1,再加很多個(gè)0,直到⻓度滿⾜mod512=448。為什么是448,因?yàn)?48+64=512。第⼆步會(huì)加上⼀個(gè)64bit的原始報(bào)⽂的 ⻓度信息。附加⻓度值
將⽤64bit 表示的初始報(bào)⽂(填充前)的位⻓度附加在步驟1的結(jié)果后(低位字節(jié)優(yōu)先)初始化緩存
使⽤⼀個(gè)256bit 的緩存來存放該散列函數(shù)的中間及最終結(jié)果。該緩存表示為:
A=0x6A09E667
B=0xBB67AE85
C=0x3C6EF372
D=0xA54FF53A
E=0x510E527F
F=0x9B05688C
G=0x1F83D9AB
H=0x5BE0CD19處理512bit(16 個(gè)字)報(bào)⽂分組序列
該算法使⽤了六種基本邏輯函數(shù),由64 步迭代運(yùn)算組成。每步都以256bit 緩存ABCDEFGH 為輸⼊,然后更新緩存內(nèi)容。每步使⽤⼀個(gè)32bit 常數(shù)值Kt 和⼀個(gè)32bit Wt。
SHA512
512bit,64字節(jié)
如:SHA512 (“hello world!”) = db9b1cd3262dee37756a09b9064973589847caa8e53d31a9d142ea2701b1b28abd97838bb9a27068ba305dc8d04a45a1fcf079de54d607666996b3cc54f6b67c // 128位16進(jìn)制數(shù)字
func Test(t *testing.T) {
str := "hello world!"
// 第一種調(diào)用方法
sum := sha512.Sum512([]byte(str))
t.Logf("%x\n", sum)
// 第二種調(diào)用方法
h := sha512.New()
io.WriteString(h,str)
t.Log(hex.EncodeToString(h.Sum(nil)))
}
加密模式
加密一般分為對(duì)稱加密(Symmetric Key Encryption)和非對(duì)稱加密(Asymmetric Key Encryption)。
對(duì)稱加密又分為分組加密和序列密碼。
分組密碼,也叫塊加密(block cyphers),一次加密明文中的一個(gè)塊。是將明文按一定的位長(zhǎng)分組,明文組經(jīng)過加密運(yùn)算得到密文組,密文組經(jīng)過解密運(yùn)算(加密運(yùn)算的逆運(yùn)算),還原成明文組。
序列密碼,也叫流加密(stream cyphers),一次加密明文中的一個(gè)位。是指利用少量的密鑰(制亂元素)通過某種復(fù)雜的運(yùn)算(密碼算法)產(chǎn)生大量的偽隨機(jī)位流,用于對(duì)明文位流的加密。
解密是指用同樣的密鑰和密碼算法及與加密相同的偽隨機(jī)位流,用以還原明文位流。
分組加密算法中,有ECB,CBC,CFB,OFB這幾種算法模式。
| 加密模式 | 解釋 |
|---|---|
| ECB | 最基本的加密模式,也就是通常理解的加密,相同的明⽂將永遠(yuǎn)加密成相同的密⽂,⽆初始向量,容易受到密碼本重放攻擊,⼀般情況下很少⽤ |
| CBC | 明⽂被加密前要與前⾯的密⽂進(jìn)⾏異或運(yùn)算后再加密,因此只要選擇不同的初始向量,相同的密⽂加密后會(huì)形成不同的密⽂,這是⽬前應(yīng)⽤最⼴泛的模式。CBC加密后的密⽂是上下⽂相關(guān)的,但明⽂的錯(cuò)誤不會(huì)傳遞到后續(xù)分組,但如果⼀個(gè)分組丟失,后⾯的分組將全部作廢(同步錯(cuò)誤) |
| CFB | 類似于⾃同步序列密碼,分組加密后,按8位分組將密⽂和明⽂進(jìn)⾏移位異或后得到輸出同時(shí)反饋回移位寄存器,優(yōu)點(diǎn)最⼩可以按字節(jié)進(jìn)⾏加解密,也可以是n位的,CFB也是上下⽂相關(guān)的,CFB模式下,明⽂的⼀個(gè)錯(cuò)誤會(huì)影響后⾯的密⽂(錯(cuò)誤擴(kuò)散)。 |
| OFB | 將分組密碼作為同步序列密碼運(yùn)⾏,和CFB相似,不過OFB⽤的是前⼀個(gè)n位密⽂輸出分組反饋回移位寄存器,OFB沒有錯(cuò)誤擴(kuò)散問題 |
對(duì)稱加密
最常用的對(duì)稱加密算法DES、3DES(TripleDES)和AES,常采用的填充⽅式是NoPadding(不填充)、Zeros填充(0填充)、PKCS5Padding填充。
加密算法要求明文需要按一定長(zhǎng)度對(duì)齊,叫做塊大小(BlockSize),比如8字節(jié),那么對(duì)于一段任意的數(shù)據(jù),加密前需要對(duì)最后一個(gè)塊填充到8 字節(jié),解密后需要?jiǎng)h除掉填充的數(shù)據(jù)。
| 填充⽅式 | 解釋 |
|---|---|
| ZeroPadding | 數(shù)據(jù)長(zhǎng)度不對(duì)齊時(shí)使用0填充,否則不填充 |
| PKCS7Padding | 假設(shè)數(shù)據(jù)長(zhǎng)度需要填充n(n>0)個(gè)字節(jié)才對(duì)齊,那么填充n個(gè)字節(jié),每個(gè)字節(jié)都是n;如果數(shù)據(jù)本身就已經(jīng)對(duì)齊了,則填充一塊長(zhǎng)度為塊大小的數(shù)據(jù),每個(gè)字節(jié)都是塊大小 |
| PKCS5Padding | PKCS7Padding的子集,塊大小固定為8字節(jié) |
由于使用PKCS7Padding/PKCS5Padding填充時(shí),最后一個(gè)字節(jié)肯定為填充數(shù)據(jù)的長(zhǎng)度,所以在解密后可以準(zhǔn)確刪除填充的數(shù)據(jù),而使用ZeroPadding填充時(shí),沒辦法區(qū)分真實(shí)數(shù)據(jù)與填充數(shù)據(jù),所以只適合以\0結(jié)尾的字符串加解密。
對(duì)稱加密需要的填充函數(shù)
func PKCS5Padding(data []byte, blockSize int) []byte {
padding := blockSize - len(data)%blockSize
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
return append(data, padtext...)
}
func PKCS5UnPadding(data []byte) []byte {
length := len(data)
// 去掉最后⼀個(gè)字節(jié) unpadding 次
unpadding := int(data[length-1])
return data[:(length - unpadding)]
}
func ZeroPadding(data []byte, blockSize int) []byte {
padding := blockSize - len(data)%blockSize
padtext := bytes.Repeat([]byte{0}, padding)
return append(data, padtext...)
}
func ZeroUnPadding(data []byte) []byte {
return bytes.TrimRightFunc(data, func(r rune) bool {
return r == rune(0)
})
}
DES
//DES加密字節(jié)數(shù)組,返回字節(jié)數(shù)組
func DesEncrypt(originalBytes, key []byte) ([]byte, error) {
block, err := des.NewCipher(key)
if err != nil {
return nil, err
}
originalBytes = PKCS5Padding(originalBytes, block.BlockSize())
blockMode := cipher.NewCBCEncrypter(block, key)
cipherArr := make([]byte, len(originalBytes))
blockMode.CryptBlocks(cipherArr, originalBytes)
return cipherArr, nil
}
//DES解密字節(jié)數(shù)組,返回字節(jié)數(shù)組
func DesDecrypt(cipherBytes, key []byte) ([]byte, error) {
block, err := des.NewCipher(key)
if err != nil {
return nil, err
}
blockMode := cipher.NewCBCDecrypter(block, key)
originalText := make([]byte, len(cipherBytes))
blockMode.CryptBlocks(originalText, cipherBytes)
originalText = PKCS5UnPadding(originalText)
return originalText, nil
}
//DES加密⽂本,返回加密后⽂本
func DesEncryptString(originalText string, key []byte) (string, error) {
cipherArr, err := DesEncrypt([]byte(originalText), key)
if err != nil {
return "", err
}
base64str := base64.StdEncoding.EncodeToString(cipherArr)
return base64str, nil
}
//對(duì)加密⽂本進(jìn)⾏DES解密,返回解密后明⽂
func DesDecryptString(cipherText string, key []byte) (string, error) {
cipherArr, _ := base64.StdEncoding.DecodeString(cipherText)
cipherArr, err := DesDecrypt(cipherArr, key)
if err != nil {
return "", err
}
return string(cipherArr), nil
}
3DES
// 3DES加密字節(jié)數(shù)組,返回字節(jié)數(shù)組
func TripleDesEncrypt(originalBytes, key []byte) ([]byte, error) {
block, err := des.NewTripleDESCipher(key)
if err != nil {
return nil, err
}
originalBytes = PKCS5Padding(originalBytes, block.BlockSize())
// originalBytes = ZeroPadding(originalBytes, block.BlockSize())
blockMode := cipher.NewCBCEncrypter(block, key[:8])
cipherArr := make([]byte, len(originalBytes))
blockMode.CryptBlocks(cipherArr, originalBytes)
return cipherArr, nil
}
// 3DES解密字節(jié)數(shù)組,返回字節(jié)數(shù)組
func TripleDesDecrypt(cipherBytes, key []byte) ([]byte, error) {
block, err := des.NewTripleDESCipher(key)
if err != nil {
return nil, err
}
blockMode := cipher.NewCBCDecrypter(block, key[:8])
originalArr := make([]byte, len(cipherBytes))
blockMode.CryptBlocks(originalArr, cipherBytes)
originalArr = PKCS5UnPadding(originalArr)
// origData = ZeroUnPadding(origData)
return originalArr, nil
}
// 3DES加密字符串,返回base64處理后字符串
func TripleDesEncrypt2Str(originalText string, key []byte) (string, error) {
block, err := des.NewTripleDESCipher(key)
if err != nil {
return "", err
}
originalData := PKCS5Padding([]byte(originalText), block.BlockSize())
// originalData = ZeroPadding(originalData, block.BlockSize())
blockMode := cipher.NewCBCEncrypter(block, key[:8])
cipherArr := make([]byte, len(originalData))
blockMode.CryptBlocks(cipherArr, originalData)
cipherText := base64.StdEncoding.EncodeToString(cipherArr)
return cipherText, nil
}
// 3DES解密base64處理后的加密字符串,返回明⽂字符串
func TripleDesDecrypt2Str(cipherText string, key []byte) (string, error) {
cipherArr, _ := base64.StdEncoding.DecodeString(cipherText)
block, err := des.NewTripleDESCipher(key)
if err != nil {
return "", err
}
blockMode := cipher.NewCBCDecrypter(block, key[:8])
originalArr := make([]byte, len(cipherArr))
blockMode.CryptBlocks(originalArr, cipherArr)
originalArr = PKCS5UnPadding(originalArr)
// origData = ZeroUnPadding(origData)
return string(originalArr), nil
}
AES
//AES加密字節(jié)數(shù)組,返回字節(jié)數(shù)組
func AesEncrypt(originalBytes, key []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
blockSize := block.BlockSize()
originalBytes = PKCS5Padding(originalBytes, blockSize)
// originalBytes = ZeroPadding(originalBytes, block.BlockSize())
blockMode := cipher.NewCBCEncrypter(block, key[:blockSize])
cipherBytes := make([]byte, len(originalBytes))
// 根據(jù)CryptBlocks⽅法的說明,如下⽅式初始化crypted也可以
// crypted := originalBytes
blockMode.CryptBlocks(cipherBytes, originalBytes)
return cipherBytes, nil
}
//AES解密字節(jié)數(shù)組,返回字節(jié)數(shù)組
func AesDecrypt(cipherBytes, key []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
blockSize := block.BlockSize()
blockMode := cipher.NewCBCDecrypter(block, key[:blockSize])
originalBytes := make([]byte, len(cipherBytes))
// origData := cipherBytes
blockMode.CryptBlocks(originalBytes, cipherBytes)
originalBytes = PKCS5UnPadding(originalBytes)
// origData = ZeroUnPadding(origData)
return originalBytes, nil
}
//AES加密⽂本,返回對(duì)加密后字節(jié)數(shù)組進(jìn)⾏base64處理后字符串
func AesEncryptString(originalText string, key []byte) (string, error) {
cipherBytes, err := AesEncrypt([]byte(originalText), key)
if err != nil {
return "", err
}
base64str := base64.StdEncoding.EncodeToString(cipherBytes)
return base64str, nil
}
//AES解密⽂本,對(duì)Base64處理后的加密⽂本進(jìn)⾏AES解密,返回解密后明⽂
func AesDecryptString(cipherText string, key []byte) (string, error) {
cipherBytes, _ := base64.StdEncoding.DecodeString(cipherText)
cipherBytes, err := AesDecrypt(cipherBytes, key)
if err != nil {
return "", err
}
return string(cipherBytes), nil
}
⾮對(duì)稱加密
RSA算法也是一個(gè)塊加密算法( block cipher algorithm),總是在一個(gè)固定長(zhǎng)度的塊上進(jìn)行操作。但跟AES等不同的是,block length是跟key length有關(guān)的。
每次RSA加密的明文的長(zhǎng)度是受RSA填充模式限制的,但是RSA每次加密的塊長(zhǎng)度就是key length。
| 填充⽅式 | 密文長(zhǎng)度 |
|---|---|
| PKCS1Padding | 必須 比 RSA 秘鑰模長(zhǎng)(modulus) 短至少11個(gè)字節(jié), 也就是RSA_SIZE(rsa) – 11 |
| OAEPPadding | RSA_SIZE(rsa) – 41 |
| NOPadding | 可以和RSA鑰模長(zhǎng)一樣長(zhǎng),如果輸入的明文過長(zhǎng),必須切割, 然后填充 |
在不同的padding模式下,使用相同長(zhǎng)度的密鑰可以加密的數(shù)據(jù)最大長(zhǎng)度不同在不同密鑰長(zhǎng)度下,使用相同的padding模式可以加密的數(shù)據(jù)最大長(zhǎng)度也不同
因此,脫離了密鑰長(zhǎng)度而討論padding模式可以加密的最大長(zhǎng)度是不嚴(yán)謹(jǐn)?shù)?。常用的密鑰長(zhǎng)度有1024bits,2048bits等,理論上1024bits的密鑰可以加密的數(shù)據(jù)最大長(zhǎng)度為1024bits(即1024/8 = 128bytes)。2048bits的密鑰可以加密的數(shù)據(jù)最大長(zhǎng)度為2048bits(2048/8 = 256bytes),但是RSA在實(shí)際應(yīng)用中不可能使用這種“教科書式的RSA”系統(tǒng)。實(shí)際應(yīng)用中RSA經(jīng)常與填充技術(shù)(padding)一起使用,可以增加RSA的安全性。
PKCS1
// 加密字節(jié)數(shù)組,返回字節(jié)數(shù)組
func RsaEncrypt(publicKey, origData []byte) ([]byte, error) {
block, _ := pem.Decode(publicKey)
if block == nil {
return nil, errors.New("public key error")
}
pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return nil, err
}
pub := pubInterface.(*rsa.PublicKey)
return rsa.EncryptPKCS1v15(rand.Reader, pub, origData)
}
// 解密字節(jié)數(shù)組,返回字節(jié)數(shù)組
func RsaDecrypt(privateKey, ciphertext []byte) ([]byte, error) {
block, _ := pem.Decode(privateKey)
if block == nil {
return nil, errors.New("private key error!")
}
priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, err
}
return rsa.DecryptPKCS1v15(rand.Reader, priv, ciphertext)
}
// 加密字符串,返回base64處理的字符串
func RsaEncryptString(publicKey []byte, origData string) (string, error) {
block, _ := pem.Decode(publicKey)
if block == nil {
return "", errors.New("public key error")
}
pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return "", err
}
pub := pubInterface.(*rsa.PublicKey)
cipherArr, err := rsa.EncryptPKCS1v15(rand.Reader, pub, []byte(origData))
if err != nil {
return "", err
} else {
return base64.StdEncoding.EncodeToString(cipherArr), nil
}
}
// 解密經(jīng)過base64處理的加密字符串,返回加密前的明⽂
func RsaDecryptString(privateKey []byte, cipherText string) (string, error) {
block, _ := pem.Decode(privateKey)
if block == nil {
return "", errors.New("private key error!")
}
priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return "", err
}
cipherArr, _ := base64.StdEncoding.DecodeString(cipherText)
originalArr, err := rsa.DecryptPKCS1v15(rand.Reader, priv, cipherArr)
if err != nil {
return "", err
} else {
return string(originalArr), nil
}
}
OAEP
// 加密
func EncryptOAEP(publicKey []byte, text string) (string, error) {
block, _ := pem.Decode(publicKey)
if block == nil {
return "", errors.New("public key error")
}
pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return "", err
}
pub := pubInterface.(*rsa.PublicKey)
secretMessage := []byte(text)
rng := rand.Reader
cipherdata, err := rsa.EncryptOAEP(sha1.New(), rng, pub, secretMessage, nil)
if err != nil {
return "", err
}
ciphertext := base64.StdEncoding.EncodeToString(cipherdata)
return ciphertext, nil
}
// 解密
func DecryptOAEP(privateKey []byte, ciphertext string) (string, error) {
block, _ := pem.Decode(privateKey)
if block == nil {
return "", errors.New("private key error!")
}
priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return "", err
}
cipherdata, _ := base64.StdEncoding.DecodeString(ciphertext)
rng := rand.Reader
plaintext, err := rsa.DecryptOAEP(sha1.New(), rng, priv, cipherdata, nil)
if err != nil {
return "", err
}
return string(plaintext), nil
}
橢圓曲線加密算法ECC
橢圓曲線密碼學(xué)(Elliptic curve cryptography,縮寫為 ECC),是基于橢圓曲線數(shù)學(xué)理論實(shí)現(xiàn)的⼀種⾮對(duì)稱加密算法。
ECC與RSA算法的優(yōu)勢(shì)對(duì)⽐
橢圓曲線公鑰系統(tǒng)是代替RSA的強(qiáng)有⼒的競(jìng)爭(zhēng)者。
與經(jīng)典的RSA、DSA等公鑰密碼體制相⽐,橢圓密碼體制有以下優(yōu)點(diǎn):
(1)安全性能更⾼(ECC可以使⽤更短的密鑰):
160位ECC加密算法的安全強(qiáng)度相當(dāng)于1024位RSA加密;
210位ECC加密算法的安全強(qiáng)度相當(dāng)于2048位RSA加密。
(2)處理速度快:計(jì)算量⼩,處理速度快 在私鑰的處理速度上(解密和簽名),ECC遠(yuǎn) ⽐RSA、DSA快得多。
(3)存儲(chǔ)空間占⽤⼩: ECC的密鑰尺⼨和系統(tǒng)參數(shù)與RSA、DSA相⽐要⼩得多, 所以占⽤的存儲(chǔ)空間⼩得多。
(4)帶寬要求低使得ECC具有⼴泛的應(yīng)⽤前景。ECC的這些特點(diǎn)使它必將取代RSA,成為通⽤的公鑰加密算法。
//生成ECC橢圓曲線密鑰對(duì)
func GenerateECCKey() (*ecdsa.PublicKey, *ecdsa.PrivateKey, error) {
privateKey, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
if err != nil {
return nil, nil, err
}
publicKey := &privateKey.PublicKey
return publicKey, privateKey, nil
}
//對(duì)消息的散列值生成數(shù)字簽名
func SignECC(msg []byte) ([]byte, []byte) {
//取得私鑰
_, privateKey, err := GenerateECCKey()
if err != nil {
panic(err)
}
//計(jì)算哈希值
hash := sha256.New()
//填入數(shù)據(jù)
hash.Write(msg)
b := hash.Sum(nil)
//對(duì)哈希值生成數(shù)字簽名
r, s, err := ecdsa.Sign(rand.Reader, privateKey, b)
if err != nil {
panic(err)
}
rtext, _ := r.MarshalText()
stext, _ := s.MarshalText()
return rtext, stext
}
//驗(yàn)證數(shù)字簽名
func VerifySignECC(msg []byte, rtext, stext []byte) bool {
//取得公鑰
publicKey, _, err := GenerateECCKey()
if err != nil {
panic(err)
}
//計(jì)算哈希值
hash := sha256.New()
hash.Write(msg)
b := hash.Sum(nil)
//驗(yàn)證數(shù)字簽名
var r, s big.Int
if err := r.UnmarshalText(rtext); err != nil {
panic(err)
}
if err := s.UnmarshalText(stext); err != nil {
panic(err)
}
verify := ecdsa.Verify(publicKey, b, &r, &s)
return verify
}
//測(cè)試
func Test(t *testing.T) {
//模擬發(fā)送者
//要發(fā)送的消息
msg := []byte("hello world")
//生成數(shù)字簽名
rtext, stext := SignECC(msg)
//模擬接受者
//接受到的消息
acceptmsg := []byte("hello world")
//接收到的簽名
acceptrtext := rtext
acceptstext := stext
//驗(yàn)證簽名
verifySignECC := VerifySignECC(acceptmsg, acceptrtext, acceptstext)
fmt.Println("驗(yàn)證結(jié)果:", verifySignECC)
}
數(shù)字簽名
數(shù)字簽名的概念
1、簽名不可偽造性;
2、簽名不可抵賴的;
3、簽名可信性,簽名的識(shí)別和應(yīng)⽤相對(duì)容易,任何⼈都可以驗(yàn)證簽名的有效
性;
4、簽名是不可復(fù)制的,簽名與原⽂是不可分割的整體;
5、簽名消息不可篡改,因?yàn)槿我?#12112;特?cái)?shù)據(jù)被篡改,其簽名便被隨之改變,那么
任何⼈可以驗(yàn)證⽽拒絕接受此簽名。
橢圓曲線數(shù)字簽名算法ECDSA
//⽣成私鑰和公鑰,⽣成的私鑰為結(jié)構(gòu)體ecdsa.PrivateKey的指針
func NewKeyPair() (ecdsa.PrivateKey, []byte) {
//⽣成secp256橢圓曲線
curve := elliptic.P256()
//產(chǎn)⽣的是⼀個(gè)結(jié)構(gòu)體指針,結(jié)構(gòu)體類型為ecdsa.PrivateKey
private, err := ecdsa.GenerateKey(curve, rand.Reader)
if err != nil {
log.Panic(err)
}
fmt.Printf("私鑰:%x\n", private)
fmt.Printf("私鑰X:%x\n", private.X.Bytes())
fmt.Printf("私鑰Y:%x\n", private.Y.Bytes())
fmt.Printf("私鑰D:%x\n", private.D.Bytes())
//x坐標(biāo)與y坐標(biāo)拼接在⼀起,⽣成公鑰
pubKey := append(private.X.Bytes(), private.Y.Bytes()...)
//打印公鑰,公鑰⽤16進(jìn)制打印出來⻓度為128,包含了x軸坐標(biāo)與y軸坐標(biāo)。
fmt.Printf("公鑰:%x \n", pubKey)
return *private, pubKey
}
//⽣成簽名的DER格式
func MakeSignatureDerString(r, s string) string {
// 獲取R和S的⻓度
lenSigR := len(r) / 2
lenSigS := len(s) / 2
// 計(jì)算DER序列的總⻓度
lenSequence := lenSigR + lenSigS + 4
// 將10進(jìn)制⻓度轉(zhuǎn)16進(jìn)制字符串
strLenSigR := DecimalToHex(int64(lenSigR))
strLenSigS := DecimalToHex(int64(lenSigS))
strLenSequence := DecimalToHex(int64(lenSequence))
// 拼湊DER編碼
derString := "30" + strLenSequence
derString = derString + "02" + strLenSigR + r
derString = derString + "02" + strLenSigS + s
derString = derString + "01"
return derString
}
func DecimalToHex(n int64) string {
if n < 0 {
log.Println("Decimal to hexadecimal error: the argument must be greater than zero.")
return ""
}
if n == 0 {
return "0"
}
hex := map[int64]int64{10: 65, 11: 66, 12: 67, 13: 68, 14: 69, 15: 70}
s := ""
for q := n; q > 0; q = q / 16 {
m := q % 16
if m > 9 && m < 16 {
m = hex[m]
s = fmt.Sprintf("%v%v", string(m), s)
continue
}
s = fmt.Sprintf("%v%v", m, s)
}
return s
}
//驗(yàn)證簽名1
func VerifySig(pubKey, message []byte, r, s *big.Int) bool {
curve := elliptic.P256()
//公鑰的⻓度
keyLen := len(pubKey)
//前⼀半為x軸坐標(biāo),后⼀半為y軸坐標(biāo)
x := big.Int{}
y := big.Int{}
x.SetBytes(pubKey[:(keyLen / 2)])
y.SetBytes(pubKey[(keyLen / 2):])
rawPubKey := ecdsa.PublicKey{curve, &x, &y}
//根據(jù)交易哈希、公鑰、數(shù)字簽名驗(yàn)證成功。ecdsa.Verify func Verify(pub *PublicKey, hash[] byte, r * big.Int, s * big.Int) bool
res := ecdsa.Verify(&rawPubKey, message, r, s)
return res
}
//驗(yàn)證簽名2
func VerifySignature(pubKey, message []byte, r, s string) bool {
curve := elliptic.P256()
//公鑰的⻓度
keyLen := len(pubKey)
//前⼀半為x軸坐標(biāo),后⼀半為y軸坐標(biāo)
x := big.Int{}
y := big.Int{}
x.SetBytes(pubKey[:(keyLen / 2)])
y.SetBytes(pubKey[(keyLen / 2):])
rawPubKey := ecdsa.PublicKey{curve, &x, &y}
//根據(jù)交易哈希、公鑰、數(shù)字簽名驗(yàn)證成功。ecdsa.Verify func Verify(pub *PublicKey, hash[] byte, r * big.Int, s * big.Int) bool
rint := big.Int{}
sint := big.Int{}
rByte, _ := hex.DecodeString(r)
sByte, _ := hex.DecodeString(s)
rint.SetBytes(rByte)
sint.SetBytes(sByte)
//fmt.Println("------", rint.SetBytes(rByte))
//fmt.Println("------", sint.SetBytes(sByte))
res := ecdsa.Verify(&rawPubKey, message, &rint, &sint)
return res
}
//驗(yàn)證過程
func Test(t *testing.T) {
//1、⽣成簽名
fmt.Println("1、⽣成簽名-------------------------------")
//調(diào)⽤函數(shù)⽣成私鑰與公鑰
privKey, pubKey := NewKeyPair()
//信息的哈希
msg := sha256.Sum256([]byte("hello world"))
//根據(jù)私鑰和信息的哈希進(jìn)⾏數(shù)字簽名,產(chǎn)⽣r和s
r, s, _ := ecdsa.Sign(rand.Reader, &privKey, msg[:])
//⽣成r、s字符串
fmt.Println("-------------------------------")
strSigR := fmt.Sprintf("%x", r)
strSigS := fmt.Sprintf("%x", s)
fmt.Println("r、s的10進(jìn)制分別為:", r, s)
fmt.Println("r、s的16進(jìn)制分別為:", strSigR, strSigS)
//r和s拼接在⼀起,形成數(shù)字簽名的der格式
signatureDer := MakeSignatureDerString(strSigR, strSigS)
//打印數(shù)字簽名的16進(jìn)制顯示
fmt.Println("數(shù)字簽名DER格式為:", signatureDer)
fmt.Println()
//2、簽名驗(yàn)證過程
fmt.Println("2、簽名驗(yàn)證過程-------------------------------")
res := VerifySig(pubKey, msg[:], r, s)
fmt.Println("簽名驗(yàn)證結(jié)果:", res)
res = VerifySignature(pubKey, msg[:], strSigR, strSigS)
fmt.Println("簽名驗(yàn)證結(jié)果:", res)
}
字符編碼/解碼
Base64
Base64就是⼀種基于64個(gè)可打印字符來表示⼆進(jìn)制數(shù)據(jù)的⽅法。Base64使⽤了26個(gè)⼩寫字⺟、26個(gè)⼤寫字⺟、10個(gè)數(shù)字以及兩個(gè)符號(hào)(例如“+”和“/”),⽤于在電⼦郵件這樣的基于⽂本的媒介中傳輸⼆進(jìn)制數(shù)據(jù)。
Base64字符集:
將每個(gè)字符轉(zhuǎn)成ASCII編碼(10進(jìn)制)將10進(jìn)制編碼轉(zhuǎn)成2進(jìn)制編碼將2進(jìn)制編碼按照6位⼀組進(jìn)⾏平分將6位⼀組的2進(jìn)制數(shù)⾼位補(bǔ)零,然后轉(zhuǎn)成10進(jìn)制數(shù)將10進(jìn)制數(shù)作為索引,從Base64編碼表中查找字符每3個(gè)字符的⽂本將編碼為4個(gè)字符⻓度(38=46)
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
Base64的步驟
a. 若⽂本為3個(gè)字符,則正好編碼為4個(gè)字符⻓度;
b. 若⽂本為2個(gè)字符,則編碼為3個(gè)字符,由于不⾜4個(gè)字符,則在尾部⽤⼀個(gè)“=”補(bǔ)⻬;
c. 若⽂本為1個(gè)字符,則編碼為2個(gè)字符,由于不⾜4個(gè)字符,則在尾部⽤兩個(gè)“=”補(bǔ)⻬。
巨人的肩膀
從他人的工作中汲取經(jīng)驗(yàn)來避免自己的錯(cuò)誤重復(fù),正如我們是站在巨人的肩膀上才能做出更好的成績(jī)。
https://github.com/rubyhan1314/Golang-100-Days
https://blog.csdn.net/luckydog612/article/details/80547758
https://www.cnblogs.com/yanzi-meng/p/9640578.html
https://www.cnblogs.com/starwolf/p/3365834.html
https://blog.csdn.net/u013073067/article/details/87086562
https://www.cnblogs.com/Terry-Wu/p/10314315.html
http://blog.studygolang.com/2013/01/go%E5%8A%A0%E5%AF%86%E8%A7%A3%E5%AF%86%E4%B9%8Bdes/
https://blog.csdn.net/kikajack/article/details/78329567
到此這篇關(guān)于Go中使用加密算法的方法的文章就介紹到這了,更多相關(guān)go 加密算法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go語言使用templ實(shí)現(xiàn)編寫HTML用戶界面
templ是一個(gè)在 Go 中編寫 HTML 用戶界面的語言,使用 templ,我們可以創(chuàng)建可呈現(xiàn) HTML 片段的組件,下面就跟隨小編一起了解一下具體的實(shí)現(xiàn)方法吧2023-12-12
GO?CountMinSketch計(jì)數(shù)器(布隆過濾器思想的近似計(jì)數(shù)器)
這篇文章主要介紹了GO?CountMinSketch計(jì)數(shù)器(布隆過濾器思想的近似計(jì)數(shù)器),文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的朋友可以參考一下2022-09-09
golang容易導(dǎo)致內(nèi)存泄漏的6種情況匯總
內(nèi)存泄漏是我們?cè)谏a(chǎn)環(huán)境中必須面臨的問題,下面這篇文章主要給大家介紹了關(guān)于golang容易導(dǎo)致內(nèi)存泄漏的6種情況,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-01-01


