I have an rsa.PublicKey which consists of a modulus and a public exponenet in a struct. I want to verify an ssh.Signature which is signed with that key, and I figure if I have an ssh.PublicKey I can call the Verify method on that interface. However, I can't find any classes that implement ssh.PublicKey and support converting from rsa.PublicKey. Do I need to write a proprietary method to do this, or is there some class I'm not finding, or a better way to go about solving my problem?
For context, I got the rsa.PublicKey from an x509.Certificate which came from a yubikey attesting a key in one of its PIV slots.
The NewPublicKey function from the crypto/ssh package http://godoc.org/golang.org/x/crypto/ssh#NewPublicKey can take an *rsa.PublicKey as parameter and returns an instance of the PublicKey interface that includes a Verify method (Verify(data []byte, sig *Signature) error) - http://godoc.org/golang.org/x/crypto/ssh#PublicKey.
The program below illustrates it - we create a new *rsa.PrivateKey and sign a message with it, then convert *rsa.PublicKey to ssh.PublicKey using NewPublicKey function and verify the signature; also check that the signature verification fails if the original data is modified. The error checking is elided for brevity.
package main
import (
"crypto/rand"
"crypto/rsa"
"fmt"
"log"
"golang.org/x/crypto/ssh"
)
func main() {
data := []byte("Hello, world!")
// create a new key as *rsa.PrivateKey
priv, _ := rsa.GenerateKey(rand.Reader, 512)
signer, _ := ssh.NewSignerFromKey(priv)
sig, _ := signer.Sign(rand.Reader, data)
// extract the ssh.PublicKey from *rsa.PublicKey to verify the signature
pub, _ := ssh.NewPublicKey(&priv.PublicKey)
if err := pub.Verify(data, sig); err != nil {
log.Fatalf("publicKey.Verify failed: %v", err)
}
fmt.Printf("Signature OK\n")
// modify the data and make sure we get a failure
data[0]++
if err := pub.Verify(data, sig); err == nil {
log.Printf("publicKey.Verify passed on bad data/signature, expect failure")
}
}
Output:
Signature OK
Related
I use a function that requires a filename as a parameter (of type string). It works fine when providing the filename.
I would like to embed this file in my binary. I can then have the contents as []byte or string but that's not useful. I can also fave it as embed.FS but my understanding is that this is an abstraction that can be used by some functions only.
What I would need is the ability to present this embedded file as a filename (a string) that would then be used by the underlying function to open the (embedded) file.
Is this possible?
Filename that Key accept as string argument is only abstraction on ioutil.ReadFile, see auth.go
What you can do is implement ssh.Auth yourself, here is small example.
package main
import (
_ "embed"
"fmt"
"github.com/melbahja/goph"
"golang.org/x/crypto/ssh"
)
//go:embed id_rsa
var privateKey []byte
func main() {
auth, err := Auth(privateKey, []byte("foobar"))
fmt.Println(auth, err)
}
func Auth(privateKey, pass []byte) (goph.Auth, error) {
signer, err := Singer(privateKey, pass)
if err != nil {
return nil, err
}
return goph.Auth{
ssh.PublicKeys(signer),
}, nil
}
func Singer(privateKey, pass []byte) (ssh.Signer, error) {
if len(pass) != 0 {
return ssh.ParsePrivateKeyWithPassphrase(privateKey, pass)
}
return ssh.ParsePrivateKey(privateKey)
}
If I omit the attribute NotAfter during initialisation of an x509.Certificate, it results in the expiry date being set to "0001-01-01", thus making the certificate expired.
In PHP + phpseclib, I can skip setting this field, making the issued certificate never expire. Is it possible to do this in Go?
RFC 5280 states:
4.1.2.5. Validity To indicate that a certificate has no well-defined expiration date, the notAfter SHOULD be assigned the GeneralizedTime value of 99991231235959Z.
How can I do this in Go?
TL;DR: Neither NotBefore nor NotAfter is optional. You must be mistaken about your Python PHP script. I would assume it uses some kind of default value for NotAfter if you don't specify it.
crypto/x509 defines the Validity as follows:
type validity struct {
NotBefore, NotAfter time.Time
}
Since neither field is a pointer, they cannot be undefined.
If you want to force the issue, you can use a modified copy of the x509 types to encode a certificate without the NotAfter field, but the result is unusable. Playground link
package main
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/pem"
"log"
"math/big"
"os"
"time"
)
// copy of x509.certificate, with TBSCertificate.Validity.NotAfter omitted
type certificate struct {
TBSCertificate struct {
Version int `asn1:"optional,explicit,default:0,tag:0"`
SerialNumber *big.Int
SignatureAlgorithm pkix.AlgorithmIdentifier
Issuer asn1.RawValue
Validity struct {
NotBefore time.Time
}
Subject asn1.RawValue
PublicKey struct {
Raw asn1.RawContent
Algorithm pkix.AlgorithmIdentifier
PublicKey asn1.BitString
}
UniqueId asn1.BitString `asn1:"optional,tag:1"`
SubjectUniqueId asn1.BitString `asn1:"optional,tag:2"`
Extensions []pkix.Extension `asn1:"optional,explicit,tag:3"`
}
SignatureAlgorithm pkix.AlgorithmIdentifier
SignatureValue asn1.BitString
}
func main() {
derBytes := generateSelfSigned()
// re-marshal to remove the NotAfter field
var c certificate
_, err := asn1.Unmarshal(derBytes, &c)
check(err)
derBytes, err = asn1.Marshal(c)
check(err)
pem.Encode(os.Stdout, &pem.Block{
Type: "CERTIFICATE",
Bytes: derBytes,
})
}
func generateSelfSigned() (derBytes []byte) {
pk, err := ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
check(err)
tpl := &x509.Certificate{SerialNumber: big.NewInt(1)}
derBytes, err = x509.CreateCertificate(rand.Reader, tpl, tpl, &pk.PublicKey, pk)
check(err)
return derBytes
}
func check(err error) {
if err != nil {
log.Fatal(err)
}
}
If you hand one of these certificates to openssl, for instance, parsing fails as expected with
unable to load certificate
139990566643520:error:0D078079:asn1 encoding routines:asn1_item_embed_d2i:field missing:crypto/asn1/tasn_dec.c:389:Field=notAfter, Type=X509_VAL
I am trying to parse out a specific field from a json string, and currently I have the following code snippet.
package main
import (
"encoding/json"
"fmt"
)
type PlatformID string
type Map map[string]interface{}
func Str2Map(str string) (Map, error) {
var dictionary Map
bytes := []byte(str)
err := json.Unmarshal(bytes, &dictionary)
if err != nil {
fmt.Println(err)
}
return dictionary, err
}
func parsePlatformID(str string) (PlatformID, error) {
fmt.Println(str)
dict, err := Str2Map(str)
fmt.Println(dict)
return dict["platform-id"].(PlatformID), err
}
func main() {
PlatformDictStr := "{\"platform-id\":\"platform_BnjliXLEUV26\",\"platform-labels\":\"test\",\"OptimizeScheme\":\"None\"}"
fmt.Println(PlatformDictStr)
ID, _ := parsePlatformID(PlatformDictStr)
fmt.Println(ID)
}
When I try to run it, it gives me the following error
{"platform-id":"platform_BnjliXLEUV26","platform-labels":"test","OptimizeScheme":"None"}
{"platform-id":"platform_BnjliXLEUV26","platform-labels":"test","OptimizeScheme":"None"}
map[platform-id:platform_BnjliXLEUV26 platform-labels:test OptimizeScheme:None]
panic: interface conversion: interface is string, not main.PlatformID
goroutine 1 [running]:
panic(0x126aa0, 0x10532300)
/usr/local/go/src/runtime/panic.go:500 +0x720
main.parsePlatformID(0x13e6fe, 0x58, 0x0, 0x0, 0x0, 0x0)
/tmp/sandbox256874711/main.go:26 +0x220
main.main()
/tmp/sandbox256874711/main.go:34 +0x100
This question sort of answers why I got panic: interface conversion: interface is string
If I try to change the type assertion to string, the underlying type of PlatformID, it won't even compile tmp/sandbox325023244/main.go:26: cannot use dict["platform-id"].(string) (type string) as type PlatformID in return argument
So how should I modify the return line so that I can retrieve PlatformID?
After looking at PlatformDictStr, I think var dictionary map[string]string should do the job.
PlatformID(dict["platform-id"].(string)) can be avoided in that case. Working example here
https://play.golang.org/p/A5kiVm_XbP
After playing around with the syntax a bit more, I think I need to do both type conversion and type assertion.
So the following line solves the problem
return PlatformID(dict["platform-id"].(string)), err
In retrospect, I need to first assert the interface type to a base type string, and from there I can just do a type conversion to PlatformID
PS 1: The use case is that I got the raw string in the request body, REST layer will parse out certain fields in a dictionary, then forward the rest of unparsed string to API layer for further processing. The keys in dictionary vary depends on workload, so I can't really Unmarshal it to a well defined struct.
Is there any specific reason for which you have created types for string and map? If not, I think you are over-engineering a simple use-case. The following works fine:
package main
import (
"encoding/json"
"fmt"
)
func Str2Map(str string) (map[string]interface{}, error) {
var dictionary map[string]interface{}
bytes := []byte(str)
err := json.Unmarshal(bytes, &dictionary)
if err != nil {
fmt.Println(err)
}
return dictionary, err
}
func parsePlatformID(str string) (string, error) {
fmt.Println(str)
dict, err := Str2Map(str)
fmt.Println(dict)
return dict["platform-id"].(string), err
}
func main() {
PlatformDictStr := "{\"platform-id\":\"platform_BnjliXLEUV26\",\"platform-labels\":\"test\",\"OptimizeScheme\":\"None\"}"
fmt.Println(PlatformDictStr)
ID, _ := parsePlatformID(PlatformDictStr)
fmt.Println(ID)
}
Working Playground example: https://play.golang.org/p/mpPpDmiz7x
I am working on a go project that will need to verify an OpenPGP public key, to be able to use it to verify file signatures.
I've generated a root key, and another key, which I've signed with the root key (let's call the second key signed). I've exported the public part of the signed key to an armored text file, for easy distribution:
gpg --export -a signed > signed.asc
I've written this go code which illustrates what I want to do, in the end:
package main
import (
"flag"
"fmt"
"golang.org/x/crypto/openpgp"
"os"
)
func main() {
var keyringpath string
var signedkeypath string
flag.StringVar(&keyringpath, "keyring", "", "keyring")
flag.StringVar(&signedkeypath, "signedkey", "", "signed key")
flag.Parse()
// read the keyring
keyring, err := os.Open(keyringpath)
if err != nil {
panic(err)
}
el, err := openpgp.ReadKeyRing(keyring)
if err != nil {
panic(err)
}
var rootidentity *openpgp.Entity
for _, entity := range el {
if _, ok := entity.Identities["root"]; ok {
rootidentity = entity
}
}
fmt.Printf("%+v\n", rootidentity)
// read the public armored key
signedkey, err := os.Open(signedkeypath)
if err != nil {
panic(err)
}
el, err = openpgp.ReadArmoredKeyRing(signedkey)
if err != nil {
panic(err)
}
signed := el[0]
fmt.Printf("%+v\n", signed)
// there is only one signature on signed, the one produced by root
signature := signed.Identities["signed"].Signatures[0]
err = rootidentity.PrimaryKey.VerifyKeySignature(signed.PrimaryKey, signature)
if err != nil {
panic(err)
}
}
When I run it, I give keyring my public keyring (~/.gnupg/pubring.gpg) and signedkey my exported signed key (signed.asc).
In production, the idea is to also export the root public key from pubring.gpg into armored text, and embed that in the code.
The signature fails to verify with the following error:
panic: openpgp: invalid signature: hash tag doesn't match
Looking at the code of VerifyKeySignature (and especially this comment), I get the feeling that it's meant to only be used to verify signatures on subkeys, rather than other keys.
So, the question is, given two public PGP keys, one signed by the other, how do I verify that signature using the openpgp library?
Not sure whether I should close this question or not: I found the answer. It isn't very clear in the docs, but VerifyKeySignature is indeed probably only used for subkeys. For verifying the signatures on other users' public keys, use VerifyUserIdSignature, like so:
err = rootidentity.PrimaryKey.VerifyUserIdSignature("signed", signed.PrimaryKey, signature)
if err != nil {
panic(err)
}
I am attempting to implement an interface based message queue where jobs are pushed as bytes to a redis queue. But I keep receiving an EOF error when attempting to decode the byte stream.
https://play.golang.org/p/l9TBvcn9qg
Could someone point me in the right direction?
Thank you!
In your Go Playground example, you're trying to encode an interface and interfaces don't have a concrete implementation. If you remove the interface from your A struct, that should work. Like the following:
package main
import "fmt"
import "encoding/gob"
import "bytes"
type testInterface interface{}
type A struct {
Name string
Interface *B // note this change here
}
type B struct {
Value string
}
func main() {
var err error
test := &A {
Name: "wut",
Interface: &B{Value: "BVALUE"},
}
buf := bytes.NewBuffer([]byte{})
enc := gob.NewEncoder(buf)
dec := gob.NewDecoder(buf)
// added error checking as per Mark's comment
err = enc.Encode(test)
if err != nil {
panic(err.Error())
}
result := &A{}
err := dec.Decode(result)
fmt.Printf("%+v\n", result)
fmt.Println("Error is:", err)
fmt.Println("Hello, playground")
}
Also, just as a side note you will see some sort of output like the following: &{Name:wut Interface:0x1040a5a0} because A is referencing a reference to a B struct. To clean that up further:
type A struct{
Name string
Interface B // no longer a pointer
}
func main() {
// ...
test := &A{Name: "wut", Interface: B{Value: "BVALUE"}}
// ...
}
Found the answer to the problem from Mark above. I have forgotten to do a gob.Register(B{})
https://play.golang.org/p/7rQDHvMhD7