Skip to content

Commit

Permalink
fix: Unmarshal private keys from SEC.1 and PKCS#8 forms
Browse files Browse the repository at this point in the history
bifrost.PrivateKey#UnmarshalText and bifrost.PrivateKey#UnmarshalBinary
can decode keys that are in PKCS#8 and in SEC.1 ASN.1, DER form.
  • Loading branch information
ananthb committed Apr 15, 2024
1 parent f4b7a4f commit 682c43e
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 35 deletions.
35 changes: 10 additions & 25 deletions cafiles/cafiles.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ package cafiles

import (
"context"
"crypto/x509"
"encoding/pem"
"fmt"
"io"
Expand Down Expand Up @@ -34,8 +33,12 @@ func GetCertificate(ctx context.Context, uri string) (*bifrost.Certificate, erro
if err != nil {
return nil, fmt.Errorf("error getting file %s: %w", uri, err)
}
block, _ := pem.Decode(certPem)
if block == nil {
return nil, fmt.Errorf("expected PEM block")
}

cert, err := bifrost.ParseCertificate(certPem.Bytes)
cert, err := bifrost.ParseCertificate(block.Bytes)
if err != nil {
return nil, fmt.Errorf("error validating certificate: %w", err)
}
Expand All @@ -50,33 +53,19 @@ func GetCertificate(ctx context.Context, uri string) (*bifrost.Certificate, erro
func GetPrivateKey(ctx context.Context, uri string) (*bifrost.PrivateKey, error) {
ctx, cancel := context.WithTimeout(ctx, fetchTimeout)
defer cancel()
pemBlock, err := getPemFile(ctx, uri)
pemData, err := getPemFile(ctx, uri)
if err != nil {
return nil, fmt.Errorf("error getting file %s: %w", uri, err)
}

var key bifrost.PrivateKey
switch pemBlock.Type {
case "PRIVATE KEY":
if err := key.UnmarshalBinary(pemBlock.Bytes); err != nil {
return nil, fmt.Errorf("error parsing PKCS#8 private key: %w", err)
}
case "EC PRIVATE KEY":
// Read EC private keys too because we used to generate private keys in SEC.1 originally.
// New keys are generated in PKCS#8 format from now on.
ecKey, err := x509.ParseECPrivateKey(pemBlock.Bytes)
if err != nil {
return nil, fmt.Errorf("error parsing EC private key: %w", err)
}
key = bifrost.PrivateKey{PrivateKey: ecKey}
default:
return nil, fmt.Errorf("unsupported PEM block type %s", pemBlock.Type)
if err := key.UnmarshalText(pemData); err != nil {
return nil, fmt.Errorf("error parsing private key: %w", err)
}

return &key, nil
}

func getPemFile(ctx context.Context, uri string) (*pem.Block, error) {
func getPemFile(ctx context.Context, uri string) ([]byte, error) {
url, err := url.Parse(uri)
if err != nil {
return nil, fmt.Errorf("error parsing file uri %w", err)
Expand Down Expand Up @@ -111,11 +100,7 @@ func getPemFile(ctx context.Context, uri string) (*pem.Block, error) {
return nil, fmt.Errorf("error fetching pem file: %w", err)
}

block, _ := pem.Decode(pemData)
if block == nil {
return nil, fmt.Errorf("no pem data found")
}
return block, nil
return pemData, nil
}

func getS3Key(ctx context.Context, bucket, key string) ([]byte, error) {
Expand Down
6 changes: 6 additions & 0 deletions keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,9 +157,15 @@ func (p PrivateKey) MarshalBinary() ([]byte, error) {
}

// UnmarshalBinary parses an unencrypted private key in PKCS #8, ASN.1 DER form.
// Unmarshal also supports private keys in SEC.1, ASN.1 DER form for backward compatibility.
func (p *PrivateKey) UnmarshalBinary(data []byte) error {
priv, err := x509.ParsePKCS8PrivateKey(data)
if err != nil {
// Try to parse as an EC private key for backward compatibility.
if priv, err := x509.ParseECPrivateKey(data); err == nil {
p.PrivateKey = priv
return nil
}
return err
}
k, ok := priv.(*ecdsa.PrivateKey)
Expand Down
39 changes: 29 additions & 10 deletions keys_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,6 @@ package bifrost

import "testing"

const testPrivKeyPemPkcs8 = `-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgKY7GC3pND13CjGYi
/U0bXQtfwJ+8VsS9wyc7yPg+lZyhRANCAASmpFKA12yjR2QwShSnDXz4YdIVr8LS
oFCWzK9ZiNV1DIYfbCr8GibUptSjDWLGAZ5P3+ZudoyCvnPYLR8ClmMH
-----END PRIVATE KEY-----
`

const testPubKeyPemPkix = `-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEpqRSgNdso0dkMEoUpw18+GHSFa/C
0qBQlsyvWYjVdQyGH2wq/Bom1KbUow1ixgGeT9/mbnaMgr5z2C0fApZjBw==
Expand All @@ -29,12 +22,38 @@ func TestPublicKey_UnmarshalMarshalText(t *testing.T) {
}
}

const testPrivKeyPemPkcs8 = `-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgrZfZw1jXZkDHsYXb
GbdftocRhSJK9Z5C0F9OT8PGKNShRANCAAQlCs+v1TsIPq1ZdYRKN/v+BWX/fzhC
nsWRaGNvP5b0ivW01Qt4RBLBMk1AU3OcCTMRphmIe4LHT1dk7sMOe3JS
-----END PRIVATE KEY-----
`

const testPrivKeyPemSec1 = `-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIK2X2cNY12ZAx7GF2xm3X7aHEYUiSvWeQtBfTk/DxijUoAoGCCqGSM49
AwEHoUQDQgAEJQrPr9U7CD6tWXWESjf7/gVl/384Qp7FkWhjbz+W9Ir1tNULeEQS
wTJNQFNznAkzEaYZiHuCx09XZO7DDntyUg==
-----END EC PRIVATE KEY-----
`

func TestPrivateKey_UnmarshalMarshalText(t *testing.T) {
p := PrivateKey{}
if err := p.UnmarshalText([]byte(testPrivKeyPemPkcs8)); err != nil {
pkcs8Key := PrivateKey{}
if err := pkcs8Key.UnmarshalText([]byte(testPrivKeyPemPkcs8)); err != nil {
t.Fatal(err)
}
text, err := p.MarshalText()
text, err := pkcs8Key.MarshalText()
if err != nil {
t.Fatal(err)
}
if string(text) != testPrivKeyPemPkcs8 {
t.Fatalf("got %q, want %q", text, testPrivKeyPemPkcs8)
}

sec1Key := PrivateKey{}
if err := sec1Key.UnmarshalText([]byte(testPrivKeyPemSec1)); err != nil {
t.Fatal(err)
}
text, err = sec1Key.MarshalText()
if err != nil {
t.Fatal(err)
}
Expand Down

0 comments on commit 682c43e

Please sign in to comment.