md5在crypto/md5包中,md5包提供了New和Sum方法。
站在用戶的角度思考問題,與客戶深入溝通,找到雙江網(wǎng)站設(shè)計(jì)與雙江網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗(yàn),讓設(shè)計(jì)與互聯(lián)網(wǎng)技術(shù)結(jié)合,創(chuàng)造個(gè)性化、用戶體驗(yàn)好的作品,建站類型包括:網(wǎng)站制作、成都網(wǎng)站設(shè)計(jì)、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣、域名注冊、網(wǎng)頁空間、企業(yè)郵箱。業(yè)務(wù)覆蓋雙江地區(qū)。func New() hash.Hash
func Sum(data []byte) [Size]byte
hash.Hash繼承了io.Writer,因此可以將其當(dāng)成一個(gè)輸入流進(jìn)行內(nèi)容的更新。
type Writer interface {
Write(p []byte) (n int, err error)
}
Write方法將p中的內(nèi)容讀入后存入到hash.Hash,最后在Sum方法通過內(nèi)部函數(shù)checkSum計(jì)算出其校驗(yàn)和。
Sum函數(shù)是對hash.Hash對象內(nèi)部存儲的內(nèi)容進(jìn)行校驗(yàn)和計(jì)算然后將其追加到data的后面形成一個(gè)新的byte切片。通常將data置為nil。
Sum方法返回一個(gè)Size大小的byte數(shù)組,對于MD5返回一個(gè)128bit的16字節(jié)byte數(shù)組。
package main
import (
"crypto/md5"
"fmt"
"io"
"os"
)
func MD5FromWriter() {
h := md5.New()
io.WriteString(h, "The fog is getting thicker!")
io.WriteString(h, "And Leon's getting laaarger!")
fmt.Printf("%x\n", h.Sum(nil))
// Output: e2c569be17396eca2a2e3c11578123ed
}
func MD5FromSum() {
data := []byte("These pretzels are making me thirsty.")
fmt.Printf("%x\n", md5.Sum(data))
// Output: b0804ec967f48520697662a204f5fe72
}
func MD5FromFile() {
f, err := os.Open("file.txt")
if err != nil {
fmt.Println("Open file failded")
}
defer f.Close()
h := md5.New()
if _, err := io.Copy(h, f); err != nil {
fmt.Println("Copy failed")
}
fmt.Printf("%x\n", h.Sum(nil))
}
func main() {
MD5FromWriter()
MD5FromSum()
MD5FromFile()
}
SHA-256算法輸入報(bào)文的大長度不超過2^64 bit,輸入按512-bit分組進(jìn)行處理,輸出一個(gè)256-bit的報(bào)文摘要。
sha256支持根據(jù)輸入生成SHA256和SHA224的報(bào)文摘要,主要方法如下:
func New() hash.Hash
func New224() hash.Hash
func Sum256(data []byte) [Size]byte
func Sum224(data []byte) (sum224 [Size224]byte)
package main
import (
"crypto/sha256"
"fmt"
"io"
"os"
)
func SHA256FromSum256() {
sum := sha256.Sum256([]byte("hello world\n"))
fmt.Printf("%x\n", sum)
// Output: a948904f2f0f479b8f8197694b30184b0d2ed1c1cd2a1ec0fb85d299a192a447
}
func SHA256FromWriter() {
h := sha256.New()
h.Write([]byte("hello world\n"))
fmt.Printf("%x\n", h.Sum(nil))
// Output: a948904f2f0f479b8f8197694b30184b0d2ed1c1cd2a1ec0fb85d299a192a447
}
func SHA256FromFile() {
f, err := os.Open("file.txt")
if err != nil {
fmt.Println("Open file failed.")
}
defer f.Close()
h := sha256.New()
if _, err := io.Copy(h, f); err != nil {
fmt.Println("Copy file failed.")
}
fmt.Printf("%x\n", h.Sum(nil))
}
func main() {
SHA256FromSum256()
SHA256FromWriter()
SHA256FromFile()
}
TLS(Transport Layer Security,傳輸層安全協(xié)議)及其前身SSL(Secure Sockets Layer,安全套接層)是一種安全協(xié)議,目的是為互聯(lián)網(wǎng)通信提供安全及數(shù)據(jù)完整性保障。
Go語言tls包實(shí)現(xiàn)了tls 1.2的功能,可以滿足日常的應(yīng)用。x509包提供證書管理的相關(guān)操作。
SSL包含記錄層(Record Layer)和傳輸層,記錄層協(xié)議確定了傳輸層數(shù)據(jù)的封裝格式。傳輸層安全協(xié)議使用X.509認(rèn)證,利用非對稱加密演算來對通信方做身份認(rèn)證,然后交換對稱密鑰作為會談密鑰(Session key)。會談密鑰用來將通信兩方交換的數(shù)據(jù)做加密,保證兩個(gè)應(yīng)用間通信的保密性和可靠性,使客戶與服務(wù)器應(yīng)用之間的通信不被***者竊聽。
生成服務(wù)端密鑰:openssl genrsa -out key.pem 2048
生成服務(wù)端證書:openssl req -new -x509 -key key.pem -out cert.pem -days 3650
生成客戶端密鑰:openssl genrsa -out client.key 2048
生成客戶端密鑰:openssl req -new -x509 -key client.key -out client.pem -days 3650
每個(gè)節(jié)點(diǎn)(不管是客戶端還是服務(wù)端)都有一個(gè)證書文件和key文件,用來互相加密解密;證書里包含public key,key文件里包含private key,證書與key文件構(gòu)成一對密鑰對,是互為加解密的。
根證書是所有節(jié)點(diǎn)公用的,不管是客戶端還是服務(wù)端,都要先注冊根證書(通常根證書注冊是注冊到操作系統(tǒng)信任的根證書數(shù)據(jù)庫里),以示根證書是可信的, 然后當(dāng)需要驗(yàn)證對方的證書時(shí),因?yàn)榇?yàn)證的證書是通過根證書簽名的,由于信任根證書,所以也可以信任對方的證書。
如果需要實(shí)現(xiàn)雙向認(rèn)證,那么每一端都需要三個(gè)文件:{node}.cer: PEM certificate
己方證書文件,將會被發(fā)給對方,讓對方認(rèn)證。{node}.key: PEM RSA private key
己方private key文件,用來解密經(jīng)己方證書(包含己方public key)加密的內(nèi)容,加密過程一般是由對方實(shí)施的。ca.cer: PEM certificate
根證書文件,用來驗(yàn)證對方發(fā)過來的證書文件,所有由同一個(gè)根證書簽名的證書都應(yīng)該能驗(yàn)證通過。
func Server(conn net.Conn, config *Config) *Conn
服務(wù)器返回一個(gè)新的TLS服務(wù)器端連接,使用conn作為底層傳輸。配置配置必須非零,并且必須包含至少一個(gè)證書或者設(shè)置GetCertificate。
func Client(conn net.Conn, config *Config) *Conn
func NewListener(inner net.Listener, config *Config) net.Listener
NewListener創(chuàng)建一個(gè) Listener,它接受來自內(nèi)部 Listener 的連接并將每個(gè)連接包裝在 Server 中。配置必須非零,并且必須包含至少一個(gè)證書或者設(shè)置 GetCertificate。func Listen(network, laddr string, config *Config) (net.Listener, error)
Listen 會使用 net.Listen 創(chuàng)建一個(gè) TLS 偵聽器來接受給定網(wǎng)絡(luò)地址上的連接。配置配置必須非零,并且必須包含至少一個(gè)證書或者設(shè)置 GetCertificate。func DialWithDialer(dialer *net.Dialer, network, addr string, config *Config) (*Conn, error)
DialWithDialer 使用 dialer.Dial 連接到給定的網(wǎng)絡(luò)地址,然后啟動(dòng)TLS handshake,返回生成的 TLS 連接。撥號程序中給出的任何超時(shí)或截止日期都適用于連接和 TLS handshake。
DialWithDialer 將零配置解釋為等同于零配置;請參閱 Config 的文檔以了解默認(rèn)值。func Dial(network, addr string, config *Config) (*Conn, error)
撥號使用 net.Dial 連接到給定的網(wǎng)絡(luò)地址,然后啟動(dòng) TLS handshake,返回生成的 TLS 連接。撥號將零配置解釋為等同于零配置;請參閱 Config 的文檔以了解默認(rèn)值。func LoadX509KeyPair(certFile, keyFile string) (Certificate, error)
LoadX509KeyPair讀取并解析來自一對文件的證書(公鑰)/私鑰對。證書和私鑰文件必須包含PEM編碼數(shù)據(jù)。證書文件可能包含leaf證書后的中間證書以形成證書鏈。成功返回時(shí),Certificate.Leaf 將為零,因?yàn)椴粫A艚馕龅淖C書形式。func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (Certificate, error)
X509KeyPair從一對PEM編碼數(shù)據(jù)中解析公鑰/私鑰對。成功返回時(shí),Certificate.Leaf將為零,因?yàn)椴粫A艚馕龅淖C書形式。
生成服務(wù)端密鑰:openssl genrsa -out key.pem 2048
生成服務(wù)端證書:openssl req -new -x509 -key key.pem -out cert.pem -days 3650
私鑰和證書存放在config/server目錄下。
服務(wù)端代碼server.go:
package main
import (
"bufio"
"crypto/tls"
"fmt"
"net"
)
func main() {
cert, err := tls.LoadX509KeyPair("../config/server/cert.pem", "../config/server/key.pem")
if err != nil {
fmt.Println(err)
return
}
config := &tls.Config{Certificates: []tls.Certificate{cert}}
ln, err := tls.Listen("tcp", ":1443", config)
if err != nil {
fmt.Println(err)
return
}
defer ln.Close()
for {
conn, err := ln.Accept()
if err != nil {
fmt.Println(err)
continue
}
go handleConn(conn)
}
}
func handleConn(conn net.Conn) {
defer conn.Close()
r := bufio.NewReader(conn)
for {
msg, err := r.ReadString('\n')
if err != nil {
fmt.Println(err)
return
}
println(msg)
n, err := conn.Write([]byte(msg))
if err != nil {
fmt.Println(n, err)
return
}
}
}
客戶端代碼client.go:
package main
import (
"crypto/tls"
"fmt"
)
func main() {
conf := &tls.Config{
InsecureSkipVerify: true,
}
conn, err := tls.Dial("tcp", "127.0.0.1:1443", conf)
if err != nil {
fmt.Println(err)
return
}
defer conn.Close()
n, err := conn.Write([]byte("hello world\n"))
if err != nil {
fmt.Println(n, err)
return
}
buf := make([]byte, 100)
n, err = conn.Read(buf)
if err != nil {
fmt.Println(n, err)
return
}
println(string(buf[:n]))
}
InsecureSkipVerify如果設(shè)置為true,則不會校驗(yàn)證書以及證書中的主機(jī)名和服務(wù)器主機(jī)名是否一致。
生成服務(wù)端密鑰:openssl genrsa -out key.pem 2048
生成服務(wù)端證書:openssl req -new -x509 -key key.pem -out cert.pem -days 3650
私鑰和證書存放在config/server目錄下。
生成客戶端密鑰:openssl genrsa -out client.key 2048
生成客戶端密鑰:openssl req -new -x509 -key client.key -out client.pem -days 3650
私鑰和證書存放在config/client目錄下。
服務(wù)端客戶端雙向認(rèn)證時(shí),服務(wù)端需要驗(yàn)證客戶端的真實(shí)性,需要服務(wù)端和客戶端進(jìn)行一點(diǎn)額外的配置。
服務(wù)端server.go代碼:
package main
import (
"bufio"
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"net"
)
func main() {
cert, err := tls.LoadX509KeyPair("../config/server/cert.pem", "../config/server/key.pem")
if err != nil {
fmt.Println(err)
return
}
certBytes, err := ioutil.ReadFile("../config/client/client.pem")
if err != nil {
panic("Unable to read cert.pem")
}
clientCertPool := x509.NewCertPool()
ok := clientCertPool.AppendCertsFromPEM(certBytes)
if !ok {
panic("failed to parse root certificate")
}
config := &tls.Config{
Certificates: []tls.Certificate{cert},
ClientAuth: tls.RequireAndVerifyClientCert,
ClientCAs: clientCertPool,
}
ln, err := tls.Listen("tcp", ":1443", config)
if err != nil {
fmt.Println(err)
return
}
defer ln.Close()
for {
conn, err := ln.Accept()
if err != nil {
fmt.Println(err)
continue
}
go handleConn(conn)
}
}
func handleConn(conn net.Conn) {
defer conn.Close()
r := bufio.NewReader(conn)
for {
msg, err := r.ReadString('\n')
if err != nil {
fmt.Println(err)
return
}
println(msg)
n, err := conn.Write([]byte(msg))
if err != nil {
fmt.Println(n, err)
return
}
}
}
客戶端client.go代碼:
package main
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
)
func main() {
cert, err := tls.LoadX509KeyPair("../config/client/client.pem", "../config/client/client.key")
if err != nil {
fmt.Println(err)
return
}
certBytes, err := ioutil.ReadFile("../config/client/client.pem")
if err != nil {
fmt.Println(err)
}
clientCertPool := x509.NewCertPool()
ok := clientCertPool.AppendCertsFromPEM(certBytes)
if !ok {
fmt.Println("failed to parse root certificate")
}
conf := &tls.Config{
RootCAs: clientCertPool,
Certificates: []tls.Certificate{cert},
InsecureSkipVerify: true,
}
conn, err := tls.Dial("tcp", "127.0.0.1:1443", conf)
if err != nil {
fmt.Println(err)
return
}
defer conn.Close()
n, err := conn.Write([]byte("hello world\n"))
if err != nil {
fmt.Println(n, err)
return
}
buf := make([]byte, 100)
n, err = conn.Read(buf)
if err != nil {
fmt.Println(n, err)
return
}
println(string(buf[:n]))
}
x509包提供了證書管理的相關(guān)操作。
PKCS(Public-Key Cryptography Standards),即公鑰密碼學(xué)標(biāo)準(zhǔn),是由 RSA 實(shí)驗(yàn)室與其它安全系統(tǒng)開發(fā)商為促進(jìn)公鑰密碼的發(fā)展而制訂的一系列標(biāo)準(zhǔn)。
X.509是常見通用的證書格式,所有的證書都符合為公鑰基礎(chǔ)設(shè)施PKI(Public Key Infrastructure ) 制定的ITU-T X509國際標(biāo)準(zhǔn)。
PEM格式通常用于數(shù)字證書認(rèn)證機(jī)構(gòu)(Certificate Authorities,CA),擴(kuò)展名為.pem,.crt,.cer,.key,內(nèi)容為Base64編碼的ASCII碼文件。
DER格式與PEM不同之處在于其使用二進(jìn)制而不是Base64編碼的ASCII。擴(kuò)展名為.der,但也經(jīng)常使用.cer用作擴(kuò)展名,所有類型的認(rèn)證證書和私鑰都可以存儲為DER格式。
PEM編碼轉(zhuǎn)換為DER編碼:openssl x509 -outform der -in certificate.pem -out certificate.der
DER編碼轉(zhuǎn)換為PEM編碼:openssl x509 -inform der -in certificate.cer -out certificate.pem
func ParsePKIXPublicKey(derBytes []byte) (pub interface{}, err error)
ParsePKIXPublicKey解析DER編碼的公鑰derBytes,成功返回*rsa.PublicKey,*dsa.PublicKey,*ecdsa.PublicKey
類型的公鑰。func MarshalPKIXPublicKey(pub interface{}) ([]byte, error)
MarshalPKIXPublicKey將公鑰pub序列化為DER編碼的PKIX格式func ParsePKCS1PrivateKey(der []byte) (*rsa.PrivateKey, error)
ParsePKCS1PrivateKey對ASN.1 PKCS#1 DER編碼的der進(jìn)行解析,返回*rsa.PrivateKey
私鑰func MarshalPKCS1PrivateKey(key *rsa.PrivateKey) []byte
MarshalPKCS1PrivateKey對key私鑰序列化為ASN.1 DER編碼格式func ParsePKCS1PublicKey(der []byte) (*rsa.PublicKey, error)
ParsePKCS1PublicKey對ASN.1 PKCS#1 DER編碼的der進(jìn)行解析,返回*rsa.PublicKey
公鑰func MarshalPKCS1PublicKey(key *rsa.PublicKey) []byte
MarshalPKCS1PublicKey對key公鑰序列化為ASN.1 DER編碼格式func ParsePKCS8PrivateKey(der []byte) (key interface{}, err error)
ParsePKCS8PrivateKey對PKCS#8格式DER編碼的私鑰進(jìn)行解析func MarshalPKCS8PrivateKey(key interface{}) ([]byte, error)
MarshalPKCS8PrivateKey將key私鑰序列化為PKCS#8編碼格式,key支持*rsa.PrivateKey,*ecdsa.PublicKey
。
pem實(shí)現(xiàn)了PEM數(shù)據(jù)編碼,PEM編碼源于 Privacy Enhanced Mail。目前最常見的PEM編碼用在TLS密鑰和證書中。
type Block struct {
Type string // The type, taken from the preamble (i.e. "RSA PRIVATE KEY").
Headers map[string]string // Optional headers.
Bytes []byte // The decoded bytes of the contents. Typically a DER encoded ASN.1 structure.
}
Block類型表示PEM編碼結(jié)構(gòu),編碼格式如下:
-----BEGIN Type-----
Headers
base64-encoded Bytes
-----END Type-----
func Decode(data []byte) (p *Block, rest []byte)
Decode將在data輸入中找到下一個(gè)PEM格式化的Block(證書,私鑰等)。返回Block和輸入的其余部分。如果沒有找到PEM數(shù)據(jù),則p為nil,并且整個(gè)輸入在rest返回。func Encode(out io.Writer, b *Block) error
Encode將b進(jìn)行編碼,并寫入outfunc EncodeToMemory(b *Block) []byte
將b進(jìn)行編碼并返回編碼結(jié)果
package main
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"io/ioutil"
"os"
)
//生成RSA私鑰和公鑰,保存到文件中
func GenerateRSAKeyPair(bits int, private string, public string) {
//GenerateKey函數(shù)使用隨機(jī)數(shù)據(jù)生成器random生成一對具有指定字位數(shù)的RSA密鑰
//Reader是一個(gè)全局、共享的密碼用強(qiáng)隨機(jī)數(shù)生成器
privateKey, err := rsa.GenerateKey(rand.Reader, bits)
if err != nil {
panic(err)
}
//保存私鑰
//通過x509標(biāo)準(zhǔn)將得到的RSA私鑰序列化為ASN.1 的 DER編碼字符串
X509PrivateKey := x509.MarshalPKCS1PrivateKey(privateKey)
//使用pem格式對x509輸出的內(nèi)容進(jìn)行編碼
//創(chuàng)建文件保存私鑰
privateFile, err := os.Create(private)
if err != nil {
panic(err)
}
defer privateFile.Close()
//構(gòu)建一個(gè)pem.Block結(jié)構(gòu)體對象
privateBlock := pem.Block{Type: "RSA Private Key", Bytes: X509PrivateKey}
//將數(shù)據(jù)保存到文件
pem.Encode(privateFile, &privateBlock)
//保存公鑰
//獲取公鑰的數(shù)據(jù)
publicKey := privateKey.PublicKey
//X509對公鑰編碼
X509PublicKey, err := x509.MarshalPKIXPublicKey(&publicKey)
if err != nil {
panic(err)
}
//pem格式編碼
//創(chuàng)建用于保存公鑰的文件
publicFile, err := os.Create(public)
if err != nil {
panic(err)
}
defer publicFile.Close()
//創(chuàng)建一個(gè)pem.Block結(jié)構(gòu)體對象
publicBlock := pem.Block{Type: "RSA Public Key", Bytes: X509PublicKey}
//保存到文件
pem.Encode(publicFile, &publicBlock)
}
func getPrivateKeyLength(private string) (int, error) {
privateKey, err := ioutil.ReadFile(private)
if err != nil {
fmt.Println(err)
}
block, _ := pem.Decode(privateKey)
if block == nil {
return 0, errors.New("Private RSA Key error")
}
priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
fmt.Println(err)
}
return priv.N.BitLen(), nil
}
func getPublicKeyLength(public string) (int, error) {
publicKey, err := ioutil.ReadFile(public)
if err != nil {
fmt.Println(err)
}
block, _ := pem.Decode(publicKey)
if block == nil {
return 0, errors.New("Public RSA Key error")
}
pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return 0, err
}
pub := pubInterface.(*rsa.PublicKey)
return pub.N.BitLen(), nil
}
func main() {
GenerateRSAKeyPair(2048, "private.pem", "public.pem")
n, _ := getPrivateKeyLength("private.pem")
fmt.Println(n)
n, _ = getPublicKeyLength("public.pem")
fmt.Println(n)
}
1977年,Ron Rivest、Adi Shamir、Leonard Adleman三人在美國公布了一種公鑰加密算法,即RSA公鑰加密算法。RSA是目前最有影響力和最常用的公鑰加密算法,可用于數(shù)據(jù)加密和數(shù)字簽名。
OpenSSL生成私鑰:openssl genrsa -out private.pem 1024
OpenSSL生成公鑰:openssl rsa -in private.pem -pubout -out public.pem
RSA私鑰中包含公鑰,私鑰的數(shù)據(jù)結(jié)構(gòu)如下:
type PrivateKey struct {
PublicKey // public part.
D *big.Int // private exponent
Primes []*big.Int // prime factors of N, has >= 2 elements.
// Precomputed contains precomputed values that speed up private
// operations, if available.
Precomputed PrecomputedValues
}
func EncryptOAEP(hash hash.Hash, random io.Reader, pub *PublicKey, msg []byte, label []byte) ([]byte, error)
使用RSA-OAEP算法對信息進(jìn)行加密func DecryptOAEP(hash hash.Hash, random io.Reader, priv *PrivateKey, ciphertext []byte, label []byte) ([]byte, error)
使用RSA-OAEP算法對信息進(jìn)行解密func GenerateKey(random io.Reader, bits int) (*PrivateKey, error)
生成私鑰func (priv *PrivateKey) Public() crypto.PublicKey
根據(jù)私鑰獲取公鑰func (priv *PrivateKey) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error)
生成私鑰的簽名func (priv *PrivateKey) Decrypt(rand io.Reader, ciphertext []byte, opts crypto.DecrypterOpts) (plaintext []byte, err error)
利用私鑰對加密信息進(jìn)行解密
RSA用于數(shù)據(jù)加密時(shí),消息發(fā)送方利用對方的公鑰進(jìn)行加密,消息接受方收到密文時(shí)使用自己的私鑰進(jìn)行解密。
package main
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
"os"
)
//生成RSA私鑰和公鑰,保存到文件中
func GenerateRSAKeyPair(bits int, private string, public string) {
//GenerateKey函數(shù)使用隨機(jī)數(shù)據(jù)生成器random生成一對具有指定字位數(shù)的RSA密鑰
//Reader是一個(gè)全局、共享的密碼用強(qiáng)隨機(jī)數(shù)生成器
privateKey, err := rsa.GenerateKey(rand.Reader, bits)
if err != nil {
panic(err)
}
//保存私鑰
//通過x509標(biāo)準(zhǔn)將得到的ras私鑰序列化為ASN.1 的 DER編碼字符串
X509PrivateKey := x509.MarshalPKCS1PrivateKey(privateKey)
//使用pem格式對x509輸出的內(nèi)容進(jìn)行編碼
//創(chuàng)建文件保存私鑰
privateFile, err := os.Create(private)
if err != nil {
panic(err)
}
defer privateFile.Close()
//構(gòu)建一個(gè)pem.Block結(jié)構(gòu)體對象
privateBlock := pem.Block{Type: "RSA Private Key", Bytes: X509PrivateKey}
//將數(shù)據(jù)保存到文件
pem.Encode(privateFile, &privateBlock)
//保存公鑰
//獲取公鑰的數(shù)據(jù)
publicKey := privateKey.PublicKey
//X509對公鑰編碼
X509PublicKey, err := x509.MarshalPKIXPublicKey(&publicKey)
if err != nil {
panic(err)
}
//pem格式編碼
//創(chuàng)建用于保存公鑰的文件
publicFile, err := os.Create(public)
if err != nil {
panic(err)
}
defer publicFile.Close()
//創(chuàng)建一個(gè)pem.Block結(jié)構(gòu)體對象
publicBlock := pem.Block{Type: "RSA Public Key", Bytes: X509PublicKey}
//保存到文件
pem.Encode(publicFile, &publicBlock)
}
//RSA加密
func RSAEncrypt(plainText []byte, path string) []byte {
//打開文件
file, err := os.Open(path)
if err != nil {
panic(err)
}
defer file.Close()
//讀取文件的內(nèi)容
info, _ := file.Stat()
buf := make([]byte, info.Size())
file.Read(buf)
//pem解碼
block, _ := pem.Decode(buf)
//x509解碼
publicKeyInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
panic(err)
}
//類型斷言
publicKey := publicKeyInterface.(*rsa.PublicKey)
//對明文進(jìn)行加密
cipherText, err := rsa.EncryptPKCS1v15(rand.Reader, publicKey, plainText)
if err != nil {
panic(err)
}
//返回密文
return cipherText
}
//RSA解密
func RSADecrypt(cipherText []byte, path string) []byte {
//打開文件
file, err := os.Open(path)
if err != nil {
panic(err)
}
defer file.Close()
//獲取文件內(nèi)容
info, _ := file.Stat()
buf := make([]byte, info.Size())
file.Read(buf)
//pem解碼
block, _ := pem.Decode(buf)
//X509解碼
privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
panic(err)
}
//對密文進(jìn)行解密
plainText, _ := rsa.DecryptPKCS1v15(rand.Reader, privateKey, cipherText)
//返回明文
return plainText
}
func main() {
//生成密鑰對,保存到文件
GenerateRSAKeyPair(2048, "private.pem", "public.pem")
message := []byte("hello world")
//加密
cipherText := RSAEncrypt(message, "public.pem")
fmt.Println("Encrypt:", cipherText)
//解密
plainText := RSADecrypt(cipherText, "private.pem")
fmt.Println("DeEncrypt:", string(plainText))
}
RSA在用于數(shù)字簽名時(shí),發(fā)送方通常先對消息生成散列值,再利用私鑰對散列值進(jìn)行簽名,接收方收到消息及簽名時(shí),也先對消息生成散列值(與發(fā)送方使用同種單向散列函數(shù)),利用發(fā)送方發(fā)的公鑰、簽名以及自己生成的散列值進(jìn)行簽名驗(yàn)證。
package main
import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/pem"
"fmt"
"os"
)
//生成RSA私鑰和公鑰,保存到文件中
func GenerateRSAKey(bits int, private string, public string) {
//GenerateKey函數(shù)使用隨機(jī)數(shù)據(jù)生成器random生成一對具有指定字位數(shù)的RSA密鑰
//Reader是一個(gè)全局、共享的密碼用強(qiáng)隨機(jī)數(shù)生成器
privateKey, err := rsa.GenerateKey(rand.Reader, bits)
if err != nil {
panic(err)
}
//保存私鑰
//通過x509標(biāo)準(zhǔn)將得到的ras私鑰序列化為ASN.1 的 DER編碼字符串
X509PrivateKey := x509.MarshalPKCS1PrivateKey(privateKey)
//使用pem格式對x509輸出的內(nèi)容進(jìn)行編碼
//創(chuàng)建文件保存私鑰
privateFile, err := os.Create(private)
if err != nil {
panic(err)
}
defer privateFile.Close()
//構(gòu)建一個(gè)pem.Block結(jié)構(gòu)體對象
privateBlock := pem.Block{Type: "RSA Private Key", Bytes: X509PrivateKey}
//將數(shù)據(jù)保存到文件
pem.Encode(privateFile, &privateBlock)
//保存公鑰
//獲取公鑰的數(shù)據(jù)
publicKey := privateKey.PublicKey
//X509對公鑰編碼
X509PublicKey, err := x509.MarshalPKIXPublicKey(&publicKey)
if err != nil {
panic(err)
}
//pem格式編碼
//創(chuàng)建用于保存公鑰的文件
publicFile, err := os.Create(public)
if err != nil {
panic(err)
}
defer publicFile.Close()
//創(chuàng)建一個(gè)pem.Block結(jié)構(gòu)體對象
publicBlock := pem.Block{Type: "RSA Public Key", Bytes: X509PublicKey}
//保存到文件
pem.Encode(publicFile, &publicBlock)
}
//讀取RSA私鑰
func GetRSAPrivateKey(path string) *rsa.PrivateKey {
//讀取文件內(nèi)容
file, err := os.Open(path)
if err != nil {
panic(err)
}
defer file.Close()
info, _ := file.Stat()
buf := make([]byte, info.Size())
file.Read(buf)
//pem解碼
block, _ := pem.Decode(buf)
//X509解碼
privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
return privateKey
}
//讀取RSA公鑰
func GetRSAPublicKey(path string) *rsa.PublicKey {
//讀取公鑰內(nèi)容
file, err := os.Open(path)
if err != nil {
panic(err)
}
defer file.Close()
info, _ := file.Stat()
buf := make([]byte, info.Size())
file.Read(buf)
//pem解碼
block, _ := pem.Decode(buf)
//x509解碼
publicKeyInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
panic(err)
}
publicKey := publicKeyInterface.(*rsa.PublicKey)
return publicKey
}
//對消息的散列值進(jìn)行數(shù)字簽名
func GetSign(msg []byte, path string) []byte {
//取得私鑰
privateKey := GetRSAPrivateKey(path)
//計(jì)算散列值
hash := sha256.New()
hash.Write(msg)
bytes := hash.Sum(nil)
//SignPKCS1v15使用RSA PKCS#1 v1.5規(guī)定的RSASSA-PKCS1-V1_5-SIGN簽名方案計(jì)算簽名
sign, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, bytes)
if err != nil {
panic(sign)
}
return sign
}
//驗(yàn)證數(shù)字簽名
func VerifySign(msg []byte, sign []byte, path string) bool {
//取得公鑰
publicKey := GetRSAPublicKey(path)
//計(jì)算消息散列值
hash := sha256.New()
hash.Write(msg)
bytes := hash.Sum(nil)
//驗(yàn)證數(shù)字簽名
err := rsa.VerifyPKCS1v15(publicKey, crypto.SHA256, bytes, sign)
return err == nil
}
//測試RSA數(shù)字簽名
func main() {
//生成密鑰文件
GenerateRSAKey(2048, "private.pem", "public.pem")
//模擬發(fā)送方
//要發(fā)送的消息
msg := []byte("hello world")
//生成簽名
sign := GetSign(msg, "private.pem")
//模擬接收方
//接受到的消息
acceptmsg := []byte("hello world")
//接受到的簽名
acceptsign := sign
//驗(yàn)證簽名
ok := VerifySign(acceptmsg, acceptsign, "public.pem")
if ok {
fmt.Println("Signature is accepted")
} else {
fmt.Println("Signature is not accepted")
}
}
AES(Advanced Encryption Standard),即高級加密標(biāo)準(zhǔn),是DES的替代標(biāo)準(zhǔn)。AES加密算法經(jīng)歷了公開選拔,最終在2000年由比利時(shí)密碼學(xué)家Joan Daemen和Vincent Rijmen設(shè)計(jì)的Rijndael算法被選中,成為AES標(biāo)準(zhǔn)。
AES算法基于排列和置換運(yùn)算。排列是對數(shù)據(jù)重新進(jìn)行安排,置換是將一個(gè)數(shù)據(jù)單元替換為另一個(gè)。AES 使用幾種不同的方法來執(zhí)行排列和置換運(yùn)算。 AES是一個(gè)迭代的、對稱密鑰分組的密碼,可以使用128、192和256位密鑰,并且用128位(16字節(jié))分組加密和解密數(shù)據(jù)。
AES是目前比較流行的對稱加密算法,是一種分組密碼(block cipher)算法,AES的分組長度為128比特(16字節(jié)),而密鑰長度可以是128比特、192比特或256比特。
func NewCipher(key []byte) (cipher.Block, error)
NewCiphe r創(chuàng)建并返回一個(gè)新的cipher.Block。參數(shù)是AES密鑰,可以是AES-128,AES-192或AES-256。func cipher.NewCBCEncrypter(b Block, iv []byte) BlockMode
NewCBCEncrypter返回一個(gè)BlockMode,使用給定的Block以密碼塊鏈接模式加密。iv的長度必須與塊的塊大小相同。func (x *cbcEncrypter) CryptBlocks(dst, src []byte)
對分組進(jìn)行加密,dst為輸出參數(shù),src為填充后的分組func cipher.NewCBCDecrypter(b Block, iv []byte) BlockMode
NewCBCDecrypter返回一個(gè)BlockMode,使用給定的Block以密碼塊鏈接模式解密。iv的長度必須與Block的塊大小相同,并且必須與用于加密數(shù)據(jù)的iv相匹配。func (x *cbcDecrypter) CryptBlocks(dst, src []byte)
堆分組進(jìn)行解密,dst為輸出參數(shù),src為密文,解密后dst需要?jiǎng)h除填充才能得到明文。func cipher.NewCTR(block Block, iv []byte) Stream
NewCTR返回一個(gè)Stream,使用計(jì)數(shù)器模式下的給定Block加密/解密。iv的長度必須與塊的塊大小相同。
type Stream interface {
XORKeyStream(dst, src []byte)
}
func (x *ctr) XORKeyStream(dst, src []byte)
加解密,src為輸入?yún)?shù),可以為要加密的明文或要解密的密文,dst為輸出參數(shù)。
CBC模式(密碼分組鏈接模式)是常用的一種分組密碼的模式。
CBC模式中的第一個(gè)分組需要用初始化向量IV(一個(gè)隨機(jī)的且長度為一個(gè)分組長度的比特序列)進(jìn)行異或操作再進(jìn)行加密,而后面的每一個(gè)分組都要先和前一個(gè)分組加密后的密文分組進(jìn)行異或操作,然后再加密。加密是連續(xù)的,不能進(jìn)行并行操作。
在CBC模式中,每個(gè)明文塊先與前一個(gè)密文塊進(jìn)行異或后,再進(jìn)行加密。因此,每個(gè)密文塊都依賴于它前面的所有明文塊。同時(shí),為了保證每條消息的唯一性,在第一個(gè)塊中需要使用初始化向量。
CBC是最為常用的工作模式,主要缺點(diǎn)在于加密過程是串行的,無法被并行化,而且消息必須被填充到塊大小的整數(shù)倍。解決后一個(gè)問題的一種方法是利用密文竊取。
在加密時(shí),明文中的微小改變會導(dǎo)致其后的全部密文塊發(fā)生改變,而在解密時(shí),從兩個(gè)鄰接的密文塊中即可得到一個(gè)明文塊。因此,解密過程可以被并行化,而解密時(shí),密文中一位的改變只會導(dǎo)致其對應(yīng)的明文塊完全改變和下一個(gè)明文塊中對應(yīng)位發(fā)生改變,不會影響到其它明文的內(nèi)容。
package main
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"fmt"
)
//對明文進(jìn)行填充
func Padding(plainText []byte, blockSize int) []byte {
//計(jì)算要填充的長度
n := blockSize - len(plainText)%blockSize
//對原來的明文填充n個(gè)n
temp := bytes.Repeat([]byte{byte(n)}, n)
plainText = append(plainText, temp...)
return plainText
}
//對密文刪除填充
func UnPadding(cipherText []byte) []byte {
//取出密文最后一個(gè)字節(jié)end
end := cipherText[len(cipherText)-1]
//刪除填充
cipherText = cipherText[:len(cipherText)-int(end)]
return cipherText
}
//AEC加密(CBC模式)
func AESCBCEncrypt(rawData []byte, key []byte, iv []byte) []byte {
//指定加密算法,返回一個(gè)AES算法的Block接口對象
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
//進(jìn)行填充
rawData = Padding(rawData, block.BlockSize())
//指定分組模式,返回一個(gè)BlockMode接口對象
blockMode := cipher.NewCBCEncrypter(block, iv)
//加密連續(xù)數(shù)據(jù)庫
cipherText := make([]byte, len(rawData))
blockMode.CryptBlocks(cipherText, rawData)
//返回密文
return cipherText
}
//AEC解密(CBC模式)
func AESCBCDecrypt(cipherText []byte, key []byte, iv []byte) []byte {
//指定解密算法,返回一個(gè)AES算法的Block接口對象
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
//指定分組模式,返回一個(gè)BlockMode接口對象
blockMode := cipher.NewCBCDecrypter(block, iv)
//解密
plainText := make([]byte, len(cipherText))
blockMode.CryptBlocks(plainText, cipherText)
//刪除填充
plainText = UnPadding(plainText)
return plainText
}
func main() {
message := []byte("Hello go!")
//指定密鑰AES-128,16byte
key := []byte("1111111111111111")
//指定初始向量IV,長度和block的塊尺寸一致
iv := []byte("12345678abcdefgh")
//加密
cipherText := AESCBCEncrypt(message, key, iv)
fmt.Println("Encrypt: ", cipherText)
//解密
plainText := AESCBCDecrypt(cipherText, key, iv)
fmt.Println("Decrypt:", string(plainText))
}
CTR分組模式每次加密時(shí)都會生成一個(gè)不同的值來作為計(jì)數(shù)器的初始值,每個(gè)分組對應(yīng)一個(gè)逐次累加的計(jì)數(shù)器,通過對計(jì)數(shù)器進(jìn)行加密來生成密鑰流,再將密鑰流與明文分組進(jìn)行異或操作得到密文分組。
package main
import (
"crypto/aes"
"crypto/cipher"
"fmt"
)
//AEC加密和解密(CRT模式)
func AECCTRCrypt(text []byte, key []byte, counter []byte) []byte {
//指定加密、解密算法為AES,返回一個(gè)AES的Block接口對象
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
//指定分組模式
blockMode := cipher.NewCTR(block, counter)
//執(zhí)行加密、解密操作
message := make([]byte, len(text))
blockMode.XORKeyStream(message, text)
//返回明文或密文
return message
}
func main() {
message := []byte("Hello go")
//指定密鑰AES-128,16byte
key := []byte("1234567812345678")
//指定計(jì)數(shù)器,長度必須等于block的塊尺寸
counter := []byte("12345678abcdefgh")
//加密
cipherText := AECCTRCrypt(message, key, counter)
fmt.Println("Encrypt: ", cipherText)
//解密
plainText := AECCTRCrypt(cipherText, key, counter)
fmt.Println("Decrypt: ", string(plainText))
}
hmac 包實(shí)現(xiàn)了美國聯(lián)邦信息處理標(biāo)準(zhǔn)出版物198中定義的HMAC(Keyed-Hash Message Authentication Code),即加密哈希認(rèn)證碼。HMAC 是使用密鑰簽署消息的加密散列。接收器通過使用相同的密鑰重新計(jì)算它來驗(yàn)證散列。
func New(h func() hash.Hash, key []byte) hash.Hash
New函數(shù)返回一個(gè)采用hash.Hash作為底層hash接口、key作為密鑰的HMAC算法的hash接口。func Equal(mac1, mac2 []byte) bool
比較兩個(gè)MAC是否相同,而不會泄露對比時(shí)間信息。(以規(guī)避時(shí)間側(cè)信道***;指通過計(jì)算比較時(shí)花費(fèi)的時(shí)間的長短來獲取密碼的信息,用于密碼破解)
func (h *hmac) Sum(in []byte) []byte
func (h *hmac) Write(p []byte) (n int, err error)
hash.Hash繼承了io.Writer,因此可以將其當(dāng)成一個(gè)輸入流進(jìn)行內(nèi)容的更新。
type Writer interface {
Write(p []byte) (n int, err error)
}
Write方法將p中的內(nèi)容讀入后存入到hash.Hash,最后在Sum方法通過內(nèi)部函數(shù)checkSum計(jì)算出其校驗(yàn)和。
Sum函數(shù)是對hash.Hash對象內(nèi)部存儲的內(nèi)容進(jìn)行校驗(yàn)和計(jì)算然后將其追加到data的后面形成一個(gè)新的byte切片。通常將data置為nil。
Sum方法返回一個(gè)Size大小的byte數(shù)組,對于MD5返回一個(gè)128bit的16字節(jié)byte數(shù)組。
package main
import (
"crypto/hmac"
"crypto/sha256"
"fmt"
"io"
)
func generateHMAC(rawData, key string) string {
h := hmac.New(sha256.New, []byte(key))
io.WriteString(h, rawData)
return fmt.Sprintf("%x", h.Sum(nil))
}
func generateSHA256(s string) string {
h := sha256.New()
h.Write([]byte(s))
return fmt.Sprintf("%x", h.Sum(nil))
}
func main() {
data := "Hello go"
key := "123456"
str := generateHMAC(data, key)
fmt.Println(str)
str = generateSHA256(data)
fmt.Println(str)
}
// output:
// 143543e2c55feb51051dccc99687c6ad215e0dca37127ecce67efc92b1d7f697
// 61c35d7993613995b32d1c5bd6d3bbdbbcf3305bb19a135b99a779a8c782d0f7
ECDSA(Elliptic Curve Digital Signature Algorithm)即橢圓曲線數(shù)字簽名算法。
ECC(Elliptic Curve Cryptography),即橢圓曲線加密算法,是基于橢圓曲線數(shù)學(xué)理論實(shí)現(xiàn)的一種非對稱加密算法。與RSA算法相比,ECC可以使用更短的密鑰,來實(shí)現(xiàn)與RSA相當(dāng)或更高的安全。據(jù)研究,160位ECC加密安全性相當(dāng)于1024位RSA加密,210位ECC加密安全性相當(dāng)于2048位RSA加密。
Go語言的ecdsa包目前智能用于數(shù)字簽名,使用私鑰加密,使用公鑰做校驗(yàn)。
type PublicKey struct {
elliptic.Curve
X, Y *big.Int
}
type PrivateKey struct {
PublicKey
D *big.Int
}
ecdsa包的私鑰數(shù)據(jù)結(jié)構(gòu)中包含公鑰。func GenerateKey(c elliptic.Curve, rand io.Reader) (*PrivateKey, error)
生成ECDSA密鑰對,可用的橢圓曲線為elliptic.P224()、elliptic.P256()、elliptic.P384()、elliptic.P521()func (priv *PrivateKey) Public() crypto.PublicKey
從私鑰獲取公鑰func (priv *PrivateKey) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error)
從私鑰生成簽名func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err error)
使用私鑰生成簽名。簽名使用私鑰priv來簽名散列。如果散列長度大于私鑰的曲線順序的位長度,則散列將被截?cái)酁樵撻L度。將簽名作為一對整數(shù)返回。私鑰的安全性取決于rand的熵。func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool
利用公鑰進(jìn)行簽名認(rèn)證
package main
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/sha1"
"crypto/x509"
"encoding/pem"
"fmt"
"math/big"
"os"
)
//生成密鑰對
func generateECCKeyPair(c elliptic.Curve, privateKeyFile string, publicKeyFile string) error {
//使用ECDSA生成密鑰對
privateKey, err := ecdsa.GenerateKey(c, rand.Reader)
if err != nil {
return err
}
//使用509
private, err := x509.MarshalECPrivateKey(privateKey)
if err != nil {
return err
}
//pem
block := pem.Block{
Type: "esdsa private key",
Bytes: private,
}
file, err := os.Create(privateKeyFile)
if err != nil {
return err
}
err = pem.Encode(file, &block)
if err != nil {
return err
}
file.Close()
//處理公鑰
public := privateKey.PublicKey
//x509序列化
publicKey, err := x509.MarshalPKIXPublicKey(&public)
if err != nil {
return err
}
//pem
public_block := pem.Block{
Type: "ecdsa public key",
Bytes: publicKey,
}
file, err = os.Create(publicKeyFile)
if err != nil {
return err
}
//pem編碼
err = pem.Encode(file, &public_block)
if err != nil {
return err
}
return nil
}
//ECC簽名--私鑰
func ECCSignature(sourceData []byte, privateKeyFile string) ([]byte, []byte) {
//讀取私鑰
file, err := os.Open(privateKeyFile)
if err != nil {
panic(err)
}
info, err := file.Stat()
buf := make([]byte, info.Size())
file.Read(buf)
//pem解密
block, _ := pem.Decode(buf)
//x509解密
privateKey, err := x509.ParseECPrivateKey(block.Bytes)
if err != nil {
panic(err)
}
//哈希運(yùn)算
hashText := sha1.Sum(sourceData)
//數(shù)字簽名
r, s, err := ecdsa.Sign(rand.Reader, privateKey, hashText[:])
if err != nil {
panic(err)
}
rText, err := r.MarshalText()
if err != nil {
panic(err)
}
sText, err := s.MarshalText()
if err != nil {
panic(err)
}
defer file.Close()
return rText, sText
}
//ECC認(rèn)證
func ECCVerify(rText, sText, sourceData []byte, publicKeyFilePath string) bool {
//讀取公鑰文件
file, err := os.Open(publicKeyFilePath)
if err != nil {
panic(err)
}
info, err := file.Stat()
if err != nil {
panic(err)
}
buf := make([]byte, info.Size())
file.Read(buf)
//pem解碼
block, _ := pem.Decode(buf)
//x509
publicStream, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
panic(err)
}
//接口轉(zhuǎn)換成公鑰
publicKey := publicStream.(*ecdsa.PublicKey)
hashText := sha1.Sum(sourceData)
var r, s big.Int
r.UnmarshalText(rText)
s.UnmarshalText(sText)
//認(rèn)證
res := ecdsa.Verify(publicKey, hashText[:], &r, &s)
defer file.Close()
return res
}
func main() {
generateECCKeyPair(elliptic.P256(), "./private.pem", "./public.pem")
rawData := []byte("Hello go")
r, s := ECCSignature(rawData, "./private.pem")
res := ECCVerify(r, s, rawData, "./public.pem")
if res {
fmt.Println("Signature is Accepted")
}
}
創(chuàng)新互聯(lián)www.cdcxhl.cn,專業(yè)提供香港、美國云服務(wù)器,動(dòng)態(tài)BGP最優(yōu)骨干路由自動(dòng)選擇,持續(xù)穩(wěn)定高效的網(wǎng)絡(luò)助力業(yè)務(wù)部署。公司持有工信部辦法的idc、isp許可證, 機(jī)房獨(dú)有T級流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確進(jìn)行流量調(diào)度,確保服務(wù)器高可用性。佳節(jié)活動(dòng)現(xiàn)已開啟,新人活動(dòng)云服務(wù)器買多久送多久。