1
0
mirror of https://github.com/smallstep/cli.git synced 2026-10-29 00:17:23 +03:00

Add method to Parse a DER-encoded file.

This commit is contained in:
Mariano Cano
2018-12-27 20:04:32 -08:00
parent 1d65ab59f8
commit 299c65ade7
2 changed files with 95 additions and 58 deletions

View File

@@ -96,6 +96,18 @@ func WithPasswordFile(filename string) Options {
}
}
// WithPasswordPrompt ask the user for a password and adds it to the context.
func WithPasswordPrompt(prompt string) Options {
return func(ctx *context) error {
b, err := ui.PromptPassword(prompt)
if err != nil {
return err
}
ctx.password = b
return nil
}
}
// WithPKCS8 with v set to true returns an option used in the Serialize method
// to use the PKCS#8 encoding form on the private keys. With v set to false
// default form will be used.
@@ -366,3 +378,26 @@ func Serialize(in interface{}, opts ...Options) (p *pem.Block, err error) {
return p, nil
}
// ParseDER parses the given DER-encoded bytes and results the public or private
// key encoded.
func ParseDER(b []byte) (interface{}, error) {
// Try private keys
key, err := ParsePKCS8PrivateKey(b)
if err != nil {
if key, err = x509.ParseECPrivateKey(b); err != nil {
key, err = x509.ParsePKCS1PrivateKey(b)
}
}
// Try public key
if err != nil {
if key, err = x509.ParsePKIXPublicKey(b); err != nil {
if key, err = x509.ParsePKCS1PublicKey(b); err != nil {
return nil, errors.New("error decoding DER; bad format")
}
}
}
return key, nil
}

View File

@@ -10,6 +10,7 @@ import (
"encoding/pem"
"io/ioutil"
"os"
"reflect"
"strings"
"testing"
@@ -488,66 +489,67 @@ func TestSerialize(t *testing.T) {
}
}
/*
func TestWriteKey(t *testing.T) {
keyOut := "./test.key"
pass := "pass"
func TestParseDER(t *testing.T) {
k1, err := Read("testdata/openssl.rsa2048.pem")
assert.FatalError(t, err)
k2, err := Read("testdata/openssl.p256.pem")
assert.FatalError(t, err)
k3, err := Read("testdata/pkcs8/openssl.ed25519.pem")
assert.FatalError(t, err)
rsaKey := k1.(*rsa.PrivateKey)
ecdsaKey := k2.(*ecdsa.PrivateKey)
edKey := k3.(ed25519.PrivateKey)
// Ed25519 der files
edPubDer, err := ioutil.ReadFile("testdata/pkcs8/openssl.ed25519.pub.der")
assert.FatalError(t, err)
edPrivDer, err := ioutil.ReadFile("testdata/pkcs8/openssl.ed25519.der")
assert.FatalError(t, err)
tests := map[string]struct {
key func() (interface{}, error)
keyOut string
pass string
err error
}{
"success": {
key: func() (interface{}, error) {
return GenerateKey(DefaultKeyType, DefaultKeyCurve, 0)
},
keyOut: keyOut,
pass: pass,
err: errors.New("failed to convert private key to PEM block: encryption passphrase cannot be empty"),
},
}
for name, test := range tests {
t.Logf("Running test case: %s", name)
key, err := test.key()
assert.FatalError(t, err)
err = WritePrivateKey(key, test.pass, test.keyOut)
if err != nil {
if assert.NotNil(t, test.err) {
assert.HasPrefix(t, err.Error(), test.err.Error())
}
} else {
// Check key permissions
fileInfo, err := os.Stat(test.keyOut)
toDER := func(k interface{}) []byte {
switch k := k.(type) {
case *rsa.PublicKey, *ecdsa.PublicKey:
b, err := x509.MarshalPKIXPublicKey(k)
assert.FatalError(t, err)
fileMode := fileInfo.Mode()
if fileMode != 0600 {
t.Errorf("FileMode mismatch for file %s -- expected: `%d`, but got: `%d`",
test.keyOut, fileMode, 0600)
}
switch k := key.(type) {
case *ecdsa.PrivateKey:
// Verify that key written to file is correct
plain, err := x509.MarshalECPrivateKey(k)
assert.FatalError(t, err)
keyFileBytes, err := ioutil.ReadFile(test.keyOut)
assert.FatalError(t, err)
pemKey, _ := pem.Decode(keyFileBytes)
assert.True(t, x509.IsEncryptedPEMBlock(pemKey))
assert.Equals(t, pemKey.Type, "EC PRIVATE KEY")
assert.Equals(t, pemKey.Headers["Proc-Type"], "4,ENCRYPTED")
der, err := x509.DecryptPEMBlock(pemKey, []byte(pass))
assert.FatalError(t, err)
assert.Equals(t, der, plain)
default:
t.Errorf("unexpected key type %T", k)
}
return b
case *rsa.PrivateKey:
return x509.MarshalPKCS1PrivateKey(k)
case *ecdsa.PrivateKey:
b, err := x509.MarshalECPrivateKey(k)
assert.FatalError(t, err)
return b
default:
t.Fatalf("unsupported key type %T", k)
return nil
}
}
type args struct {
b []byte
}
tests := []struct {
name string
args args
want interface{}
wantErr bool
}{
{"rsa public key", args{toDER(rsaKey.Public())}, rsaKey.Public(), false},
{"rsa private key", args{toDER(rsaKey)}, rsaKey, false},
{"ecdsa public key", args{toDER(ecdsaKey.Public())}, ecdsaKey.Public(), false},
{"ecdsa private key", args{toDER(ecdsaKey)}, ecdsaKey, false},
{"ed25519 public key", args{edPubDer}, edKey.Public(), false},
{"ed25519 private key", args{edPrivDer}, edKey, false},
{"fail", args{[]byte("fooo")}, nil, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := ParseDER(tt.args.b)
if (err != nil) != tt.wantErr {
t.Errorf("ParseDER() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("ParseDER() = %v, want %v", got, tt.want)
}
})
}
}
*/