1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
|
package gokey
import (
"crypto"
"crypto/ecdsa"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/pem"
"errors"
"fmt"
"io"
"golang.org/x/crypto/ed25519"
)
func getReader(password, realm string, seed []byte, allowUnsafe bool) (io.Reader, error) {
var rng io.Reader
var err error
if seed != nil {
rng, err = NewDRNGwithSeed(password, realm, seed)
if err != nil {
return nil, err
}
} else if allowUnsafe {
rng = NewDRNG(password, realm)
} else {
return nil, errors.New("generating keys without strong seed is not allowed")
}
return rng, nil
}
func GetPass(password, realm string, seed []byte, spec *PasswordSpec) (string, error) {
rng, err := getReader(password, realm+"-pass", seed, true)
if err != nil {
return "", err
}
gen := &KeyGen{rng}
return gen.GeneratePassword(spec)
}
func GetKey(password, realm string, seed []byte, kt KeyType, allowUnsafe bool) (crypto.PrivateKey, error) {
rng, err := getReader(password, realm+fmt.Sprintf("-key(%v)", kt), seed, allowUnsafe)
if err != nil {
return nil, err
}
gen := &KeyGen{rng}
return gen.GenerateKey(kt)
}
func GetRaw(password, realm string, seed []byte, allowUnsafe bool) (io.Reader, error) {
rng, err := getReader(password, realm+"-raw", seed, allowUnsafe)
if err != nil {
return nil, err
}
return rng, nil
}
// below code implements asn1 encoding of x25519 and ed25519 keys according
// to https://tools.ietf.org/id/draft-ietf-curdle-pkix-10.txt
// the output should be compatible to OpenSSL pkey functions
// this code is considered temporal and is expected to go away, when Go
// implements native marshalling for these types of keys
// as https://tools.ietf.org/id/draft-ietf-curdle-pkix-10.txt is still a draft
// future versions of gokey may produce different output
// p.3 https://tools.ietf.org/id/draft-ietf-curdle-pkix-10.txt
// id-X25519 OBJECT IDENTIFIER ::= { 1 3 101 110 }
// id-Ed25519 OBJECT IDENTIFIER ::= { 1 3 101 112 }
const (
x25519OidSuffix = 110
ed25519OidSuffix = 112
)
// x25519/ed25519 asn1 private key structure
// p.7 https://tools.ietf.org/id/draft-ietf-curdle-pkix-10.txt
// this implementation does not support optional attributes or public key
type asn25519 struct {
Version int
AlgId pkix.AlgorithmIdentifier
PrivateKey []byte
}
// Golang does not have a declaration for x25519 keys
type x25519PrivateKey []byte
func marshal25519PrivateKey(key crypto.PrivateKey) ([]byte, error) {
var a25519 asn25519
var keyBytes []byte
switch key.(type) {
case x25519PrivateKey:
a25519.AlgId = pkix.AlgorithmIdentifier{asn1.ObjectIdentifier{1, 3, 101, x25519OidSuffix}, asn1.RawValue{}}
keyBytes = key.(x25519PrivateKey)
case *ed25519.PrivateKey:
a25519.AlgId = pkix.AlgorithmIdentifier{asn1.ObjectIdentifier{1, 3, 101, ed25519OidSuffix}, asn1.RawValue{}}
keyBytes = key.(*ed25519.PrivateKey).Seed()
}
// actual key bytes are double wrapped in octet strings
// see p.7 https://tools.ietf.org/id/draft-ietf-curdle-pkix-10.txt
privKeyOctetString, err := asn1.Marshal(keyBytes)
if err != nil {
return nil, err
}
a25519.PrivateKey = privKeyOctetString
return asn1.Marshal(a25519)
}
func EncodeToPem(key crypto.PrivateKey, w io.Writer) error {
switch key.(type) {
case *ecdsa.PrivateKey:
der, err := x509.MarshalECPrivateKey(key.(*ecdsa.PrivateKey))
if err != nil {
return err
}
return pem.Encode(w, &pem.Block{Type: "EC PRIVATE KEY", Bytes: der})
case *rsa.PrivateKey:
der := x509.MarshalPKCS1PrivateKey(key.(*rsa.PrivateKey))
return pem.Encode(w, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: der})
case x25519PrivateKey, *ed25519.PrivateKey:
der, err := marshal25519PrivateKey(key)
if err != nil {
return err
}
return pem.Encode(w, &pem.Block{Type: "PRIVATE KEY", Bytes: der})
}
return fmt.Errorf("unable to encode key type %T", key)
}
|