Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GODRIVER-364 Support PKCS8 encrypted client private keys #565

Merged
merged 9 commits into from
Feb 1, 2021
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions data/certificates/client-pkcs8-encrypted.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIsqMuOBMVHM4CAggA
MBQGCCqGSIb3DQMHBAjGoriU4YaS6wSCBMhu0dQ8OjzcAJzyhe3osPWFQObhlpsi
sD6Hzx2mcJcZMAv1AG7/MPpPQixiZipQLIiQPOozgjHDqmBp1lGz4FW7sXT01SJT
xlALdUGVrHp5BZRVZGhfZ/SeRknqxoQKqIEZQZeTBJDVZ/dWrLOUfycp0rLWxctq
FVUcb0jq2YL7u9IBIAmFgFyj2ZVWu68HiiwuhKJ0botuFSHe76DP0lthwlHMOR+L
brK7IrMuGhp4xAI16pDoQbwj4AFz2JWhb5WeYhE2CMFhzoZZLgfafBeO/GG04QTt
VB2/Tj5M6jbeCO8H3rYo9yDuWijeOxNDEwRHpLrRcK0T9TouETYzcSFpZIJXhHS3
0ZV44JMGmPP5fkt34mbkBs68nNqcaWuInBx4k7FZZqkd7LyjGRP3mWeqfMhqDaw+
KhAADLRCY7P7ooa3WGGP8+/hdI+I1SR+TCwmiHzY6FRgBlnmlqNH1WS3je4/Dx3w
H8YzPurMpyLlETN1u1DIIZEQteZHVNcZBkTdW1eWOgoOt30wNzrJQPEgPEZgAzvR
4vgKVF68892g755hyOgXl5Hoqf1w2q85l+3qp1zb2UDUvcIkbU7HXnmgZWRJ9T94
kgDlJ82Y+BECQFPVTrEhoVGO0q0eF6On8r7slCoY5edqGdXcyLCuYolEgFHqUFQg
Hjj6x17HQBRa3nnkF3j8tho5kxa6pdvmJcXghJ4Y6Tfs2x9qyjHVYqJH2emuuP9f
Gk5oyicOBTjDnGHG0Hvdd3OSpzQP9P6xMhVByomnz+iGO2CYiJdplKHCeS62Lh7D
px0p7CNxIZtWCBWeQXKqofrJm7PQP+izAqurzGLhMTggCfH8QRZPhiykRnRaIwdV
Ml1veBN6JA91OBkwuioiTH+/zEL8KmpFW5f5okpicUSw6NTx5N2OGqYzqIdo1D0B
0MThRHQszOF4v89O3OPy2PXrYZdxM521CckBsEEVvyKWWd/xlXzheqZvpao2me05
Dmrjb6VTuE5kev/3I6mC6aJMi7CF6fqXaUGm+StlPEAbUudjBnwav8PWTv2hF1VS
Ogj9K6nNG1EyTik4uEltZL6h1EWeZqYap/VJXfwNQn+BAv5xX2mLieIVHpltLQY7
ushULJhMTkMJVQbtHulMyUibE/JBT70ra5GgpjRdpib+3M/7OZOZAm0tWZMovZZS
+UfoC7JApa1FXcsQ03WhGHu3q6kL7TB6VW+QrBnxi8MQBeEydu2cAwrdGKJWtIjL
gBWuAJCEwH/wenim+7YzFYg72aCsaVQxcjYvHAiqEFPDziqk+CLebfZkgF4IEfdF
XbCnAYb8GQmlaJsbEVPtLvVVCYN0/RAEn6ec3ivHkCt4tM67F9pOEaIyk7PWOfpm
ViyUn62O30fQNfWKfjfhD5ttSY/8X0Fa7B9+V4s+2Exb2r0P/wd9k2sjK2IyDNMu
rim0ILriuQ3geBXdJi+uauPA3VnS0uoJoUPs4+nrWHITPOH8ZS7KeZYb9bdEtt61
I47cMXZl9LZASzRnhzgMYN0QBcnnwOXDHKQhwC0FSpB8NZ/gFjgy141/NM1VIIw0
fC9OjnidYxYTv58uFXCPKqCk+iEDQLj68M+VUJgwMSov7M5BA/ymwIPy/548vTZ4
rgE=
-----END ENCRYPTED PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIDdjCCAl6gAwIBAgIDBpgxMA0GCSqGSIb3DQEBBQUAMG4xEzARBgNVBAMTCkNE
cml2ZXIgQ0ExEDAOBgNVBAsTB0RyaXZlcnMxEDAOBgNVBAoTB01vbmdvREIxEjAQ
BgNVBAcTCVJleWtqYXZpazESMBAGA1UECBMJUmV5a2phdmlrMQswCQYDVQQGEwJJ
UzAeFw0xODAyMjgyMDQ0NDhaFw0zODAyMjgyMDQ0NDhaMG0xETAPBgNVBAMTCGV4
dGVybmFsMQ4wDAYDVQQLEwVvdGhlcjEQMA4GA1UEChMHTW9uZ29EQjEWMBQGA1UE
BxMNTmV3IFlvcmsgQ2l0eTERMA8GA1UECBMITmV3IFlvcmsxCzAJBgNVBAYTAlVT
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmaii9Oq1jnOjD/NvCOE5
XJAU0xSdWLi28q/HhsrIUwT2aRRObkVPx57Cl90/QKfwYmY4Z1E6oEXQX0F7tcV0
/e9easzBFB69ZS/ztF91xuofeK6tOmAPzRJx4pEAgoY5tlG+Ifs4BS23qe278w51
u1JWo1v50v5qr2MzDt/zZHz1EFz0Q5SBhVe9x5/jyVgd7f6gts2aXFrwE53OavlD
QS3d1aC3xlCGo8tH7UmI0rtJNiwmcPrvbkgSkxmsUDZW4kuvrrY3oDxeLEgaUrZq
rHkVY4WXbbe2359izlpZql5JsaW9FCcqmRP0xmjMZfTcMCLVKCK9JObGj/jlkOzg
QQIDAQABox4wHDAaBgNVHREEEzARgglsb2NhbGhvc3SHBH8AAAEwDQYJKoZIhvcN
AQEFBQADggEBAJ2pQoO8zf2BW5jZ8U5WVQlRm/s9LflH7jCkqqIIxRwFENtHEeK0
tOxfW1/JYw6GAuGsLrCXwVI1OeV0KPquoh26sFtrxHeeIxJjAOZ5ov/tLDBciBBD
QCnqPOCehvv/OH9BLFoFdW/3hLqHuKTiiDL9537CiZuDqiKyjucwSlqLk3fBcrna
Se8n2w+g3btD8dEYe0+G+dh2k0EJZ+78LW8Gr642knebxMjrsqD+wooryBBn8jls
9hJjG0D5TbX1koIHDYJE727FC/kWmNhK1Rvt1AKnsMFlrrQBbNK9YvEP6y3KQrb0
R1YgAaV74AIDaGGT1Id+mSteN7KOK34Ruso=
-----END CERTIFICATE-----

50 changes: 50 additions & 0 deletions data/certificates/client-pkcs8-unencrypted.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCZqKL06rWOc6MP
828I4TlckBTTFJ1YuLbyr8eGyshTBPZpFE5uRU/HnsKX3T9Ap/BiZjhnUTqgRdBf
QXu1xXT9715qzMEUHr1lL/O0X3XG6h94rq06YA/NEnHikQCChjm2Ub4h+zgFLbep
7bvzDnW7UlajW/nS/mqvYzMO3/NkfPUQXPRDlIGFV73Hn+PJWB3t/qC2zZpcWvAT
nc5q+UNBLd3VoLfGUIajy0ftSYjSu0k2LCZw+u9uSBKTGaxQNlbiS6+utjegPF4s
SBpStmqseRVjhZdtt7bfn2LOWlmqXkmxpb0UJyqZE/TGaMxl9NwwItUoIr0k5saP
+OWQ7OBBAgMBAAECggEAUm1YHTHa+vOlQWVA5u6KqtDPmvuv/Gv6F+2bhv84vvAm
ju/JsvWTem37zSNuTuzH0sEq+KFmZZuNz8t85WFrBN1nNLtpx7VFvAYhIV0j/PSS
tVZerrXhRAzk8rj+IQaH5vmqmOf+gSipIYraC+Rx226r8y3fsgOwjy0TsqMIGZrp
puczHAaMFvSXzTn2rD7d0AYnTgdIPJNDSwnTvTbnwMNDA4F7E/pJjjkm2kABdXSJ
wTuPnPSeUz0mTYpu4cOc+q+4jhpZr5GaF1declyIl8uU9gyK+gZRdrMHLVsFzdIM
Z+6beF+aCta5wXACou7kRvXQhatiQfTmOe4cpTElwQKBgQDMnkZVhDE7BryVnRE2
N/OJD64g/ygvJOL7rT+dqrNGhJCDHw30Vast2fxmibMk6iI3PkCj3XuTnXEtEOLr
td2bDpWCreMmj0hlFnFAh/p7J6olgbsnJO1Vhn7PcKevUfQVlpcDcu+GcvBHly2r
exAwKNxj6ZxqXf+KtI8w8Ca+PQKBgQDAPnfy7mMg/ytJtSlQz3srWSUERkOI1xcF
GimnVdofKu1PeqAsHOyNTRgoLDmI1NXG+1jva9ML8yBJQAf/+4a8IXKEwCQDVVHs
9iNa7gs6ULo41YRouL8cmat4XLh/OxWwYKZdxQWGTgnHKjVNdf6Df7OOm7uZBbU4
i4sYii/uVQKBgQCCJ39HsDF8gVl9tY4YNdjkayPw+zy9WDJFsrsPeGBWz8X4kc1X
iRK8tLcXJincpk2jZCbL1PthNzmhV+dv1ZwjoFA78o3VnjiHjJH3YUdUBTP2baH1
UUjiKQ4Kt3cCTxf6j3J5kCeKFxx9/UzgkgQHDka6CwQiqK3+tcGLeIa8ZQKBgQCM
wfBvde4s7chTKor6uT/UyGubCptOKTaYrMRM2kZfxb2GESIPyonFF+qVF3R05Gk2
TTib7NXVDQnZuEFjQ1Yuj0rbOhfkPOdEWiAe5uZfp1YCYQuW5ZZAqZ9r/G+18Jv7
zXhideyKnr74DcaDVd6ph6n/w7UC4LQEl9+bcyqPFQKBgATRW/owDjadRVuDB9C4
qzdyXuibMqRRUtvm6k4hs+HyirkrTnWb2YVbRMvrdT783s2wIeKB1oR5aG12A8ln
Hn7CeoLtRU12q65yOkX7vzER6oRmcyBVLhC+izRdoaBmKRuFdmQpXOALZRb/iNzQ
2nUsi2lvjvL42tYvIa8rtm4o
-----END PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIDdjCCAl6gAwIBAgIDBpgxMA0GCSqGSIb3DQEBBQUAMG4xEzARBgNVBAMTCkNE
cml2ZXIgQ0ExEDAOBgNVBAsTB0RyaXZlcnMxEDAOBgNVBAoTB01vbmdvREIxEjAQ
BgNVBAcTCVJleWtqYXZpazESMBAGA1UECBMJUmV5a2phdmlrMQswCQYDVQQGEwJJ
UzAeFw0xODAyMjgyMDQ0NDhaFw0zODAyMjgyMDQ0NDhaMG0xETAPBgNVBAMTCGV4
dGVybmFsMQ4wDAYDVQQLEwVvdGhlcjEQMA4GA1UEChMHTW9uZ29EQjEWMBQGA1UE
BxMNTmV3IFlvcmsgQ2l0eTERMA8GA1UECBMITmV3IFlvcmsxCzAJBgNVBAYTAlVT
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmaii9Oq1jnOjD/NvCOE5
XJAU0xSdWLi28q/HhsrIUwT2aRRObkVPx57Cl90/QKfwYmY4Z1E6oEXQX0F7tcV0
/e9easzBFB69ZS/ztF91xuofeK6tOmAPzRJx4pEAgoY5tlG+Ifs4BS23qe278w51
u1JWo1v50v5qr2MzDt/zZHz1EFz0Q5SBhVe9x5/jyVgd7f6gts2aXFrwE53OavlD
QS3d1aC3xlCGo8tH7UmI0rtJNiwmcPrvbkgSkxmsUDZW4kuvrrY3oDxeLEgaUrZq
rHkVY4WXbbe2359izlpZql5JsaW9FCcqmRP0xmjMZfTcMCLVKCK9JObGj/jlkOzg
QQIDAQABox4wHDAaBgNVHREEEzARgglsb2NhbGhvc3SHBH8AAAEwDQYJKoZIhvcN
AQEFBQADggEBAJ2pQoO8zf2BW5jZ8U5WVQlRm/s9LflH7jCkqqIIxRwFENtHEeK0
tOxfW1/JYw6GAuGsLrCXwVI1OeV0KPquoh26sFtrxHeeIxJjAOZ5ov/tLDBciBBD
QCnqPOCehvv/OH9BLFoFdW/3hLqHuKTiiDL9537CiZuDqiKyjucwSlqLk3fBcrna
Se8n2w+g3btD8dEYe0+G+dh2k0EJZ+78LW8Gr642knebxMjrsqD+wooryBBn8jls
9hJjG0D5TbX1koIHDYJE727FC/kWmNhK1Rvt1AKnsMFlrrQBbNK9YvEP6y3KQrb0
R1YgAaV74AIDaGGT1Id+mSteN7KOK34Ruso=
-----END CERTIFICATE-----

3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ require (
github.com/tidwall/pretty v1.0.0
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c
github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc
golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e
golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2 // indirect
golang.org/x/text v0.3.3 // indirect
Expand Down
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,17 @@ github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c h1:u40Z8hqBAAQyv+vATcGgV
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc h1:n+nNi93yXLkJvKwXNP9d55HC7lGK4H/SRcwB5IaUZLo=
github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqToslyjUt3VOPF4J7aK/3MPcK7xp3PDk=
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5 h1:8dUaAV7K4uHsF56JQWkprecIQKdPHtR9jCHF5nB8uzc=
golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
Expand Down
138 changes: 80 additions & 58 deletions mongo/integration/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,67 +82,89 @@ func TestClient(t *testing.T) {
assert.Nil(mt, found, "SSLServerHasCertificateAuthority not found in result")
})
mt.RunOpts("x509", mtest.NewOptions().Auth(true).SSL(true), func(mt *mtest.T) {
const user = "C=US,ST=New York,L=New York City,O=MongoDB,OU=other,CN=external"
db := mt.Client.Database("$external")

// We don't care if the user doesn't already exist.
_ = db.RunCommand(
mtest.Background,
bson.D{{"dropUser", user}},
)
err := db.RunCommand(
mtest.Background,
bson.D{
{"createUser", user},
{"roles", bson.A{
bson.D{{"role", "readWrite"}, {"db", "test"}},
}},
testCases := []struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, good idea making this into a table test!

certificate string
password string
}{
{
"client.pem",
"",
},
).Err()
assert.Nil(mt, err, "createUser error: %v", err)

baseConnString := mtest.ClusterURI()
// remove username/password from base conn string
revisedConnString := "mongodb://"
split := strings.Split(baseConnString, "@")
assert.Equal(t, 2, len(split), "expected 2 parts after split, got %v (connstring %v)", split, baseConnString)
revisedConnString += split[1]

cs := fmt.Sprintf(
"%s&sslClientCertificateKeyFile=%s&authMechanism=MONGODB-X509&authSource=$external",
revisedConnString,
path.Join(certificatesDir, "client.pem"),
)
authClientOpts := options.Client().ApplyURI(cs)
testutil.AddTestServerAPIVersion(authClientOpts)
authClient, err := mongo.Connect(mtest.Background, authClientOpts)
assert.Nil(mt, err, "authClient Connect error: %v", err)
defer func() { _ = authClient.Disconnect(mtest.Background) }()

rdr, err := authClient.Database("test").RunCommand(mtest.Background, bson.D{
{"connectionStatus", 1},
}).DecodeBytes()
assert.Nil(mt, err, "connectionStatus error: %v", err)
users, err := rdr.LookupErr("authInfo", "authenticatedUsers")
assert.Nil(mt, err, "authenticatedUsers not found in response")
elems, err := users.Array().Elements()
assert.Nil(mt, err, "error getting users elements: %v", err)

for _, userElem := range elems {
rdr := userElem.Value().Document()
var u struct {
User string
DB string
}
{
"client-pkcs8-encrypted.pem",
"&sslClientCertificateKeyPassword=password",
},
{
"client-pkcs8-unencrypted.pem",
"",
},
}
for _, tc := range testCases {
mt.Run(tc.certificate, func(mt *mtest.T) {
const user = "C=US,ST=New York,L=New York City,O=MongoDB,OU=other,CN=external"
db := mt.Client.Database("$external")

// We don't care if the user doesn't already exist.
_ = db.RunCommand(
mtest.Background,
bson.D{{"dropUser", user}},
)
err := db.RunCommand(
mtest.Background,
bson.D{
{"createUser", user},
{"roles", bson.A{
bson.D{{"role", "readWrite"}, {"db", "test"}},
}},
},
).Err()
assert.Nil(mt, err, "createUser error: %v", err)

baseConnString := mtest.ClusterURI()
// remove username/password from base conn string
revisedConnString := "mongodb://"
split := strings.Split(baseConnString, "@")
assert.Equal(t, 2, len(split), "expected 2 parts after split, got %v (connstring %v)", split, baseConnString)
revisedConnString += split[1]

cs := fmt.Sprintf(
"%s&sslClientCertificateKeyFile=%s&authMechanism=MONGODB-X509&authSource=$external%s",
revisedConnString,
path.Join(certificatesDir, tc.certificate),
tc.password,
)
authClientOpts := options.Client().ApplyURI(cs)
testutil.AddTestServerAPIVersion(authClientOpts)
authClient, err := mongo.Connect(mtest.Background, authClientOpts)
assert.Nil(mt, err, "authClient Connect error: %v", err)
defer func() { _ = authClient.Disconnect(mtest.Background) }()

rdr, err := authClient.Database("test").RunCommand(mtest.Background, bson.D{
{"connectionStatus", 1},
}).DecodeBytes()
assert.Nil(mt, err, "connectionStatus error: %v", err)
users, err := rdr.LookupErr("authInfo", "authenticatedUsers")
assert.Nil(mt, err, "authenticatedUsers not found in response")
elems, err := users.Array().Elements()
assert.Nil(mt, err, "error getting users elements: %v", err)

for _, userElem := range elems {
rdr := userElem.Value().Document()
var u struct {
User string
DB string
}

if err := bson.Unmarshal(rdr, &u); err != nil {
continue
}
if u.User == user && u.DB == "$external" {
return
}
if err := bson.Unmarshal(rdr, &u); err != nil {
continue
}
if u.User == user && u.DB == "$external" {
return
}
}
mt.Fatal("unable to find authenticated user")
})
}
mt.Fatal("unable to find authenticated user")
})
mt.RunOpts("list databases", noClientOpts, func(mt *mtest.T) {
mt.RunOpts("filter", noClientOpts, func(mt *mtest.T) {
Expand Down
33 changes: 27 additions & 6 deletions mongo/options/clientoptions.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"strings"
"time"

"github.com/youmark/pkcs8"
"go.mongodb.org/mongo-driver/bson/bsoncodec"
"go.mongodb.org/mongo-driver/event"
"go.mongodb.org/mongo-driver/mongo/readconcern"
Expand Down Expand Up @@ -902,14 +903,34 @@ func addClientCertFromBytes(cfg *tls.Config, data []byte, keyPasswd string) (str
certDecodedBlock = currentBlock.Bytes
start += len(certBlock)
} else if strings.HasSuffix(currentBlock.Type, "PRIVATE KEY") {
if keyPasswd != "" && x509.IsEncryptedPEMBlock(currentBlock) {
var encoded bytes.Buffer
buf, err := x509.DecryptPEMBlock(currentBlock, []byte(keyPasswd))
if err != nil {
return "", err
isEncrypted := x509.IsEncryptedPEMBlock(currentBlock) || strings.Contains(currentBlock.Type, "ENCRYPTED PRIVATE KEY")
if isEncrypted {
if keyPasswd == "" {
return "", fmt.Errorf("no password provided to decrypt private key")
}

pem.Encode(&encoded, &pem.Block{Type: currentBlock.Type, Bytes: buf})
var keyBytes []byte
var err error
// Process the X.509-encrypted or PKCS-encrypted PEM block.
if x509.IsEncryptedPEMBlock(currentBlock) {
// Only covers encrypted PEM data with a DEK-Info header.
keyBytes, err = x509.DecryptPEMBlock(currentBlock, []byte(keyPasswd))
if err != nil {
return "", err
}
} else if strings.Contains(currentBlock.Type, "ENCRYPTED") {
// The pkcs8 package only handles the PKCS #5 v2.0 scheme.
Copy link
Contributor Author

@benjirewis benjirewis Jan 21, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This parsing code is from the TOOLS-2013 PR. This was also brought up there, but youmark/pkcs8 can only handle the PKCS 5 v2.0 scheme. I don't much about the various PKCS schemes, but it might be worth documenting somewhere how to create this scheme. I used:

openssl pkcs8 -v2 des3 -topk8 -inform PEM -outform PEM -in client.pem -out client-pkcs8-encrypted.pem

Furthermore, did we need support for a specific scheme?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, the command used to generate seems worth leaving a comment in GODRIVER-364 for future reference (anytime I need to generate certs, I need to google around...).

Given the conversation in TOOLS-2013 and related tickets, I think v2.0 is only requirement. It seems v1.5 was superseded (judging from RFC-8018 and and an article describing the v2.0 key derivation function.

cc @markbenvenuto in case you'd like to weigh in. I do not think we need to support more than PKCS 5 v2.0.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can leave the two commands (one for encrypted and one for unencrypted) in the original ticket. And good to know 2.0 supersedes other versions; thanks for doing that research.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we only need to support PKCS#5 v2.0. We don't need to support the older version since it uses a weaker key derivation function.

decrypted, err := pkcs8.ParsePKCS8PrivateKey(currentBlock.Bytes, []byte(keyPasswd))
if err != nil {
return "", err
}
keyBytes, err = x509MarshalPKCS8PrivateKey(decrypted)
if err != nil {
return "", err
}
}
var encoded bytes.Buffer
pem.Encode(&encoded, &pem.Block{Type: currentBlock.Type, Bytes: keyBytes})
keyBlock = encoded.Bytes()
start = len(data) - len(remaining)
} else {
Expand Down
4 changes: 4 additions & 0 deletions mongo/options/clientoptions_1_10.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@ import "crypto/x509"
func x509CertSubject(cert *x509.Certificate) string {
return cert.Subject.String()
}

func x509MarshalPKCS8PrivateKey(pkcs8 interface{}) ([]byte, error) {
return x509.MarshalPKCS8PrivateKey(pkcs8)
}
11 changes: 9 additions & 2 deletions mongo/options/clientoptions_1_9.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,17 @@ package options

import (
"crypto/x509"
"fmt"
)

// We don't support version less then 1.10, but Evergreen needs to be able to compile the driver
// using version 1.8.
// We don't support Go versions less than 1.10, but Evergreen needs to be able to compile the driver
// using version 1.8 and cert.Subject
benjirewis marked this conversation as resolved.
Show resolved Hide resolved
func x509CertSubject(cert *x509.Certificate) string {
return ""
}

// We don't support Go versions less than 1.10, but Evergreen needs to be able to compile the driver
// using version 1.9 and x509.MarshalPKCS8PrivateKey()
func x509MarshalPKCS8PrivateKey(pkcs8 interface{}) ([]byte, error) {
return nil, fmt.Errorf("PKCS8-encrypted client private keys are only supported with go1.10+")
}
23 changes: 23 additions & 0 deletions vendor/github.com/youmark/pkcs8/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions vendor/github.com/youmark/pkcs8/.travis.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading