github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/swarmkit/ca/keyutils/keyutils.go (about) 1 // Package keyutils serves as a utility to parse, encrypt and decrypt 2 // PKCS#1 and PKCS#8 private keys based on current FIPS mode status, 3 // supporting only EC type keys. It always allows PKCS#8 private keys 4 // and disallow PKCS#1 private keys in FIPS-mode. 5 package keyutils 6 7 import ( 8 "crypto" 9 cryptorand "crypto/rand" 10 "crypto/x509" 11 "encoding/pem" 12 "errors" 13 14 "github.com/cloudflare/cfssl/helpers" 15 "github.com/docker/swarmkit/ca/pkcs8" 16 ) 17 18 // Formatter provides an interface for converting keys to the right format, and encrypting and decrypting keys 19 type Formatter interface { 20 ParsePrivateKeyPEMWithPassword(pemBytes, password []byte) (crypto.Signer, error) 21 DecryptPEMBlock(block *pem.Block, password []byte) ([]byte, error) 22 EncryptPEMBlock(data, password []byte) (*pem.Block, error) 23 } 24 25 // ErrFIPSUnsupportedKeyFormat is returned when encryption/decryption operations are attempted on a PKCS1 key 26 // when FIPS mode is enabled. 27 var ErrFIPSUnsupportedKeyFormat = errors.New("unsupported key format due to FIPS compliance") 28 29 // Default is the default key util, where FIPS is not required 30 var Default Formatter = &utils{fips: false} 31 32 // FIPS is the key utility which enforces FIPS compliance 33 var FIPS Formatter = &utils{fips: true} 34 35 type utils struct { 36 fips bool 37 } 38 39 // IsPKCS8 returns true if the provided der bytes is encrypted/unencrypted PKCS#8 key 40 func IsPKCS8(derBytes []byte) bool { 41 if _, err := x509.ParsePKCS8PrivateKey(derBytes); err == nil { 42 return true 43 } 44 45 return pkcs8.IsEncryptedPEMBlock(&pem.Block{ 46 Type: "PRIVATE KEY", 47 Headers: nil, 48 Bytes: derBytes, 49 }) 50 } 51 52 // IsEncryptedPEMBlock checks if a PKCS#1 or PKCS#8 PEM-block is encrypted or not 53 func IsEncryptedPEMBlock(block *pem.Block) bool { 54 return pkcs8.IsEncryptedPEMBlock(block) || x509.IsEncryptedPEMBlock(block) 55 } 56 57 // ParsePrivateKeyPEMWithPassword parses an encrypted or a decrypted PKCS#1 or PKCS#8 PEM to crypto.Signer. 58 // It returns an error in FIPS mode if PKCS#1 PEM bytes are passed. 59 func (u *utils) ParsePrivateKeyPEMWithPassword(pemBytes, password []byte) (crypto.Signer, error) { 60 block, _ := pem.Decode(pemBytes) 61 if block == nil { 62 return nil, errors.New("Could not parse PEM") 63 } 64 65 if IsPKCS8(block.Bytes) { 66 return pkcs8.ParsePrivateKeyPEMWithPassword(pemBytes, password) 67 } else if u.fips { 68 return nil, ErrFIPSUnsupportedKeyFormat 69 } 70 71 return helpers.ParsePrivateKeyPEMWithPassword(pemBytes, password) 72 } 73 74 // DecryptPEMBlock requires PKCS#1 or PKCS#8 PEM Block and password to decrypt and return unencrypted der []byte 75 // It returns an error in FIPS mode when PKCS#1 PEM Block is passed. 76 func (u *utils) DecryptPEMBlock(block *pem.Block, password []byte) ([]byte, error) { 77 if IsPKCS8(block.Bytes) { 78 return pkcs8.DecryptPEMBlock(block, password) 79 } else if u.fips { 80 return nil, ErrFIPSUnsupportedKeyFormat 81 } 82 83 return x509.DecryptPEMBlock(block, password) 84 } 85 86 // EncryptPEMBlock takes DER-format bytes and password to return an encrypted PKCS#1 or PKCS#8 PEM-block 87 // It returns an error in FIPS mode when PKCS#1 PEM bytes are passed. 88 func (u *utils) EncryptPEMBlock(data, password []byte) (*pem.Block, error) { 89 if IsPKCS8(data) { 90 return pkcs8.EncryptPEMBlock(data, password) 91 } else if u.fips { 92 return nil, ErrFIPSUnsupportedKeyFormat 93 } 94 95 cipherType := x509.PEMCipherAES256 96 return x509.EncryptPEMBlock(cryptorand.Reader, 97 "EC PRIVATE KEY", 98 data, 99 password, 100 cipherType) 101 }