RSA加密——go语言版

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/BuquTianya/article/details/82958194

源起

在做rsa加密验签的过程中遇到了一些问题,在对整个rsa体系不够了解的情况下花了很多的时间去尝试,但总是各种不通、各种头疼。

在尝试和搜索方案的过程中,不断的发现原来平时对rsa的了解是如此的少,rsa的体系是如此的庞大和庞杂。

不知道大家对rsa的了解程度如何,作为一个几年经验的后端程序员来说,对加密的大致体系有所了解,知道简单异或、md5、对称、非对称、用过几种对称加密算法。非对称加密在做支付对接的时候也有所接触。对rsa加密的原理有所了解,知道如何配置ssh免密、如何配置git的ssh、如何配置nginx的单向、双向ssl证书。

然而,这些只是外围知识而已!!!

当然,由于不是密码学专业,也不是安全方向,这里不对非对称加密的体系和算法进行分析,我个人也没有去学习和分析。这里仅包含在处理问题和后续梳理过程中涉及的知识要点。

公钥和私钥格式关系

这个格式关系很重要。对于理解秘钥加载的步骤,对于拿到手上的各种样式的秘钥的理解能有一个心理准备,有一个总体的可能性判断。
在这里插入图片描述

公钥

一般来说,我们遇到的公钥是PEM格式,PEM格式是对DER格式公钥的一种封装,封装的内容包括以下几块:

块名称 块内容示例
-----BEGIN PUBLIC KEY-----
公钥 base64.encode(public_key_string)
-----END PUBLIC KEY-----

以下是一个PEM格式公钥的示例:

-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEA+xGZ/wcz9ugFpP07Nspo6U17l0YhFiFpxxU4pTk3Lifz9R3zsIsu
ERwta7+fWIfxOo208ett/jhskiVodSEt3QBGh4XBipyWopKwZ93HHaDVZAALi/2A
+xTBtWdEo7XGUujKDvC2/aZKukfjpOiUI8AhLAfjmlcD/UZ1QPh0mHsglRNCmpCw
mwSXA9VNmhz+PiB+Dml4WWnKW/VHo2ujTXxq7+efMU4H2fny3Se3KYOsFPFGZ1TN
QSYlFuShWrHPtiLmUdPoP6CV2mML1tk+l7DIIqXrQhLUKDACeM5roMx0kLhUWB8P
+0uj1CNlNN4JRZlC7xFfqiMbFRU9Z4N6YwIDAQAB
-----END RSA PUBLIC KEY-----

HEX是十六进制编码格式,用的比较少,以下是一个十六进制格式的公钥示例:

00 00 00 07 73 73 68 2d 72 73 61 00 00 00 01 25 00 00 01 00 7f 9c 09
8e 8d 39 9e cc d5 03 29 8b c4 78 84 5f d9 89 f0 33 df ee 50 6d 5d d0 
16 2c 73 cf ed 46 dc 7e 44 68 bb 37 69 54 6e 9e f6 f0 c5 c6 c1 d9 cb
f6 87 78 70 8b 73 93 2f f3 55 d2 d9 13 67 32 70 e6 b5 f3 10 4a f5 c3 
96 99 c2 92 d0 0f 05 60 1c 44 41 62 7f ab d6 15 52 06 5b 14 a7 d8 19 
a1 90 c6 c1 11 f8 0d 30 fd f5 fc 00 bb a4 ef c9 2d 3f 7d 4a eb d2 dc 
42 0c 48 b2 5e eb 37 3c 6c a0 e4 0a 27 f0 88 c4 e1 8c 33 17 33 61 38 
84 a0 bb d0 85 aa 45 40 cb 37 14 bf 7a 76 27 4a af f4 1b ad f0 75 59 
3e ac df cd fc 48 46 97 7e 06 6f 2d e7 f5 60 1d b1 99 f8 5b 4f d3 97 
14 4d c5 5e f8 76 50 f0 5f 37 e7 df 13 b8 a2 6b 24 1f ff 65 d1 fb c8 
f8 37 86 d6 df 40 e2 3e d3 90 2c 65 2b 1f 5c b9 5f fa e9 35 93 65 59 
6d be 8c 62 31 a9 9b 60 5a 0e e5 4f 2d e6 5f 2e 71 f3 7e 92 8f fe 8b

ssh生成的公钥,有自己的格式,由3部分组成“ssh-rsa”字符+公钥+备注。以下是一个ssh公钥的示例:

ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD3O26p3AlNy5JalOon2E2q62fygTBQZkVv4LJP1v7UxMH8pMNbsP1YG2keMotLtakHiqfHDd/s6eUDQYxU2nVdwruwPmP65mfhD0xTXsFFOdn+B80EVQdkm12h9Mcd9pVzMTplvB9v2j/8RX8UH45KsCgB5GcZJwdexJWys64Q+5eP386xZ7ow3XkKDS+XCEQTZaz+vN97aOQontwp+Dj0lB0VKhpnCWI5cadFRCNVitWaLLK6STRrYKV9CTUWOIuIV+2EC85UGIGmD3M+z6S3RZepoOZPDCRgaXdiNNsAAWa2ULZ9Wep9Qtly0NEsQ9EHrmeHadnDaWQy9w/KLsYj [email protected]

私钥

私钥常遇到的是pkcs1和pkcs8两种标准,有时候还会遇到加密后的私钥,需要用密码加载或者去掉秘钥后再使用。后续会说到格式转换和去掉私钥密码的方法。

go语言生成公私钥

由于不同语言对rsa公私钥支持程度不同,所以很可能遇到在java项目用的很好的秘钥,在go语言没有办法用。但是又不确定是秘钥的问题,还是代码写法的问题。这时候如果能够生成一套所使用语言支持的秘钥对,对解决问题和建立信心都有很大帮助。

func GenRsaKey(bits int) error {
	// 生成私钥文件
	privateKey, err := rsa.GenerateKey(rand.Reader, bits)
	if err != nil {
		return err
	}
	derStream := x509.MarshalPKCS1PrivateKey(privateKey)
	block := &pem.Block{
		Type:  "RSA PRIVATE KEY",
		Bytes: derStream,
	}
	file, err := os.Create("private.pem")
	if err != nil {
		return err
	}
	err = pem.Encode(file, block)
	if err != nil {
		return err
	}
	// 生成公钥文件
	publicKey := &privateKey.PublicKey
	derPkix, err := x509.MarshalPKIXPublicKey(publicKey)
	if err != nil {
		return err
	}
	block = &pem.Block{
		Type:  "PUBLIC KEY",
		Bytes: derPkix,
	}
	file, err = os.Create("public.pem")
	if err != nil {
		return err
	}
	err = pem.Encode(file, block)
	if err != nil {
		return err
	}
	return nil
}

go加载私钥

pkcs1

package main

import ("fmt"
   "crypto/x509"
   "encoding/pem"
		"crypto/rsa"
	   )

func main () {
	privateKey := `your key`
	blockPri, _ := pem.Decode([]byte(privateKey))
   if blockPri == nil {
	   fmt.Println("err1")
      return
   }       
	priKey, err := x509.ParsePKCS1PrivateKey([]byte(blockPri.Bytes))
	if err != nil {
			 fmt.Println(err)
			 fmt.Println("err2")
            return 
         }
	
	fmt.Println("ok")
	fmt.Println(priKey)
	
}

pkcs8

package main

import ("fmt"
   "crypto/x509"
   "encoding/pem"
		"crypto/rsa"
	   )

func main () {
	privateKey := `your key`
	blockPri, _ := pem.Decode([]byte(privateKey))
   if blockPri == nil {
	   fmt.Println("err1")
      return
   }
	
	prkI, err := x509.ParsePKCS8PrivateKey([]byte(blockPri.Bytes))
         if err != nil {
			 fmt.Println(err)
			 fmt.Println("err2")
            return 
         }
	priKey := prkI.(*rsa.PrivateKey)

	
	fmt.Println("ok")
	fmt.Println(priKey)
	
}

go加载公钥

	blockPub, _ := pem.Decode([]byte(publicKey))
   if blockPub == nil {
      fmt.Println("err1")
      return
   }
   pubKey, err := x509.ParsePKIXPublicKey(publicKey)
   if err != nil {
      fmt.Println(err)
			 fmt.Println("err2")
            return 
   }

	fmt.Println("ok")
	fmt.Println(pubKey)

go进行rsa加密和验签

以下是hash摘要方式的rsa加密验签,也可以使用md5摘要方式的rsa加密:

//your_private_key和your_public_key都是上述《go加载rsa公、私钥》部分创建的结构体类型
func Sign(src []byte, hash crypto.Hash) ([]byte, error) {
   h := hash.New()
   h.Write(src)
   hashed := h.Sum(nil)
   return rsa.SignPKCS1v15(rand.Reader, your_private_key, hash, hashed)
}

func Verify(src []byte, sign []byte, hash crypto.Hash) error {
   h := hash.New()
   h.Write(src)
   hashed := h.Sum(nil)
   return rsa.VerifyPKCS1v15(your_public_key, hash, hashoed, sign)
}

这里说一下为什么要先摘要,再加密和验签,这里主要考虑的是性能,因为用rsa加密方式对性能的损耗比较大,如果待加密字串长的话,加密过程对性能的影响更大。所以,一般会先对字符串进行hash或者md5摘要。

openssl生成的公私钥

//生成私钥,默认生成的是pkcs1格式私钥
genrsa -out rsa_private_key.pem 1024
//转成pkcs8格式
pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM –nocrypt
//生成公钥
rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem

去掉私钥的密码

//执行会后,会要求你输入私钥使用的密码
openssl rsa -in my_private_key.pem -out new_private_key.pem

openssl公钥和openssh公钥互转

两者的私钥是同样的格式,但是两者的公钥是不同的格式,所以转换的基础就是私钥。通过私钥生成另一种格式的公钥。

比如,如果已经有了openssl生成的私钥,可以用如下命令生成openssh的公钥:

ssh-keygen  -y -f openssh_private.pem > public_key.pub

参考

本文参考了下列文章,有的代码片段直接取自文章:

  1. Golang加密系列之RSA
  2. GO加密解密RSA番外篇:生成RSA密钥
  3. GO加密解密之RSA
  4. PKCS标准
  5. openssl数字证书常见格式与协议介绍
  6. Generating an SSH Key Pair
  7. Generate Private key with OpenSSL and Public key ssh-keygen for SSH
  8. Converting keys between openssl and openssh
  9. How are the files in ~/.ssh related to the theory?
  10. OpenSSH RSA Public Key format
  11. openssl 生成公私钥对
  12. 去掉ssh key的密码

猜你喜欢

转载自blog.csdn.net/BuquTianya/article/details/82958194