Compare commits

...

3 Commits

Author SHA1 Message Date
ee967277d4 feat: handle key encrypted looking at headers too 2023-04-15 20:42:33 +02:00
aa713af153 feat: add readme 2023-03-06 18:00:40 +01:00
d79f43f6a4 feat: rename method 2023-03-06 17:53:29 +01:00
3 changed files with 179 additions and 36 deletions

44
README.md Normal file
View File

@@ -0,0 +1,44 @@
# go-grpc-certificate
## Problem's context
I've got some trouble while I want to use gRPC through TLS certificates. I've created a certificated from a .pem key file which had
a password and I couldn't use it through `go` standard library. I found a solution based on this **[SO answer](https://stackoverflow.com/a/56131169/6329540)** to this **[question](https://stackoverflow.com/questions/56129533/tls-with-certificate-private-key-and-pass-phrase/56131169#comment132834574_56131169)**
## Solution
I've decided to use **[openssl](https://www.openssl.org/docs/manmaster/man1/)** to achieve this task as far as I was not able to found a solution in go standard library.
## Installation requirements
I've used this version on development. So we should check if it backwards compatible.
```shell
$ openssl version
OpenSSL 3.0.8 7 Feb 2023 (Library: OpenSSL 3.0.8 7 Feb 2023)
```
## How to use
In your `go` project you just have to type in your terminal:
```shell
$ go get gitea.urkob.com/urko/go-grpc-certificate
```
Then place in your code like this
```go
package main
// here should be defined your imports
certcreds "gitea.urkob.com/urko/go-grpc-certificate/pkg/credentials"
func main() {
certPath := "place your certificate path"
certKeyPath := "place your key file path"
keyPassword := "place your key password here"
creds, err = certcreds.CredentialsFromKeyWithPasswd(
certPah, certKeyPath, keyPassword,
)
if err != nil {
log.Fatalf("Failed loading certificates: %v\n", err)
}
}
```

View File

@@ -12,7 +12,10 @@ import (
"google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials"
) )
func CredentialsFromKeyWithPasswd(certFile, certKey, passwd string) (credentials.TransportCredentials, error) { // FromRSAKeyWithPassword receives a certificate .pem file which was
// requested with .pem key file secured by password. By default go doesn't provide a
// standard packag that is not deprecated:
func FromRSAKeyWithPassword(certFile, certKey, passwd string) (credentials.TransportCredentials, error) {
if certFile == "" { if certFile == "" {
return nil, errors.New("certFile cannot be empty") return nil, errors.New("certFile cannot be empty")
} }
@@ -33,7 +36,7 @@ func CredentialsFromKeyWithPasswd(certFile, certKey, passwd string) (credentials
return nil, errors.New("rest is not empty") return nil, errors.New("rest is not empty")
} }
if !strings.Contains(keyBlock.Type, "ENCRYPTED") { if !strings.Contains(keyBlock.Type, "ENCRYPTED") && !isEncryptedOnHeaders(keyBlock.Headers) {
return nil, fmt.Errorf("certificate should has been encrypted with password") return nil, fmt.Errorf("certificate should has been encrypted with password")
} }
@@ -54,6 +57,19 @@ func CredentialsFromKeyWithPasswd(certFile, certKey, passwd string) (credentials
return credentials.NewServerTLSFromCert(&cert), nil return credentials.NewServerTLSFromCert(&cert), nil
} }
func isEncryptedOnHeaders(headers map[string]string) bool {
if len(headers) == 0 {
return false
}
for _, v := range headers {
if !strings.Contains(v, "ENCRYPTED") {
return true
}
}
return false
}
func decryptRSA(keyFile, password string) (string, error) { func decryptRSA(keyFile, password string) (string, error) {
cmd := exec.Command("openssl", "rsa", "-in", keyFile, "-passin", formatPass(password), "-text") cmd := exec.Command("openssl", "rsa", "-in", keyFile, "-passin", formatPass(password), "-text")
output_bts, err := cmd.Output() output_bts, err := cmd.Output()

View File

@@ -2,6 +2,7 @@ package credentials
import ( import (
"errors" "errors"
"fmt"
"log" "log"
"os" "os"
"os/exec" "os/exec"
@@ -12,31 +13,15 @@ import (
) )
var ( var (
testDir = "testDir" testDir = "testDir"
testCertKeyError = testDir + "/testKeyError.pem" testCertKeyError = testDir + "/testKeyError.pem"
testKeyError = testDir + "/error-key.pem" testKeyError = testDir + "/error-key.pem"
testCertKey = testDir + "/testCertKey.pem" testCertKey = testDir + "/testCertKey.pem"
testCert = testDir + "/testCert.pem" testCert = testDir + "/testCert.pem"
testCertScript = testDir + "/certScript.sh" testCertScript = testDir + "/certScript.sh"
testKeyPass = "test" testCertEncryptedNotHeader = testDir + "/testCertEncryptedNotHeader.pem"
testKeyEncryptedNotHeader = testDir + "/testCertEncryptedNotHeader-key.pem"
generateKeyScript = `#!/bin/bash testKeyPass = "test"
openssl genpkey -out ./` + testCertKey + ` -algorithm RSA -pass pass:test -des3`
generateCertScript = `#!/bin/bash
openssl req -new -sha256 -key ./` + testCertKey + ` -passin pass:test -out ./` + testCert + ` -nodes -x509 -subj "/C=US/ST=Denial/L=Springfield/O=Dis/CN=www.example.com"`
certKeyOk = `-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgP+sX5Fn7WhQgAt1l
nL3YaX0RPuJFf058/r90mO/xViyhRANCAAT3qOUKYwgSbBSVAMkC14/kZAQWZIef
+SnO6GvOjMU8dcchboisMujVQRksfgJUsBZmfquh93BnkYqkSzlD+dIE
-----END PRIVATE KEY-----`
certKeyError = `-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgFrBUnTIIrSbRBZpX
j3TlomgnCQFe6JUVBO0fyRQMk1qhRANCAASTLZ8S8rWSmraKWNdM6N3pWPuATi92
yQuhZ6P2JaLnfmYemIOprHeRSqTqWy4+kus3b4LxPEzu86/248d7d
-----END PRIVATE KEY-----`
) )
func createTestDir() error { func createTestDir() error {
@@ -48,6 +33,8 @@ func deleteTestDir() error {
} }
func createEncryptedKeyFile() error { func createEncryptedKeyFile() error {
generateKeyScript := `#!/bin/bash
openssl genpkey -out ./` + testCertKey + ` -algorithm RSA -pass pass:test -des3`
if err := os.WriteFile(testCertScript, []byte(generateKeyScript), os.ModeAppend); err != nil { if err := os.WriteFile(testCertScript, []byte(generateKeyScript), os.ModeAppend); err != nil {
log.Fatalln("os.WriteFile: ", err) log.Fatalln("os.WriteFile: ", err)
} }
@@ -61,6 +48,9 @@ func createEncryptedKeyFile() error {
} }
func createCertificateFromKeyFile() error { func createCertificateFromKeyFile() error {
generateCertScript := `#!/bin/bash
openssl req -new -sha256 -key ./` + testCertKey + ` -passin pass:test -out ./` + testCert + ` -nodes -x509 -subj "/C=US/ST=Denial/L=Springfield/O=Dis/CN=www.example.com"`
if err := os.WriteFile(testCertScript, []byte(generateCertScript), os.ModeAppend); err != nil { if err := os.WriteFile(testCertScript, []byte(generateCertScript), os.ModeAppend); err != nil {
log.Fatalln("os.WriteFile: ", err) log.Fatalln("os.WriteFile: ", err)
} }
@@ -73,21 +63,102 @@ func createCertificateFromKeyFile() error {
return nil return nil
} }
func createCertificateEncNoHeader() error {
certEncryptedNotHeader := `-----BEGIN CERTIFICATE-----
MIIFfDCCA2SgAwIBAgICEAIwDQYJKoZIhvcNAQELBQAwRzELMAkGA1UEBhMCUk0x
DTALBgNVBAgMBFJFTUwxEjAQBgNVBAoMCUZ1bmdpbWFpbDEVMBMGA1UEAwwMaW50
ZXJtZWRpYXRlMB4XDTIzMDQxNTE3NDMxNFoXDTI0MDQyNDE3NDMxNFowgcIxCzAJ
BgNVBAYTAlJNMQ0wCwYDVQQIDARSRU1MMR4wHAYDVQQHDBVhcGkud2l0bmVzcy51
cmtvYi5jb20xHjAcBgNVBAoMFWFwaS53aXRuZXNzLnVya29iLmNvbTEeMBwGA1UE
CwwVYXBpLndpdG5lc3MudXJrb2IuY29tMR4wHAYDVQQDDBVhcGkud2l0bmVzcy51
cmtvYi5jb20xJDAiBgkqhkiG9w0BCQEWFWFwaS53aXRuZXNzLnVya29iLmNvbTCC
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALiufUUgzikdcbaSrX79gscy
KCfQOkGm3hNGdgiDM0S7XWAzVIgEtpWKzuTRNskyNX7te/1cBFZX0B3JKx9b9ySx
IB1ByoSLnM6EI3Rf2+o3TEi4d6/KLvxCMhESdXddbIYBzTlNabmqA0STUbdmCNd+
qSn30Q8ppzcIgZQWe8lM58VknkWJvCkR5Kaji7baySM0FVHfsF+VcSufOaxV/uPF
5hOtOneka9tlsImQBow08wARCOiMWQwk0ipEHxd1iF9zgdODzBmp/v2TXrH07/H2
29wpJAGoIbjg87p6IzkEiDIl1q8jj9AUYPY8RI1PlrJgEUXmtWxtpcrIozDAQhkC
AwEAAaOB9TCB8jAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIGQDAzBglghkgB
hvhCAQ0EJhYkT3BlblNTTCBHZW5lcmF0ZWQgU2VydmVyIENlcnRpZmljYXRlMB0G
A1UdDgQWBBTQw/IEPzQS8fPn+HH2e7bgQ8G4XjBZBgNVHSMEUjBQgBRZA1nwXhva
oMHqfcd+CkJ1tY2+5aE0pDIwMDELMAkGA1UEBhMCUk0xDTALBgNVBAgMBFJFTUwx
EjAQBgNVBAoMCUZ1bmdpbWFpbIICEAAwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQM
MAoGCCsGAQUFBwMBMA0GCSqGSIb3DQEBCwUAA4ICAQCHug9kWkHCNwSK37+YUZsJ
cRSlAdZ31O60EYLpss/Gr+rGL6aDENS0YytnY+HWfztItFGnh4uSWxZvAbXP90/5
GrLI+bdeFk05E4tazM93hmpmMf4jQyqmuZiZ6HLbxsWMCuqA+E8EiM0UzMM0toKR
imCPV0NRcWfmu/iC41OPHYKdfVMwpDNspOUDaWP1UsrgYjkfwYDyDcakK74/XNYf
26sX5/b28+VoDiVP5lkdaHxDWcHp+4YhuGYY3wg0/RLFmv9aHH9rpaPZ1VPs18sA
DhshwcL88NGz/E6dTSiEkQMhWyOaYUmW18l0CIAajgVgReohk7MwgbxgzDttZuWN
mxo8LbCle3/ePezl68O4GMXO5Z+w3veZiVB1zM7t51caBu8HAjlZI067jLL0C1ju
lSUSRN+YMfCTkchY7o7hkgC8g6WF7pACCxY9gUksbssQXQo9DcRH6dQ6T7V8hwCS
Q9bTvCiOneRFfH74da6aQH76NJDlspkdhdpIvsUjqyPDHnYDNxwCRJpwiUlAXKT6
yzsuASSBlWzaTynZNB5wwXMlMwLicriMNf33cgQ0I5HWlVimCX0C39tGO+qZCWq9
F9QpmN1nSnnhoYDx1MVuU/Ibr9+97ysuQT1saOadtcCrML5DPdtIytAl4Dtrdpj7
yQhGi+Mkkhw6oiotDn4eVA==
-----END CERTIFICATE-----
`
keyEncryptedNotHeader := `-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-256-CBC,99C1C3BE95C653A665C392B6FC8B8517
l4Ome1QW3kstGZZQ7F2/L0dXTlihM3J/poQzBuYHcsFDfAT6i41/22N1it+dujtk
N2Rv8RfgdxRM26nGlJok66sO8YTvew4rUc4YBC9ms0W6OHzWeQXSvWxBjiRjTasX
VK8OzaYOHqsMInGJ7hctJn3LVRtIJvNfjn29+13icUjvAuE8+b/oPc0eArwPyu6x
Os2C7lPO5NG6BMgiwASBTr80v+SpwWQJCgrLPqpd+5pv4Yf+iCLrYnVSNHgr2IA/
Kqw070wj9UavxPnKiRDqjgssJUMDGX/P8iTbMVJOEaGjlf5HJkhVtwOf9qoixe/f
97QZcGWTkUqWdnstw8BYNvEah/GBtVW8x7e4rzJXr50f5EIP6fOYWERHoXEqGLuw
WgPOploov7qS0pkikQEUbGF+v+EBv2Z+Z3k9Qy4HpGbukR4PNYxRPEEthWal9WIZ
493aVoSgwqUeEHgOYMBs5lrryXgJ7uVw49ShzZIJVXyiMCfi8rElY5ITaDQWM0+n
ty9EiBSEgSaf4Oxhm9tdrIhlBN0T8Byc2Sao+Xvj+NckT+M37uFB+IvhbjRlwalL
CyB7PF1W2d7VRGUrKQ3RGDpyDlcsjIDBgD3ybSRZHuPi2wz/qmZ17PwLHal+SgB4
ZPO21GIlmyPqvKj9K3PUFFUi88pX7wxIFg9AYOoqBSzcrlFeF6Iizv7aapbZFl4T
qoZ4E+uE3BAtiKYBDvQdS7Vkn230ZpRyySBKSurmVusyb783mNpC0QQJ28ldSSCD
eQFi75BadcqJnWBUta7kLA3igbONljXLm5HlWB/Ep7AWYG6PtF3U1z3JPbCB5PpC
uFJfQARZERG0d/xZ+FIyMaqKtIhwW7WRJ6D65o4GMT++k9SQo3onnNspQIGbKcfb
CnXhwGxHCPZ93pu+IhZqPH+4MquW4TRdRnK2Ce+HSVsQ3xvZQTg4bxexTChtMpp5
hz0keKqhNARMRRqGihapGq/j84jAv/dIouHdnKJ+p5DgtF1UcpCO1tUIlKDDdg54
gTJDgxunysVwupPblXKDzAdDkqB9h3stpLZb1mL6AG2+NU3lu/fXMIti8zLYWJFk
J9jadGR9cnt2vyedErIrztpxOjLQlEfagbTxKMcjee/Pjir+VeDc/WZqWf1EWuTH
zK9p87ze7oUW2UEffKDmPZZMDWMy8Th6goaxp3r1WEci/bVpWblG0PgYQQFZ8hHH
SqNhRUjmYSSJza6rhe2aoqDUBisg8/xdVHqEQIpSumYsI7AbLwovgDK8E46jW9+r
0yD/REoc8MZNd5kHTSF+tfhr8ve/4Yj0TObAeBZUv3wwqKSXhbCWW40V1iWw9+6O
kwLoIh9iWHk4oM98/hUDCV5/6bQdMNP7LpBC+KhKXzaqKBomWZciMZQXdgYzbTHr
eMvirNzmyc/BMkf3asQLAaTYbpi/ZwzfxHxsfPf3LcXRZay1BMF0IKwRh3SHWODJ
oJ4rd49tRVCnMXOHnZJAbnWWAgAiiJtvVs0yGeSuVa6lWIvQk+z1XxmMGZKVJzYR
mXTUabqj0SXf7mjBozClDrtyY4vwz77oXbg0h3HdJbQFtXH1xe3nx0/tg3jvTfJX
-----END RSA PRIVATE KEY-----
`
if err := os.WriteFile(testCertEncryptedNotHeader, []byte(certEncryptedNotHeader), os.ModeAppend); err != nil {
return fmt.Errorf("os.WriteFile(testCertEncryptedNotHeader: %s", err)
}
if err := os.WriteFile(testKeyEncryptedNotHeader, []byte(keyEncryptedNotHeader), os.ModeAppend); err != nil {
return fmt.Errorf("os.WriteFile(testKeyEncryptedNotHeader: %s", err)
}
return nil
}
func TestCredentialsFromKeyWithPasswd(t *testing.T) { func TestCredentialsFromKeyWithPasswd(t *testing.T) {
require.NoError(t, deleteTestDir()) require.NoError(t, deleteTestDir())
require.NoError(t, createTestDir()) require.NoError(t, createTestDir())
require.NoError(t, createEncryptedKeyFile()) require.NoError(t, createEncryptedKeyFile())
require.NoError(t, createCertificateFromKeyFile()) require.NoError(t, createCertificateFromKeyFile())
require.NoError(t, createCertificateEncNoHeader())
defer func() { defer func() {
require.NoError(t, deleteTestDir()) require.NoError(t, deleteTestDir())
}() }()
_, err := CredentialsFromKeyWithPasswd(testCert, testCertKey, testKeyPass) _, err := FromRSAKeyWithPassword(testCert, testCertKey, testKeyPass)
assert.NoError(t, err, "key with password should not fail") assert.NoError(t, err, "key with password should not fail")
_, err = CredentialsFromKeyWithPasswd(testCert, testCertKey, "wrong-pass") _, err = FromRSAKeyWithPassword(testCert, testCertKey, "wrong-pass")
assert.Error(t, err, "key with wrong pass password should not fail") assert.Error(t, err, "key with wrong pass password should not fail")
_, err = FromRSAKeyWithPassword(testCertEncryptedNotHeader, testKeyEncryptedNotHeader, "test")
assert.NoError(t, err)
} }
func TestCredentialsFromKeyWithPasswdError(t *testing.T) { func TestCredentialsFromKeyWithPasswdError(t *testing.T) {
@@ -98,30 +169,42 @@ func TestCredentialsFromKeyWithPasswdError(t *testing.T) {
require.NoError(t, deleteTestDir()) require.NoError(t, deleteTestDir())
}() }()
_, err := CredentialsFromKeyWithPasswd("", "", "") _, err := FromRSAKeyWithPassword("", "", "")
assert.Error(t, err) assert.Error(t, err)
_, err = CredentialsFromKeyWithPasswd(testCert, "", "") _, err = FromRSAKeyWithPassword(testCert, "", "")
assert.Error(t, err) assert.Error(t, err)
_, err = CredentialsFromKeyWithPasswd(testCert, "not-exists.txt", "") _, err = FromRSAKeyWithPassword(testCert, "not-exists.txt", "")
assert.Error(t, err) assert.Error(t, err)
require.NoError(t, os.WriteFile(testKeyError, []byte(""), os.ModeAppend)) require.NoError(t, os.WriteFile(testKeyError, []byte(""), os.ModeAppend))
_, err = CredentialsFromKeyWithPasswd(testCert, testKeyError, testKeyPass) _, err = FromRSAKeyWithPassword(testCert, testKeyError, testKeyPass)
require.Error(t, err) require.Error(t, err)
certKeyOk := `-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgP+sX5Fn7WhQgAt1l
nL3YaX0RPuJFf058/r90mO/xViyhRANCAAT3qOUKYwgSbBSVAMkC14/kZAQWZIef
+SnO6GvOjMU8dcchboisMujVQRksfgJUsBZmfquh93BnkYqkSzlD+dIE
-----END PRIVATE KEY-----`
certKeyError := `-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgFrBUnTIIrSbRBZpX
j3TlomgnCQFe6JUVBO0fyRQMk1qhRANCAASTLZ8S8rWSmraKWNdM6N3pWPuATi92
yQuhZ6P2JaLnfmYemIOprHeRSqTqWy4+kus3b4LxPEzu86/248d7d
-----END PRIVATE KEY-----`
require.NoError(t, os.WriteFile(testCertKeyError, []byte(certKeyError), os.ModeAppend)) require.NoError(t, os.WriteFile(testCertKeyError, []byte(certKeyError), os.ModeAppend))
_, err = CredentialsFromKeyWithPasswd(testCert, testCertKeyError, testKeyPass) _, err = FromRSAKeyWithPassword(testCert, testCertKeyError, testKeyPass)
assert.Error(t, err) assert.Error(t, err)
require.NoError(t, os.Remove(testCertKeyError)) require.NoError(t, os.Remove(testCertKeyError))
assert.NoError(t, os.WriteFile(testCertKeyError, []byte(certKeyOk), os.ModeAppend)) assert.NoError(t, os.WriteFile(testCertKeyError, []byte(certKeyOk), os.ModeAppend))
_, err = CredentialsFromKeyWithPasswd(testCert, testCertKeyError, testKeyPass) _, err = FromRSAKeyWithPassword(testCert, testCertKeyError, testKeyPass)
assert.Error(t, err, "key without password should fail") assert.Error(t, err, "key without password should fail")
require.NoError(t, createEncryptedKeyFile()) require.NoError(t, createEncryptedKeyFile())
_, err = CredentialsFromKeyWithPasswd(testCert, testCertKey, testKeyPass) _, err = FromRSAKeyWithPassword(testCert, testCertKey, testKeyPass)
assert.Error(t, err, "key without password should fail") assert.Error(t, err, "key without password should fail")
} }