I am trying to run an HTTPS rest server locally.
I followed this help: Golang TLS
It works fine when using self-signed certificates as in the above tutorial.
But when I replace these certificates with one I generated from GoDaddy for one of my websites, I get ListenAndServe: tls: failed to find any PEM data in key input
These certificates work properly on the apache server but not on my local Go server.
package main
import (
"log"
"net/http"
)
func HelloServer(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Content-Type", "text/plain")
_, _ = w.Write([]byte("This is an example server.\n"))
}
func main() {
//https://localhost:443/hello
http.HandleFunc("/hello", HelloServer)
err := http.ListenAndServeTLS(":443",
"../../../../../sslcert/server.crt",
"../../../../../sslcert/server.key",
nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
The issue was with the key file. The beginning of the file had some issue (like UTF-8 BOM at the start of the file or similar) as #SteffenUllrich mentioned. To fix this, I added an empty line just above the key file and it worked.
Finally, the key looks like:
<Empty line>
-----BEGIN RSA PRIVATE KEY-----
wlWPpSnGEdNjRapfW/6+xzjDVAaKC41c5b07OAviFchwqGI+88
aZGwBJnTgkbsLddddddd=
-----END RSA PRIVATE KEY-----
For me additional byte was added at the end, while serializing the data from the terminal, used below snippet to correct it.
func convert(input []byte) (output []byte) {
if input[len(input)-1] == 0 {
input = input[:len(input)-1]
}
return input
}
Related
I'm trying to migrate some python code to golang.
The code needs to get the value of unstructuredName from the ssl certificates subject.
Here is how it works in python:
from cryptography import x509
from cryptography.x509.oid import NameOID
from cryptography.hazmat.backends import default_backend
pem = b"""\
-----BEGIN CERTIFICATE-----
MIIDcTCCAlmgAwIBAgIUNjbltje6I/S9PJN8KgIxyi2Iw58wDQYJKoZIhvcNAQEL
BQAwSTEUMBIGA1UEAwwLZXhhbXBsZS5jb20xFjAUBgNVBAoMDUV4YW1wbGUsIElu
Yy4xGTAXBgNVBAsMEEV4YW1wbGUgRGl2aXNpb24wHhcNMjIxMTI5MTU1NDMxWhcN
MzcxMTI2MTU1NDMxWjCBmzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3Ju
aWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xGjAYBgNVBAoMEVNvbWUgT3JnYW5p
emF0aW9uMR4wHAYDVQQDDBVzb21lLm9yZ2FuaXphdGlvbi5jb20xIzAhBgkqhkiG
9w0BCQIMFHNvbWUgYWRkaXRpb25hbCBpbmZvMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEAyqprugdiaERkAHbx06WobPekkZ8CFyiR6dv8D49eqHO1xmCp
aVotFJSj5cktUH64885LfTCghUdPcw+rIh0fB8tOleBUA9yIMUqbYFnRgLrz7U+g
bkgyQO7w0PsB7tgUKI7esEEcvWSoygWyZKSiUZBSQOAuEcJxog7GMR6QiUtmiLaq
DY0Wwlir7iakX0Qlq8iHFY6yNf2aghET6IC9tGbkifO6HWLEP4gBWwtIvCd94l6L
6rNpGtYXHSmlaEL0jn/4QPADciXDrBnAhFfzBrqyO7SYNqJUiENXEvq+7ESIqibV
RKvt9HpAj+O/Ij4z+Z6Xys8sE3RE4FSNSdXphQIDAQABMA0GCSqGSIb3DQEBCwUA
A4IBAQCH6vxtrqUBFVgahnmjPSHci3JCaz/X9hL2QgXNTzAS3rLJBd6OrC+3ITli
vzJlWryl1hzTxFEcj3EtVG1AckNOHX0RCFggNBSQSOz6oaaZlS6BAkChbRUqmHfG
iCDK8A1pV1cr/SPBe/UjSkbG548JpqCSWEUL1jNDsNzZmjuXy+FTM2PDYiSRbGxY
Y0SpkxAhuKltblzghh1KIMU7UutEUXDGMlCCYEY9/ibWTKa9J/WNjC0CQC13jLe8
PqVLIeXIoVSkTsgbzwYxW2P+M6QbjRC83OORwm8GnEj1k2yX6JmnzLifidFiT/h6
M2Y3gjYqfGexupmK9toEejZPj1RB
-----END CERTIFICATE-----
"""
cert = x509.load_pem_x509_certificate(pem, default_backend())
unstructured_name = cert.subject.get_attributes_for_oid(NameOID.UNSTRUCTURED_NAME)[0].value
print(unstructured_name)
This code prints the value ("some additional info" in this case) of unstructuredName.
My attempt in go looks like this:
package main
import (
"crypto/x509"
"encoding/pem"
"fmt"
"log"
)
const certRaw = `-----BEGIN CERTIFICATE-----
MIIDcTCCAlmgAwIBAgIUNjbltje6I/S9PJN8KgIxyi2Iw58wDQYJKoZIhvcNAQEL
BQAwSTEUMBIGA1UEAwwLZXhhbXBsZS5jb20xFjAUBgNVBAoMDUV4YW1wbGUsIElu
Yy4xGTAXBgNVBAsMEEV4YW1wbGUgRGl2aXNpb24wHhcNMjIxMTI5MTU1NDMxWhcN
MzcxMTI2MTU1NDMxWjCBmzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3Ju
aWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xGjAYBgNVBAoMEVNvbWUgT3JnYW5p
emF0aW9uMR4wHAYDVQQDDBVzb21lLm9yZ2FuaXphdGlvbi5jb20xIzAhBgkqhkiG
9w0BCQIMFHNvbWUgYWRkaXRpb25hbCBpbmZvMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEAyqprugdiaERkAHbx06WobPekkZ8CFyiR6dv8D49eqHO1xmCp
aVotFJSj5cktUH64885LfTCghUdPcw+rIh0fB8tOleBUA9yIMUqbYFnRgLrz7U+g
bkgyQO7w0PsB7tgUKI7esEEcvWSoygWyZKSiUZBSQOAuEcJxog7GMR6QiUtmiLaq
DY0Wwlir7iakX0Qlq8iHFY6yNf2aghET6IC9tGbkifO6HWLEP4gBWwtIvCd94l6L
6rNpGtYXHSmlaEL0jn/4QPADciXDrBnAhFfzBrqyO7SYNqJUiENXEvq+7ESIqibV
RKvt9HpAj+O/Ij4z+Z6Xys8sE3RE4FSNSdXphQIDAQABMA0GCSqGSIb3DQEBCwUA
A4IBAQCH6vxtrqUBFVgahnmjPSHci3JCaz/X9hL2QgXNTzAS3rLJBd6OrC+3ITli
vzJlWryl1hzTxFEcj3EtVG1AckNOHX0RCFggNBSQSOz6oaaZlS6BAkChbRUqmHfG
iCDK8A1pV1cr/SPBe/UjSkbG548JpqCSWEUL1jNDsNzZmjuXy+FTM2PDYiSRbGxY
Y0SpkxAhuKltblzghh1KIMU7UutEUXDGMlCCYEY9/ibWTKa9J/WNjC0CQC13jLe8
PqVLIeXIoVSkTsgbzwYxW2P+M6QbjRC83OORwm8GnEj1k2yX6JmnzLifidFiT/h6
M2Y3gjYqfGexupmK9toEejZPj1RB
-----END CERTIFICATE-----
`
func main() {
block, rest := pem.Decode([]byte(certRaw))
if block == nil {
log.Fatalf("Decode CA PEM, %v", rest)
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
log.Fatalf("parse certificate")
}
fmt.Println(cert.Subject.String())
// Unmarshall and print value of unstructuredName (oid: 1.2.840.113549.1.9.2)
// ???
}
Go Playground: https://go.dev/play/p/VwkpDbNpQ78
I couldn't find a function to get the attribute by oid. When I print out the whole subject I see that the value is still (DER?) encoded.
How can I decode or unmarshal the value of the unstructuredName?
Thanks a lot.
I don't think the Go standard lib has a function to get the attribute by the oid. However, it doesn't look like it's very hard to write one yourself. The code below is based on this related answer (and the code in your question).
Here's the updated go playground - https://go.dev/play/p/NtfZ6Kl_Vl0.
package main
import (
"crypto/x509"
"encoding/pem"
"fmt"
"log"
)
const certRaw = `-----BEGIN CERTIFICATE-----
MIIDcTCCAlmgAwIBAgIUNjbltje6I/S9PJN8KgIxyi2Iw58wDQYJKoZIhvcNAQEL
BQAwSTEUMBIGA1UEAwwLZXhhbXBsZS5jb20xFjAUBgNVBAoMDUV4YW1wbGUsIElu
Yy4xGTAXBgNVBAsMEEV4YW1wbGUgRGl2aXNpb24wHhcNMjIxMTI5MTU1NDMxWhcN
MzcxMTI2MTU1NDMxWjCBmzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3Ju
aWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xGjAYBgNVBAoMEVNvbWUgT3JnYW5p
emF0aW9uMR4wHAYDVQQDDBVzb21lLm9yZ2FuaXphdGlvbi5jb20xIzAhBgkqhkiG
9w0BCQIMFHNvbWUgYWRkaXRpb25hbCBpbmZvMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEAyqprugdiaERkAHbx06WobPekkZ8CFyiR6dv8D49eqHO1xmCp
aVotFJSj5cktUH64885LfTCghUdPcw+rIh0fB8tOleBUA9yIMUqbYFnRgLrz7U+g
bkgyQO7w0PsB7tgUKI7esEEcvWSoygWyZKSiUZBSQOAuEcJxog7GMR6QiUtmiLaq
DY0Wwlir7iakX0Qlq8iHFY6yNf2aghET6IC9tGbkifO6HWLEP4gBWwtIvCd94l6L
6rNpGtYXHSmlaEL0jn/4QPADciXDrBnAhFfzBrqyO7SYNqJUiENXEvq+7ESIqibV
RKvt9HpAj+O/Ij4z+Z6Xys8sE3RE4FSNSdXphQIDAQABMA0GCSqGSIb3DQEBCwUA
A4IBAQCH6vxtrqUBFVgahnmjPSHci3JCaz/X9hL2QgXNTzAS3rLJBd6OrC+3ITli
vzJlWryl1hzTxFEcj3EtVG1AckNOHX0RCFggNBSQSOz6oaaZlS6BAkChbRUqmHfG
iCDK8A1pV1cr/SPBe/UjSkbG548JpqCSWEUL1jNDsNzZmjuXy+FTM2PDYiSRbGxY
Y0SpkxAhuKltblzghh1KIMU7UutEUXDGMlCCYEY9/ibWTKa9J/WNjC0CQC13jLe8
PqVLIeXIoVSkTsgbzwYxW2P+M6QbjRC83OORwm8GnEj1k2yX6JmnzLifidFiT/h6
M2Y3gjYqfGexupmK9toEejZPj1RB
-----END CERTIFICATE-----
`
func main() {
block, rest := pem.Decode([]byte(certRaw))
if block == nil {
log.Fatalf("Decode CA PEM, %v", rest)
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
log.Fatalf("parse certificate")
}
// 1.2.840.113549.1.9.2
var oidUnstructuredName = []int{1, 2, 840, 113549, 1, 9, 2}
var unstructuredName string
for _, n := range cert.Subject.Names {
if n.Type.Equal(oidUnstructuredName) {
if v, ok := n.Value.(string); ok {
unstructuredName = v
}
}
}
fmt.Println(unstructuredName)
}
I read through a few examples to generate DSA keys for OpenSSH in Go. And my clean code snippet and outputs are listed below.
It has two problems:
for 2048-bit length, the public key can't be loaded via ssh.ParseAuthorizedKey, for error: "ssh: no key found".
OpenSSH client and GitHub SSH can't accept it.
Code snippets:
// GenerateDSAKeys generates DSA public and private key pair with given size for SSH.
func GenerateDSAKeys(bitSize int, passphrase string) (pubKey string, privKey string, err error) {
params := new(dsa.Parameters)
// see http://golang.org/pkg/crypto/dsa/#ParameterSizes
if err = dsa.GenerateParameters(params, rand.Reader, dsaSizeFromLength(bitSize)); err != nil {
return
}
var privateKey dsa.PrivateKey
privateKey.PublicKey.Parameters = *params
// this generates a public & private key pair
if err = dsa.GenerateKey(&privateKey, rand.Reader); err != nil {
return
}
// generate public key
var publicKey ssh.PublicKey
if publicKey, err = ssh.NewPublicKey(&privateKey.PublicKey); err != nil {
return
}
// encode public key
pubBytes := ssh.MarshalAuthorizedKey(publicKey)
// encode private key
var (
bytes []byte
privBytes []byte
)
if bytes, err = asn1.Marshal(privateKey); err != nil {
return
}
privBytes, err = encodePEMBlock(&pem.Block{
Type: "DSA PRIVATE KEY",
Bytes: bytes,
}, passphrase)
if err != nil {
return
}
return string(pubBytes), string(privBytes), nil
}
func dsaSizeFromLength(l int) dsa.ParameterSizes {
switch l {
case 1024:
return dsa.L1024N160
case 2048:
return dsa.L2048N224
case 3072:
return dsa.L3072N256
default:
return dsa.L2048N256
}
}
Output for 1024:
bash-3.2$ cat id_dsa.pub
ssh-dss AAAAB3NzaC1kc3MAAACBAIbuW6wh5hU2W96tEyo7xPDZmslWnsyQBYtf4SSTeHOMTNTxznlMujjkmCuqKJ04BlHFi+ner2qzCd1GkzGhHrretQw1z5Ew8ysAGsmbb9Yc6BMTyhJkrQ2lIR5Vmqa9Ukx0PM/HdXa6GieYZWxyabN5IjN4SESXZ6G7Jb4StwCfAAAAFQCnj+WurTgW5aXGYePwhBYAWX01WwAAAIBHQpZSsJGKsEII7Oe6RMz5ek27ydHPnrLDqVOmEKqHrIrsYXoTHaMOLW+fvsDBC3q0EWMBNgu4IAZe3eL2Gx2r94+DS+GTfECWiJ3+O76DTAwDcB6fXxdYxG88nJMzCkcMZEmOjbIpuiGP5NC+OOqBv2DEHw7YEWs8Kx4T71mE0AAAAIEAhg/OB98FTcYX/gXEclKqqDByHzHIt71Y7DFZnxnmd4DFEZp/WzJ6VT2jTNMyWKRT9h0SITORLKNxTCM3ZLhQYVBinOsjIzZ8YlYYOxTn2mWntWzmsdhUeBx+aNCwuhz/cMHjbhWJWVXRDfgzPf9IHtF1FLgjtMiz6/xwzAMEPlE= vej#Vej-Work-MBP.local
bash-3.2$ cat id_dsa
-----BEGIN DSA PRIVATE KEY-----
MIIBwDCCAaYwggEeAoGBAIbuW6wh5hU2W96tEyo7xPDZmslWnsyQBYtf4SSTeHOM
TNTxznlMujjkmCuqKJ04BlHFi+ner2qzCd1GkzGhHrretQw1z5Ew8ysAGsmbb9Yc
6BMTyhJkrQ2lIR5Vmqa9Ukx0PM/HdXa6GieYZWxyabN5IjN4SESXZ6G7Jb4StwCf
AhUAp4/lrq04FuWlxmHj8IQWAFl9NVsCgYBHQpZSsJGKsEII7Oe6RMz5ek27ydHP
nrLDqVOmEKqHrIrsYXoTHaMOLW+fvsDBC3q0EWMBNgu4IAZe3eL2Gx2r94+DS+GT
fECWiJ3+O76DTAwDcB6fXxdYxG88nJMzCkcMZEmOjbIpuiGP5NC+OOqBv2DEHw7Y
EWs8Kx4T71mE0AKBgQCGD84H3wVNxhf+BcRyUqqoMHIfMci3vVjsMVmfGeZ3gMUR
mn9bMnpVPaNM0zJYpFP2HRIhM5Eso3FMIzdkuFBhUGKc6yMjNnxiVhg7FOfaZae1
bOax2FR4HH5o0LC6HP9wweNuFYlZVdEN+DM9/0ge0XUUuCO0yLPr/HDMAwQ+UQIU
RhBIeYLqxL4PJgENdgtjfjxDX80=
-----END DSA PRIVATE KEY-----
For 2048, output:
bash-3.2$ cat id_dsa.pub
ssh-dss AAAAB3NzaC1kc3MAAAEBAKbfrtje1KqerXL0DFg35Ouou2NP08vsuqCbv/cr65X2/AMaDy/Zdikq1rHw1ScWzre0vGTDwnGsFJgNDf3p0ckJqxMN9kop+8a+M1nuQ39LnkHATM+x58jgJaC06VZisLQuMoJvzdOlMPKb1v9TKfSiI0XZujhS5RVl4DkqABEXFsNXOHTIGDXToq80IPf0KV01qvOGBnICzhPkXw5HcCF9rmVD5aPfADjHZfUTt9PJfFcPk06mmfvpOEBfWgaluIDp0Wf5DsKu0RxlZVm4x3iP43hQfJ8NaFRyOKhNxd0r5t5SDUubF2Z37v0QNIvLZPrKsE0AywNkDZ66bf3HigMAAAAdAMjEhWBLtQ+nWfJIO16rpLQZQKBpXePefLrztpUAAAEALJsfHywD5PSfeFUGs901I1Z/yvOuyHQFxJGXEAQJ08CBAGvwcGA1M41sDnbrbOFm8ol91xcnUqW0HMlwDpdHIBxgytlnKlsEw69GqCx1dJAv8LnDokK/zV406I0LLwXzQ3QvfSMtM3VDsIA/jInd273LlXFEG4dmkG1mIP7SBgbzy7jZ4LpH5y8ZSUMFlZ4dXTURj4TAe/ByScaXoVO8QDLaZq5EiSRAomUxLvnwTNJHt/YqQog5EWhQNBUKp5HbPS7pk+KujaUpYwXNZZuhqjp4dmaBYppwCZnrqvsHKq6p8BCt4ZQ/589q0D1Mx0pbgI/sQLvib5jSHJ0jKq7xrAAAAQBzxG2gTr89VeVJBMsconclvXw7vYDxQGBGhZBmKQ6e/s8swxJzzUbpRzoeYGEL1nE28e4Apw6pQFY3XkuvklYTwNHPLwG/5FJV7fc2HJgwVg8/wf5hUrzqlKK9Xhjm2369iKnGig/u28e79oOWgdbqfrjWpNih/aVL60ApdWgxmxC61PGppJ2AXNubaDynhMOx1q+lSH+unu+kLml+c+lbqwZYXmI0t+gFzyyu+pL8DpVEhAG8P/AMCXMDR3JmQhWF7fKiP1QdDMidUODM8keVQaK5txIp9EUAThk0ErigG5ZsIL5V2Z4UistlwQ4WiZYJmz/IlD50+PtkjwBFiA6z vej#Vej-Work-MBP.local
bash-3.2$ cat id_dsa
-----BEGIN DSA PRIVATE KEY-----
MIIDUzCCAzAwggIoAoIBAQCm367Y3tSqnq1y9AxYN+TrqLtjT9PL7Lqgm7/3K+uV
9vwDGg8v2XYpKtax8NUnFs63tLxkw8JxrBSYDQ396dHJCasTDfZKKfvGvjNZ7kN/
S55BwEzPsefI4CWgtOlWYrC0LjKCb83TpTDym9b/Uyn0oiNF2bo4UuUVZeA5KgAR
FxbDVzh0yBg106KvNCD39CldNarzhgZyAs4T5F8OR3Ahfa5lQ+Wj3wA4x2X1E7fT
yXxXD5NOppn76ThAX1oGpbiA6dFn+Q7CrtEcZWVZuMd4j+N4UHyfDWhUcjioTcXd
K+beUg1Lmxdmd+79EDSLy2T6yrBNAMsDZA2eum39x4oDAh0AyMSFYEu1D6dZ8kg7
XquktBlAoGld4958uvO2lQKCAQAsmx8fLAPk9J94VQaz3TUjVn/K867IdAXEkZcQ
BAnTwIEAa/BwYDUzjWwOduts4WbyiX3XFydSpbQcyXAOl0cgHGDK2WcqWwTDr0ao
LHV0kC/wucOiQr/NXjTojQsvBfNDdC99Iy0zdUOwgD+Mid3bvcuVcUQbh2aQbWYg
/tIGBvPLuNngukfnLxlJQwWVnh1dNRGPhMB78HJJxpehU7xAMtpmrkSJJECiZTEu
+fBM0ke39ipCiDkRaFA0FQqnkds9LumT4q6NpSljBc1lm6GqOnh2ZoFimnAJmeuq
+wcqrqnwEK3hlD/nz2rQPUzHSluAj+xAu+JvmNIcnSMqrvGsAoIBAHPEbaBOvz1V
5UkEyxyidyW9fDu9gPFAYEaFkGYpDp7+zyzDEnPNRulHOh5gYQvWcTbx7gCnDqlA
VjdeS6+SVhPA0c8vAb/kUlXt9zYcmDBWDz/B/mFSvOqUor1eGObbfr2IqcaKD+7b
x7v2g5aB1up+uNak2KH9pUvrQCl1aDGbELrU8amknYBc25toPKeEw7HWr6VIf66e
76QuaX5z6VurBlheYjS36AXPLK76kvwOlUSEAbw/8AwJcwNHcmZCFYXt8qI/VB0M
yJ1Q4MzyR5VBorm3Ein0RQBOGTQSuKAblmwgvlXZnhSKy2XBDhaJlgmbP8iUPnT4
+2SPAEWIDrMCHQDGpe828eiBg56q0tQup2r3UpgiXevRNEuF0Gbw
-----END DSA PRIVATE KEY-----
Can you please tell me how resolve it, and make it works?
OpenSSH stopped supporting DSA (aka ssh-dss) by default over 5 years ago; see
https://security.stackexchange.com/questions/112802/why-openssh-deprecated-dsa-keys
https://security.stackexchange.com/questions/146379/does-ssh-support-dsa-with-2048-bit-keys
https://superuser.com/questions/1016989/ssh-dsa-keys-no-longer-work-
https://unix.stackexchange.com/questions/247612/ssh-keeps-skipping-my-pubkey-
You can reenable it on your client following instructions on Qs like those or the openssh website or documentation, but github won't accept it so you can't make that work. (You could set up your own server, and use it there.)
I don't know why some go code (library?) doesn't accept the 2048-bit publickey -- if you give a reference I could try to loook -- but (both) your privatekey files are incorrect, I'm guessing due to the structuring used in crypto/dsa shown at your link. The key is being marshalled (serialized) to ASN.1 as nested sequences, namely SEQUENCE { pub = SEQUENCE { params = SEQUENCE {p,q,g}, y }, x } which conceptually is a reasonable structure, but the PEM (or pseudo-PEM) type DSA PRIVATE KEY is de-facto defined by SSLeay-now-OpenSSL as using a single level: SEQUENCE { p,q,g, y, x } .
Why I allways get the "key is invalid" error in the simple code bellow.
I already tried to use different private keys with the same result. Please help me cause I have no more ideas what i am doing wrong.
package main
import (
"fmt"
"log"
"github.com/dgrijalva/jwt-go"
)
var key = `-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAuMVb3lrWKlmGIzTaJqtVJi2rPIy7/BkRKlAZ7Q1u0VlyOhzI
cXq6zAGsh31uWeJBJFKrZdwA6b2LD5vscnuilHi0nfQZA5l+meQT9LJ7STnfJ7f8
1CniBIhj5g6dOva9o/ljrLCmRSE4MjJRl3LkydvrHNokzicOAvieq4BYgHVJ2DC7
r7cSWrHeIiBIEBb1zAghc1OxtkFkxumva2gmywq0zB0VfzsYrtSpWT77qSNA+UEH
H+UCahefkun1uaBcOrEIPUh/j95N3rFTAdIAzFvDqLvLdQbmm/NT78O7izSGvJ1K
4rM2fSiecALSROZqOeJy3pf2v6Caqlp2gdDHUQIDAQABAoIBAEeOBrnhq7bS3KOd
wC3hhCQ442ubhOFoQ8GDK8clwJjKbvYaV3W69cQzkcEWzjl46YlLipzzyla61LPC
ypq7Tob5B9lzwowmUWT/csr8o8oD42vaUMtJPQJMX4OkfTdsfpyV5AfokTuMVdr6
qaZhFEEoLbEKud4sObzk023PUnbMTMyz2weQsmVLT9gm64K9GHzauoEDWGpr3Boz
+mEKo9rhySeK73qXqJFFATl0IB50GpdKondere/XofLQ2kqUB2fFEWIeRXawFd4u
6yE5nxjhIvn9pqMhW48vPAYtwTUhY8OxpCwk9U+awBYMvkCnFlrO4TwCNFqE8j3/
r6YN5VECgYEA2c0GqQVfwbcgB+8pLmd8H7dq+HqHToTA00pgNAsl/LRFUhkIVyY1
YUJkr7nJ3ogzqyjNBz0K4JeaMbMxJRwbxu4SUBgmvc+3h0oa9ibTKtBH4KWAqwCC
3TLQFoMXJCf+YJOL8ZNeqQz4rEpCVvJj86p9F4iTRhl5bd7mYqiEjusCgYEA2S1U
19iAIEiSETtUWEzj/iAKOUZJKl4Kz6PVfUMT1lqodOHsNHexzcQs+W/LW/Z9Jr7u
JtdC0ZZl2UCQ8PBTirsA/gimisnmVhx3mvKXOOO/Cf8RpN1/faTnQJj/Tu+h3aV7
n53Av2wSQPoWnnNB9W515gTYqwiV//CX44lGS7MCgYEAmjSoj4kniB8hBZ0WOi24
2zfg+/a80CH76F1TieWOysHUBtGEbze1OZxpb2WKgQ1MD9Y+e+6DQgr0eFXX6N9i
51DuFFlVLLThy17zge5xOnHnQi3L0Mb24Kg2XooIG2hZmYU94xelQOnXMx0MpUTO
8dl24e+n3kzxBZJ46cdIu2sCgYBzz5yizazlik16Ku07eSVLasKI8FYr5aJWP8Ok
3JRDhmy2h5NyFzIVzDs/eMI09Cig9MgCpl/XbCA7zhZ8pWunWzmYPfyxniDaYqvV
UPAbQjepmP9Lr2JBGiLHa88ZxOfITmqyH2mdqn/BbpuJO2U8//6W/pab/iQfK6mT
iKyXyQKBgGb49CT+zi1VCa4jh0EH78odTwRb/PleAw25CLVSj35MqQCEA2/jU92g
P8iTDU5mDNEaQMvMYN1fKiAPm6hiKp9/5Q02zQWNVzOywgc0L41Yb9K/SqxGwITa
dFwwxhKgBqzgCeIpvKLCGrhMztzPjUL9o4MSdNa92vajcilr8ld6
-----END RSA PRIVATE KEY-----`
func main() {
token := jwt.New(jwt.SigningMethodRS256)
jwt, err := token.SignedString(key)
if err != nil {
log.Println(err)
}
fmt.Println("jwt=", jwt)
}
the output:
2021/04/06 16:48:52 key is invalid
jwt=
what is wrong with my key?
I`ve generated it with:
ssh-keygen -t rsa -P "" -b 2048 -m PEM -f jwtRS256.key
ssh-keygen -e -m PEM -f jwtRS256.key > jwtRS256.key.pub
Thank you
The RSA family algorithms expect a key of type *rsa.PrivateKey. The library dgrijalva/jwt-go has a helper function jwt.ParseRSAPrivateKeyFromPEM(keyData) where keyData is a []byte slice.
The repo has some handy examples in rsa_test.go file.
Also the examples are outdated because as of Go 1.16 the ioutil package is deprecated...
Warning!
Please be aware that github.com/dgrijalva/jwt-go has been unmaintained for a long time and has critical unfixed bugs. And doesn't support Go modules, before the version 4 (which is just a preview anyway). I strongly recommend to choose an different library for dealing with JWT.
Update June 2021
There is now an official community fork of the library: golang-jwt/jwt blessed by the owner of the original project.
The correct code is:
func main() {
token := jwt.New(jwt.SigningMethodRS256)
pkey, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(key))
if err != nil {
log.Println(err)
}
jwt, err := token.SignedString(pkey)
if err != nil {
log.Println(err)
}
fmt.Println("jwt=", jwt)
}
Author of https://github.com/lestrrat-go/jwx here.
I just tried adding your case at the end of my test file so I'm omitting some imports, but your key seems to get parsed fine by jwx:
func TestSO66970208(t *testing.T) {
var src = `-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAuMVb3lrWKlmGIzTaJqtVJi2rPIy7/BkRKlAZ7Q1u0VlyOhzI
cXq6zAGsh31uWeJBJFKrZdwA6b2LD5vscnuilHi0nfQZA5l+meQT9LJ7STnfJ7f8
1CniBIhj5g6dOva9o/ljrLCmRSE4MjJRl3LkydvrHNokzicOAvieq4BYgHVJ2DC7
r7cSWrHeIiBIEBb1zAghc1OxtkFkxumva2gmywq0zB0VfzsYrtSpWT77qSNA+UEH
H+UCahefkun1uaBcOrEIPUh/j95N3rFTAdIAzFvDqLvLdQbmm/NT78O7izSGvJ1K
4rM2fSiecALSROZqOeJy3pf2v6Caqlp2gdDHUQIDAQABAoIBAEeOBrnhq7bS3KOd
wC3hhCQ442ubhOFoQ8GDK8clwJjKbvYaV3W69cQzkcEWzjl46YlLipzzyla61LPC
ypq7Tob5B9lzwowmUWT/csr8o8oD42vaUMtJPQJMX4OkfTdsfpyV5AfokTuMVdr6
qaZhFEEoLbEKud4sObzk023PUnbMTMyz2weQsmVLT9gm64K9GHzauoEDWGpr3Boz
+mEKo9rhySeK73qXqJFFATl0IB50GpdKondere/XofLQ2kqUB2fFEWIeRXawFd4u
6yE5nxjhIvn9pqMhW48vPAYtwTUhY8OxpCwk9U+awBYMvkCnFlrO4TwCNFqE8j3/
r6YN5VECgYEA2c0GqQVfwbcgB+8pLmd8H7dq+HqHToTA00pgNAsl/LRFUhkIVyY1
YUJkr7nJ3ogzqyjNBz0K4JeaMbMxJRwbxu4SUBgmvc+3h0oa9ibTKtBH4KWAqwCC
3TLQFoMXJCf+YJOL8ZNeqQz4rEpCVvJj86p9F4iTRhl5bd7mYqiEjusCgYEA2S1U
19iAIEiSETtUWEzj/iAKOUZJKl4Kz6PVfUMT1lqodOHsNHexzcQs+W/LW/Z9Jr7u
JtdC0ZZl2UCQ8PBTirsA/gimisnmVhx3mvKXOOO/Cf8RpN1/faTnQJj/Tu+h3aV7
n53Av2wSQPoWnnNB9W515gTYqwiV//CX44lGS7MCgYEAmjSoj4kniB8hBZ0WOi24
2zfg+/a80CH76F1TieWOysHUBtGEbze1OZxpb2WKgQ1MD9Y+e+6DQgr0eFXX6N9i
51DuFFlVLLThy17zge5xOnHnQi3L0Mb24Kg2XooIG2hZmYU94xelQOnXMx0MpUTO
8dl24e+n3kzxBZJ46cdIu2sCgYBzz5yizazlik16Ku07eSVLasKI8FYr5aJWP8Ok
3JRDhmy2h5NyFzIVzDs/eMI09Cig9MgCpl/XbCA7zhZ8pWunWzmYPfyxniDaYqvV
UPAbQjepmP9Lr2JBGiLHa88ZxOfITmqyH2mdqn/BbpuJO2U8//6W/pab/iQfK6mT
iKyXyQKBgGb49CT+zi1VCa4jh0EH78odTwRb/PleAw25CLVSj35MqQCEA2/jU92g
P8iTDU5mDNEaQMvMYN1fKiAPm6hiKp9/5Q02zQWNVzOywgc0L41Yb9K/SqxGwITa
dFwwxhKgBqzgCeIpvKLCGrhMztzPjUL9o4MSdNa92vajcilr8ld6
-----END RSA PRIVATE KEY-----`
key, err := jwk.ParseString(src, jwk.WithPEM(true))
if !assert.NoError(t, err, `jwk.ParseString should succeed`) {
return
}
buf, _ := json.MarshalIndent(key, "", " ")
t.Logf("%s", buf)
}
This produces:
lestrrat#finch jwk % go test -run TestSO66970208 -v
=== RUN TestSO66970208
jwk_test.go:1472: {
"keys": [
{
"d": "R44GueGrttLco53ALeGEJDjja5uE4WhDwYMrxyXAmMpu9hpXdbr1xDORwRbOOXjpiUuKnPPKVrrUs8LKmrtOhvkH2XPCjCZRZP9yyvyjygPja9pQy0k9Akxfg6R9N2x-nJXkB-iRO4xV2vqppmEUQSgtsQq53iw5vOTTbc9SdsxMzLPbB5CyZUtP2Cbrgr0YfNq6gQNYamvcGjP6YQqj2uHJJ4rvepeokUUBOXQgHnQal0qid16t79eh8tDaSpQHZ8URYh5FdrAV3i7rITmfGOEi-f2moyFbjy88Bi3BNSFjw7GkLCT1T5rAFgy-QKcWWs7hPAI0WoTyPf-vpg3lUQ",
"dp": "mjSoj4kniB8hBZ0WOi242zfg-_a80CH76F1TieWOysHUBtGEbze1OZxpb2WKgQ1MD9Y-e-6DQgr0eFXX6N9i51DuFFlVLLThy17zge5xOnHnQi3L0Mb24Kg2XooIG2hZmYU94xelQOnXMx0MpUTO8dl24e-n3kzxBZJ46cdIu2s",
"dq": "c8-cos2s5YpNeirtO3klS2rCiPBWK-WiVj_DpNyUQ4ZstoeTchcyFcw7P3jCNPQooPTIAqZf12wgO84WfKVrp1s5mD38sZ4g2mKr1VDwG0I3qZj_S69iQRoix2vPGcTnyE5qsh9pnap_wW6biTtlPP_-lv6Wm_4kHyupk4isl8k",
"e": "AQAB",
"kty": "RSA",
"n": "uMVb3lrWKlmGIzTaJqtVJi2rPIy7_BkRKlAZ7Q1u0VlyOhzIcXq6zAGsh31uWeJBJFKrZdwA6b2LD5vscnuilHi0nfQZA5l-meQT9LJ7STnfJ7f81CniBIhj5g6dOva9o_ljrLCmRSE4MjJRl3LkydvrHNokzicOAvieq4BYgHVJ2DC7r7cSWrHeIiBIEBb1zAghc1OxtkFkxumva2gmywq0zB0VfzsYrtSpWT77qSNA-UEHH-UCahefkun1uaBcOrEIPUh_j95N3rFTAdIAzFvDqLvLdQbmm_NT78O7izSGvJ1K4rM2fSiecALSROZqOeJy3pf2v6Caqlp2gdDHUQ",
"p": "2c0GqQVfwbcgB-8pLmd8H7dq-HqHToTA00pgNAsl_LRFUhkIVyY1YUJkr7nJ3ogzqyjNBz0K4JeaMbMxJRwbxu4SUBgmvc-3h0oa9ibTKtBH4KWAqwCC3TLQFoMXJCf-YJOL8ZNeqQz4rEpCVvJj86p9F4iTRhl5bd7mYqiEjus",
"q": "2S1U19iAIEiSETtUWEzj_iAKOUZJKl4Kz6PVfUMT1lqodOHsNHexzcQs-W_LW_Z9Jr7uJtdC0ZZl2UCQ8PBTirsA_gimisnmVhx3mvKXOOO_Cf8RpN1_faTnQJj_Tu-h3aV7n53Av2wSQPoWnnNB9W515gTYqwiV__CX44lGS7M",
"qi": "Zvj0JP7OLVUJriOHQQfvyh1PBFv8-V4DDbkItVKPfkypAIQDb-NT3aA_yJMNTmYM0RpAy8xg3V8qIA-bqGIqn3_lDTbNBY1XM7LCBzQvjVhv0r9KrEbAhNp0XDDGEqAGrOAJ4im8osIauEzO3M-NQv2jgxJ01r3a9qNyKWvyV3o"
}
]
}
--- PASS: TestSO66970208 (0.00s)
PASS
ok github.com/lestrrat-go/jwx/jwk 0.198s
I have public key stored in a variable of type interface{}
-----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDdlatRjRjogo3WojgGHFHYLugdUWAY9iR3fy4arWNA1KoS8kVw33cJibXr8bvwUAUparCwlvdbH6dvEOfou0/gCFQsHUfQrSDv+MuSUMAe8jzKE4qW+jK+xQU9a03GUnKHkkle+Q0pX/g6jXZ7r1/xAK5Do2kQ+X5xK9cipRgEKwIDAQAB
-----END PUBLIC KEY-----
Is there anyway I can convert this into type *rsa.PublicKey in golang. It might involve some intermediate step like converting interface{} to string first may be.
In general you would decode a pem file (assuming this is in a x509) cert using the crypto/x509 library. The provided example in the documentation is as follows:
package main
import (
"crypto/dsa"
"crypto/ecdsa"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
)
func main() {
const pubPEM = `
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAlRuRnThUjU8/prwYxbty
WPT9pURI3lbsKMiB6Fn/VHOKE13p4D8xgOCADpdRagdT6n4etr9atzDKUSvpMtR3
CP5noNc97WiNCggBjVWhs7szEe8ugyqF23XwpHQ6uV1LKH50m92MbOWfCtjU9p/x
qhNpQQ1AZhqNy5Gevap5k8XzRmjSldNAFZMY7Yv3Gi+nyCwGwpVtBUwhuLzgNFK/
yDtw2WcWmUU7NuC8Q6MWvPebxVtCfVp/iQU6q60yyt6aGOBkhAX0LpKAEhKidixY
nP9PNVBvxgu3XZ4P36gZV6+ummKdBVnc3NqwBLu5+CcdRdusmHPHd5pHf4/38Z3/
6qU2a/fPvWzceVTEgZ47QjFMTCTmCwNt29cvi7zZeQzjtwQgn4ipN9NibRH/Ax/q
TbIzHfrJ1xa2RteWSdFjwtxi9C20HUkjXSeI4YlzQMH0fPX6KCE7aVePTOnB69I/
a9/q96DiXZajwlpq3wFctrs1oXqBp5DVrCIj8hU2wNgB7LtQ1mCtsYz//heai0K9
PhE4X6hiE0YmeAZjR0uHl8M/5aW9xCoJ72+12kKpWAa0SFRWLy6FejNYCYpkupVJ
yecLk/4L1W0l6jQQZnWErXZYe0PNFcmwGXy1Rep83kfBRNKRy5tvocalLlwXLdUk
AIU+2GKjyT3iMuzZxxFxPFMCAwEAAQ==
-----END PUBLIC KEY-----`
block, _ := pem.Decode([]byte(pubPEM))
if block == nil {
panic("failed to parse PEM block containing the public key")
}
pub, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
panic("failed to parse DER encoded public key: " + err.Error())
}
switch pub := pub.(type) {
case *rsa.PublicKey:
fmt.Println("pub is of type RSA:", pub)
case *dsa.PublicKey:
fmt.Println("pub is of type DSA:", pub)
case *ecdsa.PublicKey:
fmt.Println("pub is of type ECDSA:", pub)
default:
panic("unknown type of public key")
}
}
I'm not really sure about the encoding you have there though. The following might help you past that hurdle:
RSA Public Key format
you can use:
rsaPublickey, _ := pub.(*rsa.PublicKey)
to convert to rsa.PublicKey
I'm trying out JWT (JSON Web Tokens) in a Go web service. Here's what I've done so far:
package jwt
import(
"fmt"
"net/http"
"github.com/gorilla/mux"
"github.com/dgrijalva/jwt-go"
"io/ioutil"
)
var privateKey []byte
var publicKey []byte
func JSONWebTokensHandler(w http.ResponseWriter, r * http.Request){
// Create the token
encodeToken := jwt.New(jwt.SigningMethodHS256)
// Set some claims
encodeToken.Claims["Latitude"] = "25.000"
encodeToken.Claims["Longitude"] = "27.000"
// Sign and get the complete encoded token as a string
tokenString, err := encodeToken.SignedString(privateKey)
decodeToken, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
}
return publicKey,nil
})
if decodeToken.Valid {
fmt.Fprintf(w,"Lat: %s, Lng: %s",decodeToken.Claims["Latitude"],decodeToken.Claims["Longitude"])
} else {
fmt.Fprintf(w,"Couldn't handle this token: %s", err)
}
}
func init(){
privateKey,_ = ioutil.ReadFile("demo.rsa")
publicKey,_ = ioutil.ReadFile("demo.rsa.pub")
r := mux.NewRouter()
r.HandleFunc("/jwt",JSONWebTokensHandler).Methods("GET")
http.Handle("/", r)
}
Now if my understanding is correct, A token that is encoded using a private key can be decoded using the public key. That is what I've presumed in the code above however when I run the code I get the error:
Couldn't handle this token: signature is invalid
If I use the same key for encoding and decoding, then the code works.
What I'd like to know is, is there something wrong with my understanding or in the code?
The JWT isn't signed using an asymmetric cipher like RSA. It uses a HMAC, which uses a single, secret key. Indeed, the point here is not to prove to someone else that you signed the token. It's to prove to yourself that you signed it, and thus forbid anyone who doesn't have your secret key to modify the token.
You are using jwt.SigningMethodHMAC. Therefore, you are signing using HMAC, the signature is the token ciphered by a symmetric key (secret).
You shall use: jwt.New(jwt.SigningMethodRS256) to sign with an asymmetric key-pair.
Very interesting, since I have a similar problem, when I have micro service and a client app that need to verify the token that comes from another internal server, so if you advice using HMAC over RSA them that means that I need to put the private key in both micro service and Client App? that wouldn't be a serious security hole?