From 0f7a9070eed673cab61a5a34c0cb5d23fd8a1fdb Mon Sep 17 00:00:00 2001 From: Emman Date: Sun, 10 Apr 2022 09:56:28 +0800 Subject: [PATCH] pkcs1 --- smx509/pem_decrypt.go | 268 ++++++++++++++++++++++++++++++++++++ smx509/pem_decrypt_test.go | 271 +++++++++++++++++++++++++++++++++++++ 2 files changed, 539 insertions(+) create mode 100644 smx509/pem_decrypt.go create mode 100644 smx509/pem_decrypt_test.go diff --git a/smx509/pem_decrypt.go b/smx509/pem_decrypt.go new file mode 100644 index 0000000..b17c794 --- /dev/null +++ b/smx509/pem_decrypt.go @@ -0,0 +1,268 @@ +// 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 smx509 + +// 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" + + "github.com/emmansun/gmsm/sm4" +) + +type PEMCipher int + +// Possible values for the EncryptPEMBlock encryption algorithm. +const ( + _ PEMCipher = iota + PEMCipherDES + PEMCipher3DES + PEMCipherAES128 + PEMCipherAES192 + PEMCipherAES256 + PEMCipherSM4 +) + +// 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, +}, { + cipher: PEMCipherSM4, + name: "SM4-CBC", + cipherFunc: sm4.NewCipher, + keySize: 16, + blockSize: sm4.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 whether the PEM block is password encrypted +// according to RFC 1423. +// +// Deprecated: Legacy PEM encryption as specified in RFC 1423 is insecure by +// design. Since it does not authenticate the ciphertext, it is vulnerable to +// padding oracle attacks that can let an attacker recover the plaintext. +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 PEM block encrypted according to RFC 1423 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 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. +// +// Deprecated: Legacy PEM encryption as specified in RFC 1423 is insecure by +// design. Since it does not authenticate the ciphertext, it is vulnerable to +// padding oracle attacks that can let an attacker recover the plaintext. +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") + } + // [Emman] strings.Cut is supported since golang 1.18. + // + // mode, hexIV, ok := strings.Cut(dek, ",") + // if !ok { + // return nil, errors.New("x509: malformed DEK-Info header") + // } + // + 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 according to RFC 1423. +// +// Deprecated: Legacy PEM encryption as specified in RFC 1423 is insecure by +// design. Since it does not authenticate the ciphertext, it is vulnerable to +// padding oracle attacks that can let an attacker recover the plaintext. +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 == strings.ToUpper(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 +} diff --git a/smx509/pem_decrypt_test.go b/smx509/pem_decrypt_test.go new file mode 100644 index 0000000..508a680 --- /dev/null +++ b/smx509/pem_decrypt_test.go @@ -0,0 +1,271 @@ +// 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 smx509 + +import ( + "bytes" + "crypto/rand" + "crypto/x509" + "encoding/base64" + "encoding/pem" + "fmt" + "strings" + "testing" +) + +func TestDecrypt(t *testing.T) { + for i, data := range testData { + t.Logf("test %v. %v", i, data.kind) + block, rest := pem.Decode(data.pemData) + if len(rest) > 0 { + t.Error("extra data") + } + der, err := DecryptPEMBlock(block, data.password) + if err != nil { + t.Error("decrypt failed: ", err) + continue + } + if _, err := x509.ParsePKCS1PrivateKey(der); err != nil { + t.Error("invalid private key: ", err) + } + plainDER, err := base64.StdEncoding.DecodeString(data.plainDER) + if err != nil { + t.Fatal("cannot decode test DER data: ", err) + } + if !bytes.Equal(der, plainDER) { + t.Error("data mismatch") + } + } +} + +func TestEncrypt(t *testing.T) { + for i, data := range testData { + t.Logf("test %v. %v", i, data.kind) + plainDER, err := base64.StdEncoding.DecodeString(data.plainDER) + if err != nil { + t.Fatal("cannot decode test DER data: ", err) + } + password := []byte("kremvax1") + block, err := EncryptPEMBlock(rand.Reader, "RSA PRIVATE KEY", plainDER, password, data.kind) + if err != nil { + t.Error("encrypt: ", err) + continue + } + if !IsEncryptedPEMBlock(block) { + t.Error("PEM block does not appear to be encrypted") + } + if block.Type != "RSA PRIVATE KEY" { + t.Errorf("unexpected block type; got %q want %q", block.Type, "RSA PRIVATE KEY") + } + if block.Headers["Proc-Type"] != "4,ENCRYPTED" { + t.Errorf("block does not have correct Proc-Type header") + } + der, err := DecryptPEMBlock(block, password) + if err != nil { + t.Error("decrypt: ", err) + continue + } + if !bytes.Equal(der, plainDER) { + t.Errorf("data mismatch") + } + pemContent := string(pem.EncodeToMemory(block)) + fmt.Printf("%s\n", pemContent) + } +} + +var testData = []struct { + kind PEMCipher + password []byte + pemData []byte + plainDER string +}{ + { + kind: PEMCipherDES, + password: []byte("asdf"), + pemData: []byte(testingKey(` +-----BEGIN RSA TESTING KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-CBC,34F09A4FC8DE22B5 +WXxy8kbZdiZvANtKvhmPBLV7eVFj2A5z6oAxvI9KGyhG0ZK0skfnt00C24vfU7m5 +ICXeoqP67lzJ18xCzQfHjDaBNs53DSDT+Iz4e8QUep1xQ30+8QKX2NA2coee3nwc +6oM1cuvhNUDemBH2i3dKgMVkfaga0zQiiOq6HJyGSncCMSruQ7F9iWEfRbFcxFCx +qtHb1kirfGKEtgWTF+ynyco6+2gMXNu70L7nJcnxnV/RLFkHt7AUU1yrclxz7eZz +XOH9VfTjb52q/I8Suozq9coVQwg4tXfIoYUdT//O+mB7zJb9HI9Ps77b9TxDE6Gm +4C9brwZ3zg2vqXcwwV6QRZMtyll9rOpxkbw6NPlpfBqkc3xS51bbxivbO/Nve4KD +r12ymjFNF4stXCfJnNqKoZ50BHmEEUDu5Wb0fpVn82XrGw7CYc4iug== +-----END RSA TESTING KEY-----`)), + plainDER: ` +MIIBPAIBAAJBAPASZe+tCPU6p80AjHhDkVsLYa51D35e/YGa8QcZyooeZM8EHozo +KD0fNiKI+53bHdy07N+81VQ8/ejPcRoXPlsCAwEAAQJBAMTxIuSq27VpR+zZ7WJf +c6fvv1OBvpMZ0/d1pxL/KnOAgq2rD5hDtk9b0LGhTPgQAmrrMTKuSeGoIuYE+gKQ +QvkCIQD+GC1m+/do+QRurr0uo46Kx1LzLeSCrjBk34wiOp2+dwIhAPHfTLRXS2fv +7rljm0bYa4+eDZpz+E8RcXEgzhhvcQQ9AiAI5eHZJGOyml3MXnQjiPi55WcDOw0w +glcRgT6QCEtz2wIhANSyqaFtosIkHKqrDUGfz/bb5tqMYTAnBruVPaf/WEOBAiEA +9xORWeRG1tRpso4+dYy4KdDkuLPIO01KY6neYGm3BCM=`, + }, + { + kind: PEMCipher3DES, + password: []byte("asdf"), + pemData: []byte(testingKey(` +-----BEGIN RSA TESTING KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-EDE3-CBC,C1F4A6A03682C2C7 +0JqVdBEH6iqM7drTkj+e2W/bE3LqakaiWhb9WUVonFkhyu8ca/QzebY3b5gCvAZQ +YwBvDcT/GHospKqPx+cxDHJNsUASDZws6bz8ZXWJGwZGExKzr0+Qx5fgXn44Ms3x +8g1ENFuTXtxo+KoNK0zuAMAqp66Llcds3Fjl4XR18QaD0CrVNAfOdgATWZm5GJxk +Fgx5f84nT+/ovvreG+xeOzWgvtKo0UUZVrhGOgfKLpa57adumcJ6SkUuBtEFpZFB +ldw5w7WC7d13x2LsRkwo8ZrDKgIV+Y9GNvhuCCkTzNP0V3gNeJpd201HZHR+9n3w +3z0VjR/MGqsfcy1ziEWMNOO53At3zlG6zP05aHMnMcZoVXadEK6L1gz++inSSDCq +gI0UJP4e3JVB7AkgYymYAwiYALAkoEIuanxoc50njJk= +-----END RSA TESTING KEY-----`)), + plainDER: ` +MIIBOwIBAAJBANOCXKdoNS/iP/MAbl9cf1/SF3P+Ns7ZeNL27CfmDh0O6Zduaax5 +NBiumd2PmjkaCu7lQ5JOibHfWn+xJsc3kw0CAwEAAQJANX/W8d1Q/sCqzkuAn4xl +B5a7qfJWaLHndu1QRLNTRJPn0Ee7OKJ4H0QKOhQM6vpjRrz+P2u9thn6wUxoPsef +QQIhAP/jCkfejFcy4v15beqKzwz08/tslVjF+Yq41eJGejmxAiEA05pMoqfkyjcx +fyvGhpoOyoCp71vSGUfR2I9CR65oKh0CIC1Msjs66LlfJtQctRq6bCEtFCxEcsP+ +eEjYo/Sk6WphAiEAxpgWPMJeU/shFT28gS+tmhjPZLpEoT1qkVlC14u0b3ECIQDX +tZZZxCtPAm7shftEib0VU77Lk8MsXJcx2C4voRsjEw==`, + }, + { + kind: PEMCipherAES128, + password: []byte("asdf"), + pemData: []byte(testingKey(` +-----BEGIN RSA TESTING KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-128-CBC,D4492E793FC835CC038A728ED174F78A +EyfQSzXSjv6BaNH+NHdXRlkHdimpF9izWlugVJAPApgXrq5YldPe2aGIOFXyJ+QE +ZIG20DYqaPzJRjTEbPNZ6Es0S2JJ5yCpKxwJuDkgJZKtF39Q2i36JeGbSZQIuWJE +GZbBpf1jDH/pr0iGonuAdl2PCCZUiy+8eLsD2tyviHUkFLOB+ykYoJ5t8ngZ/B6D +33U43LLb7+9zD4y3Q9OVHqBFGyHcxCY9+9Qh4ZnFp7DTf6RY5TNEvE3s4g6aDpBs +3NbvRVvYTgs8K9EPk4K+5R+P2kD8J8KvEIGxVa1vz8QoCJ/jr7Ka2rvNgPCex5/E +080LzLHPCrXKdlr/f50yhNWq08ZxMWQFkui+FDHPDUaEELKAXV8/5PDxw80Rtybo +AVYoCVIbZXZCuCO81op8UcOgEpTtyU5Lgh3Mw5scQL0= +-----END RSA TESTING KEY-----`)), + plainDER: ` +MIIBOgIBAAJBAMBlj5FxYtqbcy8wY89d/S7n0+r5MzD9F63BA/Lpl78vQKtdJ5dT +cDGh/rBt1ufRrNp0WihcmZi7Mpl/3jHjiWECAwEAAQJABNOHYnKhtDIqFYj1OAJ3 +k3GlU0OlERmIOoeY/cL2V4lgwllPBEs7r134AY4wMmZSBUj8UR/O4SNO668ElKPE +cQIhAOuqY7/115x5KCdGDMWi+jNaMxIvI4ETGwV40ykGzqlzAiEA0P9oEC3m9tHB +kbpjSTxaNkrXxDgdEOZz8X0uOUUwHNsCIAwzcSCiGLyYJTULUmP1ESERfW1mlV78 +XzzESaJpIM/zAiBQkSTcl9VhcJreQqvjn5BnPZLP4ZHS4gPwJAGdsj5J4QIhAOVR +B3WlRNTXR2WsJ5JdByezg9xzdXzULqmga0OE339a`, + }, + { + kind: PEMCipherAES192, + password: []byte("asdf"), + pemData: []byte(testingKey(` +-----BEGIN RSA TESTING KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-192-CBC,E2C9FB02BCA23ADE1829F8D8BC5F5369 +cqVslvHqDDM6qwU6YjezCRifXmKsrgEev7ng6Qs7UmDJOpHDgJQZI9fwMFUhIyn5 +FbCu1SHkLMW52Ld3CuEqMnzWMlhPrW8tFvUOrMWPYSisv7nNq88HobZEJcUNL2MM +Y15XmHW6IJwPqhKyLHpWXyOCVEh4ODND2nV15PCoi18oTa475baxSk7+1qH7GuIs +Rb7tshNTMqHbCpyo9Rn3UxeFIf9efdl8YLiMoIqc7J8E5e9VlbeQSdLMQOgDAQJG +ReUtTw8exmKsY4gsSjhkg5uiw7/ZB1Ihto0qnfQJgjGc680qGkT1d6JfvOfeYAk6 +xn5RqS/h8rYAYm64KnepfC9vIujo4NqpaREDmaLdX5MJPQ+SlytITQvgUsUq3q/t +Ss85xjQEZH3hzwjQqdJvmA4hYP6SUjxYpBM+02xZ1Xw= +-----END RSA TESTING KEY-----`)), + plainDER: ` +MIIBOwIBAAJBAMGcRrZiNNmtF20zyS6MQ7pdGx17aFDl+lTl+qnLuJRUCMUG05xs +OmxmL/O1Qlf+bnqR8Bgg65SfKg21SYuLhiMCAwEAAQJBAL94uuHyO4wux2VC+qpj +IzPykjdU7XRcDHbbvksf4xokSeUFjjD3PB0Qa83M94y89ZfdILIqS9x5EgSB4/lX +qNkCIQD6cCIqLfzq/lYbZbQgAAjpBXeQVYsbvVtJrPrXJAlVVQIhAMXpDKMeFPMn +J0g2rbx1gngx0qOa5r5iMU5w/noN4W2XAiBjf+WzCG5yFvazD+dOx3TC0A8+4x3P +uZ3pWbaXf5PNuQIgAcdXarvhelH2w2piY1g3BPeFqhzBSCK/yLGxR82KIh8CIQDD ++qGKsd09NhQ/G27y/DARzOYtml1NvdmCQAgsDIIOLA==`, + }, + { + kind: PEMCipherAES256, + password: []byte("asdf"), + pemData: []byte(testingKey(` +-----BEGIN RSA TESTING KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-256-CBC,8E7ED5CD731902CE938957A886A5FFBD +4Mxr+KIzRVwoOP0wwq6caSkvW0iS+GE2h2Ov/u+n9ZTMwL83PRnmjfjzBgfRZLVf +JFPXxUK26kMNpIdssNnqGOds+DhB+oSrsNKoxgxSl5OBoYv9eJTVYm7qOyAFIsjr +DRKAcjYCmzfesr7PVTowwy0RtHmYwyXMGDlAzzZrEvaiySFFmMyKKvtoavwaFoc7 +Pz3RZScwIuubzTGJ1x8EzdffYOsdCa9Mtgpp3L136+23dOd6L/qK2EG2fzrJSHs/ +2XugkleBFSMKzEp9mxXKRfa++uidQvMZTFLDK9w5YjrRvMBo/l2BoZIsq0jAIE1N +sv5Z/KwlX+3MDEpPQpUwGPlGGdLnjI3UZ+cjgqBcoMiNc6HfgbBgYJSU6aDSHuCk +clCwByxWkBNgJ2GrkwNrF26v+bGJJJNR4SKouY1jQf0= +-----END RSA TESTING KEY-----`)), + plainDER: ` +MIIBOgIBAAJBAKy3GFkstoCHIEeUU/qO8207m8WSrjksR+p9B4tf1w5k+2O1V/GY +AQ5WFCApItcOkQe/I0yZZJk/PmCqMzSxrc8CAwEAAQJAOCAz0F7AW9oNelVQSP8F +Sfzx7O1yom+qWyAQQJF/gFR11gpf9xpVnnyu1WxIRnDUh1LZwUsjwlDYb7MB74id +oQIhANPcOiLwOPT4sIUpRM5HG6BF1BI7L77VpyGVk8xNP7X/AiEA0LMHZtk4I+lJ +nClgYp4Yh2JZ1Znbu7IoQMCEJCjwKDECIGd8Dzm5tViTkUW6Hs3Tlf73nNs65duF +aRnSglss8I3pAiEAonEnKruawgD8RavDFR+fUgmQiPz4FnGGeVgfwpGG1JECIBYq +PXHYtPqxQIbD2pScR5qum7iGUh11lEUPkmt+2uqS`, + }, + { + kind: PEMCipherSM4, + password: []byte("asdf"), + pemData: []byte(testingKey(` +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: sm4-cbc,4cf10431aa24f6a4c1490ce93ff086ef + +OSUW2Zbd7E+MKulT70SpQIsR0FdV1MP41OKvxT3XfOMZDk3naDyJoBFon8FEftlN +xKNkRnzZTmzteRzySbNQBKIwwHnak8Vcqwy5UB61+RWC5rk+kDRiwG6sZp6HFs4Z +GLrzVQlh6Ag/ecckhms0FfLbmKLeCwG1MlLaI8ZHvZSQ5R1JPdF43rmxkoA6RTg+ +SWWf2T5T3DyOOPmgaxrRQJZURJuJZK3eyAvU395t1xDsvdgOIQlmgEA6nmrNcJQt +cn88qJ6NpRpVjdDupnOynJ726diIcugYwLcxhMir5eSNkMkjR5SZdEwuFgcbicvc +TkCGwp6aSyLYpHsIwTJupY8Lk32WuqpC34K/IJlNDichVyeLILEj9GqUGJNm/1N3 +FKMobhAYj14+AsMKzR+4rOsYeJxX+8ws3D5RbL7nTKs= +-----END RSA PRIVATE KEY-----`)), + plainDER: ` +MIIBOgIBAAJBAMBlj5FxYtqbcy8wY89d/S7n0+r5MzD9F63BA/Lpl78vQKtdJ5dT +cDGh/rBt1ufRrNp0WihcmZi7Mpl/3jHjiWECAwEAAQJABNOHYnKhtDIqFYj1OAJ3 +k3GlU0OlERmIOoeY/cL2V4lgwllPBEs7r134AY4wMmZSBUj8UR/O4SNO668ElKPE +cQIhAOuqY7/115x5KCdGDMWi+jNaMxIvI4ETGwV40ykGzqlzAiEA0P9oEC3m9tHB +kbpjSTxaNkrXxDgdEOZz8X0uOUUwHNsCIAwzcSCiGLyYJTULUmP1ESERfW1mlV78 +XzzESaJpIM/zAiBQkSTcl9VhcJreQqvjn5BnPZLP4ZHS4gPwJAGdsj5J4QIhAOVR +B3WlRNTXR2WsJ5JdByezg9xzdXzULqmga0OE339a`, + }, + { + // generated with: + // openssl genrsa -aes128 -passout pass:asdf -out server.orig.key 128 + kind: PEMCipherAES128, + password: []byte("asdf"), + pemData: []byte(testingKey(` +-----BEGIN RSA TESTING KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-128-CBC,74611ABC2571AF11B1BF9B69E62C89E7 +6ei/MlytjE0FFgZOGQ+jrwomKfpl8kdefeE0NSt/DMRrw8OacHAzBNi3pPEa0eX3 +eND9l7C9meCirWovjj9QWVHrXyugFuDIqgdhQ8iHTgCfF3lrmcttVrbIfMDw+smD +hTP8O1mS/MHl92NE0nhv0w== +-----END RSA TESTING KEY-----`)), + plainDER: ` +MGMCAQACEQC6ssxmYuauuHGOCDAI54RdAgMBAAECEQCWIn6Yv2O+kBcDF7STctKB +AgkA8SEfu/2i3g0CCQDGNlXbBHX7kQIIK3Ww5o0cYbECCQDCimPb0dYGsQIIeQ7A +jryIst8=`, + }, +} + +var incompleteBlockPEM = testingKey(` +-----BEGIN RSA TESTING KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-128-CBC,74611ABC2571AF11B1BF9B69E62C89E7 +6L8yXK2MTQUWBk4ZD6OvCiYp+mXyR1594TQ1K38MxGvDw5pwcDME2Lek8RrR5fd40P2XsL2Z4KKt +ai+OP1BZUetfK6AW4MiqB2FDyIdOAJ8XeWuZy21Wtsh8wPD6yYOFM/w7WZL8weX3Y0TSeG/T +-----END RSA TESTING KEY-----`) + +func TestIncompleteBlock(t *testing.T) { + // incompleteBlockPEM contains ciphertext that is not a multiple of the + // block size. This previously panicked. See #11215. + block, _ := pem.Decode([]byte(incompleteBlockPEM)) + _, err := DecryptPEMBlock(block, []byte("foo")) + if err == nil { + t.Fatal("Bad PEM data decrypted successfully") + } + const expectedSubstr = "block size" + if e := err.Error(); !strings.Contains(e, expectedSubstr) { + t.Fatalf("Expected error containing %q but got: %q", expectedSubstr, e) + } +} + +func testingKey(s string) string { return strings.ReplaceAll(s, "TESTING KEY", "PRIVATE KEY") }