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 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
|
// Package keyutil implements utilities to generate cryptographic keys.
package keyutil
import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"math/big"
"sync/atomic"
"github.com/pkg/errors"
"go.step.sm/crypto/x25519"
"golang.org/x/crypto/ssh"
)
var (
// DefaultKeyType is the default type of a private key.
DefaultKeyType = "EC"
// DefaultKeySize is the default size (in # of bits) of a private key.
DefaultKeySize = 2048
// DefaultKeyCurve is the default curve of a private key.
DefaultKeyCurve = "P-256"
// DefaultSignatureAlgorithm is the default signature algorithm used on a
// certificate with the default key type.
DefaultSignatureAlgorithm = x509.ECDSAWithSHA256
// MinRSAKeyBytes is the minimum acceptable size (in bytes) for RSA keys
// signed by the authority.
MinRSAKeyBytes = 256
)
type atomicBool int32
func (b *atomicBool) isSet() bool { return atomic.LoadInt32((*int32)(b)) != 0 }
func (b *atomicBool) setTrue() { atomic.StoreInt32((*int32)(b), 1) }
func (b *atomicBool) setFalse() { atomic.StoreInt32((*int32)(b), 0) }
var insecureMode atomicBool
// Insecure enables the insecure mode in this package and returns a function to
// revert the configuration. The insecure mode removes the minimum limits when
// generating RSA keys.
func Insecure() (revert func()) {
insecureMode.setTrue()
return func() {
insecureMode.setFalse()
}
}
// PublicKey extracts a public key from a private key.
func PublicKey(priv interface{}) (crypto.PublicKey, error) {
switch k := priv.(type) {
case *rsa.PrivateKey:
return &k.PublicKey, nil
case *ecdsa.PrivateKey:
return &k.PublicKey, nil
case ed25519.PrivateKey:
return k.Public(), nil
case x25519.PrivateKey:
return k.Public(), nil
case *rsa.PublicKey, *ecdsa.PublicKey, ed25519.PublicKey, x25519.PublicKey:
return k, nil
case crypto.Signer:
return k.Public(), nil
default:
return nil, errors.Errorf("unrecognized key type: %T", priv)
}
}
// GenerateDefaultKey generates a public/private key pair using sane defaults
// for key type, curve, and size.
func GenerateDefaultKey() (crypto.PrivateKey, error) {
return GenerateKey(DefaultKeyType, DefaultKeyCurve, DefaultKeySize)
}
// GenerateDefaultKeyPair generates a public/private key pair using configured
// default values for key type, curve, and size.
func GenerateDefaultKeyPair() (crypto.PublicKey, crypto.PrivateKey, error) {
return GenerateKeyPair(DefaultKeyType, DefaultKeyCurve, DefaultKeySize)
}
// GenerateKey generates a key of the given type (kty).
func GenerateKey(kty, crv string, size int) (crypto.PrivateKey, error) {
switch kty {
case "EC", "RSA", "OKP":
return GenerateSigner(kty, crv, size)
case "oct":
return generateOctKey(size)
default:
return nil, errors.Errorf("unrecognized key type: %s", kty)
}
}
// GenerateKeyPair creates an asymmetric crypto keypair using input
// configuration.
func GenerateKeyPair(kty, crv string, size int) (crypto.PublicKey, crypto.PrivateKey, error) {
signer, err := GenerateSigner(kty, crv, size)
if err != nil {
return nil, nil, err
}
return signer.Public(), signer, nil
}
// GenerateDefaultSigner returns an asymmetric crypto key that implements
// crypto.Signer using sane defaults.
func GenerateDefaultSigner() (crypto.Signer, error) {
return GenerateSigner(DefaultKeyType, DefaultKeyCurve, DefaultKeySize)
}
// GenerateSigner creates an asymmetric crypto key that implements
// crypto.Signer.
func GenerateSigner(kty, crv string, size int) (crypto.Signer, error) {
switch kty {
case "EC":
return generateECKey(crv)
case "RSA":
return generateRSAKey(size)
case "OKP":
return generateOKPKey(crv)
default:
return nil, errors.Errorf("unrecognized key type: %s", kty)
}
}
// ExtractKey returns the given public or private key or extracts the public key
// if a x509.Certificate or x509.CertificateRequest is given.
func ExtractKey(in interface{}) (interface{}, error) {
switch k := in.(type) {
case *rsa.PublicKey, *rsa.PrivateKey,
*ecdsa.PublicKey, *ecdsa.PrivateKey,
ed25519.PublicKey, ed25519.PrivateKey,
x25519.PublicKey, x25519.PrivateKey:
return in, nil
case []byte:
return in, nil
case *x509.Certificate:
return k.PublicKey, nil
case *x509.CertificateRequest:
return k.PublicKey, nil
case ssh.CryptoPublicKey:
return k.CryptoPublicKey(), nil
case *ssh.Certificate:
return ExtractKey(k.Key)
default:
return nil, errors.Errorf("cannot extract the key from type '%T'", k)
}
}
// VerifyPair that the public key matches the given private key.
func VerifyPair(pub crypto.PublicKey, priv crypto.PrivateKey) error {
signer, ok := priv.(crypto.Signer)
if !ok {
return errors.New("private key type does implement crypto.Signer")
}
if !Equal(pub, signer.Public()) {
return errors.New("private key does not match public key")
}
return nil
}
// Equal reports if x and y are the same key.
func Equal(x, y any) bool {
switch xx := x.(type) {
case *ecdsa.PublicKey:
yy, ok := y.(*ecdsa.PublicKey)
return ok && xx.Equal(yy)
case *ecdsa.PrivateKey:
yy, ok := y.(*ecdsa.PrivateKey)
return ok && xx.Equal(yy)
case *rsa.PublicKey:
yy, ok := y.(*rsa.PublicKey)
return ok && xx.Equal(yy)
case *rsa.PrivateKey:
yy, ok := y.(*rsa.PrivateKey)
return ok && xx.Equal(yy)
case ed25519.PublicKey:
yy, ok := y.(ed25519.PublicKey)
return ok && xx.Equal(yy)
case ed25519.PrivateKey:
yy, ok := y.(ed25519.PrivateKey)
return ok && xx.Equal(yy)
case x25519.PublicKey:
yy, ok := y.(x25519.PublicKey)
return ok && xx.Equal(yy)
case x25519.PrivateKey:
yy, ok := y.(x25519.PrivateKey)
return ok && xx.Equal(yy)
case []byte: // special case for symmetric keys
yy, ok := y.([]byte)
return ok && bytes.Equal(xx, yy)
default:
return false
}
}
func generateECKey(crv string) (crypto.Signer, error) {
var c elliptic.Curve
switch crv {
case "P-256":
c = elliptic.P256()
case "P-384":
c = elliptic.P384()
case "P-521":
c = elliptic.P521()
default:
return nil, errors.Errorf("invalid value for argument crv (crv: '%s')", crv)
}
key, err := ecdsa.GenerateKey(c, rand.Reader)
if err != nil {
return nil, errors.Wrap(err, "error generating EC key")
}
return key, nil
}
func generateRSAKey(bits int) (crypto.Signer, error) {
if minBits := MinRSAKeyBytes * 8; !insecureMode.isSet() && bits < minBits {
return nil, errors.Errorf("the size of the RSA key should be at least %d bits", minBits)
}
key, err := rsa.GenerateKey(rand.Reader, bits)
if err != nil {
return nil, errors.Wrap(err, "error generating RSA key")
}
return key, nil
}
func generateOKPKey(crv string) (crypto.Signer, error) {
switch crv {
case "Ed25519":
_, key, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
return nil, errors.Wrap(err, "error generating Ed25519 key")
}
return key, nil
case "X25519":
_, key, err := x25519.GenerateKey(rand.Reader)
if err != nil {
return nil, errors.Wrap(err, "error generating X25519 key")
}
return key, nil
default:
return nil, errors.Errorf("missing or invalid value for argument 'crv'. "+
"expected 'Ed25519' or 'X25519', but got '%s'", crv)
}
}
func generateOctKey(size int) (interface{}, error) {
const chars = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
result := make([]byte, size)
for i := range result {
num, err := rand.Int(rand.Reader, big.NewInt(int64(len(chars))))
if err != nil {
return nil, err
}
result[i] = chars[num.Int64()]
}
return result, nil
}
|