github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/swarmkit/ca/pkcs8/pkcs8.go (about) 1 // Package pkcs8 implements functions to encrypt, decrypt, parse and to convert 2 // EC private keys to PKCS#8 format. However this package is hard forked from 3 // https://github.com/youmark/pkcs8 and modified function signatures to match 4 // signatures of crypto/x509 and cloudflare/cfssl/helpers to simplify package 5 // swapping. License for original package is as follow: 6 // 7 // The MIT License (MIT) 8 // 9 // Copyright (c) 2014 youmark 10 // 11 // Permission is hereby granted, free of charge, to any person obtaining a copy 12 // of this software and associated documentation files (the "Software"), to deal 13 // in the Software without restriction, including without limitation the rights 14 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 // copies of the Software, and to permit persons to whom the Software is 16 // furnished to do so, subject to the following conditions: 17 // 18 // The above copyright notice and this permission notice shall be included in all 19 // copies or substantial portions of the Software. 20 // 21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 // SOFTWARE. 28 package pkcs8 29 30 import ( 31 "bytes" 32 "crypto" 33 "crypto/aes" 34 "crypto/cipher" 35 "crypto/rand" 36 "crypto/sha1" 37 "encoding/asn1" 38 "encoding/pem" 39 "errors" 40 41 "github.com/cloudflare/cfssl/helpers/derhelpers" 42 "golang.org/x/crypto/pbkdf2" 43 ) 44 45 // Copy from crypto/x509 46 var ( 47 oidPublicKeyECDSA = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1} 48 ) 49 50 // Unencrypted PKCS#8 51 var ( 52 oidPKCS5PBKDF2 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 5, 12} 53 oidPBES2 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 5, 13} 54 oidAES256CBC = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 42} 55 ) 56 57 type ecPrivateKey struct { 58 Version int 59 PrivateKey []byte 60 NamedCurveOID asn1.ObjectIdentifier `asn1:"optional,explicit,tag:0"` 61 PublicKey asn1.BitString `asn1:"optional,explicit,tag:1"` 62 } 63 64 type privateKeyInfo struct { 65 Version int 66 PrivateKeyAlgorithm []asn1.ObjectIdentifier 67 PrivateKey []byte 68 } 69 70 // Encrypted PKCS8 71 type pbkdf2Params struct { 72 Salt []byte 73 IterationCount int 74 } 75 76 type pbkdf2Algorithms struct { 77 IDPBKDF2 asn1.ObjectIdentifier 78 PBKDF2Params pbkdf2Params 79 } 80 81 type pbkdf2Encs struct { 82 EncryAlgo asn1.ObjectIdentifier 83 IV []byte 84 } 85 86 type pbes2Params struct { 87 KeyDerivationFunc pbkdf2Algorithms 88 EncryptionScheme pbkdf2Encs 89 } 90 91 type pbes2Algorithms struct { 92 IDPBES2 asn1.ObjectIdentifier 93 PBES2Params pbes2Params 94 } 95 96 type encryptedPrivateKeyInfo struct { 97 EncryptionAlgorithm pbes2Algorithms 98 EncryptedData []byte 99 } 100 101 // ParsePrivateKeyPEMWithPassword parses an encrypted or a decrypted PKCS#8 PEM to crypto.signer 102 func ParsePrivateKeyPEMWithPassword(pemBytes, password []byte) (crypto.Signer, error) { 103 block, _ := pem.Decode(pemBytes) 104 if block == nil { 105 return nil, errors.New("invalid pem file") 106 } 107 108 var ( 109 der []byte 110 err error 111 ) 112 der = block.Bytes 113 114 if ok := IsEncryptedPEMBlock(block); ok { 115 der, err = DecryptPEMBlock(block, password) 116 if err != nil { 117 return nil, err 118 } 119 } 120 121 return derhelpers.ParsePrivateKeyDER(der) 122 } 123 124 // IsEncryptedPEMBlock checks if a PKCS#8 PEM-block is encrypted or not 125 func IsEncryptedPEMBlock(block *pem.Block) bool { 126 der := block.Bytes 127 128 var privKey encryptedPrivateKeyInfo 129 if _, err := asn1.Unmarshal(der, &privKey); err != nil { 130 return false 131 } 132 133 return true 134 } 135 136 // DecryptPEMBlock requires PKCS#8 PEM Block and password to decrypt and return unencrypted der []byte 137 func DecryptPEMBlock(block *pem.Block, password []byte) ([]byte, error) { 138 der := block.Bytes 139 140 var privKey encryptedPrivateKeyInfo 141 if _, err := asn1.Unmarshal(der, &privKey); err != nil { 142 return nil, errors.New("pkcs8: only PKCS #5 v2.0 supported") 143 } 144 145 if !privKey.EncryptionAlgorithm.IDPBES2.Equal(oidPBES2) { 146 return nil, errors.New("pkcs8: only PBES2 supported") 147 } 148 149 if !privKey.EncryptionAlgorithm.PBES2Params.KeyDerivationFunc.IDPBKDF2.Equal(oidPKCS5PBKDF2) { 150 return nil, errors.New("pkcs8: only PBKDF2 supported") 151 } 152 153 encParam := privKey.EncryptionAlgorithm.PBES2Params.EncryptionScheme 154 kdfParam := privKey.EncryptionAlgorithm.PBES2Params.KeyDerivationFunc.PBKDF2Params 155 156 switch { 157 case encParam.EncryAlgo.Equal(oidAES256CBC): 158 iv := encParam.IV 159 salt := kdfParam.Salt 160 iter := kdfParam.IterationCount 161 162 encryptedKey := privKey.EncryptedData 163 symkey := pbkdf2.Key(password, salt, iter, 32, sha1.New) 164 block, err := aes.NewCipher(symkey) 165 if err != nil { 166 return nil, err 167 } 168 mode := cipher.NewCBCDecrypter(block, iv) 169 mode.CryptBlocks(encryptedKey, encryptedKey) 170 171 if _, err := derhelpers.ParsePrivateKeyDER(encryptedKey); err != nil { 172 return nil, errors.New("pkcs8: incorrect password") 173 } 174 175 // Remove padding from key as it might be used to encode to memory as pem 176 keyLen := len(encryptedKey) 177 padLen := int(encryptedKey[keyLen-1]) 178 if padLen > keyLen || padLen > aes.BlockSize { 179 return nil, errors.New("pkcs8: invalid padding size") 180 } 181 encryptedKey = encryptedKey[:keyLen-padLen] 182 183 return encryptedKey, nil 184 default: 185 return nil, errors.New("pkcs8: only AES-256-CBC supported") 186 } 187 } 188 189 func encryptPrivateKey(pkey, password []byte) ([]byte, error) { 190 // Calculate key from password based on PKCS5 algorithm 191 // Use 8 byte salt, 16 byte IV, and 2048 iteration 192 iter := 2048 193 salt := make([]byte, 8) 194 iv := make([]byte, 16) 195 196 if _, err := rand.Reader.Read(salt); err != nil { 197 return nil, err 198 } 199 200 if _, err := rand.Reader.Read(iv); err != nil { 201 return nil, err 202 } 203 204 key := pbkdf2.Key(password, salt, iter, 32, sha1.New) 205 206 // Use AES256-CBC mode, pad plaintext with PKCS5 padding scheme 207 n := len(pkey) 208 padLen := aes.BlockSize - n%aes.BlockSize 209 if padLen > 0 { 210 padValue := []byte{byte(padLen)} 211 padding := bytes.Repeat(padValue, padLen) 212 pkey = append(pkey, padding...) 213 } 214 215 encryptedKey := make([]byte, len(pkey)) 216 block, err := aes.NewCipher(key) 217 if err != nil { 218 return nil, err 219 } 220 mode := cipher.NewCBCEncrypter(block, iv) 221 mode.CryptBlocks(encryptedKey, pkey) 222 223 pbkdf2algo := pbkdf2Algorithms{oidPKCS5PBKDF2, pbkdf2Params{salt, iter}} 224 pbkdf2encs := pbkdf2Encs{oidAES256CBC, iv} 225 pbes2algo := pbes2Algorithms{oidPBES2, pbes2Params{pbkdf2algo, pbkdf2encs}} 226 227 encryptedPkey := encryptedPrivateKeyInfo{pbes2algo, encryptedKey} 228 return asn1.Marshal(encryptedPkey) 229 } 230 231 // EncryptPEMBlock takes DER-format bytes and password to return an encrypted PKCS#8 PEM-block 232 func EncryptPEMBlock(data, password []byte) (*pem.Block, error) { 233 encryptedBytes, err := encryptPrivateKey(data, password) 234 if err != nil { 235 return nil, err 236 } 237 238 return &pem.Block{ 239 Type: "ENCRYPTED PRIVATE KEY", 240 Headers: map[string]string{}, 241 Bytes: encryptedBytes, 242 }, nil 243 } 244 245 // ConvertECPrivateKeyPEM takes an EC Private Key as input and returns PKCS#8 version of it 246 func ConvertECPrivateKeyPEM(inPEM []byte) ([]byte, error) { 247 block, _ := pem.Decode(inPEM) 248 if block == nil { 249 return nil, errors.New("invalid pem bytes") 250 } 251 252 var ecPrivKey ecPrivateKey 253 if _, err := asn1.Unmarshal(block.Bytes, &ecPrivKey); err != nil { 254 return nil, errors.New("invalid ec private key") 255 } 256 257 var pkey privateKeyInfo 258 pkey.Version = 0 259 pkey.PrivateKeyAlgorithm = make([]asn1.ObjectIdentifier, 2) 260 pkey.PrivateKeyAlgorithm[0] = oidPublicKeyECDSA 261 pkey.PrivateKeyAlgorithm[1] = ecPrivKey.NamedCurveOID 262 263 // remove curve oid from private bytes as it is already mentioned in algorithm 264 ecPrivKey.NamedCurveOID = nil 265 266 privatekey, err := asn1.Marshal(ecPrivKey) 267 if err != nil { 268 return nil, err 269 } 270 pkey.PrivateKey = privatekey 271 272 der, err := asn1.Marshal(pkey) 273 if err != nil { 274 return nil, err 275 } 276 277 return pem.EncodeToMemory(&pem.Block{ 278 Type: "PRIVATE KEY", 279 Bytes: der, 280 }), nil 281 } 282 283 // ConvertToECPrivateKeyPEM takes an unencrypted PKCS#8 PEM and converts it to 284 // EC Private Key 285 func ConvertToECPrivateKeyPEM(inPEM []byte) ([]byte, error) { 286 block, _ := pem.Decode(inPEM) 287 if block == nil { 288 return nil, errors.New("invalid pem bytes") 289 } 290 291 var pkey privateKeyInfo 292 if _, err := asn1.Unmarshal(block.Bytes, &pkey); err != nil { 293 return nil, errors.New("invalid pkcs8 key") 294 } 295 296 var ecPrivKey ecPrivateKey 297 if _, err := asn1.Unmarshal(pkey.PrivateKey, &ecPrivKey); err != nil { 298 return nil, errors.New("invalid private key") 299 } 300 301 ecPrivKey.NamedCurveOID = pkey.PrivateKeyAlgorithm[1] 302 key, err := asn1.Marshal(ecPrivKey) 303 if err != nil { 304 return nil, err 305 } 306 307 return pem.EncodeToMemory(&pem.Block{ 308 Type: "EC PRIVATE KEY", 309 Bytes: key, 310 }), nil 311 }