Go: get value of 'subject/unstructeredName' from a ssl certificate - go

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)
}

Related

How to generate the ECDSA public key from its private key?

The following site is frequently referenced and, I assume, accurate:
https://gobittest.appspot.com/Address
I'm trying to repro these steps in Golang but failing at the first step :-(
Is someone able to provide me with a Golang snippet that, given a ECDSA private key, returns the public key? I think I may specifically mean the private key exponent and public key exponent per the above site's examples.
i.e. given e.g. a randomly-generated (hex-encoded) private key (exponent?) E83385AF76B2B1997326B567461FB73DD9C27EAB9E1E86D26779F4650C5F2B75 returns the public key 04369D83469A66920F31E4CF3BD92CB0BC20C6E88CE010DFA43E5F08BC49D11DA87970D4703B3ADBC9A140B4AD03A0797A6DE2D377C80C369FE76A0F45A7A39D3F
I've found many (relevant) results:
https://crypto.stackexchange.com/questions/5756/how-to-generate-a-public-key-from-a-private-ecdsa-key
But none that includes a definitive example.
Go's crypto/ecdsa module allows keys to generated and includes a Public function on the type but this returns the PublicKey property.
Alternative ways that start from a private key appear to require going through a PEM-encoded (including a DER-encoded ASN) form of the key which feels circuitous (and I would need to construct).
Update:
See the answers below: andrew-w-phillips# and kelsnare# provided the (same|correct) solution. Thanks to both of them!
For posterity, Bitcoin (and Ethereum) use an elliptic curve defined by secp256k1. The following code from andrew-w-phillips# and kelsnare# using Ethereum's implementation of this curve, works:
import (
"crypto/ecdsa"
"crypto/elliptic"
"fmt"
"math/big"
"strings"
"github.com/ethereum/go-ethereum/crypto/secp256k1"
)
func Public(privateKey string) (publicKey string) {
var e ecdsa.PrivateKey
e.D, _ = new(big.Int).SetString(privateKey, 16)
e.PublicKey.Curve = secp256k1.S256()
e.PublicKey.X, e.PublicKey.Y = e.PublicKey.Curve.ScalarBaseMult(e.D.Bytes())
return fmt.Sprintf("%x", elliptic.Marshal(secp256k1.S256(), e.X, e.Y))
}
func main() {
privateKey := "E83385AF76B2B1997326B567461FB73DD9C27EAB9E1E86D26779F4650C5F2B75"
log.Println(strings.ToUpper(Public(privateKey)))
}
yields:
04369D83469A66920F31E4CF3BD92CB0BC20C6E88CE010DFA43E5F08BC49D11DA87970D4703B3ADBC9A140B4AD03A0797A6DE2D377C80C369FE76A0F45A7A39D3F
After reading the answer by Andrew W. Phillips and a little help from https://github.com/bitherhq/go-bither/tree/release/1.7/crypto
package main
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"fmt"
"log"
"math/big"
)
func PubBytes(pub *ecdsa.PublicKey) []byte {
if pub == nil || pub.X == nil || pub.Y == nil {
return nil
}
return elliptic.Marshal(elliptic.P256(), pub.X, pub.Y)
}
func toECDSAFromHex(hexString string) (*ecdsa.PrivateKey, error) {
pk := new(ecdsa.PrivateKey)
pk.D, _ = new(big.Int).SetString(hexString, 16)
pk.PublicKey.Curve = elliptic.P256()
pk.PublicKey.X, pk.PublicKey.Y = pk.PublicKey.Curve.ScalarBaseMult(pk.D.Bytes())
return pk, nil
}
func main() {
pHex := "E83385AF76B2B1997326B567461FB73DD9C27EAB9E1E86D26779F4650C5F2B75"
pk, err := toECDSAFromHex(pHex)
if err != nil {
log.Fatal(err.Error())
}
fmt.Printf("Generated Public Key: %x\n", PubBytes(&pk.PublicKey))
hash := []byte("Hello Gopher!")
fmt.Printf("\nSigning...\n\n")
r, s, err := ecdsa.Sign(rand.Reader, pk, hash)
if err != nil {
log.Fatal(err.Error())
}
fmt.Printf("\nVerifying..\n\n")
if ecdsa.Verify(&pk.PublicKey, hash, r, s) {
fmt.Println("Success!!")
} else {
fmt.Println("Failure!!")
}
}
// Output
// Generated Public Key: 04265a5015c0cfd960e5a41f35e0a87874c1d8a28289d0d6ef6ac521ad49c3d80a8a7019ceef189819f066a947ad5726db1a4fe70a3208954c46b0e60f2bf7809c
//
// Signing...
//
//
// Verifying..
//
// Success!!
======== ORIGINAL ANSWER ===========
Dunno much of crypto but crypto/elliptic has a Marshal function
So once you have a *PrivateKey maybe the below will work
import (
"crypto/elliptic"
"crypto/ecdsa"
)
var privKey *ecdsa.PrivateKey
func main() {
// some init to privKey
pk := privKey.PublicKey()
keybuf := elliptic.Marshal(pk.Curve, pk.X, pk.Y)
log.Printf("Key: %s\n", string(keybuf))
}
I'm shooting completely in the dark with this. Hope it helps
I haven't got that low-level but maybe something like this:
var pri ecdsa.PrivateKey
pri.D, _ = new(big.Int).SetString("E83385AF76B2B1997326B567461FB73DD9C27EAB9E1E86D26779F4650C5F2B75",16)
pri.PublicKey.Curve = elliptic.P256()
pri.PublicKey.X, pri.PublicKey.Y = pri.PublicKey.Curve.ScalarBaseMult(pri.D.Bytes())

Failed to find any PEM data in key

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
}

How to create PKCS#8 Private Key using Go [duplicate]

This question already has answers here:
golang - marshal PKCS8 private key?
(2 answers)
Closed 5 years ago.
I want to generate tls certificates.
So first I need a Private Key.
-----BEGIN PRIVATE KEY-----
BASE64 ENCODED DATA
-----END PRIVATE KEY-----
But, I can generate RSA Private Key
// "crypto/rsa"
rsa.GenerateKey(cryptorand.Reader, 2048)
which gives me following
-----BEGIN RSA PRIVATE KEY-----
BASE64 ENCODED DATA
-----END RSA PRIVATE KEY-----
And I can't use it. I need PKCS#8 Private Key start with BEGIN PRIVATE KEY
How can I generate PKCS#8 Private Key?
Or Is there any way to convert PKCS#1 to PKCS#8 Key?
I don't know how exactly you get literal -----BEGIN RSA PRIVATE KEY-----
Go stdlib crypto do not has PKCS#8 marshaller but there is generic asn1.Marchal() in encoding/asn1 and PKCS#8 standard(rfc5208) provide ASN#1 syntax definition, so you can do something like:
import (
"crypto/rsa"
"crypto/x509"
"encoding/asn1"
)
type Pkcs8Key struct {
Version int
PrivateKeyAlgorithm []asn1.ObjectIdentifier
PrivateKey []byte
}
func RsaToPkcs8(key *rsa.PrivateKey) ([]byte, error) {
var pkey Pkcs8Key
pkey.Version = 0
pkey.PrivateKeyAlgorithm = make([]asn1.ObjectIdentifier, 1)
pkey.PrivateKeyAlgorithm[0] = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
pkey.PrivateKey = x509.MarshalPKCS1PrivateKey(key)
return asn1.Marshal(pkey)
}
Here's a solution based on the custom PKCS#8 marshal function in this answer:
package main
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/asn1"
"encoding/pem"
"fmt"
)
type PKCS8Key struct {
Version int
PrivateKeyAlgorithm []asn1.ObjectIdentifier
PrivateKey []byte
}
func MarshalPKCS8PrivateKey(key *rsa.PrivateKey) ([]byte, error) {
var pkey PKCS8Key
pkey.Version = 0
pkey.PrivateKeyAlgorithm = make([]asn1.ObjectIdentifier, 1)
pkey.PrivateKeyAlgorithm[0] = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
pkey.PrivateKey = x509.MarshalPKCS1PrivateKey(key)
return asn1.Marshal(pkey)
}
func main() {
// Generate the private key.
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
fatal(err)
// Encode the private key into PEM data.
bytes, err := MarshalPKCS8PrivateKey(privateKey)
fatal(err)
privatePem := pem.EncodeToMemory(
&pem.Block{
Type: "PRIVATE KEY",
Bytes: bytes,
},
)
fmt.Printf("%s\n", privatePem)
// -----BEGIN PRIVATE KEY-----
// MIIEvAIBADALBgkqhkiG9w0BAQEEggSoMIIEpAIBAAKCAQEAz5xD5cdqdE0PMmk1
// 4YN6Tj0ybTsvS5C95ogQmBJ4bGxiuGPR5JtIc+UmT8bnCHtK5xnHiP+gPWunwmhS
// ...
// qpb1NZsMLz2lRXqx+3Pq7Q==
// -----END PRIVATE KEY-----
}
func fatal(err error) {
if err != nil {
panic(err)
}
}

How to convert a interface{} into type *rsa.PublicKey golang

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

About VerifyPKCS1v15 (base64 and rsa check )

Given "notify" ,"publicKey" and "sign" , it doesnt pass the VerifyPKCS1v15 in go . That 's mycode , is there something wrong ?
package main
import (
"crypto"
"crypto/sha1"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"fmt"
)
func main() {
notify := `YFSGlJTpNYakrZuZqZ55dcA5mVUb/JQBr3hdDjODsAVSdoVVytIagk9Wt0CD/uX+7jGL9pqev8/u0I0ZBKEmz5huXp8TdZSnskCZ7GTeHNW0VPJcW8OcBxAValA0jQSv2mBP+tc1r6mdvf66GEzhvgBfTnp3Sp7V3dijJ9bNstIDyrGm/BlByhcMr3UqXjTFJaui6t5TxvZhCuSV9sg+xVVA+sR3uFI78b5lKomg5Vu31EBZvXASlFfaOc4StltRUH2aSiRqjnbXe8dlRZO0Ih44htYs2QfehzeQnPHtTwNHUvtVIVcIdI/7j9yfy5es13QeIgfKghY/ENUnB2V7iA==`
sign := `s8XIN2TyC5niX1HFPDXOQj2eRvhW2qMPOdDuuXlOspYhxkjxunV4Ytgcw8GXg761HSbk4e5QsgKpU+vM2ggLhYni2GfXhGBVj/P13B6JhMmdrucU8ktlaH+fJGUmc3rqGMU3qiQgNAh/8PV1BS/5li7qzXHc0tgKL1zRgeu1CVw=`
notifyData, err := base64.StdEncoding.DecodeString(notify)
if err != nil {
fmt.Println("error1:", err)
return
}
signData, err := base64.StdEncoding.DecodeString(sign)
if err != nil {
fmt.Println("error2:", err)
return
}
publicKey := []byte(`-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC2kcrRvxURhFijDoPpqZ/IgPlA
gppkKrek6wSrua1zBiGTwHI2f+YCa5vC1JEiIi9uw4srS0OSCB6kY3bP2DGJagBo
Egj/rYAGjtYJxJrEiTxVs5/GfPuQBYmU0XAtPXFzciZy446VPJLHMPnmTALmIOR5
Dddd1Zklod9IQBMjjwIDAQAB
-----END PUBLIC KEY-----
`)
block, _ := pem.Decode(publicKey)
if block == nil {
fmt.Println("pem error :")
return
}
public, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
fmt.Println("public key error :", err)
return
}
pub := public.(*rsa.PublicKey)
fmt.Println(pub.N)
h := sha1.New()
h.Write([]byte(notifyData))
digest := h.Sum(nil)
err = rsa.VerifyPKCS1v15(pub, crypto.SHA1, digest, signData)
if err == nil {
fmt.Println("OK")
} else {
fmt.Println("verify fail", err)
}
}
P.S.
This is php code, and it'll pass with the same data.
<?php
$pubKey = "-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC2kcrRvxURhFijDoPpqZ/IgPlA
gppkKrek6wSrua1zBiGTwHI2f+YCa5vC1JEiIi9uw4srS0OSCB6kY3bP2DGJagBo
Egj/rYAGjtYJxJrEiTxVs5/GfPuQBYmU0XAtPXFzciZy446VPJLHMPnmTALmIOR5
Dddd1Zklod9IQBMjjwIDAQAB
-----END PUBLIC KEY-----";
$pubRes = openssl_get_publickey($pubKey);
//通知数据
$notify_data = "YFSGlJTpNYakrZuZqZ55dcA5mVUb/JQBr3hdDjODsAVSdoVVytIagk9Wt0CD/uX+7jGL9pqev8/u0I0ZBKEmz5huXp8TdZSnskCZ7GTeHNW0VPJcW8OcBxAValA0jQSv2mBP+tc1r6mdvf66GEzhvgBfTnp3Sp7V3dijJ9bNstIDyrGm/BlByhcMr3UqXjTFJaui6t5TxvZhCuSV9sg+xVVA+sR3uFI78b5lKomg5Vu31EBZvXASlFfaOc4StltRUH2aSiRqjnbXe8dlRZO0Ih44htYs2QfehzeQnPHtTwNHUvtVIVcIdI/7j9yfy5es13QeIgfKghY/ENUnB2V7iA==";
//签名
$sign = "s8XIN2TyC5niX1HFPDXOQj2eRvhW2qMPOdDuuXlOspYhxkjxunV4Ytgcw8GXg761HSbk4e5QsgKpU+vM2ggLhYni2GfXhGBVj/P13B6JhMmdrucU8ktlaH+fJGUmc3rqGMU3qiQgNAh/8PV1BS/5li7qzXHc0tgKL1zRgeu1CVw=";
$data = base64_decode($notify_data);
$maxlength = 128;
$output = '';
while ($data) {
$input = substr($data, 0, $maxlength);
$data = substr($data, $maxlength);
openssl_public_decrypt($input, $out, $pubRes, OPENSSL_PKCS1_PADDING);
$output .= $out;
}
if (openssl_verify($output, base64_decode($sign), $pubRes)) {
echo "success";
}else{
echo "fail";
}
?>
You appear to have several different problems in your code.
There is no need to truncate the data to 128 characters in your PHP code as you are not doing the same in your go code. This discrepancy will cause the bytes to be different and therefore the computed signatures to be different.
You are using the openssl_public_decrypt function to sign the data. While this does work in theory it's error prone. You are also using a public-key to sign the data, which is wrong - only private-keys can sign. It's much better to use PHP's openssl_sign function.
Another source of error could be your signing code that uses the private key, which is not shown here.
PHP's and Go's public key cryptography should be entirely compatible. To test this I've created the following identical signing scripts in both PHP and Go.
<?php
$data = "TEST DATA TO COMPUTE";
$privKeyPEM = "-----BEGIN RSA PRIVATE KEY-----
MIIBOgIBAAJBAK3ADijXKw72+YbC5QKK2y7IosCp7rWOhTf8Ph07ZA0KjdbKtfL/
7dmNKjSP6EkC/DJUWfZJNLIlGOtDLLA/AnsCAwEAAQJAQj9kJrZDuKT6ZyOQZfPD
tobRZ1xjo93/dWU72bF3aHDo4ILMy2Kigy5yhZU0ZGjOuPv5eUOLRe/yxYQf6B5J
AQIhANbhfZ4QJC8dLXAqcsxOXuLgztzbKixUre0gnhiVSd1hAiEAzv+sHJ4PMjKs
Iuf6/nUI9XFgQQRd+NGRovyHRZC18VsCIAX7AKQFjvxAs6MLi2ZkR//IgfljoCjb
snuHDN9iSEwBAiEAmAc1XCtGE+Mdg+GG+T3xn3pubDIN5oHcia0YmKIIzsMCIEy1
fWM5cIJ9bAUExKB6MV8PF+9EjDvXzbSk1/Ycta8z
-----END RSA PRIVATE KEY-----";
// Parse private key
$privkey = openssl_pkey_get_private($privKeyPEM);
if (!$privkey) {
exit("Could not parse private key");
}
// Compute the signature
$signature = '';
$ok = openssl_sign($data, $signature, $privkey, OPENSSL_ALGO_SHA1); //SHA1 of $data is computed automatically by this function
if (!$ok) {
exit("Could not compute signature");
}
// Print the output
print base64_encode($signature);
And the same thing in Go:
package main
import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"fmt"
"log"
)
const (
data = "TEST DATA TO COMPUTE"
privKeyPEM = `-----BEGIN RSA PRIVATE KEY-----
MIIBOgIBAAJBAK3ADijXKw72+YbC5QKK2y7IosCp7rWOhTf8Ph07ZA0KjdbKtfL/
7dmNKjSP6EkC/DJUWfZJNLIlGOtDLLA/AnsCAwEAAQJAQj9kJrZDuKT6ZyOQZfPD
tobRZ1xjo93/dWU72bF3aHDo4ILMy2Kigy5yhZU0ZGjOuPv5eUOLRe/yxYQf6B5J
AQIhANbhfZ4QJC8dLXAqcsxOXuLgztzbKixUre0gnhiVSd1hAiEAzv+sHJ4PMjKs
Iuf6/nUI9XFgQQRd+NGRovyHRZC18VsCIAX7AKQFjvxAs6MLi2ZkR//IgfljoCjb
snuHDN9iSEwBAiEAmAc1XCtGE+Mdg+GG+T3xn3pubDIN5oHcia0YmKIIzsMCIEy1
fWM5cIJ9bAUExKB6MV8PF+9EjDvXzbSk1/Ycta8z
-----END RSA PRIVATE KEY-----`
)
func main() {
// Parse private key into rsa.PrivateKey
PEMBlock, _ := pem.Decode([]byte(privKeyPEM))
if PEMBlock == nil {
log.Fatal("Could not parse Private Key PEM")
}
if PEMBlock.Type != "RSA PRIVATE KEY" {
log.Fatal("Found wrong key type")
}
privkey, err := x509.ParsePKCS1PrivateKey(PEMBlock.Bytes)
if err != nil {
log.Fatal(err)
}
// Compute the sha1
h := sha1.New()
h.Write([]byte(data))
// Sign the data
signature, err := rsa.SignPKCS1v15(rand.Reader, privkey, crypto.SHA1, h.Sum(nil))
if err != nil {
log.Fatal(err)
}
// Print the results
fmt.Print(base64.StdEncoding.EncodeToString(signature))
}
You can verify that these do, indeed, produce the same output and sign the same data in the same way.
We can also use both PHP and Go to verify the signatures. Here are a set of PHP and Go scripts that will both read a signature from standard input and verify it.
<?php
$data = "TEST DATA TO COMPUTE";
$pubKeyPEM = "-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAK3ADijXKw72+YbC5QKK2y7IosCp7rWO
hTf8Ph07ZA0KjdbKtfL/7dmNKjSP6EkC/DJUWfZJNLIlGOtDLLA/AnsCAwEAAQ==
-----END PUBLIC KEY-----";
// Parse public key
$pubkey = openssl_pkey_get_public($pubKeyPEM);
if (!$pubkey) {
exit("Could not parse public key");
}
// Read the signature from stdin
$stdin = file_get_contents("php://stdin");
$signature = base64_decode($stdin);
// Verify the signature
$ok = openssl_verify($data, $signature, $pubkey, OPENSSL_ALGO_SHA1); //SHA1 of $data is computed automatically by this function
if ($ok == 1) {
print "OK\n"; // it worked!
exit(0);
}
else if ($ok == 0) {
exit("Signature verification failed");
}
else {
exit("Error verifying signature");
}
And the same verification code in Go:
package main
import (
"crypto"
"crypto/rsa"
"crypto/sha1"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"fmt"
"io/ioutil"
"log"
"os"
)
const (
data = "TEST DATA TO COMPUTE"
pubKeyPEM = `-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAK3ADijXKw72+YbC5QKK2y7IosCp7rWO
hTf8Ph07ZA0KjdbKtfL/7dmNKjSP6EkC/DJUWfZJNLIlGOtDLLA/AnsCAwEAAQ==
-----END PUBLIC KEY-----`
)
func main() {
// Parse public key into rsa.PublicKey
PEMBlock, _ := pem.Decode([]byte(pubKeyPEM))
if PEMBlock == nil {
log.Fatal("Could not parse Public Key PEM")
}
if PEMBlock.Type != "PUBLIC KEY" {
log.Fatal("Found wrong key type")
}
pubkey, err := x509.ParsePKIXPublicKey(PEMBlock.Bytes)
if err != nil {
log.Fatal(err)
}
// compute the sha1
h := sha1.New()
h.Write([]byte(data))
// Read the signature from stdin
b64 := base64.NewDecoder(base64.StdEncoding, os.Stdin)
signature, err := ioutil.ReadAll(b64)
if err != nil {
log.Fatal(err)
}
// Verify
err = rsa.VerifyPKCS1v15(pubkey.(*rsa.PublicKey), crypto.SHA1, h.Sum(nil), signature)
if err != nil {
log.Fatal(err)
}
// It verified!
fmt.Println("OK")
}
We can mix and match these different scripts together and verify that PHP and Go are indeed fully compatible:
$ go run go-sign.go | go run go-verify.go
OK
$ go run go-sign.go | php php-verify.php
OK
$ php php-sign.php | php php-verify.php
OK
$ php php-sign.php | go run go-verify.go
OK

Resources