GO语言密码学
推荐网站:https://studygolang.com/pkgdoc
对称加密和非对称加密
对称加密
- 秘钥数量:1个
- 特点:
- 加密效率高
- 双方使用的秘钥相同
- 安全性:相对于非对称加密安全性低
- 使用情况:主流的加密方式
非对称加密
- 秘钥数量:2个
- 公钥:任何人都可以使用,用于加密
- 私钥:只有自己持有,用于签名,可以证明私钥持有人发送的数据
- 特点:
- 公钥加密只有自己的私钥能解
- 加解密效率低,一般不做大量数据使用
- 安全性:安全性高
- 使用情况:
- 配合对称加密一起使用
- 建立连接之初,先使用非对称加密协商对称加密的算法和秘钥
- 然后使用对称加密进行后续的加解密
编码和解码
- 程序在计算机内部的存储形式
- 由字符转成二进制比特流的过程叫做编码
- 由比特流转成可读字符的过程叫做解码
- 加解密就是对比特流进行编码解码
常见的编码方式
- gob包->go内置的编解码包
- html编码
- json编码
- binary包->go内置的编解码包
对称加密算法
存在的问题
当通信对象很多时会面临众多秘钥的有效管理问题
- 对于一个新的数据通信对象,秘钥怎样进行传输的问题
解决方法:
引入非对称加密
1. DES(Data Encryption Standard)
特点:
- 不安全,不建议使用
- 秘钥:8字节,64bit,但由于每7个bit就会设置一个校验位,所以实际长度位56bit
- 加密时,会对明文进行分组,分组长度是8bytes,得到的密文也是8bytes为一组
2. 3DES(Triple Data Encryption Standard)
特点:
将DES重复三次得到的一种密码算法,但不是单纯的重复三次,加密过程是加密->解密->加密,解密时为了兼容以前的DES,以解密的形式进行加密。解密过程是解密->加密->解密
秘钥:8bytes*3=24bytes,如果秘钥1与秘钥2相同或者秘钥2与秘钥3相同,相当于DES,若秘钥1与秘钥3相同,是3DES-EDE2
若3个秘钥都不相同,是3DES-EDE3
加密时,会对明文进行分组,分组长度是8bytes,得到的密文也是8bytes为一组,与DES相同
加密效率低
3. AES(Advance Encryption Standard)
特点:
1. 秘钥长度可在128、192、256bit三种进行选择
2. 分组:16bytes
3. 加密效率高
五种分组模式
加密算法可以和任何分组模式进行连接,只有对明文进行加密,才需要填充
1. ECB,电子密码本模式:不使用,淘汰
特点:
- 明文消息被分成固定大小的分组,块大小由加密算法决定,并且每个块被使用相同的方法单独加解密
- 一旦有一个块被破解,那么所有的块都能使用相同的方法进行破解
- 安全性差,适用于数据较小的情形,加密前需要把明文数据填充到块大小的整倍数
- 加密效率高,加密不彻底,go语言不支持着这种模式
2.CBC,密文分组链接模式:常用,推荐使用
特点:
1. 先异或,再加密,模式中的每一个分组都要先和前一个分组加密后的数据进行异或操作,然后再加密,第一个数据块进行加密之前需要用初始化向量进行异或操作,初始化向量长度必须与分组长度相同
2. 连续加密,无法并行处理,加密前需要把明文数据填充到块大小的整倍数
3. 加密强度高
3.CFB,密文反馈模式:偶尔使用
特点:
- 先加密,再异或,前一个分组的密文加密后和当前分组的明文异或操作生成当前分组的密文,同样需要初始化向量,初始化向量长度必须与分组长度相同
- 连续加密,无法并行处理,没有直接对明文进行加密,所以不需要填充
- 解密的时候,是对初始向量进行加密操作,这样才能得到同样的数据
4.OFB,输出反馈模式:偶尔使用
特点:
- 通过明文分组和密码算法的输出来进行异或的,是对初始向量的结果进行不断的加密,作为下一次的数据来源
- 不需要进行数据填充
- 解密的时候,是对初始向量进行不断的加密与密文分组异或得到明文分组
- 不支持并行操作
5. CTR,计数器模式:建议使用
特点:
- 通过将逐次累加的计数器进行加密来生产密钥流的流密码
- 流密码与明文分组进行异或得到密文分组
- 解密的时候,是对计数器进行加密与密文分组异或得到明文分组
- 当分组为16字节时,计数器前8个字节为随机数,这个值在加密的时候都是不同的,后8个字节为分组序号,会逐次累加
- 加密解密并行操作,不需要字节填充,推荐使用
DES-CBC加密实现
推荐网站:https://studygolang.com/pkgdoc
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| func desCBCEncrypt(src,key []byte) []byte{ fmt.Printf("加密开始,输入的数据为:%s\n",src) block,err:=des.NewCipher(key)
if err!=nil{ log.Panic(err) } src = paddingInfo(src,block.BlockSize()) iv:=bytes.Repeat([]byte("1"),block.BlockSize()) blockMode:=cipher.NewCBCEncrypter(block,iv)
blockMode.CryptBlocks(src,src) fmt.Printf("加密结束,加密数据为:%x\n",src) return src }
func paddingInfo(src []byte,blockSize int) []byte { length:=len(src) paddingNum:=blockSize-length%blockSize s1:=byte(paddingNum) s2:=bytes.Repeat([]byte{s1},paddingNum) src=append(src,s2...) return src }
func main(){ src:=[]byte("123456789") key:=[]byte("12345678") desCBCEncrypt(src,key) }
|
DES-CBC解密实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| func desCBCDecrypt(cipherData,key []byte) []byte { fmt.Printf("解密开始,需要解密的数据为:%x\n",cipherData) block,err:=des.NewCipher(key)
if err!=nil{ log.Panic(err) }
iv:=bytes.Repeat([]byte("1"),block.BlockSize()) blockMode:=cipher.NewCBCDecrypter(block,iv) blockMode.CryptBlocks(cipherData,cipherData)
cipherData=unpaddingInfo(cipherData)
fmt.Printf("解密结束,解密的数据为:%s\n",cipherData) return cipherData
}
func unpaddingInfo(plainText []byte) []byte { length:=len(plainText) if length ==0{ return []byte{} } lastByte:=plainText[length-1] unpaddingNum:=int(lastByte) return plainText[:length-unpaddingNum] }
|
AES-CTR加解密
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| func aesCTREncrypt(src,key []byte) []byte { fmt.Printf("加密开始,需要加密的数据为:%s\n",src)
block,err:=aes.NewCipher(key)
if err!=nil{ log.Panic(err) }
iv:=bytes.Repeat([]byte("1"),block.BlockSize()) stream:=cipher.NewCTR(block,iv)
stream.XORKeyStream(src,src)
fmt.Printf("加密结束,加密数据为:%x\n",src) return src }
func aesCTRDecrypt(cipherData,key []byte) []byte { fmt.Printf("解密开始,需要解密的数据为:%x\n",cipherData) block,err:=aes.NewCipher(key)
if err!=nil{ log.Panic(err) }
iv:=bytes.Repeat([]byte("1"),block.BlockSize()) stream:=cipher.NewCTR(block,iv)
stream.XORKeyStream(cipherData,cipherData)
fmt.Printf("加密结束,解密数据为:%s\n",cipherData) return cipherData
}
func main() { src:=[]byte("夜行书生") key:=[]byte("1234567812345678") cipherData:=aesCTREncrypt(src,key) aesCTRDecrypt(cipherData,key) }
|
非对称加密算法
存在的问题
- 直接传递公钥,容易被截取
- 放到固定的位置,容易被替换
解决的办法:
引入第三方认证机构,CA,它是一系列具有社会公信力的机构的总称,它们负责为厂商提供数字证书
常见使用场景
- 加密通信:私钥加密,公钥解密
- https:验证服务器,数字证书,使用ca认证公钥
- 签名:防止篡改,哈希+非对称加密
- 网银U盾:验证client,U盾相当于私钥,公钥在服务器
- github ssh(secure shell)登录
- ssh是一种网络协议,主要用于计算机之间的加密登录与数据传递
- ssh登录的时候没有ca认证,需要用户自己确认登录主机的指纹,点击yes后把远程主机的指纹存放到本地的know_hosts中,后续登录会跳过警告
- ssh-keygen -t rsa 演示
非对称加密使用公钥加密,使用私钥解密
私钥:使用随机数按照一定的规则生成的,只有自己持有
公钥:由私钥推导而来,任何人可以持有,公钥加密的数据只能被配套私钥解开
RSA生成私钥、公钥
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
| const privateKeyFile = "./privateRSAKey.pem" const publicKeyFile = "./publicRSAKey.pem"
func generateKeyPair(bits int) error { privateKey, err := rsa.GenerateKey(rand.Reader, bits) if err != nil { return err }
priDerText:=x509.MarshalPKCS1PrivateKey(privateKey)
block:=pem.Block{ Type: "RSA PRIVATE KEY", Headers: nil, Bytes: priDerText, }
filehandler1,err:=os.Create(privateKeyFile) if err!=nil{ return nil } defer filehandler1.Close()
err=pem.Encode(filehandler1,&block) if err!=nil{ return nil } fmt.Printf("私钥写入成功!\n")
pubKey:=privateKey.PublicKey pubKeyDerText:=x509.MarshalPKCS1PublicKey(&pubKey) block=pem.Block{ Type: "RSA PUBLIC KEY", Headers: nil, Bytes: pubKeyDerText, } filehandler2,err:=os.Create(publicKeyFile) if err!=nil{ return nil } defer filehandler2.Close()
err=pem.Encode(filehandler2,&block) if err!=nil{ return nil } fmt.Printf("公钥写入成功!\n") return nil }
|
RSA加解密
可以使用openssl生成公钥私钥
1 2
| openssl genrsa -out rsa_private_key.pem 1024 #生成私钥,1024时秘钥长度,不指定长度默认2048位 openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem #生成公钥
|
加解密过程:
1 2
| 密文=明文^E mod N #加密过程 明文=密文^D mod N #解密过程
|
- 加密的数据都是明文对应的数字值
- 对数值一次进行E或D次方处理
- 对N取模
加密时:公钥(E,N),由E和N组成公钥,E是encrypt(根据特定规则限定一个区间,在区间内随意选择),N是两个素数的乘积
解密时:私钥(D,N),由D和N组成私钥,D时Decrypt,只有知道了时哪两个大素数,才能推出D
算法描述:转载自:http://bank.hexun.com/2009-06-24/118958531.html
- 选择一对不同的、足够大的素数p,q。
- 计算n=p*q。
- 计算f(n)=(p-1)(q-1),同时对p, q严加保密,不让任何人知道。
- 找一个与f(n)互质的数e,且1<e<f(n)。
- 计算d,使得d ≡e-1 mod f(n),两边的同余运算相同,或者相当于(d*****e)%f(n)=1
- 公钥KU=(e,n),私钥KR=(d,n)。
1.公钥加密
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| const publicKeyFile = "./publicRSAKey.pem"
func rsaPubEncrypt(fileName string,src []byte) (error ,[]byte) { info,err:=ioutil.ReadFile(fileName) if err!=nil{ return err,nil } block,_:=pem.Decode(info) derText:=block.Bytes pubKey,err:=x509.ParsePKCS1PublicKey(derText) if err!=nil{ return err,nil } cipherData,err:=rsa.EncryptPKCS1v15(rand.Reader,pubKey,src) if err!=nil{ return err,nil } return nil,cipherData }
|
2.私钥解密
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| func rsaPriKeyDecrypt(fileName string,cipherData []byte) (error,[]byte) { info,err:=ioutil.ReadFile(fileName) if err!=nil{ return err,nil } block,_:=pem.Decode(info)
derText:=block.Bytes
privateKey,err:=x509.ParsePKCS1PrivateKey(derText)
if err!=nil{ return err,nil }
plainText,err:=rsa.DecryptPKCS1v15(rand.Reader,privateKey,cipherData) if err!=nil{ return err,nil } return nil,plainText }
|
3.输出
1 2
| 加密成功,加密后的信息为 : 68590b1ce53a7a6958603ae3df5ee965f2d13fae0616a36b5c72def9e867c3a20d9b00a3ca33f8de82800448a642356b25791f9f6807281fc6b30f23b272502e5550799a12d683de8386d6dc69994b7d1fa70382d6e787199bc3600170c897c3e97c66b0ce0322e21baabeeaa242bad9cdf15163af27db8f42e2adafd951ed57 解密成功,解密后的信息为 : 夜行书生
|
ECC椭圆曲线
golang中不支持ECC加解密,支持ECC签名
-私钥:16(不是16G点)
-公钥:G点和16G点组成
ECC公私钥创建
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| const PrivateKeyFile = "./ECC/privateECCKey.pem" const PublicKeyFile = "./ECC/publicECCKey.pem"
func generateEccKeypair() { curve := elliptic.P256() privateKey, err := ecdsa.GenerateKey(curve, rand.Reader) if err != nil { log.Panic(err) } derText1, err := x509.MarshalECPrivateKey(privateKey) block1 := pem.Block{ Type: "ECC PRIVATE KEY", Headers: nil, Bytes: derText1, } filehandler1, err := os.Create(PrivateKeyFile) if err != nil { log.Panic(err) } defer filehandler1.Close() pem.Encode(filehandler1, &block1)
publicKey := privateKey.PublicKey derText2, err := x509.MarshalPKIXPublicKey(&publicKey) if err != nil { log.Panic(err) } block2 := pem.Block{ Type: "ECC PUBLIC KEY", Headers: nil, Bytes: derText2, } filehandler2, err := os.Create(PublicKeyFile) if err != nil { log.Panic(err) } defer filehandler2.Close() pem.Encode(filehandler2, &block2)
|
Hash
可以对输入的内容生产一个唯一的数值
- 输入内容不变,输出内容不变(散列值)
- 输入内容有一点点改变,输出也会千差万别
- 无论输入的内容大小是多少,生产的哈希长度相同
- 哈希运算时对输入内容做指纹摘要,无法推回原文
Base64
Base64编码,是我们程序开发中经常使用到的编码方式,因为Base64编码的字符串更适合不同的平台,不同语言的传输。它是一种基于用64个可打印字符来表示二进制数据的表示方法,它通常用作存储、传输一些二进制数据编码方法。就是将二进制数据文本化(就是转成ASCLL码)
作用:
- 由于某些系统中只能使用ASCLL字符,Base64就是用来将非ASCLL字符的数据转换成ASCLL字符的一种方法
- 对二进制文件进行文本化后的输出
- 前后台交互时,经常使用base64,可以避免特殊字符传输错误
单向散列函数MD5
也是一个hash算法,不同于sha256的256位,md5是128位的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
func md5Test1(info []byte) []byte { hasher:=md5.New() io.WriteString(hasher,string(info)) hash:=hasher.Sum(nil) return hash }
func md5Test2(info []byte) []byte { hash:=md5.Sum(info) return hash[:] }
|
SHA256
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| const fileName = "D:/GoCode/go语言密码学/SHA256.go"
func sha256Test1(info []byte) []byte { hash:=sha256.Sum256(info) return hash[:] }
func sha256Test2() []byte { file,err:=os.Open(fileName) if err!=nil{ log.Panic(err) } hasher:=sha256.New() _,err=io.Copy(hasher,file) if err!=nil{ log.Panic(err) } hash:=hasher.Sum(nil) return hash[:] }
|
消息认证码
如果接受方接收到了乱码,无法判断是否是原本的数据信息,需要使用消息认证码,它是一种确认完整性并进行认证的技术,是一种与密钥相关联的单向散列函数,取三个单词的首字母,简称为MAC
认证步骤
- 发送者事先与接收者共享密钥
- 发送至根据请求信息和密钥计算MAC值
- 发送者将请求信息和MAC值发送给接收者
- 接收者根据请求信息和密钥计算MAC值
- 两者的MAC值进行对比,如果一致则认证成功
使用场景
- SWIFT(环球银行金融电信协会),曾经使用了消息认证码
- https,握手协议使用了消息认证码
- IPSec,IP协议的增强版,使用了消息认证码
HMAC
是一种使用单向散列函数来构造消息认证码的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
|
func generateHMAC(src []byte,key []byte) []byte {
hasher:=hmac.New(sha256.New,key) hasher.Write(src) mac:=hasher.Sum(nil) return mac }
func verifyHMAC(src []byte,mac1 []byte,key []byte) bool {
hasher:=hmac.New(sha256.New,key)
hasher.Write(src) mac2:=hasher.Sum(nil)
return hmac.Equal(mac1,mac2) }
|
存在问题
- 无法有效的配送密钥
- 无法进行第三方证明
- 无法防止发送方否认
解决办法: 非对称加密的数字签名
数字签名
签名流程
发送方对原文做哈希运算得到哈希值1
发送方用私钥对哈希值1进行加密签名
发送方将原文和私钥签名发送给接收方
接收方对接收到的原文进行哈希运算得到哈希值2
接收方用公钥解密签名得到哈希值1
接收方对哈希值1和哈希值2进行比较
因此:数字签名解决了消息认证码的问题
- 不需要协商密钥
- 任何人都持有公钥,都可以帮忙验证
- 私钥只有发送方持有,无法否认
通过RSA进行数字签名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| func rsaSignData(fileName string,src []byte) (error, []byte) {
info,err:=ioutil.ReadFile(fileName) if err!=nil{ return err,nil } block,_:=pem.Decode(info) derText:=block.Bytes privateKey,err:=x509.ParsePKCS1PrivateKey(derText) if err!=nil{ return err,nil } hash:=sha256.Sum256(src) signature,err:=rsa.SignPKCS1v15(rand.Reader,privateKey,crypto.SHA256,hash[:]) if err!=nil{ return err,nil } return nil,signature
}
func rsaVerifySignature(fileName string,src []byte,signature []byte) error {
info,err:=ioutil.ReadFile(fileName) if err!=nil{ return err } block,_:=pem.Decode(info) derText:=block.Bytes pubKey,err:=x509.ParsePKCS1PublicKey(derText) if err!=nil{ return err }
hash:=sha256.Sum256(src) err=rsa.VerifyPKCS1v15(pubKey,crypto.SHA256,hash[:],signature)
return err }
|
通过ECC进行数字签名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| const PrivateKeyFile = "./ECC/privateECCKey.pem" const PublicKeyFile = "./ECC/publicECCKey.pem"
type Signature struct { r *big.Int s *big.Int }
func eccSignData(filename string,src []byte) (Signature,error) { info,err:=ioutil.ReadFile(filename) if err!=nil{ log.Panic(err) } block,_:=pem.Decode(info) derText:=block.Bytes privateKey,err:=x509.ParseECPrivateKey(derText)
if err!=nil{ log.Panic(err) }
hash:=sha256.Sum256(src)
r,s,err:=ecdsa.Sign(rand.Reader,privateKey,hash[:]) if err!=nil{ log.Panic(err) }
sig:=Signature{ r: r, s: s, }
return sig,nil
}
func eccVerifySign(filename string,src []byte,sig Signature) bool { info,err:=ioutil.ReadFile(filename) if err!=nil{ log.Panic(err) } block,_:=pem.Decode(info) derText:=block.Bytes publicKeyInterface,err:=x509.ParsePKIXPublicKey(derText) publicKey,ok:=publicKeyInterface.(*ecdsa.PublicKey) if !ok{ fmt.Println("断言失败,非ecdsa公钥") os.Exit(1) } hash:=sha256.Sum256(src) return ecdsa.Verify(publicKey,hash[:],sig.r,sig.s) }
|
数字证书
对公钥进行数字签名
推荐网站:https://blog.csdn.net/suchahaerkang/article/details/84849093
PKI(公钥基础设施)和CA(证书颁发机构)
PKI:用来实现基于公钥密码体制的密钥和证书的产生、管理、存储、分发和撤销等功能。
PKI的组成元素主要有三个:
- 用户:使用PKI的人
- 认证机构:办法证书的人
- 仓库:保存证书的数据库
CA:负责发放和管理数字证书,作为电子商务交易中受信任的第三方
证书使用
所有的网站都转成https,https就等于http加ssl,ssl是一个通讯协议,在通讯过程中,使用了数字证书
https通信
- 所有的通信不再传输公钥,而是传输数字证书
- 证书里面包含公钥,由CA机构认证
- 网站提供者会自己在本地生成公钥私钥,也可以不自己生成,全部由CA处理
- 服务器将公钥发送给CA机构
- CA机构也有自己的私钥公钥
- CA使用自己的私钥对服务器的公钥进行签名
- CA向服务器颁发一个数字证书
- 当用户访问服务器时,服务器会将CA证书发送给用户
- 在客户的浏览器中,已经随着操作系统,预装了知名CA机构的根证书,这里面包含了CA机构的公钥,浏览器会对服务器的证书进行验证
- 验证成功则服务器可靠,即可正常通信,否则显示Warning
- 证书有效时,浏览器会将自己支持的对称加密算法,生成一个随机秘钥,使用服务器的公钥进行加密,发送给服务器
- 服务器选择一个加密算法,使用对称秘钥加密信息,发送给客户端
- 双方达成一致,接下来的通信转换为对称加密
SSL/TLS
安全传输层协议,用于在两个通信应用程序之间提供保密性和数据完整性。
windows下查看数字证书
1 2
| win+R输入certmgr.msc导出数字证书 终端输入 openssl x509 -in 证书名称 -inform der -text
|
证书信任链
通过一个证书证明另一个证书是真实可信的,根证书是最安全的证书,不需要验证
生成自签名证书
方法一:分步生成
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #启动openssl openssl #生成一个RSA私钥,执行后输入密码并确认 genrsa -des3 -out server.key 2048 #des3是对私钥进行加密 #生成CSR(证书签名请求) req -new -key server.key -out server.csr #查看csr文件细节 openssl req -in server.csr -noout -text #删除私钥中的密码 rsa -in server.key -out server.key #生成自签名证书 x509 -req -days 365 -in server.csr -signkey server.key -out server.crt #查看crt文件细节 x509 -in server.crt -text -noout
|
方法二:一步生成
1 2 3 4
| #不需要生成csr,直接生成证书 req -x509 -newkey rsa:4096 -keyout server2.key -out cert.crt -days 365 -nodes #nodes不设置密码 #查看证书细节 x509 -in cert.crt -text -noout
|
常见证书格式
pem格式
使用openssl生成的都是pem格式
1
| openssl x509 -in cert.crt -text
|
der格式
1
| openssl x509 -in cert.der -inform der -text
|
单向认证
客户端单向认证服务器,服务器不认证客户端,服务器证书使用openssl自签名
创建证书:
1
| req -x509 -newkey rsa:4096 -keyout server.key -out server.crt -days 365 -nodes
|
代码流程:
推荐网站:http://www.youkud.com/m/view.php?id=1487
创建http服务器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| func main() { server:=http.Server{ Addr: ":433", Handler: nil, TLSConfig: nil, }
http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) { fmt.Printf("HandleFunc called\n") writer.Write([]byte("hello world")) })
err:=server.ListenAndServeTLS("server.crt","server.key") if err!=nil{ log.Fatal(err) } }
|
客户端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| func main() { caCertInfo,err:=ioutil.ReadFile("server.crt") if err!=nil{ log.Fatal(err) } certPool:=x509.NewCertPool() certPool.AppendCertsFromPEM(caCertInfo) cfg:=tls.Config{ RootCAs: certPool, InsecureSkipVerify: true, } client:=http.Client{Transport: &http.Transport{TLSClientConfig: &cfg}} response,err:=client.Get("https://localhost:433") if err!=nil { log.Fatal(err) } bodyInfo,err:=ioutil.ReadAll(response.Body) if err!=nil { log.Fatal(err) }
response.Body.Close() fmt.Printf("body : %s\n",bodyInfo) fmt.Printf("response : %s\n",response.Status) }
|
双向认证
客户端认证服务器,服务器认证客户端,服务器和客户端证书都使用openssl自签名证书
创建证书:
1 2
| req -x509 -newkey rsa:4096 -keyout server.key -out server.crt -days 365 -nodes req -x509 -newkey rsa:4096 -keyout client.key -out client.crt -days 365 -nodes
|
服务器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| func main() { caInfo,err:=ioutil.ReadFile("client.crt") if err!=nil{ log.Fatal(err) } certPool:=x509.NewCertPool() certPool.AppendCertsFromPEM(caInfo) cfg:=tls.Config{ ClientCAs: certPool, ClientAuth: tls.RequireAndVerifyClientCert, InsecureSkipVerify: true, } server:=http.Server{ Addr:":433", Handler: nil, TLSConfig: &cfg, }
http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) { fmt.Printf("ServerHttp called\n") writer.Write([]byte("夜行书生")) })
err = server.ListenAndServeTLS("server.crt","server.key") if err!=nil{ log.Fatal(err) } }
|
客户端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| func main() { caInfo,err:=ioutil.ReadFile("server.crt") if err!=nil{ log.Fatal(err) } certPool:=x509.NewCertPool() certPool.AppendCertsFromPEM(caInfo)
clientCert,err:=tls.LoadX509KeyPair("client.crt","client.key") if err!=nil{ log.Fatal(err) } cfg:=tls.Config{ RootCAs: certPool, Certificates: []tls.Certificate{clientCert}, InsecureSkipVerify: true, } client:=http.Client{Transport: &http.Transport{TLSClientConfig: &cfg}} response,err:=client.Get("https://localhost:433") if err!=nil{ log.Fatal(err) } bodyInfo,err:=ioutil.ReadAll(response.Body) if err!=nil { log.Fatal(err) } defer response.Body.Close() fmt.Printf("body : %s\n",bodyInfo) fmt.Printf("response : %s\n",response.Status) }
|