如何从文件中读取 RSA 密钥
How to read an RSA key from file
我需要从文件中读入 RSA 私钥来签署 JWT。我找到了一些关于如何将生成的 RSA 密钥保存到磁盘的示例,但没有显示如何基于文件中的预生成密钥构建密钥结构。
密钥是这样生成的:
openssl genrsa 2048 | openssl pkcs8 -topk8 -nocrypt
示例键:
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQClHYNDPVSFFmWF
oKGTqd/n7Dt2+tGXh97KJjVLAqCBZZHlQJ534v2OzFjTgzuMNehD9Y6HnkYFdkRb
QzYi2YDROOzRl1bhyyWPA35OGf50r7LiNvSvNPNtswsCuK7ywOcH0yEMKSiW4q5R
GKYi42w961EcTQQPrfihavY+c2FYPv4+pXymzaIz9hGBPLHwaHq/QTAyHxPCfkOo
s/x3mxUVd7Ni2bz1VJGlyqcNEeU88wTAYMmv8oQ3y2NfKExtYn+W6TCDiq/+ZkOp
wacuAU0J7tCNgcXvkq39KH5uza2uSiTniye6uhlkvYWD3s9riIIiekTEiHk/kkc6
jMg8HN/7AgMBAAECggEBAJ12u8vQHV6esUrymaTdCG+BVmRtZpyA ...
-----END RSA PRIVATE KEY-----
pem.Decode
and x509.ParsePKCS1PrivateKey
的组合应该可以解决问题:
package main
import (
"crypto/x509"
"encoding/pem"
"fmt"
)
func main() {
pemString := `-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDLets8+7M+iAQAqN/5BVyCIjhTQ4cmXulL+gm3v0oGMWzLupUS
v8KPA+Tp7dgC/DZPfMLaNH1obBBhJ9DhS6RdS3AS3kzeFrdu8zFHLWF53DUBhS92
5dCAEuJpDnNizdEhxTfoHrhuCmz8l2nt1pe5eUK2XWgd08Uc93h5ij098wIDAQAB
AoGAHLaZeWGLSaen6O/rqxg2laZ+jEFbMO7zvOTruiIkL/uJfrY1kw+8RLIn+1q0
wLcWcuEIHgKKL9IP/aXAtAoYh1FBvRPLkovF1NZB0Je/+CSGka6wvc3TGdvppZJe
rKNcUvuOYLxkmLy4g9zuY5qrxFyhtIn2qZzXEtLaVOHzPQECQQDvN0mSajpU7dTB
w4jwx7IRXGSSx65c+AsHSc1Rj++9qtPC6WsFgAfFN2CEmqhMbEUVGPv/aPjdyWk9
pyLE9xR/AkEA2cGwyIunijE5v2rlZAD7C4vRgdcMyCf3uuPcgzFtsR6ZhyQSgLZ8
YRPuvwm4cdPJMmO3YwBfxT6XGuSc2k8MjQJBAI0+b8prvpV2+DCQa8L/pjxp+VhR
Xrq2GozrHrgR7NRokTB88hwFRJFF6U9iogy9wOx8HA7qxEbwLZuhm/4AhbECQC2a
d8h4Ht09E+f3nhTEc87mODkl7WJZpHL6V2sORfeq/eIkds+H6CJ4hy5w/bSw8tjf
sz9Di8sGIaUbLZI2rd0CQQCzlVwEtRtoNCyMJTTrkgUuNufLP19RZ5FpyXxBO5/u
QastnN77KfUwdj3SJt44U/uh1jAIv4oSLBr8HYUkbnI8
-----END RSA PRIVATE KEY-----`
block, _ := pem.Decode([]byte(pemString))
key, _ := x509.ParsePKCS1PrivateKey(block.Bytes)
fmt.Println(key.N)
}
如果您使用的是 PKCS#8 编码的密钥,您可以执行如下操作:
func main() {
pemString := `-----BEGIN PRIVATE KEY-----
MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAKhPSTDs4cpKfnMc
p86fCkpnuER7bGc+mGkhkw6bE+BnROfrDCFBSjrENLS5JcsenANQ1kYGt9iVW2fd
ZAWUdDoj+t7g6+fDpzY1BzPSUls421Dmu7joDPY8jSdMzFCeg7Lyj0I36bJJ7ooD
VPW6Q0XQcb8FfBiFPAKuY4elj/YDAgMBAAECgYBo2GMWmCmbM0aL/KjH/KiTawMN
nfkMY6DbtK9/5LjADHSPKAt5V8ueygSvI7rYSiwToLKqEptJztiO3gnls/GmFzj1
V/QEvFs6Ux3b0hD2SGpGy1m6NWWoAFlMISRkNiAxo+AMdCi4I1hpk4+bHr9VO2Bv
V0zKFxmgn1R8qAR+4QJBANqKxJ/qJ5+lyPuDYf5s+gkZWjCLTC7hPxIJQByDLICw
iEnqcn0n9Gslk5ngJIGQcKBXIp5i0jWSdKN/hLxwgHECQQDFKGmo8niLzEJ5sa1r
spww8Hc2aJM0pBwceshT8ZgVPnpgmITU1ENsKpJ+y1RTjZD6N0aj9gS9UB/UXdTr
HBezAkEAqkDRTYOtusH9AXQpM3zSjaQijw72Gs9/wx1RxOSsFtVwV6U97CLkV1S+
2HG1/vn3w/IeFiYGfZXLKFR/pA5BAQJAbFeu6IaGM9yFUzaOZDZ8mnAqMp349t6Q
DB5045xJxLLWsSpfJE2Y12H1qvO1XUzYNIgXq5ZQOHBFbYA6txBy/QJBAKDRQN47
6YClq9652X+1lYIY/h8MxKiXpVZVncXRgY6pbj4pmWEAM88jra9Wq6R77ocyECzi
XCqi18A/sl6ymWc=
-----END PRIVATE KEY-----`
block, _ := pem.Decode([]byte(pemString))
parseResult, _ := x509.ParsePKCS8PrivateKey(block.Bytes)
key := parseResult.(*rsa.PrivateKey)
fmt.Println(key.N)
}
这是私有和 public pem 文件的完整示例:
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"io/ioutil"
"log"
)
func RSAConfigSetup(rsaPrivateKeyLocation, privatePassphrase, rsaPublicKeyLocation string) (*rsa.PrivateKey, error) {
if rsaPrivateKeyLocation == "" {
log.Println("No RSA Key given, generating temp one")
return GenRSA(4096)
}
priv, err := ioutil.ReadFile(rsaPrivateKeyLocation)
if err != nil {
log.Println("No RSA private key found, generating temp one")
return GenRSA(4096)
}
privPem, _ := pem.Decode(priv)
if privPem.Type != "RSA PRIVATE KEY" {
log.Println("RSA private key is of the wrong type", privPem.Type)
}
if x509.IsEncryptedPEMBlock(privPem) && privatePassphrase == "" {
log.Println("Passphrase is required to open private pem file")
return GenRSA(4096)
}
var privPemBytes []byte
if privatePassphrase != "" {
privPemBytes, err = x509.DecryptPEMBlock(privPem, []byte(privatePassphrase))
} else {
privPemBytes = privPem.Bytes
}
var parsedKey interface{}
//PKCS1
if parsedKey, err = x509.ParsePKCS1PrivateKey(privPemBytes); err != nil {
//If what you are sitting on is a PKCS#8 encoded key
if parsedKey, err = x509.ParsePKCS8PrivateKey(privPemBytes); err != nil { // note this returns type `interface{}`
log.Println("Unable to parse RSA private key, generating a temp one", err)
return GenRSA(4096)
}
}
var privateKey *rsa.PrivateKey
var ok bool
privateKey, ok = parsedKey.(*rsa.PrivateKey)
if !ok {
log.Println("Unable to parse RSA private key, generating a temp one", err)
return GenRSA(4096)
}
pub, err := ioutil.ReadFile(rsaPublicKeyLocation)
if err != nil {
log.Println("No RSA public key found, generating temp one")
return GenRSA(4096)
}
pubPem, _ := pem.Decode(pub)
if pubPem == nil {
log.Println("Use `ssh-keygen -f id_rsa.pub -e -m pem > id_rsa.pem` to generate the pem encoding of your RSA public key - rsa public key not in pem format")
return GenRSA(4096)
}
if pubPem.Type != "RSA PUBLIC KEY" {
log.Println("RSA public key is of the wrong type", pubPem.Type)
return GenRSA(4096)
}
if parsedKey, err = x509.ParsePKIXPublicKey(pubPem.Bytes); err != nil {
log.Println("Unable to parse RSA public key, generating a temp one", err)
return GenRSA(4096)
}
var pubKey *rsa.PublicKey
if pubKey, ok = parsedKey.(*rsa.PublicKey); !ok {
log.Println("Unable to parse RSA public key, generating a temp one", err)
return GenRSA(4096)
}
privateKey.PublicKey = *pubKey
return privateKey, nil
}
// GenRSA returns a new RSA key of bits length
func GenRSA(bits int) (*rsa.PrivateKey, error) {
key, err := rsa.GenerateKey(rand.Reader, bits)
return key, err
}
有一个有用的函数 ssh.ParseRawPrivateKey
,它已经实现了一些必需的逻辑。我建议以此为基础,尤其是当您的密钥用作 SSH 密钥时,在我的例子中就是这样。
import (
"os"
"crypto/rsa"
"golang.org/x/crypto/ssh"
"io/ioutil"
)
func mustNot(err error) {
if err != nil {
panic(err)
}
}
func loadRsaPrivateKey() *rsa.PrivateKey {
bytes, err := ioutil.ReadFile(os.Getenv("HOME") + "/.ssh/id_rsa")
mustNot(err)
key, err := ssh.ParseRawPrivateKey(bytes)
mustNot(err)
return key.(*rsa.PrivateKey)
}
我需要从文件中读入 RSA 私钥来签署 JWT。我找到了一些关于如何将生成的 RSA 密钥保存到磁盘的示例,但没有显示如何基于文件中的预生成密钥构建密钥结构。
密钥是这样生成的:
openssl genrsa 2048 | openssl pkcs8 -topk8 -nocrypt
示例键:
-----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQClHYNDPVSFFmWF oKGTqd/n7Dt2+tGXh97KJjVLAqCBZZHlQJ534v2OzFjTgzuMNehD9Y6HnkYFdkRb QzYi2YDROOzRl1bhyyWPA35OGf50r7LiNvSvNPNtswsCuK7ywOcH0yEMKSiW4q5R GKYi42w961EcTQQPrfihavY+c2FYPv4+pXymzaIz9hGBPLHwaHq/QTAyHxPCfkOo s/x3mxUVd7Ni2bz1VJGlyqcNEeU88wTAYMmv8oQ3y2NfKExtYn+W6TCDiq/+ZkOp wacuAU0J7tCNgcXvkq39KH5uza2uSiTniye6uhlkvYWD3s9riIIiekTEiHk/kkc6 jMg8HN/7AgMBAAECggEBAJ12u8vQHV6esUrymaTdCG+BVmRtZpyA ... -----END RSA PRIVATE KEY-----
pem.Decode
and x509.ParsePKCS1PrivateKey
的组合应该可以解决问题:
package main
import (
"crypto/x509"
"encoding/pem"
"fmt"
)
func main() {
pemString := `-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDLets8+7M+iAQAqN/5BVyCIjhTQ4cmXulL+gm3v0oGMWzLupUS
v8KPA+Tp7dgC/DZPfMLaNH1obBBhJ9DhS6RdS3AS3kzeFrdu8zFHLWF53DUBhS92
5dCAEuJpDnNizdEhxTfoHrhuCmz8l2nt1pe5eUK2XWgd08Uc93h5ij098wIDAQAB
AoGAHLaZeWGLSaen6O/rqxg2laZ+jEFbMO7zvOTruiIkL/uJfrY1kw+8RLIn+1q0
wLcWcuEIHgKKL9IP/aXAtAoYh1FBvRPLkovF1NZB0Je/+CSGka6wvc3TGdvppZJe
rKNcUvuOYLxkmLy4g9zuY5qrxFyhtIn2qZzXEtLaVOHzPQECQQDvN0mSajpU7dTB
w4jwx7IRXGSSx65c+AsHSc1Rj++9qtPC6WsFgAfFN2CEmqhMbEUVGPv/aPjdyWk9
pyLE9xR/AkEA2cGwyIunijE5v2rlZAD7C4vRgdcMyCf3uuPcgzFtsR6ZhyQSgLZ8
YRPuvwm4cdPJMmO3YwBfxT6XGuSc2k8MjQJBAI0+b8prvpV2+DCQa8L/pjxp+VhR
Xrq2GozrHrgR7NRokTB88hwFRJFF6U9iogy9wOx8HA7qxEbwLZuhm/4AhbECQC2a
d8h4Ht09E+f3nhTEc87mODkl7WJZpHL6V2sORfeq/eIkds+H6CJ4hy5w/bSw8tjf
sz9Di8sGIaUbLZI2rd0CQQCzlVwEtRtoNCyMJTTrkgUuNufLP19RZ5FpyXxBO5/u
QastnN77KfUwdj3SJt44U/uh1jAIv4oSLBr8HYUkbnI8
-----END RSA PRIVATE KEY-----`
block, _ := pem.Decode([]byte(pemString))
key, _ := x509.ParsePKCS1PrivateKey(block.Bytes)
fmt.Println(key.N)
}
如果您使用的是 PKCS#8 编码的密钥,您可以执行如下操作:
func main() {
pemString := `-----BEGIN PRIVATE KEY-----
MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAKhPSTDs4cpKfnMc
p86fCkpnuER7bGc+mGkhkw6bE+BnROfrDCFBSjrENLS5JcsenANQ1kYGt9iVW2fd
ZAWUdDoj+t7g6+fDpzY1BzPSUls421Dmu7joDPY8jSdMzFCeg7Lyj0I36bJJ7ooD
VPW6Q0XQcb8FfBiFPAKuY4elj/YDAgMBAAECgYBo2GMWmCmbM0aL/KjH/KiTawMN
nfkMY6DbtK9/5LjADHSPKAt5V8ueygSvI7rYSiwToLKqEptJztiO3gnls/GmFzj1
V/QEvFs6Ux3b0hD2SGpGy1m6NWWoAFlMISRkNiAxo+AMdCi4I1hpk4+bHr9VO2Bv
V0zKFxmgn1R8qAR+4QJBANqKxJ/qJ5+lyPuDYf5s+gkZWjCLTC7hPxIJQByDLICw
iEnqcn0n9Gslk5ngJIGQcKBXIp5i0jWSdKN/hLxwgHECQQDFKGmo8niLzEJ5sa1r
spww8Hc2aJM0pBwceshT8ZgVPnpgmITU1ENsKpJ+y1RTjZD6N0aj9gS9UB/UXdTr
HBezAkEAqkDRTYOtusH9AXQpM3zSjaQijw72Gs9/wx1RxOSsFtVwV6U97CLkV1S+
2HG1/vn3w/IeFiYGfZXLKFR/pA5BAQJAbFeu6IaGM9yFUzaOZDZ8mnAqMp349t6Q
DB5045xJxLLWsSpfJE2Y12H1qvO1XUzYNIgXq5ZQOHBFbYA6txBy/QJBAKDRQN47
6YClq9652X+1lYIY/h8MxKiXpVZVncXRgY6pbj4pmWEAM88jra9Wq6R77ocyECzi
XCqi18A/sl6ymWc=
-----END PRIVATE KEY-----`
block, _ := pem.Decode([]byte(pemString))
parseResult, _ := x509.ParsePKCS8PrivateKey(block.Bytes)
key := parseResult.(*rsa.PrivateKey)
fmt.Println(key.N)
}
这是私有和 public pem 文件的完整示例:
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"io/ioutil"
"log"
)
func RSAConfigSetup(rsaPrivateKeyLocation, privatePassphrase, rsaPublicKeyLocation string) (*rsa.PrivateKey, error) {
if rsaPrivateKeyLocation == "" {
log.Println("No RSA Key given, generating temp one")
return GenRSA(4096)
}
priv, err := ioutil.ReadFile(rsaPrivateKeyLocation)
if err != nil {
log.Println("No RSA private key found, generating temp one")
return GenRSA(4096)
}
privPem, _ := pem.Decode(priv)
if privPem.Type != "RSA PRIVATE KEY" {
log.Println("RSA private key is of the wrong type", privPem.Type)
}
if x509.IsEncryptedPEMBlock(privPem) && privatePassphrase == "" {
log.Println("Passphrase is required to open private pem file")
return GenRSA(4096)
}
var privPemBytes []byte
if privatePassphrase != "" {
privPemBytes, err = x509.DecryptPEMBlock(privPem, []byte(privatePassphrase))
} else {
privPemBytes = privPem.Bytes
}
var parsedKey interface{}
//PKCS1
if parsedKey, err = x509.ParsePKCS1PrivateKey(privPemBytes); err != nil {
//If what you are sitting on is a PKCS#8 encoded key
if parsedKey, err = x509.ParsePKCS8PrivateKey(privPemBytes); err != nil { // note this returns type `interface{}`
log.Println("Unable to parse RSA private key, generating a temp one", err)
return GenRSA(4096)
}
}
var privateKey *rsa.PrivateKey
var ok bool
privateKey, ok = parsedKey.(*rsa.PrivateKey)
if !ok {
log.Println("Unable to parse RSA private key, generating a temp one", err)
return GenRSA(4096)
}
pub, err := ioutil.ReadFile(rsaPublicKeyLocation)
if err != nil {
log.Println("No RSA public key found, generating temp one")
return GenRSA(4096)
}
pubPem, _ := pem.Decode(pub)
if pubPem == nil {
log.Println("Use `ssh-keygen -f id_rsa.pub -e -m pem > id_rsa.pem` to generate the pem encoding of your RSA public key - rsa public key not in pem format")
return GenRSA(4096)
}
if pubPem.Type != "RSA PUBLIC KEY" {
log.Println("RSA public key is of the wrong type", pubPem.Type)
return GenRSA(4096)
}
if parsedKey, err = x509.ParsePKIXPublicKey(pubPem.Bytes); err != nil {
log.Println("Unable to parse RSA public key, generating a temp one", err)
return GenRSA(4096)
}
var pubKey *rsa.PublicKey
if pubKey, ok = parsedKey.(*rsa.PublicKey); !ok {
log.Println("Unable to parse RSA public key, generating a temp one", err)
return GenRSA(4096)
}
privateKey.PublicKey = *pubKey
return privateKey, nil
}
// GenRSA returns a new RSA key of bits length
func GenRSA(bits int) (*rsa.PrivateKey, error) {
key, err := rsa.GenerateKey(rand.Reader, bits)
return key, err
}
有一个有用的函数 ssh.ParseRawPrivateKey
,它已经实现了一些必需的逻辑。我建议以此为基础,尤其是当您的密钥用作 SSH 密钥时,在我的例子中就是这样。
import (
"os"
"crypto/rsa"
"golang.org/x/crypto/ssh"
"io/ioutil"
)
func mustNot(err error) {
if err != nil {
panic(err)
}
}
func loadRsaPrivateKey() *rsa.PrivateKey {
bytes, err := ioutil.ReadFile(os.Getenv("HOME") + "/.ssh/id_rsa")
mustNot(err)
key, err := ssh.ParseRawPrivateKey(bytes)
mustNot(err)
return key.(*rsa.PrivateKey)
}