1. 程式人生 > >RSA演算法講解與Go語言例項

RSA演算法講解與Go語言例項

一、RSA演算法概述

RSA是"非對稱加密演算法",非對稱加密演算法需要兩個金鑰:公開金鑰(publickey)和私有金鑰(privatekey)。公鑰與私鑰是配對的,用公鑰加密的資料只有配對的私鑰才能解密,反之亦然。因加解密使用兩個不同的金鑰,所以這種演算法叫作非對稱加密演算法。 使用RSA加密演算法流程如下:

1.訊息接收方B先把公鑰廣播,訊息傳送方A儲存B的公鑰 2.當A需要向B傳送訊息時,先用B的公鑰將訊息進行加密,再將密文傳送給A 3.A接受到密文以後,使用自己的私鑰進行解密

RSA具有一個離散對數和橢圓曲線加密都沒有的特性: 既可以用公鑰加密然後私鑰解密,也可以用私鑰加密然後公鑰解密(對稱性)。

公鑰加密然後私鑰解密,可以用於通訊中擁有公鑰的一方向擁有私鑰的另一方傳遞機密資訊,不被第三方竊聽。

那麼私鑰加密然後公鑰解密是用在什麼場合呢?就是數字簽名。

二、數字簽名

RSA中的每一個公鑰都有唯一的私鑰與之對應,任一公鑰只能解開對應私鑰加密的內容。換句話說,其它私鑰加密的內容,這個公鑰是解不開的。

如果你生成了一對RSA金鑰,你把公鑰公佈出去,並告訴全世界人這個公鑰是你的。之後你只要在傳送的訊息,比如“123456”,後面加上用私鑰加密過的密文,其他人拿公鑰解密,看解密得到的內容是是“123456”就可以知道這個“123456”是不是你發的。

簽名:   1、提取訊息摘要,使用傳送方私鑰對訊息摘要加密,生成訊息簽名。   2、將訊息簽名和訊息一起,使用接收方公鑰加密,獲得密文。

驗籤:   1、使用接收方私鑰對密文解密,獲得訊息和訊息簽名。   2、使用傳送方公鑰解密訊息簽名,獲得訊息摘要。   3、使用相同辦法重新提取訊息摘要,與上一步中訊息摘要對比,如相同則驗籤成功。

三、Go語言使用RSA演算法加解密例項

package main

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/hex"
	"encoding/pem"
	"fmt"
	"os"
)

//生成金鑰對(公鑰和私鑰)
func RsaGenKey(bits int) error{
	//通過隨機數生成金鑰對
	priveKey,err := rsa.GenerateKey(rand.Reader,bits)
	if err != nil {
		return err
	}
	//x509通用的證書格式:序列號,簽名演算法,頒發者,有效時間等
	//PKCS:由RSA實驗室和開發商指定的標準
	priStream := x509.MarshalPKCS1PrivateKey(priveKey)
	block := pem.Block{
		Type:"RSA Private key",
		Bytes:priStream,
	}
	/*
	-------------------BEGIN RSA Private Key---------------------
	內容
	-------------------END RSA Private Key-----------------------
	 */

	 //建立儲存私鑰檔案
	priveFile,err := os.Create("private.pem")
	defer priveFile.Close()
	if err != nil{
		return err
	}

	//將塊編碼到檔案中
	err = pem.Encode(priveFile,&block)
	if err != nil{
		return err
	}

	//從公鑰獲取私鑰
	pubKey := priveKey.PublicKey
	//通過x509標準達到rsa公鑰序列化後的切片
	pubStream,err := x509.MarshalPKIXPublicKey(&pubKey)
	if err != nil{
		return err
	}

	//建立公鑰塊
	block = pem.Block{
		Type:"RSA Public Key",
		Bytes:pubStream,
	}

	pubFile,err := os.Create("public.pem")
	defer pubFile.Close()
	if err != nil{
		return err
	}

	//將公鑰塊編碼到檔案
	err = pem.Encode(pubFile,&block)
	if err!= nil{
		return err
	}

	return nil
}

//公鑰加密函式
//src待加密的資料,pathName公鑰路徑
func RsaPublicEncrypt(src []byte,pathName string)([]byte,error){
	//開啟公鑰檔案
	file,err := os.Open(pathName)
	defer file.Close()
	if err != nil{
		return []byte(""),err
	}

	//獲取檔案資訊
	info,err := file.Stat()
	if err != nil{
		return []byte(""),err
	}

	//建立切片,用於儲存檔案中讀取到的公鑰資訊
	recvBuf := make([]byte,info.Size())
	//讀取公鑰檔案
	file.Read(recvBuf)
	//將得到的切片解碼到塊中
	block,_ := pem.Decode(recvBuf)
	//使用x509包中的ParsePKIXPublicKey解析公鑰
	pubInter,err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil{
		return []byte(""),err
	}
	//通過斷言將介面轉換成公鑰
	pubKey := pubInter.(*rsa.PublicKey)
	msg,err := rsa.EncryptPKCS1v15(rand.Reader,pubKey,src)
	if err != nil{
		return []byte(""),err
	}

	return msg,err
}

//使用私鑰解密資訊
//src:待解密的私鑰資訊  pathName:私鑰路徑
func RsaPrivateDecrypt(src []byte,pathName string)([]byte,error){
	//通過私鑰檔案路徑開啟私鑰檔案
	file,err := os.Open(pathName)
	defer file.Close()
	if err != nil{
		return []byte(""),err
	}

	info,err := file.Stat()
	if err != nil {
		return []byte(""), err
	}

	//建立切片用於接受私鑰內容
	recvBuf := make([]byte,info.Size())
	//讀取私鑰檔案
	file.Read(recvBuf)
	block,_ := pem.Decode(recvBuf)

	privateKey,err := x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil{
		return []byte(""),err
	}

	msg,err := rsa.DecryptPKCS1v15(rand.Reader,privateKey,src)
	if err != nil{
		return []byte(""),err
	}

	return msg,nil
}

func main(){
	err := RsaGenKey(1024)
	if err != nil{
		fmt.Println("err=",err)
	}


	src := []byte("單槍匹馬你別怕,一腔孤勇又如何!")

	data,err := RsaPublicEncrypt(src,"public.pem")
	if err != nil{
		fmt.Println("err:",err)
		return
	}

	fmt.Println("加密:",hex.EncodeToString(data))

	data,err = RsaPrivateDecrypt(data,"private.pem")
	if err != nil{
		fmt.Println("Decrypt err:",err)
		return
	}

	fmt.Println("解密:",string(data))
}

四、Go語言使用RSA演算法實現數字簽名認證

package main

import (
	"crypto"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/pem"
	"errors"
	"fmt"
	"os"
)

//生成金鑰對(公鑰和私鑰)
func RsaGenKey(bits int) error{
	//通過隨機數生成金鑰對
	priveKey,err := rsa.GenerateKey(rand.Reader,bits)
	if err != nil {
		return err
	}
	//x509通用的證書格式:序列號,簽名演算法,頒發者,有效時間等
	//PKCS:由RSA實驗室和開發商指定的標準
	priStream := x509.MarshalPKCS1PrivateKey(priveKey)
	block := pem.Block{
		Type:"RSA Private key",
		Bytes:priStream,
	}
	/*
	-------------------BEGIN RSA Private Key---------------------
	內容
	-------------------END RSA Private Key-----------------------
	 */

	 //建立儲存私鑰檔案
	priveFile,err := os.Create("private.pem")
	defer priveFile.Close()
	if err != nil{
		return err
	}

	//將塊編碼到檔案中
	err = pem.Encode(priveFile,&block)
	if err != nil{
		return err
	}

	//從公鑰獲取私鑰
	pubKey := priveKey.PublicKey
	//通過x509標準達到rsa公鑰序列化後的切片
	pubStream,err := x509.MarshalPKIXPublicKey(&pubKey)
	if err != nil{
		return err
	}

	//建立公鑰塊
	block = pem.Block{
		Type:"RSA Public Key",
		Bytes:pubStream,
	}

	pubFile,err := os.Create("public.pem")
	defer pubFile.Close()
	if err != nil{
		return err
	}

	//將公鑰塊編碼到檔案
	err = pem.Encode(pubFile,&block)
	if err!= nil{
		return err
	}

	return nil
}

//公鑰加密函式
//src待加密的資料,pathName公鑰路徑
func RsaPublicEncrypt(src []byte,pathName string)([]byte,error){
	//開啟公鑰檔案
	file,err := os.Open(pathName)
	defer file.Close()
	if err != nil{
		return []byte(""),err
	}

	//獲取檔案資訊
	info,err := file.Stat()
	if err != nil{
		return []byte(""),err
	}

	//建立切片,用於儲存檔案中讀取到的公鑰資訊
	recvBuf := make([]byte,info.Size())
	//讀取公鑰檔案
	file.Read(recvBuf)
	//將得到的切片解碼到塊中
	block,_ := pem.Decode(recvBuf)
	//使用x509包中的ParsePKIXPublicKey解析公鑰
	pubInter,err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil{
		return []byte(""),err
	}
	//通過斷言將介面轉換成公鑰
	pubKey := pubInter.(*rsa.PublicKey)
	msg,err := rsa.EncryptPKCS1v15(rand.Reader,pubKey,src)
	if err != nil{
		return []byte(""),err
	}

	return msg,err
}

//使用私鑰解密資訊
//src:待解密的私鑰資訊  pathName:私鑰路徑
func RsaPrivateDecrypt(src []byte,pathName string)([]byte,error){
	//通過私鑰檔案路徑開啟私鑰檔案
	file,err := os.Open(pathName)
	defer file.Close()
	if err != nil{
		return []byte(""),err
	}

	info,err := file.Stat()
	if err != nil {
		return []byte(""), err
	}

	//建立切片用於接受私鑰內容
	recvBuf := make([]byte,info.Size())
	//讀取私鑰檔案
	file.Read(recvBuf)
	block,_ := pem.Decode(recvBuf)

	privateKey,err := x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil{
		return []byte(""),err
	}

	msg,err := rsa.DecryptPKCS1v15(rand.Reader,privateKey,src)
	if err != nil{
		return []byte(""),err
	}

	return msg,nil
}

//私鑰簽名
//data:訊息內容
//pathName:私鑰檔名
func RsaSign(data []byte,pathName string)([]byte,error){
	h := sha256.New()
	h.Write(data)

	hashed := h.Sum(nil)

	//獲取私鑰
	file,err := os.Open(pathName)
	defer file.Close()
	if err != nil{
		return []byte(""),err
	}

	info,err := file.Stat()
	if err != nil {
		return []byte(""), err
	}

	//建立切片用於接受私鑰內容
	recvBuf := make([]byte,info.Size())
	//讀取私鑰檔案
	file.Read(recvBuf)
	block,_ := pem.Decode(recvBuf)

	privateKey,err := x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil{
		return []byte(""),err
	}

	return rsa.SignPKCS1v15(rand.Reader,privateKey,crypto.SHA256,hashed)
}


//公鑰驗證
func RsaSignVer(data []byte,pathName string,signature []byte)error{
	//獲取私鑰
	file,err := os.Open(pathName)
	defer file.Close()
	if err != nil{
		return err
	}

	info,err := file.Stat()
	if err != nil {
		return err
	}

	//建立切片用於接受公鑰內容
	recvBuf := make([]byte,info.Size())
	//讀取公鑰檔案
	file.Read(recvBuf)

	hashed := sha256.Sum256(data)
	block,_ := pem.Decode(recvBuf)

	if block == nil{
		return errors.New("public key error")
	}

	// 解析公鑰
	pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		return err
	}
	// 型別斷言
	pub := pubInterface.(*rsa.PublicKey)
	//驗證簽名
	return rsa.VerifyPKCS1v15(pub, crypto.SHA256, hashed[:], signature)
}

func main(){
	err := RsaGenKey(1024)
	if err != nil{
		fmt.Println("err=",err)
	}


	msg := []byte("單槍匹馬你別怕,一腔孤勇又如何!")

	fmt.Println("msg:",string(msg))

	sig,err := RsaSign(msg,"private.pem")
	if err !=nil{
		fmt.Println("RsaSign error:",err)
		return
	}

	fmt.Println("sig:",sig)

	if err := RsaSignVer(msg,"public.pem",sig);err != nil{
		fmt.Println("RsaSignVer error:",err)
		return
	}

	fmt.Println("簽名驗證成功")
}