diff --git a/go.mod b/go.mod index b2f6438..f79acd1 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/prometheus/client_model v0.2.0 golang.org/x/crypto v0.24.0 golang.org/x/sys v0.21.0 - software.sslmate.com/src/go-pkcs12 v0.0.0-20200830195227-52f69702a001 + software.sslmate.com/src/go-pkcs12 v0.4.0 ) require ( diff --git a/go.sum b/go.sum index f2ca3f7..eeb2ad6 100644 --- a/go.sum +++ b/go.sum @@ -151,3 +151,5 @@ gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= software.sslmate.com/src/go-pkcs12 v0.0.0-20200830195227-52f69702a001 h1:AVd6O+azYjVQYW1l55IqkbL8/JxjrLtO6q4FCmV8N5c= software.sslmate.com/src/go-pkcs12 v0.0.0-20200830195227-52f69702a001/go.mod h1:/xvNRWUqm0+/ZMiF4EX00vrSCMsE4/NHb+Pt3freEeQ= +software.sslmate.com/src/go-pkcs12 v0.4.0 h1:H2g08FrTvSFKUj+D309j1DPfk5APnIdAQAB8aEykJ5k= +software.sslmate.com/src/go-pkcs12 v0.4.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI= diff --git a/vendor/modules.txt b/vendor/modules.txt index 7d060fd..0969174 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -81,7 +81,7 @@ google.golang.org/protobuf/runtime/protoimpl google.golang.org/protobuf/types/known/anypb google.golang.org/protobuf/types/known/durationpb google.golang.org/protobuf/types/known/timestamppb -# software.sslmate.com/src/go-pkcs12 v0.0.0-20200830195227-52f69702a001 -## explicit +# software.sslmate.com/src/go-pkcs12 v0.4.0 +## explicit; go 1.19 software.sslmate.com/src/go-pkcs12 software.sslmate.com/src/go-pkcs12/internal/rc2 diff --git a/vendor/software.sslmate.com/src/go-pkcs12/README.md b/vendor/software.sslmate.com/src/go-pkcs12/README.md index 011af5f..b0af9ab 100644 --- a/vendor/software.sslmate.com/src/go-pkcs12/README.md +++ b/vendor/software.sslmate.com/src/go-pkcs12/README.md @@ -1,6 +1,6 @@ # package pkcs12 -[![GoDoc](https://godoc.org/software.sslmate.com/src/go-pkcs12?status.svg)](https://godoc.org/software.sslmate.com/src/go-pkcs12) +[![Documentation](https://pkg.go.dev/badge/software.sslmate.com/src/go-pkcs12)](https://pkg.go.dev/software.sslmate.com/src/go-pkcs12) import "software.sslmate.com/src/go-pkcs12" @@ -11,14 +11,12 @@ do not support newer formats. Since PKCS#12 uses weak encryption primitives, it SHOULD NOT be used for new applications. Note that only DER-encoded PKCS#12 files are supported, even though PKCS#12 -allows BER encoding. This is becuase encoding/asn1 only supports DER. +allows BER encoding. This is because encoding/asn1 only supports DER. This package is forked from `golang.org/x/crypto/pkcs12`, which is frozen. The implementation is distilled from https://tools.ietf.org/html/rfc7292 and referenced documents. -This repository holds supplementary Go cryptography libraries. - ## Import Path Note that although the source code and issue tracker for this package are hosted @@ -28,11 +26,6 @@ on GitHub, the import path is: Please be sure to use this path when you `go get` and `import` this package. -## Download/Install - -The easiest way to install is to run `go get -u software.sslmate.com/src/go-pkcs12`. You -can also manually git clone the repository to `$GOPATH/src/software.sslmate.com/src/go-pkcs12`. - ## Report Issues / Send Patches Open an issue or PR at https://github.com/SSLMate/go-pkcs12 diff --git a/vendor/software.sslmate.com/src/go-pkcs12/bmp-string.go b/vendor/software.sslmate.com/src/go-pkcs12/bmp-string.go index 233b8b6..2bfbf2e 100644 --- a/vendor/software.sslmate.com/src/go-pkcs12/bmp-string.go +++ b/vendor/software.sslmate.com/src/go-pkcs12/bmp-string.go @@ -9,14 +9,27 @@ import ( "unicode/utf16" ) -// bmpString returns s encoded in UCS-2 with a zero terminator. +// bmpStringZeroTerminated returns s encoded in UCS-2 with a zero terminator. +func bmpStringZeroTerminated(s string) ([]byte, error) { + // References: + // https://tools.ietf.org/html/rfc7292#appendix-B.1 + // The above RFC provides the info that BMPStrings are NULL terminated. + + ret, err := bmpString(s) + if err != nil { + return nil, err + } + + return append(ret, 0, 0), nil +} + +// bmpString returns s encoded in UCS-2 func bmpString(s string) ([]byte, error) { // References: // https://tools.ietf.org/html/rfc7292#appendix-B.1 // https://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_Multilingual_Plane // - non-BMP characters are encoded in UTF 16 by using a surrogate pair of 16-bit codes // EncodeRune returns 0xfffd if the rune does not need special encoding - // - the above RFC provides the info that BMPStrings are NULL terminated. ret := make([]byte, 0, 2*len(s)+2) @@ -27,7 +40,7 @@ func bmpString(s string) ([]byte, error) { ret = append(ret, byte(r/256), byte(r%256)) } - return append(ret, 0, 0), nil + return ret, nil } func decodeBMPString(bmpString []byte) (string, error) { diff --git a/vendor/software.sslmate.com/src/go-pkcs12/crypto.go b/vendor/software.sslmate.com/src/go-pkcs12/crypto.go index 70425f8..5d564df 100644 --- a/vendor/software.sslmate.com/src/go-pkcs12/crypto.go +++ b/vendor/software.sslmate.com/src/go-pkcs12/crypto.go @@ -16,6 +16,7 @@ import ( "encoding/asn1" "errors" "hash" + "io" "golang.org/x/crypto/pbkdf2" "software.sslmate.com/src/go-pkcs12/internal/rc2" @@ -23,11 +24,14 @@ import ( var ( oidPBEWithSHAAnd3KeyTripleDESCBC = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 1, 3}) + oidPBEWithSHAAnd128BitRC2CBC = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 1, 5}) oidPBEWithSHAAnd40BitRC2CBC = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 1, 6}) oidPBES2 = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 5, 13}) oidPBKDF2 = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 5, 12}) oidHmacWithSHA1 = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 2, 7}) oidHmacWithSHA256 = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 2, 9}) + oidAES128CBC = asn1.ObjectIdentifier([]int{2, 16, 840, 1, 101, 3, 4, 1, 2}) + oidAES192CBC = asn1.ObjectIdentifier([]int{2, 16, 840, 1, 101, 3, 4, 1, 22}) oidAES256CBC = asn1.ObjectIdentifier([]int{2, 16, 840, 1, 101, 3, 4, 1, 42}) ) @@ -55,6 +59,20 @@ func (shaWithTripleDESCBC) deriveIV(salt, password []byte, iterations int) []byt return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 2, 8) } +type shaWith128BitRC2CBC struct{} + +func (shaWith128BitRC2CBC) create(key []byte) (cipher.Block, error) { + return rc2.New(key, len(key)*8) +} + +func (shaWith128BitRC2CBC) deriveKey(salt, password []byte, iterations int) []byte { + return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 1, 16) +} + +func (shaWith128BitRC2CBC) deriveIV(salt, password []byte, iterations int) []byte { + return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 2, 8) +} + type shaWith40BitRC2CBC struct{} func (shaWith40BitRC2CBC) create(key []byte) (cipher.Block, error) { @@ -80,6 +98,8 @@ func pbeCipherFor(algorithm pkix.AlgorithmIdentifier, password []byte) (cipher.B switch { case algorithm.Algorithm.Equal(oidPBEWithSHAAnd3KeyTripleDESCBC): cipherType = shaWithTripleDESCBC{} + case algorithm.Algorithm.Equal(oidPBEWithSHAAnd128BitRC2CBC): + cipherType = shaWith128BitRC2CBC{} case algorithm.Algorithm.Equal(oidPBEWithSHAAnd40BitRC2CBC): cipherType = shaWith40BitRC2CBC{} case algorithm.Algorithm.Equal(oidPBES2): @@ -146,6 +166,7 @@ func pbDecrypt(info decryptable, password []byte) (decrypted []byte, err error) if len(decrypted) < psLen { return nil, ErrDecryption } + ps := decrypted[len(decrypted)-psLen:] decrypted = decrypted[:len(decrypted)-psLen] if bytes.Compare(ps, bytes.Repeat([]byte{byte(psLen)}, psLen)) != 0 { @@ -155,30 +176,30 @@ func pbDecrypt(info decryptable, password []byte) (decrypted []byte, err error) return } -// PBES2-params ::= SEQUENCE { -// keyDerivationFunc AlgorithmIdentifier {{PBES2-KDFs}}, -// encryptionScheme AlgorithmIdentifier {{PBES2-Encs}} -// } +// PBES2-params ::= SEQUENCE { +// keyDerivationFunc AlgorithmIdentifier {{PBES2-KDFs}}, +// encryptionScheme AlgorithmIdentifier {{PBES2-Encs}} +// } type pbes2Params struct { Kdf pkix.AlgorithmIdentifier EncryptionScheme pkix.AlgorithmIdentifier } -// PBKDF2-params ::= SEQUENCE { -// salt CHOICE { -// specified OCTET STRING, -// otherSource AlgorithmIdentifier {{PBKDF2-SaltSources}} -// }, -// iterationCount INTEGER (1..MAX), -// keyLength INTEGER (1..MAX) OPTIONAL, -// prf AlgorithmIdentifier {{PBKDF2-PRFs}} DEFAULT -// algid-hmacWithSHA1 -// } +// PBKDF2-params ::= SEQUENCE { +// salt CHOICE { +// specified OCTET STRING, +// otherSource AlgorithmIdentifier {{PBKDF2-SaltSources}} +// }, +// iterationCount INTEGER (1..MAX), +// keyLength INTEGER (1..MAX) OPTIONAL, +// prf AlgorithmIdentifier {{PBKDF2-PRFs}} DEFAULT +// algid-hmacWithSHA1 +// } type pbkdf2Params struct { Salt asn1.RawValue Iterations int - KeyLength int `asn1:"optional"` - Prf pkix.AlgorithmIdentifier + KeyLength int `asn1:"optional"` + Prf pkix.AlgorithmIdentifier `asn1:"optional"` } func pbes2CipherFor(algorithm pkix.AlgorithmIdentifier, password []byte) (cipher.Block, []byte, error) { @@ -207,22 +228,29 @@ func pbes2CipherFor(algorithm pkix.AlgorithmIdentifier, password []byte) (cipher prf = sha1.New case kdfParams.Prf.Algorithm.Equal(asn1.ObjectIdentifier([]int{})): prf = sha1.New + default: + return nil, nil, NotImplementedError("pbes2 prf " + kdfParams.Prf.Algorithm.String() + " is not supported") } - key := pbkdf2.Key(password, kdfParams.Salt.Bytes, kdfParams.Iterations, 32, prf) - iv := params.EncryptionScheme.Parameters.Bytes - - var block cipher.Block + var keyLen int switch { case params.EncryptionScheme.Algorithm.Equal(oidAES256CBC): - b, err := aes.NewCipher(key) - if err != nil { - return nil, nil, err - } - block = b + keyLen = 32 + case params.EncryptionScheme.Algorithm.Equal(oidAES192CBC): + keyLen = 24 + case params.EncryptionScheme.Algorithm.Equal(oidAES128CBC): + keyLen = 16 default: return nil, nil, NotImplementedError("pbes2 algorithm " + params.EncryptionScheme.Algorithm.String() + " is not supported") } + + key := pbkdf2.Key(password, kdfParams.Salt.Bytes, kdfParams.Iterations, keyLen, prf) + iv := params.EncryptionScheme.Parameters.Bytes + + block, err := aes.NewCipher(key) + if err != nil { + return nil, nil, err + } return block, iv, nil } @@ -263,3 +291,31 @@ type encryptable interface { Algorithm() pkix.AlgorithmIdentifier SetData([]byte) } + +func makePBES2Parameters(rand io.Reader, salt []byte, iterations int) ([]byte, error) { + var err error + + randomIV := make([]byte, 16) + if _, err := rand.Read(randomIV); err != nil { + return nil, err + } + + var kdfparams pbkdf2Params + if kdfparams.Salt.FullBytes, err = asn1.Marshal(salt); err != nil { + return nil, err + } + kdfparams.Iterations = iterations + kdfparams.Prf.Algorithm = oidHmacWithSHA256 + + var params pbes2Params + params.Kdf.Algorithm = oidPBKDF2 + if params.Kdf.Parameters.FullBytes, err = asn1.Marshal(kdfparams); err != nil { + return nil, err + } + params.EncryptionScheme.Algorithm = oidAES256CBC + if params.EncryptionScheme.Parameters.FullBytes, err = asn1.Marshal(randomIV); err != nil { + return nil, err + } + + return asn1.Marshal(params) +} diff --git a/vendor/software.sslmate.com/src/go-pkcs12/mac.go b/vendor/software.sslmate.com/src/go-pkcs12/mac.go index b8a3439..587904f 100644 --- a/vendor/software.sslmate.com/src/go-pkcs12/mac.go +++ b/vendor/software.sslmate.com/src/go-pkcs12/mac.go @@ -31,7 +31,7 @@ var ( oidSHA256 = asn1.ObjectIdentifier([]int{2, 16, 840, 1, 101, 3, 4, 2, 1}) ) -func verifyMac(macData *macData, message, password []byte) error { +func doMac(macData *macData, message, password []byte) ([]byte, error) { var hFn func() hash.Hash var key []byte switch { @@ -42,13 +42,19 @@ func verifyMac(macData *macData, message, password []byte) error { hFn = sha256.New key = pbkdf(sha256Sum, 32, 64, macData.MacSalt, password, macData.Iterations, 3, 32) default: - return NotImplementedError("unknown digest algorithm: " + macData.Mac.Algorithm.Algorithm.String()) + return nil, NotImplementedError("unknown digest algorithm: " + macData.Mac.Algorithm.Algorithm.String()) } mac := hmac.New(hFn, key) mac.Write(message) - expectedMAC := mac.Sum(nil) + return mac.Sum(nil), nil +} +func verifyMac(macData *macData, message, password []byte) error { + expectedMAC, err := doMac(macData, message, password) + if err != nil { + return err + } if !hmac.Equal(macData.Mac.Digest, expectedMAC) { return ErrIncorrectPassword } @@ -56,15 +62,10 @@ func verifyMac(macData *macData, message, password []byte) error { } func computeMac(macData *macData, message, password []byte) error { - if !macData.Mac.Algorithm.Algorithm.Equal(oidSHA1) { - return NotImplementedError("unknown digest algorithm: " + macData.Mac.Algorithm.Algorithm.String()) + digest, err := doMac(macData, message, password) + if err != nil { + return err } - - key := pbkdf(sha1Sum, 20, 64, macData.MacSalt, password, macData.Iterations, 3, 20) - - mac := hmac.New(sha1.New, key) - mac.Write(message) - macData.Mac.Digest = mac.Sum(nil) - + macData.Mac.Digest = digest return nil } diff --git a/vendor/software.sslmate.com/src/go-pkcs12/pkcs12.go b/vendor/software.sslmate.com/src/go-pkcs12/pkcs12.go index 3769d4d..14dd3a5 100644 --- a/vendor/software.sslmate.com/src/go-pkcs12/pkcs12.go +++ b/vendor/software.sslmate.com/src/go-pkcs12/pkcs12.go @@ -10,7 +10,7 @@ // primitives, it SHOULD NOT be used for new applications. // // Note that only DER-encoded PKCS#12 files are supported, even though PKCS#12 -// allows BER encoding. This is becuase encoding/asn1 only supports DER. +// allows BER encoding. This is because encoding/asn1 only supports DER. // // This package is forked from golang.org/x/crypto/pkcs12, which is frozen. // The implementation is distilled from https://tools.ietf.org/html/rfc7292 @@ -19,6 +19,7 @@ package pkcs12 // import "software.sslmate.com/src/go-pkcs12" import ( "crypto/ecdsa" + "crypto/rand" "crypto/rsa" "crypto/sha1" "crypto/x509" @@ -27,15 +28,160 @@ import ( "encoding/hex" "encoding/pem" "errors" + "fmt" "io" ) // DefaultPassword is the string "changeit", a commonly-used password for -// PKCS#12 files. Due to the weak encryption used by PKCS#12, it is -// RECOMMENDED that you use DefaultPassword when encoding PKCS#12 files, -// and protect the PKCS#12 files using other means. +// PKCS#12 files. const DefaultPassword = "changeit" +// An Encoder contains methods for encoding PKCS#12 files. This package +// defines several different Encoders with different parameters. +type Encoder struct { + macAlgorithm asn1.ObjectIdentifier + certAlgorithm asn1.ObjectIdentifier + keyAlgorithm asn1.ObjectIdentifier + macIterations int + encryptionIterations int + saltLen int + rand io.Reader +} + +// WithIterations creates a new Encoder identical to enc except that +// it will use the given number of KDF iterations for deriving the MAC +// and encryption keys. +// +// Note that even with a large number of iterations, a weak +// password can still be brute-forced in much less time than it would +// take to brute-force a high-entropy encrytion key. For the best +// security, don't worry about the number of iterations and just +// use a high-entropy password (e.g. one generated with `openssl rand -hex 16`). +// See https://neilmadden.blog/2023/01/09/on-pbkdf2-iterations/ for more detail. +// +// Panics if iterations is less than 1. +func (enc Encoder) WithIterations(iterations int) *Encoder { + if iterations < 1 { + panic("pkcs12: number of iterations is less than 1") + } + enc.macIterations = iterations + enc.encryptionIterations = iterations + return &enc +} + +// WithRand creates a new Encoder identical to enc except that +// it will use the given io.Reader for its random number generator +// instead of [crypto/rand.Reader]. +func (enc Encoder) WithRand(rand io.Reader) *Encoder { + enc.rand = rand + return &enc +} + +// LegacyRC2 encodes PKCS#12 files using weak algorithms that were +// traditionally used in PKCS#12 files, including those produced +// by OpenSSL before 3.0.0, go-pkcs12 before 0.3.0, and Java when +// keystore.pkcs12.legacy is defined. Specifically, certificates +// are encrypted using PBE with RC2, and keys are encrypted using PBE +// with 3DES, using keys derived with 2048 iterations of HMAC-SHA-1. +// MACs use HMAC-SHA-1 with keys derived with 1 iteration of HMAC-SHA-1. +// +// Due to the weak encryption, it is STRONGLY RECOMMENDED that you use [DefaultPassword] +// when encoding PKCS#12 files using this encoder, and protect the PKCS#12 files +// using other means. +// +// By default, OpenSSL 3 can't decode PKCS#12 files created using this encoder. +// For better compatibility, use [LegacyDES]. For better security, use +// [Modern2023]. +var LegacyRC2 = &Encoder{ + macAlgorithm: oidSHA1, + certAlgorithm: oidPBEWithSHAAnd40BitRC2CBC, + keyAlgorithm: oidPBEWithSHAAnd3KeyTripleDESCBC, + macIterations: 1, + encryptionIterations: 2048, + saltLen: 8, + rand: rand.Reader, +} + +// LegacyDES encodes PKCS#12 files using weak algorithms that are +// supported by a wide variety of software. Certificates and keys +// are encrypted using PBE with 3DES using keys derived with 2048 +// iterations of HMAC-SHA-1. MACs use HMAC-SHA-1 with keys derived +// with 1 iteration of HMAC-SHA-1. These are the same parameters +// used by OpenSSL's -descert option. As of 2023, this encoder is +// likely to produce files that can be read by the most software. +// +// Due to the weak encryption, it is STRONGLY RECOMMENDED that you use [DefaultPassword] +// when encoding PKCS#12 files using this encoder, and protect the PKCS#12 files +// using other means. To create more secure PKCS#12 files, use [Modern2023]. +var LegacyDES = &Encoder{ + macAlgorithm: oidSHA1, + certAlgorithm: oidPBEWithSHAAnd3KeyTripleDESCBC, + keyAlgorithm: oidPBEWithSHAAnd3KeyTripleDESCBC, + macIterations: 1, + encryptionIterations: 2048, + saltLen: 8, + rand: rand.Reader, +} + +// Passwordless encodes PKCS#12 files without any encryption or MACs. +// A lot of software has trouble reading such files, so it's probably only +// useful for creating Java trust stores using [Encoder.EncodeTrustStore] +// or [Encoder.EncodeTrustStoreEntries]. +// +// When using this encoder, you MUST specify an empty password. +var Passwordless = &Encoder{ + macAlgorithm: nil, + certAlgorithm: nil, + keyAlgorithm: nil, + rand: rand.Reader, +} + +// Modern2023 encodes PKCS#12 files using algorithms that are considered modern +// as of 2023. Private keys and certificates are encrypted using PBES2 with +// PBKDF2-HMAC-SHA-256 and AES-256-CBC. The MAC algorithm is HMAC-SHA-2. These +// are the same algorithms used by OpenSSL 3 (by default), Java 20 (by default), +// and Windows Server 2019 (when "stronger" is used). +// +// Files produced with this encoder can be read by OpenSSL 1.1.1 and higher, +// Java 12 and higher, and Windows Server 2019 and higher. +// +// For passwords, it is RECOMMENDED that you do one of the following: +// 1) Use [DefaultPassword] and protect the file using other means, or +// 2) Use a high-entropy password, such as one generated with `openssl rand -hex 16`. +// +// You SHOULD NOT use a lower-entropy password with this encoder because the number of KDF +// iterations is only 2048 and doesn't provide meaningful protection against +// brute-forcing. You can increase the number of iterations using [Encoder.WithIterations], +// but as https://neilmadden.blog/2023/01/09/on-pbkdf2-iterations/ explains, this doesn't +// help as much as you think. +var Modern2023 = &Encoder{ + macAlgorithm: oidSHA256, + certAlgorithm: oidPBES2, + keyAlgorithm: oidPBES2, + macIterations: 2048, + encryptionIterations: 2048, + saltLen: 16, + rand: rand.Reader, +} + +// Legacy encodes PKCS#12 files using weak, legacy parameters that work in +// a wide variety of software. +// +// Currently, this encoder is the same as [LegacyDES], but this +// may change in the future if another encoder is found to provide better +// compatibility. +// +// Due to the weak encryption, it is STRONGLY RECOMMENDED that you use [DefaultPassword] +// when encoding PKCS#12 files using this encoder, and protect the PKCS#12 files +// using other means. +var Legacy = LegacyDES + +// Modern encodes PKCS#12 files using modern, robust parameters. +// +// Currently, this encoder is the same as [Modern2023], but this +// may change in the future to keep up with modern practices. +var Modern = Modern2023 + var ( oidDataContentType = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 7, 1}) oidEncryptedDataContentType = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 7, 6}) @@ -44,7 +190,8 @@ var ( oidLocalKeyID = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 9, 21}) oidMicrosoftCSPName = asn1.ObjectIdentifier([]int{1, 3, 6, 1, 4, 1, 311, 17, 1}) - oidJavaTrustStore = asn1.ObjectIdentifier([]int{2, 16, 840, 1, 113894, 746875, 1, 1}) + oidJavaTrustStore = asn1.ObjectIdentifier([]int{2, 16, 840, 1, 113894, 746875, 1, 1}) + oidAnyExtendedKeyUsage = asn1.ObjectIdentifier([]int{2, 5, 29, 37, 0}) ) type pfxPdu struct { @@ -134,17 +281,18 @@ func unmarshal(in []byte, out interface{}) error { } // ToPEM converts all "safe bags" contained in pfxData to PEM blocks. -// DO NOT USE THIS FUNCTION. ToPEM creates invalid PEM blocks; private keys +// +// Deprecated: ToPEM creates invalid PEM blocks (private keys // are encoded as raw RSA or EC private keys rather than PKCS#8 despite being -// labeled "PRIVATE KEY". To decode a PKCS#12 file, use DecodeChain instead, -// and use the encoding/pem package to convert to PEM if necessary. +// labeled "PRIVATE KEY"). To decode a PKCS#12 file, use [DecodeChain] instead, +// and use the [encoding/pem] package to convert to PEM if necessary. func ToPEM(pfxData []byte, password string) ([]*pem.Block, error) { - encodedPassword, err := bmpString(password) + encodedPassword, err := bmpStringZeroTerminated(password) if err != nil { return nil, ErrIncorrectPassword } - bags, encodedPassword, err := getSafeContents(pfxData, encodedPassword, 2) + bags, encodedPassword, err := getSafeContents(pfxData, encodedPassword, 2, 2) if err != nil { return nil, err @@ -246,7 +394,7 @@ func convertAttribute(attribute *pkcs12Attribute) (key, value string, err error) // Decode extracts a certificate and private key from pfxData, which must be a DER-encoded PKCS#12 file. This function // assumes that there is only one certificate and only one private key in the // pfxData. Since PKCS#12 files often contain more than one certificate, you -// probably want to use DecodeChain instead. +// probably want to use [DecodeChain] instead. func Decode(pfxData []byte, password string) (privateKey interface{}, certificate *x509.Certificate, err error) { var caCerts []*x509.Certificate privateKey, certificate, caCerts, err = DecodeChain(pfxData, password) @@ -262,12 +410,12 @@ func Decode(pfxData []byte, password string) (privateKey interface{}, certificat // be the leaf certificate, and subsequent certificates, if any, are assumed to // comprise the CA certificate chain. func DecodeChain(pfxData []byte, password string) (privateKey interface{}, certificate *x509.Certificate, caCerts []*x509.Certificate, err error) { - encodedPassword, err := bmpString(password) + encodedPassword, err := bmpStringZeroTerminated(password) if err != nil { return nil, nil, nil, err } - bags, encodedPassword, err := getSafeContents(pfxData, encodedPassword, 2) + bags, encodedPassword, err := getSafeContents(pfxData, encodedPassword, 1, 2) if err != nil { return nil, nil, nil, err } @@ -293,6 +441,15 @@ func DecodeChain(pfxData []byte, password string) (privateKey interface{}, certi caCerts = append(caCerts, certs[0]) } + case bag.Id.Equal(oidKeyBag): + if privateKey != nil { + err = errors.New("pkcs12: expected exactly one key bag") + return nil, nil, nil, err + } + + if privateKey, err = x509.ParsePKCS8PrivateKey(bag.Value.Bytes); err != nil { + return nil, nil, nil, err + } case bag.Id.Equal(oidPKCS8ShroundedKeyBag): if privateKey != nil { err = errors.New("pkcs12: expected exactly one key bag") @@ -318,13 +475,16 @@ func DecodeChain(pfxData []byte, password string) (privateKey interface{}, certi // DecodeTrustStore extracts the certificates from pfxData, which must be a DER-encoded // PKCS#12 file containing exclusively certificates with attribute 2.16.840.1.113894.746875.1.1, // which is used by Java to designate a trust anchor. +// +// If the password argument is empty, DecodeTrustStore will decode either password-less +// PKCS#12 files (i.e. those without encryption) or files with a literal empty password. func DecodeTrustStore(pfxData []byte, password string) (certs []*x509.Certificate, err error) { - encodedPassword, err := bmpString(password) + encodedPassword, err := bmpStringZeroTerminated(password) if err != nil { return nil, err } - bags, encodedPassword, err := getSafeContents(pfxData, encodedPassword, 1) + bags, encodedPassword, err := getSafeContents(pfxData, encodedPassword, 1, 1) if err != nil { return nil, err } @@ -359,7 +519,7 @@ func DecodeTrustStore(pfxData []byte, password string) (certs []*x509.Certificat return } -func getSafeContents(p12Data, password []byte, expectedItems int) (bags []safeBag, updatedPassword []byte, err error) { +func getSafeContents(p12Data, password []byte, expectedItemsMin int, expectedItemsMax int) (bags []safeBag, updatedPassword []byte, err error) { pfx := new(pfxPdu) if err := unmarshal(p12Data, pfx); err != nil { return nil, nil, errors.New("pkcs12: error reading P12 data: " + err.Error()) @@ -379,10 +539,10 @@ func getSafeContents(p12Data, password []byte, expectedItems int) (bags []safeBa } if len(pfx.MacData.Mac.Algorithm.Algorithm) == 0 { - return nil, nil, errors.New("pkcs12: no MAC in data") - } - - if err := verifyMac(&pfx.MacData, pfx.AuthSafe.Content.Bytes, password); err != nil { + if !(len(password) == 2 && password[0] == 0 && password[1] == 0) { + return nil, nil, errors.New("pkcs12: no MAC in data") + } + } else if err := verifyMac(&pfx.MacData, pfx.AuthSafe.Content.Bytes, password); err != nil { if err == ErrIncorrectPassword && len(password) == 2 && password[0] == 0 && password[1] == 0 { // some implementations use an empty byte array // for the empty string password try one more @@ -400,8 +560,11 @@ func getSafeContents(p12Data, password []byte, expectedItems int) (bags []safeBa return nil, nil, err } - if len(authenticatedSafe) != expectedItems { - return nil, nil, NotImplementedError("expected exactly two items in the authenticated safe") + if len(authenticatedSafe) < expectedItemsMin || len(authenticatedSafe) > expectedItemsMax { + if expectedItemsMin == expectedItemsMax { + return nil, nil, NotImplementedError(fmt.Sprintf("expected exactly %d items in the authenticated safe, but this file has %d", expectedItemsMin, len(authenticatedSafe))) + } + return nil, nil, NotImplementedError(fmt.Sprintf("expected between %d and %d items in the authenticated safe, but this file has %d", expectedItemsMin, expectedItemsMax, len(authenticatedSafe))) } for _, ci := range authenticatedSafe { @@ -437,26 +600,35 @@ func getSafeContents(p12Data, password []byte, expectedItems int) (bags []safeBa return bags, password, nil } +// Encode is equivalent to LegacyRC2.WithRand(rand).Encode. +// See [Encoder.Encode] and [LegacyRC2] for details. +// +// Deprecated: for the same behavior, use LegacyRC2.Encode; for +// better compatibility, use Legacy.Encode; for better +// security, use Modern.Encode. +func Encode(rand io.Reader, privateKey interface{}, certificate *x509.Certificate, caCerts []*x509.Certificate, password string) (pfxData []byte, err error) { + return LegacyRC2.WithRand(rand).Encode(privateKey, certificate, caCerts, password) +} + // Encode produces pfxData containing one private key (privateKey), an // end-entity certificate (certificate), and any number of CA certificates // (caCerts). // -// The private key is encrypted with the provided password, but due to the -// weak encryption primitives used by PKCS#12, it is RECOMMENDED that you -// specify a hard-coded password (such as pkcs12.DefaultPassword) and protect -// the resulting pfxData using other means. -// -// The rand argument is used to provide entropy for the encryption, and -// can be set to rand.Reader from the crypto/rand package. +// The pfxData is encrypted and authenticated with keys derived from +// the provided password. // // Encode emulates the behavior of OpenSSL's PKCS12_create: it creates two -// SafeContents: one that's encrypted with RC2 and contains the certificates, -// and another that is unencrypted and contains the private key shrouded with -// 3DES The private key bag and the end-entity certificate bag have the -// LocalKeyId attribute set to the SHA-1 fingerprint of the end-entity -// certificate. -func Encode(rand io.Reader, privateKey interface{}, certificate *x509.Certificate, caCerts []*x509.Certificate, password string) (pfxData []byte, err error) { - encodedPassword, err := bmpString(password) +// SafeContents: one that's encrypted with the certificate encryption algorithm +// and contains the certificates, and another that is unencrypted and contains the +// private key shrouded with the key encryption algorithm. The private key bag and +// the end-entity certificate bag have the LocalKeyId attribute set to the SHA-1 +// fingerprint of the end-entity certificate. +func (enc *Encoder) Encode(privateKey interface{}, certificate *x509.Certificate, caCerts []*x509.Certificate, password string) (pfxData []byte, err error) { + if enc.macAlgorithm == nil && enc.certAlgorithm == nil && enc.keyAlgorithm == nil && password != "" { + return nil, errors.New("password must be empty") + } + + encodedPassword, err := bmpStringZeroTerminated(password) if err != nil { return nil, err } @@ -475,26 +647,37 @@ func Encode(rand io.Reader, privateKey interface{}, certificate *x509.Certificat } var certBags []safeBag - var certBag *safeBag - if certBag, err = makeCertBag(certificate.Raw, []pkcs12Attribute{localKeyIdAttr}); err != nil { + if certBag, err := makeCertBag(certificate.Raw, []pkcs12Attribute{localKeyIdAttr}); err != nil { return nil, err - } - certBags = append(certBags, *certBag) - - for _, cert := range caCerts { - if certBag, err = makeCertBag(cert.Raw, []pkcs12Attribute{}); err != nil { - return nil, err - } + } else { certBags = append(certBags, *certBag) } + for _, cert := range caCerts { + if certBag, err := makeCertBag(cert.Raw, []pkcs12Attribute{}); err != nil { + return nil, err + } else { + certBags = append(certBags, *certBag) + } + } + var keyBag safeBag - keyBag.Id = oidPKCS8ShroundedKeyBag - keyBag.Value.Class = 2 - keyBag.Value.Tag = 0 - keyBag.Value.IsCompound = true - if keyBag.Value.Bytes, err = encodePkcs8ShroudedKeyBag(rand, privateKey, encodedPassword); err != nil { - return nil, err + if enc.keyAlgorithm == nil { + keyBag.Id = oidKeyBag + keyBag.Value.Class = 2 + keyBag.Value.Tag = 0 + keyBag.Value.IsCompound = true + if keyBag.Value.Bytes, err = x509.MarshalPKCS8PrivateKey(privateKey); err != nil { + return nil, err + } + } else { + keyBag.Id = oidPKCS8ShroundedKeyBag + keyBag.Value.Class = 2 + keyBag.Value.Tag = 0 + keyBag.Value.IsCompound = true + if keyBag.Value.Bytes, err = encodePkcs8ShroudedKeyBag(enc.rand, privateKey, enc.keyAlgorithm, encodedPassword, enc.encryptionIterations, enc.saltLen); err != nil { + return nil, err + } } keyBag.Attributes = append(keyBag.Attributes, localKeyIdAttr) @@ -502,10 +685,10 @@ func Encode(rand io.Reader, privateKey interface{}, certificate *x509.Certificat // The first SafeContents is encrypted and contains the cert bags. // The second SafeContents is unencrypted and contains the shrouded key bag. var authenticatedSafe [2]contentInfo - if authenticatedSafe[0], err = makeSafeContents(rand, certBags, encodedPassword); err != nil { + if authenticatedSafe[0], err = makeSafeContents(enc.rand, certBags, enc.certAlgorithm, encodedPassword, enc.encryptionIterations, enc.saltLen); err != nil { return nil, err } - if authenticatedSafe[1], err = makeSafeContents(rand, []safeBag{keyBag}, nil); err != nil { + if authenticatedSafe[1], err = makeSafeContents(enc.rand, []safeBag{keyBag}, nil, nil, 0, 0); err != nil { return nil, err } @@ -514,15 +697,17 @@ func Encode(rand io.Reader, privateKey interface{}, certificate *x509.Certificat return nil, err } - // compute the MAC - pfx.MacData.Mac.Algorithm.Algorithm = oidSHA1 - pfx.MacData.MacSalt = make([]byte, 8) - if _, err = rand.Read(pfx.MacData.MacSalt); err != nil { - return nil, err - } - pfx.MacData.Iterations = 1 - if err = computeMac(&pfx.MacData, authenticatedSafeBytes, encodedPassword); err != nil { - return nil, err + if enc.macAlgorithm != nil { + // compute the MAC + pfx.MacData.Mac.Algorithm.Algorithm = enc.macAlgorithm + pfx.MacData.MacSalt = make([]byte, enc.saltLen) + if _, err = enc.rand.Read(pfx.MacData.MacSalt); err != nil { + return nil, err + } + pfx.MacData.Iterations = enc.macIterations + if err = computeMac(&pfx.MacData, authenticatedSafeBytes, encodedPassword); err != nil { + return nil, err + } } pfx.AuthSafe.ContentType = oidDataContentType @@ -539,21 +724,73 @@ func Encode(rand io.Reader, privateKey interface{}, certificate *x509.Certificat return } +// EncodeTrustStore is equivalent to LegacyRC2.WithRand(rand).EncodeTrustStore. +// See [Encoder.EncodeTrustStore] and [LegacyRC2] for details. +// +// Deprecated: for the same behavior, use LegacyRC2.EncodeTrustStore; to generate passwordless trust stores, +// use Passwordless.EncodeTrustStore. +func EncodeTrustStore(rand io.Reader, certs []*x509.Certificate, password string) (pfxData []byte, err error) { + return LegacyRC2.WithRand(rand).EncodeTrustStore(certs, password) +} + // EncodeTrustStore produces pfxData containing any number of CA certificates // (certs) to be trusted. The certificates will be marked with a special OID that // allow it to be used as a Java TrustStore in Java 1.8 and newer. // -// Due to the weak encryption primitives used by PKCS#12, it is RECOMMENDED that -// you specify a hard-coded password (such as pkcs12.DefaultPassword) and protect -// the resulting pfxData using other means. -// -// The rand argument is used to provide entropy for the encryption, and -// can be set to rand.Reader from the crypto/rand package. -// -// EncodeTrustStore creates a single SafeContents that's encrypted with RC2 +// EncodeTrustStore creates a single SafeContents that's optionally encrypted // and contains the certificates. -func EncodeTrustStore(rand io.Reader, certs []*x509.Certificate, password string) (pfxData []byte, err error) { - encodedPassword, err := bmpString(password) +// +// The Subject of the certificates are used as the Friendly Names (Aliases) +// within the resulting pfxData. If certificates share a Subject, then the +// resulting Friendly Names (Aliases) will be identical, which Java may treat as +// the same entry when used as a Java TrustStore, e.g. with `keytool`. To +// customize the Friendly Names, use [EncodeTrustStoreEntries]. +func (enc *Encoder) EncodeTrustStore(certs []*x509.Certificate, password string) (pfxData []byte, err error) { + var certsWithFriendlyNames []TrustStoreEntry + for _, cert := range certs { + certsWithFriendlyNames = append(certsWithFriendlyNames, TrustStoreEntry{ + Cert: cert, + FriendlyName: cert.Subject.String(), + }) + } + return enc.EncodeTrustStoreEntries(certsWithFriendlyNames, password) +} + +// TrustStoreEntry represents an entry in a Java TrustStore. +type TrustStoreEntry struct { + Cert *x509.Certificate + FriendlyName string +} + +// EncodeTrustStoreEntries is equivalent to LegacyRC2.WithRand(rand).EncodeTrustStoreEntries. +// See [Encoder.EncodeTrustStoreEntries] and [LegacyRC2] for details. +// +// Deprecated: for the same behavior, use LegacyRC2.EncodeTrustStoreEntries; to generate passwordless trust stores, +// use Passwordless.EncodeTrustStoreEntries. +func EncodeTrustStoreEntries(rand io.Reader, entries []TrustStoreEntry, password string) (pfxData []byte, err error) { + return LegacyRC2.WithRand(rand).EncodeTrustStoreEntries(entries, password) +} + +// EncodeTrustStoreEntries produces pfxData containing any number of CA +// certificates (entries) to be trusted. The certificates will be marked with a +// special OID that allow it to be used as a Java TrustStore in Java 1.8 and newer. +// +// This is identical to [Encoder.EncodeTrustStore], but also allows for setting specific +// Friendly Names (Aliases) to be used per certificate, by specifying a slice +// of TrustStoreEntry. +// +// If the same Friendly Name is used for more than one certificate, then the +// resulting Friendly Names (Aliases) in the pfxData will be identical, which Java +// may treat as the same entry when used as a Java TrustStore, e.g. with `keytool`. +// +// EncodeTrustStoreEntries creates a single SafeContents that's optionally +// encrypted and contains the certificates. +func (enc *Encoder) EncodeTrustStoreEntries(entries []TrustStoreEntry, password string) (pfxData []byte, err error) { + if enc.macAlgorithm == nil && enc.certAlgorithm == nil && password != "" { + return nil, errors.New("password must be empty") + } + + encodedPassword, err := bmpStringZeroTerminated(password) if err != nil { return nil, err } @@ -561,16 +798,54 @@ func EncodeTrustStore(rand io.Reader, certs []*x509.Certificate, password string var pfx pfxPdu pfx.Version = 3 - // Setting this attribute will make the certificates trusted in Java >= 1.8 - var javaTrustStoreAttr pkcs12Attribute - javaTrustStoreAttr.Id = oidJavaTrustStore - javaTrustStoreAttr.Value.Class = 0 - javaTrustStoreAttr.Value.Tag = 17 - javaTrustStoreAttr.Value.IsCompound = true + var certAttributes []pkcs12Attribute + + extKeyUsageOidBytes, err := asn1.Marshal(oidAnyExtendedKeyUsage) + if err != nil { + return nil, err + } + + // the oidJavaTrustStore attribute contains the EKUs for which + // this trust anchor will be valid + certAttributes = append(certAttributes, pkcs12Attribute{ + Id: oidJavaTrustStore, + Value: asn1.RawValue{ + Class: 0, + Tag: 17, + IsCompound: true, + Bytes: extKeyUsageOidBytes, + }, + }) var certBags []safeBag - for _, cert := range certs { - certBag, err := makeCertBag(cert.Raw, []pkcs12Attribute{javaTrustStoreAttr}) + for _, entry := range entries { + + bmpFriendlyName, err := bmpString(entry.FriendlyName) + if err != nil { + return nil, err + } + + encodedFriendlyName, err := asn1.Marshal(asn1.RawValue{ + Class: 0, + Tag: 30, + IsCompound: false, + Bytes: bmpFriendlyName, + }) + if err != nil { + return nil, err + } + + friendlyName := pkcs12Attribute{ + Id: oidFriendlyName, + Value: asn1.RawValue{ + Class: 0, + Tag: 17, + IsCompound: true, + Bytes: encodedFriendlyName, + }, + } + + certBag, err := makeCertBag(entry.Cert.Raw, append(certAttributes, friendlyName)) if err != nil { return nil, err } @@ -578,9 +853,9 @@ func EncodeTrustStore(rand io.Reader, certs []*x509.Certificate, password string } // Construct an authenticated safe with one SafeContent. - // The SafeContents is encrypted and contains the cert bags. + // The SafeContents is contains the cert bags. var authenticatedSafe [1]contentInfo - if authenticatedSafe[0], err = makeSafeContents(rand, certBags, encodedPassword); err != nil { + if authenticatedSafe[0], err = makeSafeContents(enc.rand, certBags, enc.certAlgorithm, encodedPassword, enc.encryptionIterations, enc.saltLen); err != nil { return nil, err } @@ -589,15 +864,17 @@ func EncodeTrustStore(rand io.Reader, certs []*x509.Certificate, password string return nil, err } - // compute the MAC - pfx.MacData.Mac.Algorithm.Algorithm = oidSHA1 - pfx.MacData.MacSalt = make([]byte, 8) - if _, err = rand.Read(pfx.MacData.MacSalt); err != nil { - return nil, err - } - pfx.MacData.Iterations = 1 - if err = computeMac(&pfx.MacData, authenticatedSafeBytes, encodedPassword); err != nil { - return nil, err + if enc.macAlgorithm != nil { + // compute the MAC + pfx.MacData.Mac.Algorithm.Algorithm = enc.macAlgorithm + pfx.MacData.MacSalt = make([]byte, enc.saltLen) + if _, err = enc.rand.Read(pfx.MacData.MacSalt); err != nil { + return nil, err + } + pfx.MacData.Iterations = enc.macIterations + if err = computeMac(&pfx.MacData, authenticatedSafeBytes, encodedPassword); err != nil { + return nil, err + } } pfx.AuthSafe.ContentType = oidDataContentType @@ -627,13 +904,13 @@ func makeCertBag(certBytes []byte, attributes []pkcs12Attribute) (certBag *safeB return } -func makeSafeContents(rand io.Reader, bags []safeBag, password []byte) (ci contentInfo, err error) { +func makeSafeContents(rand io.Reader, bags []safeBag, algoID asn1.ObjectIdentifier, password []byte, iterations int, saltLen int) (ci contentInfo, err error) { var data []byte if data, err = asn1.Marshal(bags); err != nil { return } - if password == nil { + if algoID == nil { ci.ContentType = oidDataContentType ci.Content.Class = 2 ci.Content.Tag = 0 @@ -642,15 +919,21 @@ func makeSafeContents(rand io.Reader, bags []safeBag, password []byte) (ci conte return } } else { - randomSalt := make([]byte, 8) + randomSalt := make([]byte, saltLen) if _, err = rand.Read(randomSalt); err != nil { return } var algo pkix.AlgorithmIdentifier - algo.Algorithm = oidPBEWithSHAAnd40BitRC2CBC - if algo.Parameters.FullBytes, err = asn1.Marshal(pbeParams{Salt: randomSalt, Iterations: 2048}); err != nil { - return + algo.Algorithm = algoID + if algoID.Equal(oidPBES2) { + if algo.Parameters.FullBytes, err = makePBES2Parameters(rand, randomSalt, iterations); err != nil { + return + } + } else { + if algo.Parameters.FullBytes, err = asn1.Marshal(pbeParams{Salt: randomSalt, Iterations: iterations}); err != nil { + return + } } var encryptedData encryptedData diff --git a/vendor/software.sslmate.com/src/go-pkcs12/safebags.go b/vendor/software.sslmate.com/src/go-pkcs12/safebags.go index be83a49..b4ab7d6 100644 --- a/vendor/software.sslmate.com/src/go-pkcs12/safebags.go +++ b/vendor/software.sslmate.com/src/go-pkcs12/safebags.go @@ -15,6 +15,7 @@ import ( var ( // see https://tools.ietf.org/html/rfc7292#appendix-D oidCertTypeX509Certificate = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 9, 22, 1}) + oidKeyBag = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 10, 1, 1}) oidPKCS8ShroundedKeyBag = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 10, 1, 2}) oidCertBag = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 10, 1, 3}) ) @@ -47,23 +48,30 @@ func decodePkcs8ShroudedKeyBag(asn1Data, password []byte) (privateKey interface{ return privateKey, nil } -func encodePkcs8ShroudedKeyBag(rand io.Reader, privateKey interface{}, password []byte) (asn1Data []byte, err error) { +func encodePkcs8ShroudedKeyBag(rand io.Reader, privateKey interface{}, algoID asn1.ObjectIdentifier, password []byte, iterations int, saltLen int) (asn1Data []byte, err error) { var pkData []byte if pkData, err = x509.MarshalPKCS8PrivateKey(privateKey); err != nil { return nil, errors.New("pkcs12: error encoding PKCS#8 private key: " + err.Error()) } - randomSalt := make([]byte, 8) + randomSalt := make([]byte, saltLen) if _, err = rand.Read(randomSalt); err != nil { return nil, errors.New("pkcs12: error reading random salt: " + err.Error()) } + var paramBytes []byte - if paramBytes, err = asn1.Marshal(pbeParams{Salt: randomSalt, Iterations: 2048}); err != nil { - return nil, errors.New("pkcs12: error encoding params: " + err.Error()) + if algoID.Equal(oidPBES2) { + if paramBytes, err = makePBES2Parameters(rand, randomSalt, iterations); err != nil { + return nil, errors.New("pkcs12: error encoding params: " + err.Error()) + } + } else { + if paramBytes, err = asn1.Marshal(pbeParams{Salt: randomSalt, Iterations: iterations}); err != nil { + return nil, errors.New("pkcs12: error encoding params: " + err.Error()) + } } var pkinfo encryptedPrivateKeyInfo - pkinfo.AlgorithmIdentifier.Algorithm = oidPBEWithSHAAnd3KeyTripleDESCBC + pkinfo.AlgorithmIdentifier.Algorithm = algoID pkinfo.AlgorithmIdentifier.Parameters.FullBytes = paramBytes if err = pbEncrypt(&pkinfo, pkData, password); err != nil {