pax_global_header00006660000000000000000000000064133741133020014507gustar00rootroot0000000000000052 comment=1be2e3e5546da8a58903ff4adcfab015022538ea pkcs8-1.1/000077500000000000000000000000001337411330200124005ustar00rootroot00000000000000pkcs8-1.1/.gitignore000066400000000000000000000004031337411330200143650ustar00rootroot00000000000000# Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe *.test pkcs8-1.1/.travis.yml000066400000000000000000000001241337411330200145060ustar00rootroot00000000000000language: go go: - "1.9.x" - "1.10.x" - master script: - go test -v ./... pkcs8-1.1/LICENSE000066400000000000000000000020611337411330200134040ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2014 youmark Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.pkcs8-1.1/README000066400000000000000000000001451337411330200132600ustar00rootroot00000000000000pkcs8 package: implement PKCS#8 private key parsing and conversion as defined in RFC5208 and RFC5958 pkcs8-1.1/README.md000066400000000000000000000024401337411330200136570ustar00rootroot00000000000000pkcs8 === OpenSSL can generate private keys in both "traditional format" and PKCS#8 format. Newer applications are advised to use more secure PKCS#8 format. Go standard crypto package provides a [function](http://golang.org/pkg/crypto/x509/#ParsePKCS8PrivateKey) to parse private key in PKCS#8 format. There is a limitation to this function. It can only handle unencrypted PKCS#8 private keys. To use this function, the user has to save the private key in file without encryption, which is a bad practice to leave private keys unprotected on file systems. In addition, Go standard package lacks the functions to convert RSA/ECDSA private keys into PKCS#8 format. pkcs8 package fills the gap here. It implements functions to process private keys in PKCS#8 format, as defined in [RFC5208](https://tools.ietf.org/html/rfc5208) and [RFC5958](https://tools.ietf.org/html/rfc5958). It can handle both unencrypted PKCS#8 PrivateKeyInfo format and EncryptedPrivateKeyInfo format with PKCS#5 (v2.0) algorithms. [**Godoc**](http://godoc.org/github.com/youmark/pkcs8) ## Installation Supports Go 1.9+ ```text go get github.com/youmark/pkcs8 ``` ## dependency This package depends on golang.org/x/crypto/pbkdf2 package. Use the following command to retrive pbkdf2 package ```text go get golang.org/x/crypto/pbkdf2 ``` pkcs8-1.1/pkcs8.go000066400000000000000000000223111337411330200137560ustar00rootroot00000000000000// Package pkcs8 implements functions to parse and convert private keys in PKCS#8 format, as defined in RFC5208 and RFC5958 package pkcs8 import ( "crypto/aes" "crypto/cipher" "crypto/des" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/rsa" "crypto/sha1" "crypto/sha256" "crypto/x509" "encoding/asn1" "errors" "golang.org/x/crypto/pbkdf2" ) // Copy from crypto/x509 var ( oidPublicKeyRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1} oidPublicKeyDSA = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1} oidPublicKeyECDSA = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1} ) // Copy from crypto/x509 var ( oidNamedCurveP224 = asn1.ObjectIdentifier{1, 3, 132, 0, 33} oidNamedCurveP256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 3, 1, 7} oidNamedCurveP384 = asn1.ObjectIdentifier{1, 3, 132, 0, 34} oidNamedCurveP521 = asn1.ObjectIdentifier{1, 3, 132, 0, 35} ) // Copy from crypto/x509 func oidFromNamedCurve(curve elliptic.Curve) (asn1.ObjectIdentifier, bool) { switch curve { case elliptic.P224(): return oidNamedCurveP224, true case elliptic.P256(): return oidNamedCurveP256, true case elliptic.P384(): return oidNamedCurveP384, true case elliptic.P521(): return oidNamedCurveP521, true } return nil, false } // Unecrypted PKCS8 var ( oidPKCS5PBKDF2 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 5, 12} oidPBES2 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 5, 13} oidAES256CBC = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 42} oidAES128CBC = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 2} oidHMACWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 113549, 2, 9} oidDESEDE3CBC = asn1.ObjectIdentifier{1, 2, 840, 113549, 3, 7} ) type ecPrivateKey struct { Version int PrivateKey []byte NamedCurveOID asn1.ObjectIdentifier `asn1:"optional,explicit,tag:0"` PublicKey asn1.BitString `asn1:"optional,explicit,tag:1"` } type privateKeyInfo struct { Version int PrivateKeyAlgorithm []asn1.ObjectIdentifier PrivateKey []byte } // Encrypted PKCS8 type prfParam struct { IdPRF asn1.ObjectIdentifier NullParam asn1.RawValue } type pbkdf2Params struct { Salt []byte IterationCount int PrfParam prfParam `asn1:"optional"` } type pbkdf2Algorithms struct { IdPBKDF2 asn1.ObjectIdentifier PBKDF2Params pbkdf2Params } type pbkdf2Encs struct { EncryAlgo asn1.ObjectIdentifier IV []byte } type pbes2Params struct { KeyDerivationFunc pbkdf2Algorithms EncryptionScheme pbkdf2Encs } type pbes2Algorithms struct { IdPBES2 asn1.ObjectIdentifier PBES2Params pbes2Params } type encryptedPrivateKeyInfo struct { EncryptionAlgorithm pbes2Algorithms EncryptedData []byte } // ParsePKCS8PrivateKeyRSA parses encrypted/unencrypted private keys in PKCS#8 format. To parse encrypted private keys, a password of []byte type should be provided to the function as the second parameter. // // The function can decrypt the private key encrypted with AES-256-CBC mode, and stored in PKCS #5 v2.0 format. func ParsePKCS8PrivateKeyRSA(der []byte, v ...[]byte) (*rsa.PrivateKey, error) { key, err := ParsePKCS8PrivateKey(der, v...) if err != nil { return nil, err } typedKey, ok := key.(*rsa.PrivateKey) if !ok { return nil, errors.New("key block is not of type RSA") } return typedKey, nil } // ParsePKCS8PrivateKeyECDSA parses encrypted/unencrypted private keys in PKCS#8 format. To parse encrypted private keys, a password of []byte type should be provided to the function as the second parameter. // // The function can decrypt the private key encrypted with AES-256-CBC mode, and stored in PKCS #5 v2.0 format. func ParsePKCS8PrivateKeyECDSA(der []byte, v ...[]byte) (*ecdsa.PrivateKey, error) { key, err := ParsePKCS8PrivateKey(der, v...) if err != nil { return nil, err } typedKey, ok := key.(*ecdsa.PrivateKey) if !ok { return nil, errors.New("key block is not of type ECDSA") } return typedKey, nil } // ParsePKCS8PrivateKey parses encrypted/unencrypted private keys in PKCS#8 format. To parse encrypted private keys, a password of []byte type should be provided to the function as the second parameter. // // The function can decrypt the private key encrypted with AES-256-CBC mode, and stored in PKCS #5 v2.0 format. func ParsePKCS8PrivateKey(der []byte, v ...[]byte) (interface{}, error) { // No password provided, assume the private key is unencrypted if v == nil { return x509.ParsePKCS8PrivateKey(der) } // Use the password provided to decrypt the private key password := v[0] var privKey encryptedPrivateKeyInfo if _, err := asn1.Unmarshal(der, &privKey); err != nil { return nil, errors.New("pkcs8: only PKCS #5 v2.0 supported") } if !privKey.EncryptionAlgorithm.IdPBES2.Equal(oidPBES2) { return nil, errors.New("pkcs8: only PBES2 supported") } if !privKey.EncryptionAlgorithm.PBES2Params.KeyDerivationFunc.IdPBKDF2.Equal(oidPKCS5PBKDF2) { return nil, errors.New("pkcs8: only PBKDF2 supported") } encParam := privKey.EncryptionAlgorithm.PBES2Params.EncryptionScheme kdfParam := privKey.EncryptionAlgorithm.PBES2Params.KeyDerivationFunc.PBKDF2Params iv := encParam.IV salt := kdfParam.Salt iter := kdfParam.IterationCount keyHash := sha1.New if kdfParam.PrfParam.IdPRF.Equal(oidHMACWithSHA256) { keyHash = sha256.New } encryptedKey := privKey.EncryptedData var symkey []byte var block cipher.Block var err error switch { case encParam.EncryAlgo.Equal(oidAES128CBC): symkey = pbkdf2.Key(password, salt, iter, 16, keyHash) block, err = aes.NewCipher(symkey) case encParam.EncryAlgo.Equal(oidAES256CBC): symkey = pbkdf2.Key(password, salt, iter, 32, keyHash) block, err = aes.NewCipher(symkey) case encParam.EncryAlgo.Equal(oidDESEDE3CBC): symkey = pbkdf2.Key(password, salt, iter, 24, keyHash) block, err = des.NewTripleDESCipher(symkey) default: return nil, errors.New("pkcs8: only AES-256-CBC, AES-128-CBC and DES-EDE3-CBC are supported") } if err != nil { return nil, err } mode := cipher.NewCBCDecrypter(block, iv) mode.CryptBlocks(encryptedKey, encryptedKey) key, err := x509.ParsePKCS8PrivateKey(encryptedKey) if err != nil { return nil, errors.New("pkcs8: incorrect password") } return key, nil } func convertPrivateKeyToPKCS8(priv interface{}) ([]byte, error) { var pkey privateKeyInfo switch priv := priv.(type) { case *ecdsa.PrivateKey: eckey, err := x509.MarshalECPrivateKey(priv) if err != nil { return nil, err } oidNamedCurve, ok := oidFromNamedCurve(priv.Curve) if !ok { return nil, errors.New("pkcs8: unknown elliptic curve") } // Per RFC5958, if publicKey is present, then version is set to v2(1) else version is set to v1(0). // But openssl set to v1 even publicKey is present pkey.Version = 1 pkey.PrivateKeyAlgorithm = make([]asn1.ObjectIdentifier, 2) pkey.PrivateKeyAlgorithm[0] = oidPublicKeyECDSA pkey.PrivateKeyAlgorithm[1] = oidNamedCurve pkey.PrivateKey = eckey case *rsa.PrivateKey: // Per RFC5958, if publicKey is present, then version is set to v2(1) else version is set to v1(0). // But openssl set to v1 even publicKey is present pkey.Version = 0 pkey.PrivateKeyAlgorithm = make([]asn1.ObjectIdentifier, 1) pkey.PrivateKeyAlgorithm[0] = oidPublicKeyRSA pkey.PrivateKey = x509.MarshalPKCS1PrivateKey(priv) } return asn1.Marshal(pkey) } func convertPrivateKeyToPKCS8Encrypted(priv interface{}, password []byte) ([]byte, error) { // Convert private key into PKCS8 format pkey, err := convertPrivateKeyToPKCS8(priv) if err != nil { return nil, err } // Calculate key from password based on PKCS5 algorithm // Use 8 byte salt, 16 byte IV, and 2048 iteration iter := 2048 salt := make([]byte, 8) iv := make([]byte, 16) _, err = rand.Read(salt) if err != nil { return nil, err } _, err = rand.Read(iv) if err != nil { return nil, err } key := pbkdf2.Key(password, salt, iter, 32, sha256.New) // Use AES256-CBC mode, pad plaintext with PKCS5 padding scheme padding := aes.BlockSize - len(pkey)%aes.BlockSize if padding > 0 { n := len(pkey) pkey = append(pkey, make([]byte, padding)...) for i := 0; i < padding; i++ { pkey[n+i] = byte(padding) } } encryptedKey := make([]byte, len(pkey)) block, err := aes.NewCipher(key) if err != nil { return nil, err } mode := cipher.NewCBCEncrypter(block, iv) mode.CryptBlocks(encryptedKey, pkey) // pbkdf2algo := pbkdf2Algorithms{oidPKCS5PBKDF2, pbkdf2Params{salt, iter, prfParam{oidHMACWithSHA256}}} pbkdf2algo := pbkdf2Algorithms{oidPKCS5PBKDF2, pbkdf2Params{salt, iter, prfParam{oidHMACWithSHA256, asn1.RawValue{Tag: asn1.TagNull}}}} pbkdf2encs := pbkdf2Encs{oidAES256CBC, iv} pbes2algo := pbes2Algorithms{oidPBES2, pbes2Params{pbkdf2algo, pbkdf2encs}} encryptedPkey := encryptedPrivateKeyInfo{pbes2algo, encryptedKey} return asn1.Marshal(encryptedPkey) } // ConvertPrivateKeyToPKCS8 converts the private key into PKCS#8 format. // To encrypt the private key, the password of []byte type should be provided as the second parameter. // // The only supported key types are RSA and ECDSA (*rsa.PublicKey or *ecdsa.PublicKey for priv) func ConvertPrivateKeyToPKCS8(priv interface{}, v ...[]byte) ([]byte, error) { if v == nil { return convertPrivateKeyToPKCS8(priv) } password := string(v[0]) return convertPrivateKeyToPKCS8Encrypted(priv, []byte(password)) } pkcs8-1.1/pkcs8_test.go000066400000000000000000000273001337411330200150200ustar00rootroot00000000000000package pkcs8_test import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/rsa" "encoding/pem" "testing" "github.com/youmark/pkcs8" ) const rsa2048 = `-----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDBMF0LikTFOU/T 8DUDSvORootvhUD67f6AXmEnntfXRvQ3O91+qt40tevS8JtFaq4gKxugRjjZRtni 50aUGcEZ4leq3DboBL9XH089IEmxxLbJeJIXxgPeRHrXRINvUSspwRrJkX6fnXyi MdRhqdH2tG1yrXKkt9UvdSHfRYimDcJ+ry2zYlcbz9aoLDO1vEdS/IBu0jXAZ/Z/ xaEVfkoWMzZM2SU+lfJeyzobii00VXGuSQKnI8E/e16kDpBXJ6PFSm6EyZmAad6O f+B9d/ZEXGQlbaooG54v5sGj54mg7m/75qMaxL2H8NER31gAeyvoyovfXI0vbswH 8AozxGwDAgMBAAECggEAautIY62nt/urKaIExQjDWvO59gOq3fW/5+3UGWh5DqUv Xi5cvND2X/fbR4hwdu++5QDWrlKO/fmPd1wGnMrQK3IwkNiF7s1J1H74jN0EzEUR 4NlBCbVGyMnfrqo1j/M9T0OXfr1udgpkQyQO5epl0QM0m8ZQ78bqTvSlxXsnULbQ py0Tx0uCWaP6FzDsZ+t2rj/SVH7hQNf8ITfQJhVol/n5Hza4+NRfp/DPXWZEvPlo GeMs9PDCa16tw8wI9EUnmFaeFlmtJPdTs5rVo9Ya/zmtoxN6AGTCG0IE6YRvh3Qn jttIp2QitOSBKmXpu1ZI6UTtimGgnfiJKK1BGVaMOQKBgQDfF6ZBMY/tLmDg1mgS QQKAOWMB0/3CvzcM96R0VACO2vr1BbePMXQQ/i27rD001Xl2wNTsETRk1Ji6btwQ 64m4uxRSZCJmYyBAcJjfBtMWIDiihQTL55NFTd9YIPmqGmbj1ASQgtpQR5Cq/5YR 9Vu0kTxMmADoiq1tR2VGZeScnwKBgQDdr4ITDFGSpqWKnyHQaQgTIW4uxQ5pQKIx aKbCNZOtSgJfqUCY+8gJMkFOtQzawrburD4qllFxdqhHLiXSx6/8zSTrsiexml2i 7HxUZaSmn5Q4HFNngKKHXd4NGsWp237k8fJ2953KX89yEov8FpIiq6qvZH/LS8DN +GORAPSSHQKBgCHobUuRZefN2cmyrOTBXsjwb/zyJKq593sQFL3dmqwb2nLtaIXq JVMD3x2cQz1JiQmkq3gp8UW2DnSfrvEfa7JZNPCE6bmYLWm9825KkkDVquYAw8be LsMk3+J8OJZDJwpPylXQnbAAAJwM9tlJ6qNaQ8j8fX7avRtT86+sgv/PAoGABjJp yG6HuTm/Vuir4U+OUjqVAemwRXDxF8B9KOCmiCmRd2sbyyr+pIMrIDAfc94Njw5x jm81R56xhYvcss+yM7boWU5ZnbVa+LrznshYme/MDOV9z17hLDeLhYJCFEV2fp/k zz6MwqN7AQ1TrHBVFXMHCnAcwmoTsa5H2j3UmGECgYEAvvJ+o5+FPnBs+VU5FJxF fAGFpF3AwfbSCm2ARZOxMHAkpsz/FBXlo+rVZv6loTKTPQFMxIB15il7ls0CGI9q 6UaZ5hkKjEOQUW8UYc8Cv0xpSkcuxcGrWzw4AMdc84XXi6F1+48ab9Gt0pN3tgUG qg+KU+JDsQLHHmykZ92cHPA= -----END PRIVATE KEY----- ` const encryptedRSA2048aes = `-----BEGIN ENCRYPTED PRIVATE KEY----- MIIFLTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIrrW09+9XumECAggA MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBAxglavSPtrKNsM9cDXmrS1BIIE 0Gy226c9+zxZ8jUsUIbDdsq1mPbqAWs1xImAj4nA7NMv6G/5QH9CrsmB+4r4GIiy CafN1W9YvFg3SISUbe+h156Pt2iKoZlVCbSa4XVo4diwmjloZIHM4Jk0Pu28CbJo QDVwPCuWMKppkfwr63RT+FBSfBEBaRCi4eXz6tOcMduBOlaiQvSREvDCCOeY9gja RgvyUa2Hf8oHNkSG9yXoMrvz0FayMWK/i7LU+2NqiPZVTvfGkqNkJJF/M7INKgLs d6A1hgyA7HVv4czQOPQJCArXeCycI1EJ4uSthJxqd/iYX0z52Tfa7q/0oAZ4HZt+ wmcov8GwqfAg7Cu9soifYwfMYTghXOX2UKmQa/0UNK5ibj5cC9+oA09Ucx5twKDs nwSGEIb+7qNhZSRtEXtOL7bxQL8PUvAXWrTXluvZ+bv/9S53XYPL4E95rrLnTF/L csEYleNIpY/6HkPFtqPZiWCsVUZep9uPjZo29kh/246yKBFjsw5mXtm1S6ha4Xb9 gUxqKQiWe9+tCkPHRVo2KJX1H4Al7UB9GqDR5oUhIayp6nYCeI/dLwPpikq1F8HO iJva/qV2iltxwyQHhEenyM9TPkPawqOOUKvDd1hZR0wzABcC3koLtwwKyEGzQPPW bxp5GBim9Pu/EGWY1d1H38eVu44jRP/3ONk8wvZcsIbn6U8bOeToUFmcjuuQ3cxf pDUruIA9PjWL9Se6TI3CytTUCbCb4bKRP+eE0B2LPwq6+dyvcY2yidYj9C2D25tb F+E1Wr7ro97OXQ8grMWwrTpZ9rUzmz5wzYWmOFaKJRiepkuUpx4HWl+fKn5r5LyV +cyYoSjApNgHe/9Pz7mNXNdeSmWcn4BVs1XgKi1MiJNWn5tNlKB3kz1kgraKOWbs 9/dspegd5fQ6Lzvlt7CsJh/I76rE+90LAbXWVlQ/jm/4jrWownjW1oVIj0Xfxx7i UlmtTFoCIvNWRyoyK5pfL0JvxOtd5leHZniJoww0CPKYS0mibxYLc883Q5Hq5ZH/ C7iBJN0aDJfVfkl0o4EQWaWQS0rAInhe7xTHmFFe5NP9lVTEwQt+C/fz7qalHe9P ulV8MsT/vg2/9twvxKbVCSzaDyta/TyhX76LTULprPr6ahDhP9rybmmK548m86kZ IxWdmed7Pt3YPeEImoLBoXh8eaWpYDlX2Be5/eqjw2wbg6srBKoA7swSkMsFXm5q +HgF4X6R9lfmLjs/UMOi9SM6ODh4xgq1DxX+bJZLfJwXj90i56Ij8OhjcBJ+DwUi ntosYkXp6lMZIyfI3jWG4IZwE9nt8oXJZfUtIU5mYF9DAV92fRwm1mCLMx0iznv1 bvCu7yJ51nWB3xkIOqCYbzbREWmL+6/akGOqu1KDrFKBu0IyAqUWt0XrY3b4V5jm WjTXywDkCcGC6W0t4yhu1Yz8QhE5Giw2PHwwZ3940QZQsFcBM6RJOcnkbYTu8TFm 7s9ZItSShwAN/i1nN1daF9lgdm4WKHWd/jqHIgl2NijiDgb5F5YaWgurKg9tOrEK oGJlPmBUiNynhqcz69ljjW6q4U2cfF4g6Onl2sucLdsFXejgVdsKBVXw+gjGr2TS lgmeHTcvZmTShvbN/TrHETjO7jEB4V2I4a4L7uybuWF/ -----END ENCRYPTED PRIVATE KEY----- ` const encryptedRSA2048des3 = `-----BEGIN ENCRYPTED PRIVATE KEY----- MIIFHDBOBgkqhkiG9w0BBQ0wQTApBgkqhkiG9w0BBQwwHAQIKXB+OWAc6pwCAggA MAwGCCqGSIb3DQIJBQAwFAYIKoZIhvcNAwcECCeQ2z+ohlaTBIIEyAbgxv69udAD N0JzM0+E/dYKTRxEVED1cyY+fcROfxkJvT3FOOWS65vaPo5i8n0e7pFHvm/ezkoL mNRYhoyd45pog4ALJ6O03bUBTLJPiowz90uHC7GWQelMl7LeNyX/7/5s2jOpgW82 oB6JizF9SjZzCTzKTmZLOAz3GjIERWHmoIczy40nxP7zmHzVrxTp1V6gnzxgUIuA X/7FTMRlWvEbX9gzODx7stI/5/bLla1Y7PDWEs2aJCnsN7pXJSd0Ry2/iBnQKe6n p4RW7jRAiFTGXbR1E5ZoFsSUs0K9JLEJA+kq6x+smRGxioV3I/r6MLaeumNZ37Bx 9OfiJAWk0Ei9EUjM4ZLWjnhgRyI2mThEXTbCevv2GonwG9G968QEMjfbXcLA6Opt 0mmRutT6IgvflEZRi9BlmCGOecNHl+cojVCwmAPZKkk2e9lZe+x9+TXW66GJVFiK 6BlgRwTcNPKePCYWPjsV5wUZACq0Y61nksBViyRUFsEkEEYMXIbh6bbUTTlJg/tk tCp/LF9oTf1XacJ8a/s6oLuz95R07u9E/liibzVavK0nVNSR5Xdo7QDivWxnaSLd wt8qUOnVbW0eSyq2BAKK7yvZfhz44D9WS8M8jp8gwj7Eti81LGqeh5IvqekDYmoz BFiY24PnRcZnpETA/e6v5dNrpE/OLHmdY1ag6aifIJCc1UG84Oi/nPBTZ7eHLGCd Kn4/9xdCVHd4077Qx9JLW9LutZXkqYaBckOEHtvaMfyWUaXiNty/N5RECGvn5wmM dwC6td6CqtojiHOB7GAUiwjHgbQLpNoIz1BiVTIo1eoD32+4RHYUxNmhsk0r22Zf ZnfnKBGgV7KKNKP3eFQnzSeNE0qFd5AtSpeJX0G0IsbuvXOE/7P0pj7DhD4HoYS7 Mf2za6Wm/CVWNM4ekc3MsKb9D+ogzdQ4VYI2mzBdLulrYvfPCE6SHZlZ+ePE4LSr jexB6LYLZJU7Bxnslt9E/mjSzWHctF9LhHf7sl7NUhCHdDvij6Hd0l4+noQlDTtd rnXgL9fTjSfaVDv3Rt+AmNN9Cu9Y1FSBLYMe8LfGXXUPg86bTbGk3uFjkyIY3hE2 /Kz1re4KXdDdjYe4ja5qZK8fWx0704NkzH1UO/HMd4Cnx3agyVeyJVG3iRSIesRG vI1ESJMMv1+MMGiWwRzHYvv7yrqp2steAVwjGu26/s1vwkZrvy8SjzYAXo1RLT9o cNnlPra6N7xReSohOibAp0kx1d81JqvEOvJIhR7KDXSRutgIPlqQgHXtmDW/VlCb w05Ptg3SXaCE0+pY0T+FYHusA3JEmyU0e629Ed/dl/j7Xpynl1V6/ndf3gdRGX0l d2IGneJsnj8yvP0dUsB2l71W/ZIM3HERDLxP9JByyINCBQ1BFsN81qUXpj6vGYjb hPyUmmsAGibXJOiGzmaP3nGgF9qbe1XiTRdbm2AZ3pEaJxkkFWsT+Yivz9yzZE0P 3/w14HvS94X/Z2+yDLtQQgsLNkfw/Gpc4O0GMnLuOl4KSaTA37IdJR2jOFP7LtHR 9Egbm93atZWSAyTO7OtZGmna6k6eGUsk8Dxp7cWOUkLf7C5sL6l3bBH7omlQHx9P RIiDkxAd7hbpm4/C/DoUZQ== -----END ENCRYPTED PRIVATE KEY----- ` const ec256 = `-----BEGIN PRIVATE KEY----- MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgjLFzKb/8hsdSmPft s98RZ7AWzQnLDsMhy6v+/3BZlZ6hRANCAASKkodoH+hHmBfwoFfrvv1E+iMLt3g1 s6hxOUMbkv6ZTVFXND/3z9zlJli6/YGrlSnsHOJc0GbwSYD1AMwZyr0T -----END PRIVATE KEY----- ` const encryptedEC256aes = `-----BEGIN ENCRYPTED PRIVATE KEY----- MIHsMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAjVvKZtHlmIbAICCAAw DAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEEL3jdkBvObn+QELgKVE2cnMEgZAl wgo3AjtXevJaGgep5GsW2krw9S7dC7xG9dR33Z/a9nBnO1rKm7Htf0+986w/1vmj 4k3M2QiI/VY+tnDFE+46DLLKYtJGRT1aoAH+mwhzaQGwzJnKhbeA23aE0f7KWCAK +f999+SeHWro7FiRZjHEYVVLGQr/I7K5Wyh24YjN2nR4CU4X+GQU25My/pgSRog= -----END ENCRYPTED PRIVATE KEY----- ` const ec128 = `-----BEGIN PRIVATE KEY----- MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgjLFzKb/8hsdSmPft s98RZ7AWzQnLDsMhy6v+/3BZlZ6hRANCAASKkodoH+hHmBfwoFfrvv1E+iMLt3g1 s6hxOUMbkv6ZTVFXND/3z9zlJli6/YGrlSnsHOJc0GbwSYD1AMwZyr0T -----END PRIVATE KEY-----` const encryptedEC128aes = `-----BEGIN ENCRYPTED PRIVATE KEY----- MIHeMEkGCSqGSIb3DQEFDTA8MBsGCSqGSIb3DQEFDDAOBAg7qE4RYQEEugICCAAw HQYJYIZIAWUDBAECBBBa+6eKv6il/iEjOw8/AmEHBIGQ24YmBiMfzjJjFU+PAwXr zCfR3NPOHBwn3+BkpyivaezSrFWIF919cnDyI15Omd+Iz2oljrT/R4IDC9NOmoAy 5uKixYGAOi74Qr9kdgrT2Bfvu9wq+dYqPwLjR4WFHl2ofrLn7RCaOa8mOh3bgfHP SnXPiACchx53PDh6bZTIZ0V9v0ymcMuXf758OXbUmSGN -----END ENCRYPTED PRIVATE KEY-----` func TestParsePKCS8PrivateKeyRSA(t *testing.T) { keyList := []struct { name string clear string encrypted string }{ { name: "encryptedRSA2048aes", clear: rsa2048, encrypted: encryptedRSA2048aes, }, { name: "encryptedRSA2048des3", clear: rsa2048, encrypted: encryptedRSA2048des3, }, } for i, key := range keyList { t.Run(key.name, func(t *testing.T) { block, _ := pem.Decode([]byte(key.encrypted)) _, err := pkcs8.ParsePKCS8PrivateKeyRSA(block.Bytes, []byte("password")) if err != nil { t.Errorf("%d: ParsePKCS8PrivateKeyRSA returned: %s", i, err) } _, err = pkcs8.ParsePKCS8PrivateKeyRSA(block.Bytes, []byte("wrong password")) if err == nil { t.Errorf("%d: should have failed", i) } _, err = pkcs8.ParsePKCS8PrivateKeyRSA(block.Bytes) if err == nil { t.Errorf("%d: should have failed", i) } block, _ = pem.Decode([]byte(key.clear)) _, err = pkcs8.ParsePKCS8PrivateKeyRSA(block.Bytes) if err != nil { t.Errorf("%d: ParsePKCS8PrivateKeyRSA returned: %s", i, err) } }) } } func TestParsePKCS8PrivateKeyECDSA(t *testing.T) { keyList := []struct { name string clear string encrypted string }{ { name: "encryptedEC256aes", clear: ec256, encrypted: encryptedEC256aes, }, } for i, key := range keyList { t.Run(key.name, func(t *testing.T) { block, _ := pem.Decode([]byte(key.encrypted)) _, err := pkcs8.ParsePKCS8PrivateKeyECDSA(block.Bytes, []byte("password")) if err != nil { t.Errorf("%d: ParsePKCS8PrivateKeyECDSA returned: %s", i, err) } _, err = pkcs8.ParsePKCS8PrivateKeyECDSA(block.Bytes, []byte("wrong password")) if err == nil { t.Errorf("%d: should have failed", i) } _, err = pkcs8.ParsePKCS8PrivateKeyECDSA(block.Bytes) if err == nil { t.Errorf("%d: should have failed", i) } block, _ = pem.Decode([]byte(key.clear)) _, err = pkcs8.ParsePKCS8PrivateKeyECDSA(block.Bytes) if err != nil { t.Errorf("%d: ParsePKCS8PrivateKeyECDSA returned: %s", i, err) } }) } } func TestParsePKCS8PrivateKey(t *testing.T) { keyList := []struct { name string clear string encrypted string }{ { name: "encryptedRSA2048aes", clear: rsa2048, encrypted: encryptedRSA2048aes, }, { name: "encryptedRSA2048des3", clear: rsa2048, encrypted: encryptedRSA2048des3, }, { name: "encryptedEC256aes", clear: ec256, encrypted: encryptedEC256aes, }, { name: "encryptedEC128aes", clear: ec128, encrypted: encryptedEC128aes, }, } for i, key := range keyList { t.Run(key.name, func(t *testing.T) { block, _ := pem.Decode([]byte(key.encrypted)) _, err := pkcs8.ParsePKCS8PrivateKey(block.Bytes, []byte("password")) if err != nil { t.Errorf("%d: ParsePKCS8PrivateKey returned: %s", i, err) } _, err = pkcs8.ParsePKCS8PrivateKey(block.Bytes, []byte("wrong password")) if err == nil { t.Errorf("%d: should have failed", i) } _, err = pkcs8.ParsePKCS8PrivateKey(block.Bytes) if err == nil { t.Errorf("%d: should have failed", i) } block, _ = pem.Decode([]byte(key.clear)) _, err = pkcs8.ParsePKCS8PrivateKey(block.Bytes) if err != nil { t.Errorf("%d: ParsePKCS8PrivateKey returned: %s", i, err) } }) } } func TestConvertPrivateKeyToPKCS8(t *testing.T) { for i, password := range [][]byte{nil, []byte("password")} { var args [][]byte if password != nil { args = append(args, password) } rsaPrivateKey, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { t.Fatalf("%d: ConvertPrivateKeyToPKCS8 returned: %s", i, err) } der, err := pkcs8.ConvertPrivateKeyToPKCS8(rsaPrivateKey, args...) if err != nil { t.Fatalf("%d: ConvertPrivateKeyToPKCS8 returned: %s", i, err) } decodedRSAPrivateKey, err := pkcs8.ParsePKCS8PrivateKey(der, args...) if err != nil { t.Fatalf("%d: ParsePKCS8PrivateKey returned: %s", i, err) } if rsaPrivateKey.D.Cmp(decodedRSAPrivateKey.(*rsa.PrivateKey).D) != 0 { t.Fatalf("%d: Decoded key does not match original key", i) } for _, curve := range []elliptic.Curve{ elliptic.P224(), elliptic.P256(), elliptic.P384(), elliptic.P521(), } { ecPrivateKey, err := ecdsa.GenerateKey(curve, rand.Reader) if err != nil { t.Fatalf("%d, %s: ConvertPrivateKeyToPKCS8 returned: %s", i, curve, err) } der, err = pkcs8.ConvertPrivateKeyToPKCS8(ecPrivateKey, args...) if err != nil { t.Fatalf("%d, %s: ConvertPrivateKeyToPKCS8 returned: %s", i, curve, err) } decodedECPrivateKey, err := pkcs8.ParsePKCS8PrivateKey(der, args...) if err != nil { t.Fatalf("%d, %s: ParsePKCS8PrivateKey returned: %s", i, curve, err) } if ecPrivateKey.D.Cmp(decodedECPrivateKey.(*ecdsa.PrivateKey).D) != 0 { t.Fatalf("%d, %s: Decoded key does not match original key", i, curve) } } } }