|  | // Copyright 2012 The Go Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style | 
|  | // license that can be found in the LICENSE file. | 
|  |  | 
|  | package x509 | 
|  |  | 
|  | // RFC 1423 describes the encryption of PEM blocks. The algorithm used to | 
|  | // generate a key from the password was derived by looking at the OpenSSL | 
|  | // implementation. | 
|  |  | 
|  | import ( | 
|  | "crypto/aes" | 
|  | "crypto/cipher" | 
|  | "crypto/des" | 
|  | "crypto/md5" | 
|  | "encoding/hex" | 
|  | "encoding/pem" | 
|  | "errors" | 
|  | "io" | 
|  | "strings" | 
|  | ) | 
|  |  | 
|  | type PEMCipher int | 
|  |  | 
|  | // Possible values for the EncryptPEMBlock encryption algorithm. | 
|  | const ( | 
|  | _ PEMCipher = iota | 
|  | PEMCipherDES | 
|  | PEMCipher3DES | 
|  | PEMCipherAES128 | 
|  | PEMCipherAES192 | 
|  | PEMCipherAES256 | 
|  | ) | 
|  |  | 
|  | // rfc1423Algo holds a method for enciphering a PEM block. | 
|  | type rfc1423Algo struct { | 
|  | cipher     PEMCipher | 
|  | name       string | 
|  | cipherFunc func(key []byte) (cipher.Block, error) | 
|  | keySize    int | 
|  | blockSize  int | 
|  | } | 
|  |  | 
|  | // rfc1423Algos holds a slice of the possible ways to encrypt a PEM | 
|  | // block. The ivSize numbers were taken from the OpenSSL source. | 
|  | var rfc1423Algos = []rfc1423Algo{{ | 
|  | cipher:     PEMCipherDES, | 
|  | name:       "DES-CBC", | 
|  | cipherFunc: des.NewCipher, | 
|  | keySize:    8, | 
|  | blockSize:  des.BlockSize, | 
|  | }, { | 
|  | cipher:     PEMCipher3DES, | 
|  | name:       "DES-EDE3-CBC", | 
|  | cipherFunc: des.NewTripleDESCipher, | 
|  | keySize:    24, | 
|  | blockSize:  des.BlockSize, | 
|  | }, { | 
|  | cipher:     PEMCipherAES128, | 
|  | name:       "AES-128-CBC", | 
|  | cipherFunc: aes.NewCipher, | 
|  | keySize:    16, | 
|  | blockSize:  aes.BlockSize, | 
|  | }, { | 
|  | cipher:     PEMCipherAES192, | 
|  | name:       "AES-192-CBC", | 
|  | cipherFunc: aes.NewCipher, | 
|  | keySize:    24, | 
|  | blockSize:  aes.BlockSize, | 
|  | }, { | 
|  | cipher:     PEMCipherAES256, | 
|  | name:       "AES-256-CBC", | 
|  | cipherFunc: aes.NewCipher, | 
|  | keySize:    32, | 
|  | blockSize:  aes.BlockSize, | 
|  | }, | 
|  | } | 
|  |  | 
|  | // deriveKey uses a key derivation function to stretch the password into a key | 
|  | // with the number of bits our cipher requires. This algorithm was derived from | 
|  | // the OpenSSL source. | 
|  | func (c rfc1423Algo) deriveKey(password, salt []byte) []byte { | 
|  | hash := md5.New() | 
|  | out := make([]byte, c.keySize) | 
|  | var digest []byte | 
|  |  | 
|  | for i := 0; i < len(out); i += len(digest) { | 
|  | hash.Reset() | 
|  | hash.Write(digest) | 
|  | hash.Write(password) | 
|  | hash.Write(salt) | 
|  | digest = hash.Sum(digest[:0]) | 
|  | copy(out[i:], digest) | 
|  | } | 
|  | return out | 
|  | } | 
|  |  | 
|  | // IsEncryptedPEMBlock returns if the PEM block is password encrypted. | 
|  | func IsEncryptedPEMBlock(b *pem.Block) bool { | 
|  | _, ok := b.Headers["DEK-Info"] | 
|  | return ok | 
|  | } | 
|  |  | 
|  | // IncorrectPasswordError is returned when an incorrect password is detected. | 
|  | var IncorrectPasswordError = errors.New("x509: decryption password incorrect") | 
|  |  | 
|  | // DecryptPEMBlock takes a password encrypted PEM block and the password used to | 
|  | // encrypt it and returns a slice of decrypted DER encoded bytes. It inspects | 
|  | // the DEK-Info header to determine the algorithm used for decryption. If no | 
|  | // DEK-Info header is present, an error is returned. If an incorrect password | 
|  | // is detected an IncorrectPasswordError is returned. Because of deficiencies | 
|  | // in the encrypted-PEM format, it's not always possible to detect an incorrect | 
|  | // password. In these cases no error will be returned but the decrypted DER | 
|  | // bytes will be random noise. | 
|  | func DecryptPEMBlock(b *pem.Block, password []byte) ([]byte, error) { | 
|  | dek, ok := b.Headers["DEK-Info"] | 
|  | if !ok { | 
|  | return nil, errors.New("x509: no DEK-Info header in block") | 
|  | } | 
|  |  | 
|  | idx := strings.Index(dek, ",") | 
|  | if idx == -1 { | 
|  | return nil, errors.New("x509: malformed DEK-Info header") | 
|  | } | 
|  |  | 
|  | mode, hexIV := dek[:idx], dek[idx+1:] | 
|  | ciph := cipherByName(mode) | 
|  | if ciph == nil { | 
|  | return nil, errors.New("x509: unknown encryption mode") | 
|  | } | 
|  | iv, err := hex.DecodeString(hexIV) | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  | if len(iv) != ciph.blockSize { | 
|  | return nil, errors.New("x509: incorrect IV size") | 
|  | } | 
|  |  | 
|  | // Based on the OpenSSL implementation. The salt is the first 8 bytes | 
|  | // of the initialization vector. | 
|  | key := ciph.deriveKey(password, iv[:8]) | 
|  | block, err := ciph.cipherFunc(key) | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  |  | 
|  | if len(b.Bytes)%block.BlockSize() != 0 { | 
|  | return nil, errors.New("x509: encrypted PEM data is not a multiple of the block size") | 
|  | } | 
|  |  | 
|  | data := make([]byte, len(b.Bytes)) | 
|  | dec := cipher.NewCBCDecrypter(block, iv) | 
|  | dec.CryptBlocks(data, b.Bytes) | 
|  |  | 
|  | // Blocks are padded using a scheme where the last n bytes of padding are all | 
|  | // equal to n. It can pad from 1 to blocksize bytes inclusive. See RFC 1423. | 
|  | // For example: | 
|  | //	[x y z 2 2] | 
|  | //	[x y 7 7 7 7 7 7 7] | 
|  | // If we detect a bad padding, we assume it is an invalid password. | 
|  | dlen := len(data) | 
|  | if dlen == 0 || dlen%ciph.blockSize != 0 { | 
|  | return nil, errors.New("x509: invalid padding") | 
|  | } | 
|  | last := int(data[dlen-1]) | 
|  | if dlen < last { | 
|  | return nil, IncorrectPasswordError | 
|  | } | 
|  | if last == 0 || last > ciph.blockSize { | 
|  | return nil, IncorrectPasswordError | 
|  | } | 
|  | for _, val := range data[dlen-last:] { | 
|  | if int(val) != last { | 
|  | return nil, IncorrectPasswordError | 
|  | } | 
|  | } | 
|  | return data[:dlen-last], nil | 
|  | } | 
|  |  | 
|  | // EncryptPEMBlock returns a PEM block of the specified type holding the | 
|  | // given DER-encoded data encrypted with the specified algorithm and | 
|  | // password. | 
|  | func EncryptPEMBlock(rand io.Reader, blockType string, data, password []byte, alg PEMCipher) (*pem.Block, error) { | 
|  | ciph := cipherByKey(alg) | 
|  | if ciph == nil { | 
|  | return nil, errors.New("x509: unknown encryption mode") | 
|  | } | 
|  | iv := make([]byte, ciph.blockSize) | 
|  | if _, err := io.ReadFull(rand, iv); err != nil { | 
|  | return nil, errors.New("x509: cannot generate IV: " + err.Error()) | 
|  | } | 
|  | // The salt is the first 8 bytes of the initialization vector, | 
|  | // matching the key derivation in DecryptPEMBlock. | 
|  | key := ciph.deriveKey(password, iv[:8]) | 
|  | block, err := ciph.cipherFunc(key) | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  | enc := cipher.NewCBCEncrypter(block, iv) | 
|  | pad := ciph.blockSize - len(data)%ciph.blockSize | 
|  | encrypted := make([]byte, len(data), len(data)+pad) | 
|  | // We could save this copy by encrypting all the whole blocks in | 
|  | // the data separately, but it doesn't seem worth the additional | 
|  | // code. | 
|  | copy(encrypted, data) | 
|  | // See RFC 1423, Section 1.1. | 
|  | for i := 0; i < pad; i++ { | 
|  | encrypted = append(encrypted, byte(pad)) | 
|  | } | 
|  | enc.CryptBlocks(encrypted, encrypted) | 
|  |  | 
|  | return &pem.Block{ | 
|  | Type: blockType, | 
|  | Headers: map[string]string{ | 
|  | "Proc-Type": "4,ENCRYPTED", | 
|  | "DEK-Info":  ciph.name + "," + hex.EncodeToString(iv), | 
|  | }, | 
|  | Bytes: encrypted, | 
|  | }, nil | 
|  | } | 
|  |  | 
|  | func cipherByName(name string) *rfc1423Algo { | 
|  | for i := range rfc1423Algos { | 
|  | alg := &rfc1423Algos[i] | 
|  | if alg.name == name { | 
|  | return alg | 
|  | } | 
|  | } | 
|  | return nil | 
|  | } | 
|  |  | 
|  | func cipherByKey(key PEMCipher) *rfc1423Algo { | 
|  | for i := range rfc1423Algos { | 
|  | alg := &rfc1423Algos[i] | 
|  | if alg.cipher == key { | 
|  | return alg | 
|  | } | 
|  | } | 
|  | return nil | 
|  | } |