diff --git a/go.mod b/go.mod index 178d6186..1e0778ea 100644 --- a/go.mod +++ b/go.mod @@ -23,6 +23,7 @@ require ( ) require ( + github.com/pkg/errors v0.9.1 golang.org/x/crypto v0.26.0 k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 ) @@ -55,7 +56,6 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/openshift/api v3.9.0+incompatible // indirect - github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.18.0 // indirect github.com/prometheus/client_model v0.5.0 // indirect github.com/prometheus/common v0.46.0 // indirect diff --git a/pkg/octavia/amphora_certs.go b/pkg/octavia/amphora_certs.go index 062e57ee..56cf76d8 100644 --- a/pkg/octavia/amphora_certs.go +++ b/pkg/octavia/amphora_certs.go @@ -29,7 +29,6 @@ import ( "github.com/openstack-k8s-operators/lib-common/modules/common/helper" "github.com/openstack-k8s-operators/lib-common/modules/common/secret" octaviav1 "github.com/openstack-k8s-operators/octavia-operator/api/v1beta1" - "go.step.sm/crypto/pemutil" corev1 "k8s.io/api/core/v1" k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -61,9 +60,7 @@ func generateKey(passphrase []byte) (*rsa.PrivateKey, []byte, error) { var pemBlock *pem.Block if passphrase != nil { - pemBlock, err = pemutil.EncryptPKCS8PrivateKey( - rand.Reader, pkcs8Key, passphrase, pemutil.DefaultEncCipher, - ) + pemBlock, err = EncryptPrivateKey(pkcs8Key, passphrase) if err != nil { err = fmt.Errorf("Error encrypting private key: %w", err) return priv, nil, err diff --git a/pkg/octavia/pkcs8_aes.go b/pkg/octavia/pkcs8_aes.go new file mode 100644 index 00000000..f3fe7a2f --- /dev/null +++ b/pkg/octavia/pkcs8_aes.go @@ -0,0 +1,165 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package octavia + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "crypto/sha256" + "encoding/asn1" + "encoding/pem" + "fmt" + + "github.com/pkg/errors" + "golang.org/x/crypto/pbkdf2" +) + +var ( + // key derivation functions + oidPKCS5PBKDF2 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 5, 12} + oidPBES2 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 5, 13} + oidHMACWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 113549, 2, 9} + + // encryption + oidAES256CBC = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 42} +) + +// Encrypted pkcs8 +// Based on https://github.com/youmark/pkcs8 +// MIT license +type prfParam struct { + Algo asn1.ObjectIdentifier + NullParam asn1.RawValue +} + +type pbkdf2Params struct { + Salt []byte + IterationCount int + PrfParam prfParam `asn1:"optional"` +} + +type pbkdf2Algorithms struct { + Algo asn1.ObjectIdentifier + PBKDF2Params pbkdf2Params +} + +type pbkdf2Encs struct { + EncryAlgo asn1.ObjectIdentifier + IV []byte +} + +type pbes2Params struct { + KeyDerivationFunc pbkdf2Algorithms + EncryptionScheme pbkdf2Encs +} + +type encryptedlAlgorithmIdentifier struct { + Algorithm asn1.ObjectIdentifier + Parameters pbes2Params +} + +type encryptedPrivateKeyInfo struct { + Algo encryptedlAlgorithmIdentifier + PrivateKey []byte +} + +func EncryptPrivateKey(data, password []byte) (*pem.Block, error) { + pbkdf2_iterations := 600000 + aes256_keysize := 32 + aes256_blocksize := aes.BlockSize + data_length := len(data) + pad_size := aes256_blocksize - data_length%aes256_blocksize + // encrypted_size := data_length + pad_size + + // Generate salt using random data + pbkdf2_salt := make([]byte, 16) + _, err := rand.Read(pbkdf2_salt) + if err != nil { + err = fmt.Errorf("error generating random data for salt for private key encryption: %w", err) + return nil, err + } + + iv := make([]byte, aes256_blocksize) + _, err = rand.Read(iv) + if err != nil { + err = fmt.Errorf("error generating random data for init vector for private key encryption: %w", err) + return nil, err + } + + key := pbkdf2.Key(password, pbkdf2_salt, pbkdf2_iterations, + aes256_keysize, sha256.New) + encrypted_block, err := aes.NewCipher(key) + if err != nil { + err = fmt.Errorf("error creating new cipher block for private key encryption: %w", err) + return nil, err + } + + // crypt_source := make([]byte, encrypted_size) + // copy(crypt_source, data) + // // Set padding data according to RFC1423, 1.1 + // for i := data_length; i < encrypted_size; i++ { + // crypt_source[i] = byte(pad_size) + // } + // encrypted := make([]byte, encrypted_size) + + // encrypter := cipher.NewCBCEncrypter(encrypted_block, iv) + // encrypter.CryptBlocks(encrypted, crypt_source) + + encrypted := make([]byte, len(data), len(data)+pad_size) + // 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_size; i++ { + encrypted = append(encrypted, byte(pad_size)) + } + encrypter := cipher.NewCBCEncrypter(encrypted_block, iv) + encrypter.CryptBlocks(encrypted, encrypted) + + // Build encrypted ans1 data + pki := encryptedPrivateKeyInfo{ + Algo: encryptedlAlgorithmIdentifier{ + Algorithm: oidPBES2, + Parameters: pbes2Params{ + KeyDerivationFunc: pbkdf2Algorithms{ + Algo: oidPKCS5PBKDF2, + PBKDF2Params: pbkdf2Params{ + Salt: pbkdf2_salt, + IterationCount: pbkdf2_iterations, + PrfParam: prfParam{ + Algo: oidHMACWithSHA256, + }, + }, + }, + EncryptionScheme: pbkdf2Encs{ + EncryAlgo: oidAES256CBC, + IV: iv, + }, + }, + }, + PrivateKey: encrypted, + } + + b, err := asn1.Marshal(pki) + if err != nil { + return nil, errors.Wrap(err, "error marshaling encrypted key") + } + return &pem.Block{ + Type: "ENCRYPTED PRIVATE KEY", + Bytes: b, + }, nil +}