summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.third_party.md4
-rw-r--r--buildscripts/resmokeconfig/suites/ocsp.yml15
-rw-r--r--etc/evergreen.yml13
-rw-r--r--jstests/libs/ocsp/ca.cnf70
-rw-r--r--jstests/libs/ocsp/ca.crt22
-rw-r--r--jstests/libs/ocsp/ca.key27
-rw-r--r--jstests/libs/ocsp/ca_ocsp.pem49
-rw-r--r--jstests/libs/ocsp/client_ocsp.pem52
-rw-r--r--jstests/libs/ocsp/ocsp_cert.crt20
-rw-r--r--jstests/libs/ocsp/ocsp_cert.key28
-rw-r--r--jstests/libs/ocsp/ocsp_responder.crt21
-rw-r--r--jstests/libs/ocsp/ocsp_responder.key28
-rw-r--r--jstests/libs/ocsp/server_ocsp.pem52
-rw-r--r--jstests/ocsp/lib/mock_ocsp.js15
-rw-r--r--jstests/ocsp/lib/ocsp_helpers.js11
-rw-r--r--jstests/ocsp/lib/ocsp_mock.py33
-rw-r--r--jstests/ocsp/ocsp_basic.js39
-rw-r--r--jstests/ssl/x509/certs.yml75
-rwxr-xr-xjstests/ssl/x509/mkcert.py43
-rw-r--r--src/mongo/util/net/ocsp/ocsp_manager.cpp4
-rw-r--r--src/mongo/util/net/ocsp/ocsp_manager.h2
-rw-r--r--src/mongo/util/net/ssl_manager_openssl.cpp9
-rw-r--r--src/third_party/mock_ocsp_responder/mock_ocsp_responder.py611
23 files changed, 1027 insertions, 216 deletions
diff --git a/README.third_party.md b/README.third_party.md
index 975d606109d..da40a71f26d 100644
--- a/README.third_party.md
+++ b/README.third_party.md
@@ -36,6 +36,8 @@ a notice will be included in
| [linenoise] | BSD-3-Clause | | Unknown + changes | | ✗ |
| [MozJS] | MPL-2.0 | ESR 60.9 | ESR 60.3.0 | | ✗ |
| [MurmurHash3] | Public Domain | | Unknown + changes | ✗ | ✗ |
+| [ocspbuilder] | MIT | 0.10.2 | 0.10.2 | | |
+| [ocspresponder] | Apache-2.0 | 0.5.0 | 0.5.0 | | |
| [Pcre] | BSD-3-Clause | 8.43 | 8.42 | | ✗ |
| [S2] | Apache-2.0 | | Unknown | ✗ | ✗ |
| [SafeInt] | MIT | 3.21 | 3.0.20p | | |
@@ -66,6 +68,8 @@ a notice will be included in
[linenoise]: https://github.com/antirez/linenoise
[MozJS]: https://www.mozilla.org/en-US/security/known-vulnerabilities/firefox-esr
[MurmurHash3]: https://github.com/aappleby/smhasher/blob/master/src/MurmurHash3.cpp
+[ocspbuilder]: https://github.com/wbond/ocspbuilder
+[ocspresponder]: https://github.com/threema-ch/ocspresponder
[Pcre]: http://www.pcre.org/
[S2]: https://github.com/google/s2geometry
[SafeInt]: https://github.com/dcleblanc/SafeInt
diff --git a/buildscripts/resmokeconfig/suites/ocsp.yml b/buildscripts/resmokeconfig/suites/ocsp.yml
new file mode 100644
index 00000000000..25313657c53
--- /dev/null
+++ b/buildscripts/resmokeconfig/suites/ocsp.yml
@@ -0,0 +1,15 @@
+test_kind: js_test
+
+selector:
+ roots:
+ - jstests/ocsp/*.js
+
+executor:
+ config:
+ shell_options:
+ nodb: ''
+ readMode: commands
+ ssl: ''
+ tlsCAFile: jstests/libs/ocsp/ca_ocsp.pem
+ tlsCertificateKeyFile: jstests/libs/ocsp/client_ocsp.pem
+ sslAllowInvalidHostnames: ''
diff --git a/etc/evergreen.yml b/etc/evergreen.yml
index 417c5ffdd0e..f91859df734 100644
--- a/etc/evergreen.yml
+++ b/etc/evergreen.yml
@@ -3906,6 +3906,7 @@ tasks:
- "src/mongo/util/options_parser/test_config_files/**"
- "library_dependency_graph.json"
- "src/third_party/JSON-Schema-Test-Suite/tests/draft4/**"
+ - "src/third_party/mock_ocsp_responder/**"
- "bypass_compile_expansions.yml"
- "patch_files.txt"
- "artifacts.json"
@@ -8207,6 +8208,16 @@ tasks:
resmoke_jobs_max: 1
- <<: *task_template
+ name: ocsp
+ tags: ["ocsp"]
+ commands:
+ - func: "do setup"
+ - func: "run tests"
+ vars:
+ resmoke_args: --suites=ocsp
+ resmoke_jobs_max: 1
+
+- <<: *task_template
name: jsonSchema
commands:
- func: "do setup"
@@ -9263,6 +9274,7 @@ buildvariants:
- name: .logical_session_cache
- name: .multi_shard
- name: multi_stmt_txn_jscore_passthrough_with_migration_gen
+ - name: .ocsp
- name: .read_write_concern
- name: .replica_sets !.encrypt
- name: .retry
@@ -10976,6 +10988,7 @@ buildvariants:
- name: .multi_shard
- name: multi_stmt_txn_jscore_passthrough_with_migration_gen
- name: multiversion
+ - name: .ocsp
- name: .query_fuzzer
- name: .read_write_concern .large
distros:
diff --git a/jstests/libs/ocsp/ca.cnf b/jstests/libs/ocsp/ca.cnf
deleted file mode 100644
index 6c986139087..00000000000
--- a/jstests/libs/ocsp/ca.cnf
+++ /dev/null
@@ -1,70 +0,0 @@
-[ ca ]
-default_ca = CA_default # The default ca section
-
-[ CA_default ]
-dir = . # top dir
-database = $dir/ca_state/index.txt # index file.
-new_certs_dir = $dir/ca_state/newcerts # new certs dir
-RANDFILE = $dir/ca_state/.rand # random number file
-
-certificate = $dir/ca.pem # The CA cert
-serial = $dir/ca_state/serial # serial no file
-private_key = $dir/ca.pem # CA private key
-
-default_days = 3650 # how long to certify for
-default_crl_days= 3650 # how long before next CRL
-default_md = sha256 # md to use
-copy_extensions = copy
-unique_subject = no
-
-policy = policy_any # default policy
-
-[ policy_any ]
-countryName = optional
-stateOrProvinceName = optional
-localityName = optional
-organizationName = optional
-organizationalUnitName = optional
-commonName = supplied
-emailAddress = optional
-
-[ req ]
-default_bits = 2048
-default_keyfile = privkey.pem
-distinguished_name = req_distinguished_name
-attributes = req_attributes
-x509_extensions = usr_cert
-
-string_mask = utf8only
-
-[ req_attributes ]
-
-[ req_distinguished_name ]
-commonName = Common Name (e.g. server FQDN or YOUR name)
-commonName_max = 64
-
-organizationalUnitName = Organizational Unit Name (eg, section)
-organizationalUnitName_default = Kernel
-
-0.organizationName = Organization Name (eg, company)
-0.organizationName_default = MongoDB
-
-localityName = Locality Name (eg, city)
-localityName_default = New York City
-
-stateOrProvinceName = State or Province Name (full name)
-stateOrProvinceName_default = New York
-
-countryName = Country Name (2 letter code)
-countryName_default = US
-countryName_min = 2
-countryName_max = 2
-
-[ usr_cert ]
-basicConstraints = CA:TRUE
-authorityInfoAccess = OCSP;URI:http://localhost:8080
-keyUsage = nonRepudiation, digitalSignature, keyEncipherment
-extendedKeyUsage = OCSPSigning
-
-[ crl_ext ]
-authorityKeyIdentifier=keyid:always
diff --git a/jstests/libs/ocsp/ca.crt b/jstests/libs/ocsp/ca.crt
deleted file mode 100644
index 4f867be9638..00000000000
--- a/jstests/libs/ocsp/ca.crt
+++ /dev/null
@@ -1,22 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDkzCCAnugAwIBAgIJANyMXUrZ5xe8MA0GCSqGSIb3DQEBCwUAMG8xEjAQBgNV
-BAMMCWxvY2FsaG9zdDEPMA0GA1UECwwGS2VybmVsMRAwDgYDVQQKDAdNb25nb0RC
-MRYwFAYDVQQHDA1OZXcgWW9yayBDaXR5MREwDwYDVQQIDAhOZXcgWW9yazELMAkG
-A1UEBhMCVVMwHhcNMTkwOTIzMTU1MTU3WhcNMzkwOTE4MTU1MTU3WjBvMRIwEAYD
-VQQDDAlsb2NhbGhvc3QxDzANBgNVBAsMBktlcm5lbDEQMA4GA1UECgwHTW9uZ29E
-QjEWMBQGA1UEBwwNTmV3IFlvcmsgQ2l0eTERMA8GA1UECAwITmV3IFlvcmsxCzAJ
-BgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3IruGz+g
-xvKymbCPPQx/op8r+C5AFonxSqnZgyqT9oUzVQOXUM1/pZ5MLa6eA8C5vyaG+S1F
-2wChIgo2Zhzqb/f57uMXP+lH1PTytuv0nFrqvC3Di5WG9ooeBASoxk6ARVxgbDj4
-W6sqQzt6hxQW1GyC6M0ArZtVkKm4svDhsxd8SpVvpVh0BvSta6G6PhicG8Tz8Xop
-/uWuvBOXOlf33Yffx2Y7MRe0+/4L9WLUcK9GS3IobUlLPfNXX/kJPoq3DvoxAwZj
-6JOd7zSLFBAReGr8pwZkqMetLX2aXTMpbUFIdcefCgvgLrr1HDz7pTAsaG/wm3wf
-A+lzwV8qIymZvQIDAQABozIwMDAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIF4DAT
-BgNVHSUEDDAKBggrBgEFBQcDCTANBgkqhkiG9w0BAQsFAAOCAQEAGVGozVWRo8MB
-QuJomR8d4uysZPVnYAvputJbZ6BqMv1/bOpjNmuw9h4P9ZFy8Q236qKklXJfun4K
-+ZM008qUu6TX3l2EqjyQ+11jynHFt7v7r7hOzMFNjhsXyakspetOEPcsFQL4XCKl
-fXkKaEwve7AXyh0t3fPawozjD15XUOpFTGAMpjX1ttlOze0f0DaCctYCYNkkE3HD
-R48QW2j5+dw9VnzPD8q0RKWoBcg+VQYJDvRQj0ZCrcqhBHgG2+fkZUNZVEm/4Eko
-mrG/OZ5yuN0ut3urfFNT+r3IiUk7wmBVTWifdC3JCUGirVYIcMg5nCaaFha8CUXX
-YQaQWl6E7Q==
------END CERTIFICATE-----
diff --git a/jstests/libs/ocsp/ca.key b/jstests/libs/ocsp/ca.key
deleted file mode 100644
index f77ad4d1ee8..00000000000
--- a/jstests/libs/ocsp/ca.key
+++ /dev/null
@@ -1,27 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIIEowIBAAKCAQEA3IruGz+gxvKymbCPPQx/op8r+C5AFonxSqnZgyqT9oUzVQOX
-UM1/pZ5MLa6eA8C5vyaG+S1F2wChIgo2Zhzqb/f57uMXP+lH1PTytuv0nFrqvC3D
-i5WG9ooeBASoxk6ARVxgbDj4W6sqQzt6hxQW1GyC6M0ArZtVkKm4svDhsxd8SpVv
-pVh0BvSta6G6PhicG8Tz8Xop/uWuvBOXOlf33Yffx2Y7MRe0+/4L9WLUcK9GS3Io
-bUlLPfNXX/kJPoq3DvoxAwZj6JOd7zSLFBAReGr8pwZkqMetLX2aXTMpbUFIdcef
-CgvgLrr1HDz7pTAsaG/wm3wfA+lzwV8qIymZvQIDAQABAoIBAFBE7KbUQMNIYELZ
-5Th2DRp2Tn2U79SEPmNLcJRx8cqe7nD5zqAlO1lfRSC3VmHNSo3NCwE0PiGYmA0Y
-pHYD/jvkiH4u/y5OzMswoAp91Nj3qkn1ah+qu6WN3aDexULwHXWhSqL5FUPgB5DS
-3CS/5hNIr1jmDGIjkEAH1eajD7Kro0j9hj4I1yqMVphb1BVORw2NzmLfqLR9k/ru
-UrobKuUHCw3HTgsVFinE11yGvCyUlSgH8NsdM044V7lyZlrneoAg+mqgky+Lolur
-LuPrS97SekllJEXFqXZPek4hAG55X1sM2xpaBUDu7iStZFQDlekdmDt/DODwHfCj
-oHTZhS0CgYEA/XGFIYQwBoQBXlUgf1YJlvQ2IH7q7gPYFSXlA/sXL6eateIltwYZ
-Q4k/dgmSDMD1UxA0KZ776EJfCLlJCcLZ4QD5ylxaahfLBe/wAMBtI+hgCoOeWrpo
-glswa1ZUUkzMoR3N6THoM2JE9mQjr2r7wKlpFxclDu2ESEbdX8weOYsCgYEA3sRy
-4/rlYVu31BOMgU9B8Qh3uEFJXDKfWgYeHYeMMts/v6WKZmnfqPUvoO28UQlNNlWk
-kO1L/2cqKhPNcGf/o8yPm7oeddwAOId3g2R5PB9hiKEhxD8fgAl89+5PunzU4doo
-Po/osaZ0nBTzJ51iQoE5YoPdZXQA1Xt/wA2iktcCgYAGVpt0viCRWSXLnSdl59nv
-i4X7BBGOCEyUItF6awImb/OfjttQm3dwR/NtWUnsxbqc7C/RxqcYJOjJiBC6jMP0
-K5kTpYGhW5z5Ngnzitxv5HTabnY8YKMSd4Nbe9o6AK+xa9Rle5kKB/AflVuLHIrV
-Q6dcK3kCyY/gWt1YcvtihwKBgQCfPq2mvZc15rvdle/uDDtmU5hJlhocF3AEibUD
-hP8657KFjJAIMA0bmdJnZyskftK8UbKni4q1VAk3NRq6DQ4ZCMVsHDtkI1XhEy3j
-NnLYANLXOeCKilk5TQ8RqUFDXbNMlIgKWRON0yEu5mb/3aj9Q6VDAGLHAIsMERG8
-t8KM1wKBgGtYe4kC1PqlX8Uz+fbJuCP/dWkXzUcM5qsqQlO7ZRrBFZ5l+zGUyL+V
-5IloSihaf16PAGaHIPjpKx/6lAo7BoFh/L2Y5s9KS5H+p2AGNKrJEOodMJp8A1dg
-dUsWmD4/K7jpHWQEZeBZltlZRg9SFvJGtTQBTKQJx2OXHchxZWDu
------END RSA PRIVATE KEY-----
diff --git a/jstests/libs/ocsp/ca_ocsp.pem b/jstests/libs/ocsp/ca_ocsp.pem
new file mode 100644
index 00000000000..fe336fa4a12
--- /dev/null
+++ b/jstests/libs/ocsp/ca_ocsp.pem
@@ -0,0 +1,49 @@
+-----BEGIN CERTIFICATE-----
+MIIDdDCCAlwCBBqPSvUwDQYJKoZIhvcNAQELBQAwdDELMAkGA1UEBhMCVVMxETAP
+BgNVBAgMCE5ldyBZb3JrMRYwFAYDVQQHDA1OZXcgWW9yayBDaXR5MRAwDgYDVQQK
+DAdNb25nb0RCMQ8wDQYDVQQLDAZLZXJuZWwxFzAVBgNVBAMMDktlcm5lbCBUZXN0
+IENBMB4XDTE5MTAzMTE5MDEyOVoXDTM5MTEwMjE5MDEyOVowdDELMAkGA1UEBhMC
+VVMxETAPBgNVBAgMCE5ldyBZb3JrMRYwFAYDVQQHDA1OZXcgWW9yayBDaXR5MRAw
+DgYDVQQKDAdNb25nb0RCMQ8wDQYDVQQLDAZLZXJuZWwxFzAVBgNVBAMMDktlcm5l
+bCBUZXN0IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs/9y4fkM
+Xd3Tr6MIRNkyMvuaV4u7qEIpMGP/bHr7bMQnXFaGMnGBBCTCkIOg5lt34WwGC+U9
+BgZYxZovHs1KpGy5d41CZERlEGw2s2QPoam3f3u1i7qvYzeVmduYf91/39rBuiEu
+BYPgLwhLY1Kka8WqCKEJCPcBj5SPCSQKIptl2hYHlvqYuGiVvnxSIPF00X/k7M6j
+UEGMbTIzHEyYoEMpmgXbLf9H8Boy26cqcY3QJ+Bvd8xTw0kd2mhBgVieeGKvWm5s
+k8rCoy+RtvPeDROZZ4qT0NTPYIhJaPRfHG+itu/CY2HQSSltOf9SSLSHlTm1wLFP
+ujiUfsjNFFKaUQIDAQABoxMwETAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEB
+CwUAA4IBAQBDAHBBHA/v/wgzOH6aXOB6UXxXI8/KvcgrgdNFPt9DcIdRQiPKxtTs
+P1olzu7cs5IvU5bsxZMU+L5NOFAozRFq4OLn2S6L7Hdh2akLJuOVKu3aDFpr7klT
+VFZfgNvnP86eGoiaaqFX0ltY7mS4OAAIwikGOFSpyB6lGhPFAScwgSNaNGf8PAwu
+6k+qA6WTdRkl9XO4o7e177zIV1GqRoD7QRe4dTNSJmLW9ZErO+u/+dgNLvTe4Me0
+DQ7an21tQmQ7I1WvdtHK074Trhj+XkcxwrjOr/Ee0EIz4O/dvDWFr9+VsCRwqjeC
+IoBtRLeP/9erx5so5CuezhnjMuHTCM0p
+-----END CERTIFICATE-----
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCz/3Lh+Qxd3dOv
+owhE2TIy+5pXi7uoQikwY/9sevtsxCdcVoYycYEEJMKQg6DmW3fhbAYL5T0GBljF
+mi8ezUqkbLl3jUJkRGUQbDazZA+hqbd/e7WLuq9jN5WZ25h/3X/f2sG6IS4Fg+Av
+CEtjUqRrxaoIoQkI9wGPlI8JJAoim2XaFgeW+pi4aJW+fFIg8XTRf+TszqNQQYxt
+MjMcTJigQymaBdst/0fwGjLbpypxjdAn4G93zFPDSR3aaEGBWJ54Yq9abmyTysKj
+L5G2894NE5lnipPQ1M9giElo9F8cb6K278JjYdBJKW05/1JItIeVObXAsU+6OJR+
+yM0UUppRAgMBAAECggEBAJyuyHocKL+s0RAagqAr0J7AlWGPqRKWRPRyl2z+wM21
+Ee43Ayn107Cfrx3M9sqO5JO9TKYeLjiIXc3koQ/W9Bqc6fU/aYOOZcsd55/Qgirf
+aXP3Y9j18KYVRIxhsyXa0e0yp2A2cbErUoKeXcFftvB5Z8GLLCo5mYo6bygtzwS/
+eSY6HpQ/IJiA6ptbkxOdlahNcNgZNYBUKwltYY8ExOHDyMJNAgK8izDmHKxjAQc8
+rqTxrgHf4Msd2jMc8B1I93q264gyZ/xMYS6OzVA/5ptCoFtgBRFLUxjTWXjsccXQ
+M1Nj9E7TYIwV6WJJ8ZuKj3QimJ025ZgT4yTtJnD2QAECgYEA7PU0bkjpL/zTP6jE
+6xI8hxcXxT/aIk41FTsSoiOBOWXk1bX7z7zdpdZieIJWHacrKUK/uZ53lzEA7X6l
+HmWiA+JBvJtASw/nodl78Lq1mrmI5gdgYIexopXocFXnRMCv3tSLssZXBzjJo3yQ
+99azJyThLdT741QfePQb9bGOQYECgYEAwnZwfloIaZiTD4Xf+ePyhtBBdGjhWCW5
+s7KIxkyFECXUtpZcz0+t8Chb3yuwjfJ+17YP3DWsm1nRaMCbq0Pzrv8tl77LZ/3t
+HpW4GVM0XB5npX3Q87OdJF9T1pQDCfphanptyoe4HIht6G7nWnTlAKoijXM7Wiqb
+FebdLth/INECgYBZi3gazRbB+ari/aYcpzGUY1eqRKzsFd3dfgtWllA5HpZRZIoa
+QOcJynA/1hEw94FKAF1vicDrs6sEFL3VNNb63L+2xHyRYRtJgGRj3krOg5qGQvda
+wyyPolLKUMwqoR5U+MEp9+XfZshcLbCi19Gv0zH9HeVqTNnv3V/LgyOdAQKBgQCY
+KjmohSbkCiG5GdbDIV1bpIYzYwZCBXMka+We1a2hjxi9rzb8earvq4UIQpWq3T/n
+52zNsapY/ZVhVKX7A/cgzkiM3x0nSIlNT3Z5SqREGP6dfrAMolWqanWeCL/ABQ5J
+VXen7T9iBFeMc4vROnsfZAIZkTu2OHSWVMpqp7oCoQKBgCbUUhSlH1sdDOfiAzSp
+YM2IVsVPNQJnraHb6UY2tFkkBAWM3nfnO/8FSN03k7g1/V3jgp9K6r3rGnCqgvxS
+ufYvTgaaAkwdmZhcWbi0g2ffjmPvJ4axWmJ65RSeD6vkUZtCdD/XsX6AeHFvIK4O
+kaeIUDVZ+r/DVZoV+/beBzcs
+-----END PRIVATE KEY-----
diff --git a/jstests/libs/ocsp/client_ocsp.pem b/jstests/libs/ocsp/client_ocsp.pem
new file mode 100644
index 00000000000..3ab77920eea
--- /dev/null
+++ b/jstests/libs/ocsp/client_ocsp.pem
@@ -0,0 +1,52 @@
+-----BEGIN CERTIFICATE-----
+MIID9jCCAt4CBHU3PtcwDQYJKoZIhvcNAQELBQAwdDELMAkGA1UEBhMCVVMxETAP
+BgNVBAgMCE5ldyBZb3JrMRYwFAYDVQQHDA1OZXcgWW9yayBDaXR5MRAwDgYDVQQK
+DAdNb25nb0RCMQ8wDQYDVQQLDAZLZXJuZWwxFzAVBgNVBAMMDktlcm5lbCBUZXN0
+IENBMB4XDTE5MTAzMTE5MDEyOVoXDTM5MTEwMjE5MDEyOVowYjEQMA4GA1UECgwH
+TW9uZ29EQjEPMA0GA1UECwwGS2VybmVsMRIwEAYDVQQDDAlsb2NhbGhvc3QxCzAJ
+BgNVBAYTAlVTMQswCQYDVQQIDAJOWTEPMA0GA1UEBwwGT0NTUC0yMIIBIjANBgkq
+hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5eA6yCW44Zt82dWcMZbGB5rs+o/ePTQj
+ild2zCmzK3icQB+km8gPSzMMlwAzvDMOoqFw95xqi7y5QIp54N1K+3IwDeaTTp/B
+bnd8u/5p7l2RevDJBa4xOQg1bivuLZd/heINH767beUx1AbjeMz+gvjO5L5XW9U5
+FaH7sRpTbqfb0CmaRwDXjuWbsThGJFWLCupzo6qvqTTviS1bWPzVXuUWSF/EgMFY
+tSdlL901Unlz1oe70JxqL0/eRFw6sIdOWnMoz2Fzf2b4cbQT53U20GbJGEbOfSTE
+xPoucNSvA8XJ09zYSG1PlAApAZAjO1dNoIAeBcfxKzxg3NAzL21W0QIDAQABo4Gm
+MIGjMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMC
+MB0GA1UdDgQWBBQsP/tgzwdRz/N6UkXc7ljFBTPCSTA5BggrBgEFBQcBAQQtMCsw
+KQYIKwYBBQUHMAGGHWh0dHA6Ly9sb2NhbGhvc3Q6ODEwMC9zdGF0dXMvMBoGA1Ud
+EQQTMBGCCWxvY2FsaG9zdIcEfwAAATANBgkqhkiG9w0BAQsFAAOCAQEAN9T1RR/4
+cii8Qw2elnvPRlsYgzILH2BjbWcqFvoxbY8XMeMtHBltyzw5AFypV5V6PToU/j0/
+UkjDb3KNT07uLVjaSiIQVLSm26jLlWVSFQ1tDD6pQtdhIp6brYqLuw51FVMsFmTN
+jI79YFQ2OyR2CrKA4Yrt7wYMePgz7DgftI84BLwkb5nTspt639/oik076ChVd98R
+biJ8isIW5Nj9SGyvHC2WSq+xwtQMuK8at1WsQ1WO3dq0qZugPtDKxprSyAHvdG77
+kGAdyg3F3ljtuOvuik+4sMX31Uv3sLHYsZqg4GaPVMW4qzd1t0vs6lkxDiOkZvbJ
+wOPtN/Vc75oxlw==
+-----END CERTIFICATE-----
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDl4DrIJbjhm3zZ
+1ZwxlsYHmuz6j949NCOKV3bMKbMreJxAH6SbyA9LMwyXADO8Mw6ioXD3nGqLvLlA
+inng3Ur7cjAN5pNOn8Fud3y7/mnuXZF68MkFrjE5CDVuK+4tl3+F4g0fvrtt5THU
+BuN4zP6C+M7kvldb1TkVofuxGlNup9vQKZpHANeO5ZuxOEYkVYsK6nOjqq+pNO+J
+LVtY/NVe5RZIX8SAwVi1J2Uv3TVSeXPWh7vQnGovT95EXDqwh05acyjPYXN/Zvhx
+tBPndTbQZskYRs59JMTE+i5w1K8DxcnT3NhIbU+UACkBkCM7V02ggB4Fx/ErPGDc
+0DMvbVbRAgMBAAECggEASO0M2OgWsgP45MZ+icDSTCFbItY3Y+VBjSbYOkbUX2sg
+7rrbJsyeBXZJfTIEN6Ve9DsRMox9xJz3jEEiDzNFEWYVVqOagevumpPvj1hT1Nn8
+SCtRKqsrVHOFN9FZ+z6/pwKitTp2DZQTbbdNvMoTyAsU375+ylRnirRbu8ClTUn7
+UcQkfvjWLjSanq8VxpnfUd0+6PCTZOmaRCRI1ZSU9Ctco7F9ZB6OSoJjvQcny/lY
+GV7wsb2CWQcH4fxBxMB31s7YG3WlbAP4nPKqydylir78lUg4wGgKcSh8XqMJPyA6
+Hu4zx+oi7EFrMlGmJKssjz9BkYouSAE0kSHWhn6iwQKBgQD62ISTLsiYC8b4jy31
+VO9VqwjVL7eO71HfKi4vRQeIgSDCWHxz13UUXAP/dir91Tn/QJ3DmFfWudVb9oTs
+MvUR8WuhBh2mR4+hTCzVN5++nsYQACUM3WYn6TwxpHTmy5fdG4L1QWaI15XbHudS
+ml7wBJ/B8PCeLPF1X0na3QYkGQKBgQDqmWhL8pZH30kIelcxR8aSN8ukm2585Tdu
+OVyEeYw9LrvCp7HzflakTYGjXxxO0iHRK7OLlgEfy8zr0Gs8/lzNcSKh5ftacLj0
+2k368kgpCnps7DBojmLCbEX0JAmw3zlsCOlGhwvD3LuTsO3WB8FfdoVeZhXji8A+
+XXgQ7UlfeQKBgHAnmq8L/dgte9rqeYv1W8Ub4akf2pxn2F1FSL0Nyh54TaNXWN2a
+ediOg6MWvIYdbf74bxFi7fMtx32ErU3GQtiw7oRibaNI36kom+sBIcViy/+fcPFd
+lC1IFQfFFheS8+WCRX1Orn+ElJYSLS87ojMCfkOCbLLsOVFXN3hJ1lMRAoGBAIca
+XbQPOnidoEOfoQMiYLroIwDi+lg28RsrMZqhHSBG/550UMj3YxIwCI57QaQq44rA
+bh/e/TrAdX0AhCGWIv+1Pcqa1YwaCooIBoyUZvYrsKF1y9MK3Nk23XpiHqqbg0gy
+QZV6RE5BmkzNwcpWoQhHCzt38vceDlJcJb8px+2RAoGAUezn+Bokp1fB0tR7uxSd
+gipIwRX8LTcgBhq56C0GbF5ku8yWs8Dl7FWvFOZdK7BBFf7o2g1KWDjL5EVjCIvx
+sy3hknhq3EE6NVz1LmoBNM4aVmawtTZMn9u+sODgzTXaoSfkZ3IOZX7oXEFPSTBO
+dVtTs0BXBHm3LzMYWtMI3/4=
+-----END PRIVATE KEY-----
diff --git a/jstests/libs/ocsp/ocsp_cert.crt b/jstests/libs/ocsp/ocsp_cert.crt
deleted file mode 100644
index dd469331881..00000000000
--- a/jstests/libs/ocsp/ocsp_cert.crt
+++ /dev/null
@@ -1,20 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDPjCCAiYCAQEwDQYJKoZIhvcNAQELBQAwbzESMBAGA1UEAwwJbG9jYWxob3N0
-MQ8wDQYDVQQLDAZLZXJuZWwxEDAOBgNVBAoMB01vbmdvREIxFjAUBgNVBAcMDU5l
-dyBZb3JrIENpdHkxETAPBgNVBAgMCE5ldyBZb3JrMQswCQYDVQQGEwJVUzAeFw0x
-OTA5MjMxNTU1MTVaFw0yOTA5MjAxNTU1MTVaMFsxCzAJBgNVBAYTAlVTMQswCQYD
-VQQIDAJDQTEMMAoGA1UEBwwDTllDMQwwCgYDVQQKDANNREIxDzANBgNVBAsMBktl
-cm5lbDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
-MIIBCgKCAQEApl5gyAVH6ENyByOvPuTZ65L4TftlqUkzO4WflZ/DzLNU5B05F7ki
-HLQQ9u0RtZCrCEvebPwRSpEmuKO733y7g22kKdkd8Nih8eWvl5zcMesGFf4GIgQY
-vRqMEXEhQRE3bsgs+8YhA/rZrxXR+/1TP5Ph14jWPlsd6AgRWOCQAmsPO1kSDw8t
-4f8Vtc/kmoRpF7SlMaaVjxFp7uyamBkwooCIzh8xzy/hf7wo71YiQ3qD22DeFHSV
-Hyly5kwvap16HtGzyL9b9PgoD39D2knczAMVHf+qMjUlfMzjwdiB1vTup59JeEli
-KyT+Gk2cy3z5LK4qsHbsSDNYlibYAYOvSwIDAQABMA0GCSqGSIb3DQEBCwUAA4IB
-AQCTEVMZETg2oiZtWspO7My87jtJkh5LcLLTU+JkGQFjXUxkHfag/TzH54fdZEDi
-NF6hk73atdR4vANi8TID683840FtqRVvvk6Wx6MAwJq7//+54LBCPOiIVE+C0Tic
-WeezDNheNb6XTGkwO5wiQ0UJFPdxb5lmTQYUo+VTBco0zgph+FcbHn6Phc4ZpS1H
-D09r2J8/Jzip1ugqqdY8lO8wuKyCnVJqNiVsMBhnnW0nog89UVX9zSRPgh/gA6SN
-XUZrel6WnmMihUJWtCkdwUDxiRXgqUWHbyRf2iX7UWXNBVrFeZYWQwmEJkJ0OKkT
-l+F8o3nUue/sg98MWI9Zjx2U
------END CERTIFICATE-----
diff --git a/jstests/libs/ocsp/ocsp_cert.key b/jstests/libs/ocsp/ocsp_cert.key
deleted file mode 100644
index 1a3f11e8a8c..00000000000
--- a/jstests/libs/ocsp/ocsp_cert.key
+++ /dev/null
@@ -1,28 +0,0 @@
------BEGIN PRIVATE KEY-----
-MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCmXmDIBUfoQ3IH
-I68+5NnrkvhN+2WpSTM7hZ+Vn8PMs1TkHTkXuSIctBD27RG1kKsIS95s/BFKkSa4
-o7vffLuDbaQp2R3w2KHx5a+XnNwx6wYV/gYiBBi9GowRcSFBETduyCz7xiED+tmv
-FdH7/VM/k+HXiNY+Wx3oCBFY4JACaw87WRIPDy3h/xW1z+SahGkXtKUxppWPEWnu
-7JqYGTCigIjOHzHPL+F/vCjvViJDeoPbYN4UdJUfKXLmTC9qnXoe0bPIv1v0+CgP
-f0PaSdzMAxUd/6oyNSV8zOPB2IHW9O6nn0l4SWIrJP4aTZzLfPksriqwduxIM1iW
-JtgBg69LAgMBAAECggEARyhw3rybf/VxDSqvJwh9SOwotjoDxn9foWLK2hVmiuRd
-N+Bj7giqVXqTEvpdK7p/3OGOdjDuOGWTGUA2+CUCkvlPUPLjgoM16R/jlcZqzBMJ
-HFj1SJFV/3QeWst3fLwfaIvH6bxZnLkraF693LkCZVwcKbMGZ89TIE5rFQefsNhn
-NL09s5szt+A1q3gNf3U0qTf/+raquI0UUGp/X+fuLvqBRcn7lFow6iNybC3PI2oT
-B4kJ3sPpoJZHW1VP0lqFUR0WaTo+GyJA2+TGSh5zz+AcDB56H5L04Tjq+WXhiZd2
-0vzxl8jbFQ/gwhzq7yg/PoHFrN5pupb6Wokuvc530QKBgQDP85z3cukyDQiw+3u/
-8lCCWJFUnTXFbReB3XxsqludNJ9b71t66XZcg48btdJ8DxZUjPShgBSHDH3lhfyT
-yAil1v/xdfL12RmDWS/CKp9tLUBQa6P9fOWNObrDgl8pEE9/I6qxrSigVvsGDHMU
-ed7l0z2Dtti5QXivpVuQfDREGQKBgQDMzx+n7nQchGvj8Ng2TlSBEWGk2a3jjASx
-ioWY/PieR53BSvngnNjzssXWXiAUxdQQ5dosRTJAK60rVKIaRdXPzLl6vPOVnS1N
-xrUK88xkZvOJtMqwqYm/efOJ4i8wgV/UxnP9TBsKkA1ilnIpvUp4g9o09L2Cko3d
-RDwtBIJbAwKBgQC3JRAOYvb8bLxF2wbPM2MymTxNsDOmUAEPD/lAXv1Ww/zB5FnG
-M41Zx0gnmIfrWQJKruXYwsvRcPAEUG/124u6O1Oo3j9/xJeStd64+/7zEZJ+AX8p
-hnFJYxmYjN6wdLv+9nOkDiQkqtZkKlzEIN1tvhCES2ouJnpa/6BX7Pu/KQKBgQCo
-K6atqLH0vJMuvpN6vATNWi1YRFlxT4aHFdwvTAEFTn7tuZfGZGQMbw3dwLoM9If2
-PBZSZZsmb/aobr0QX6l6EHt4mfBVOJN7mp8L2DZLdvGgcDBCsWNnM0bTQENJS+IZ
-f768DSLjIHetLSWeukyM+o9RDmkR7y7HsgDLYjl7awKBgF7gUQDp+WG70Faixt5Y
-/pmOgKqXWfKSuf2v4VVMX82rZrNz8XYGJ31vZ2qztpqwHkQK2yT55I9ekkPDuqHH
-eP78YJVGKWNmRAzKmFQXZ5iIzdK1UF4C8gAoCjSNmBcRllKm28SEoKATTg+Gp54Y
-51BtTFfdDlOy4nHD1OUHUT2i
------END PRIVATE KEY-----
diff --git a/jstests/libs/ocsp/ocsp_responder.crt b/jstests/libs/ocsp/ocsp_responder.crt
new file mode 100644
index 00000000000..2d2d4c283ef
--- /dev/null
+++ b/jstests/libs/ocsp/ocsp_responder.crt
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDfjCCAmYCBDxNPHcwDQYJKoZIhvcNAQELBQAwdDELMAkGA1UEBhMCVVMxETAP
+BgNVBAgMCE5ldyBZb3JrMRYwFAYDVQQHDA1OZXcgWW9yayBDaXR5MRAwDgYDVQQK
+DAdNb25nb0RCMQ8wDQYDVQQLDAZLZXJuZWwxFzAVBgNVBAMMDktlcm5lbCBUZXN0
+IENBMB4XDTE5MTAzMTE5MDEyOVoXDTM5MTEwMjE5MDEyOVowYjEQMA4GA1UECgwH
+TW9uZ29EQjEPMA0GA1UECwwGS2VybmVsMRIwEAYDVQQDDAlsb2NhbGhvc3QxCzAJ
+BgNVBAYTAlVTMQswCQYDVQQIDAJOWTEPMA0GA1UEBwwGT0NTUC0zMIIBIjANBgkq
+hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAytlQOVc3n/5gB3BipK8bo9QDyB/Q1rBA
+1ObExPJ/bBoHdZlV2+IJfjRiEAghiNQjo3J7vhfOZv5LZN5TAl+Gy1U00bbtfaRi
+tiwi8hCNs8O3KrnOdVBWtAQsSpXHhPvGxuS0SIiRNqfu9jIFE3OZPWnfhkRlB5Oh
++ShWju+SOVZIDsK/HzQrl4YHCRbxigeTttmw9Rn/Bkma1YnX1/p6RoUjOi9T9u4h
+8CcJMJq4H6sacOazu0f3/iT+LsNYvZFT8i4KzFgzipShKAYkyyLfIRl5P7sipagX
+/rzzJ3EAkKXpKmwYRAg2vpjLnVspXKNF4PySynjBmoHzHf0vTsqRNQIDAQABoy8w
+LTAJBgNVHRMEAjAAMAsGA1UdDwQEAwIF4DATBgNVHSUEDDAKBggrBgEFBQcDCTAN
+BgkqhkiG9w0BAQsFAAOCAQEAb7TpxM48jx8u726LnKRbc2EWNWkLr11ILcm0gq2b
+a321dQ8AGo+XhAjL1x61VEgoG4teMDdNrii/ahRzs2Aflcn/lNWgeZJku6WShjS1
+LHl5qqxtugWYNymdWPOjxGd1f0ye2qShxARvmkjtTBR5xP9D0/VmZDDw3WwvzU9B
++MHxYWxjjFcfLQiGZfgOVO/rqXQJMaIyMgHwfPqnSczmOpFuAlxvuuuMpD2pw4Gm
+HfxSwJWKUxXg+vsNVXuOHmgJv+BAQ9NLAvS1MiYNXUmLrbUc7sdDdti7irue3OUC
+1QorLXgomL526IC92v+aR4ZIS9tC0LxYig29E3e+kpRLtg==
+-----END CERTIFICATE-----
diff --git a/jstests/libs/ocsp/ocsp_responder.key b/jstests/libs/ocsp/ocsp_responder.key
new file mode 100644
index 00000000000..96408c69aed
--- /dev/null
+++ b/jstests/libs/ocsp/ocsp_responder.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDK2VA5Vzef/mAH
+cGKkrxuj1APIH9DWsEDU5sTE8n9sGgd1mVXb4gl+NGIQCCGI1COjcnu+F85m/ktk
+3lMCX4bLVTTRtu19pGK2LCLyEI2zw7cquc51UFa0BCxKlceE+8bG5LRIiJE2p+72
+MgUTc5k9ad+GRGUHk6H5KFaO75I5VkgOwr8fNCuXhgcJFvGKB5O22bD1Gf8GSZrV
+idfX+npGhSM6L1P27iHwJwkwmrgfqxpw5rO7R/f+JP4uw1i9kVPyLgrMWDOKlKEo
+BiTLIt8hGXk/uyKlqBf+vPMncQCQpekqbBhECDa+mMudWylco0Xg/JLKeMGagfMd
+/S9OypE1AgMBAAECggEAD7ei5a6CWt1E546ntgbzZ1BIMrt0XQ4vF9ABsxjvEps5
+tLz60BzFi4fXiVJ39rgpQsLeH66MiaJuiRRI7kgkk6C4hP9di3yvQVpOIt9xe1pQ
+2PiorKJa9XvVSrosSXuQvVx/M5eRWODOPTlG92jfmMiZTzgBdALWbA3aFx0tAVAw
+qenguNR0FDVR0qwuzS3YVW3N9uxIBva2y7n46cN+HHxfnenZJ+LjIDglGoiongrS
+gVvZSOf+jlh9AUZhzx2C1idzFII/LeI+7N61bZkK4q9k3x9+/1eU11CcEI0Fd953
+Y5N+yT+nz51FIsDYHlQsxFo0qdDlQzzwSAbo+39xyQKBgQD8RtayVQeJlCRlOeEW
+3bONeU3Al6fs5WYFCQER0050i5kfdChB4Gm/8zeGXYvMNoMANOUPW8xkt4ygzWkN
+O01a9xr25oFL1dFidacuE2BaNTxCF0tCd3tfIPlw8sv21cbYJKyMjeJEj50Rs9hJ
+wWqvh/umXRjDS49FEnz/n7JVuwKBgQDN17mA6ClcTu5il1oT39F6VDnLoJWDmNX8
+UWK6vSpDo5s/Pz+ue/JvV52sbouv+/BRRuC9cNx/S3upanVDEJEblNJI1YoXMGfa
+0loCGIe2cPEZyoxnWOVtmqP7I8B2Rp76VY8HlywLBhgFVGOSt4nmRugRoaAqdsO3
++magSYpNzwKBgQCHTEag5dFhC75pO51ocgZ1/XShsbQx7OXGFYopUTZmcufwYo+k
+pQKSG5LP0SPEpgej+LpQfl8kNPyK0R9iJUfbXWNsvzrgvQ/Kymaa43Ftb9edanjG
+cOYNotWkqdKR0x0Nlr2jtASB2LPYZqe9Bqp88WUZWByLwN5ZoQzA25j9OQKBgQCk
+OeuvdAqjsfmxQS3F03kujwo5cto2zvZSw5/wesHAi0RuVoj5LbyDprSOgPgDpQ7b
+2Z+upN/W06NKzDvYENyk07xeDbiJvb3uMItNRsLoBP3m5NnWDm+lXY5c7O49ryRh
+d2eQ18R02xzJC8YRpUtpJMM8ZeYCWxTVsPq2AVZNswKBgQDwRyayKvdl/ciL3z/J
+cJ3iTBOhU1puC94ioVrb1K0v1iwtD6JCoaWywUwnQWWYp/m6CnOG7FGQHb3RVLg/
+T1YXvNuqFpz+OfO4wsaPQDq+Gt3vMSrj2WDtIyyua3ti6Z80cfXxugxmY5fQWg6O
+orZaC+mlIkYHVFmJeE23qN7FZA==
+-----END PRIVATE KEY-----
diff --git a/jstests/libs/ocsp/server_ocsp.pem b/jstests/libs/ocsp/server_ocsp.pem
new file mode 100644
index 00000000000..0d94228ab04
--- /dev/null
+++ b/jstests/libs/ocsp/server_ocsp.pem
@@ -0,0 +1,52 @@
+-----BEGIN CERTIFICATE-----
+MIIEADCCAugCBG/khX0wDQYJKoZIhvcNAQELBQAwdDELMAkGA1UEBhMCVVMxETAP
+BgNVBAgMCE5ldyBZb3JrMRYwFAYDVQQHDA1OZXcgWW9yayBDaXR5MRAwDgYDVQQK
+DAdNb25nb0RCMQ8wDQYDVQQLDAZLZXJuZWwxFzAVBgNVBAMMDktlcm5lbCBUZXN0
+IENBMB4XDTE5MTAzMTE5MDEyOVoXDTM5MTEwMjE5MDEyOVowYjEQMA4GA1UECgwH
+TW9uZ29EQjEPMA0GA1UECwwGS2VybmVsMRIwEAYDVQQDDAlsb2NhbGhvc3QxCzAJ
+BgNVBAYTAlVTMQswCQYDVQQIDAJOWTEPMA0GA1UEBwwGT0NTUC0xMIIBIjANBgkq
+hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3KAkwbKU90m5++/fBgFZY1GyxjzAhNdf
+tlf+6V4ci7S/cz5byt1h0fEqwHDsWCOeU4RAm3jXcAwAU0ofYBkWIUM3llIxNkBx
+RGo4MT5NmQqmT0mQ9Eq2295KxG/jRDOFYahhEb0j2VjKl5bbDBFMJVs1VBvijk5i
+GFCWz3ZLlsEPNMtUfKjL0hhirJc9WShk89hst7dq19Vwg2Q9h4/cJe+17jT+YuO+
+av2xEgIGokZJCKlBkUYJ7tI/Fklc1FsRWohIiiKBcDOT0nlC0Avw6oJRVP8VewA9
+/KXCdnP5HnKP7hDi60u3AVekXwtwBXfa1L9cHRlWQ9MPnCySQBJuwwIDAQABo4Gw
+MIGtMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMB
+BggrBgEFBQcDAjAdBgNVHQ4EFgQUh9JgouAbzLBeyTGHLOxqTlEUD9IwOQYIKwYB
+BQUHAQEELTArMCkGCCsGAQUFBzABhh1odHRwOi8vbG9jYWxob3N0OjgxMDAvc3Rh
+dHVzLzAaBgNVHREEEzARgglsb2NhbGhvc3SHBH8AAAEwDQYJKoZIhvcNAQELBQAD
+ggEBAIIRYrXwMa2MXXB3IBnKZZq8bxuUJCUiSPJtMoegNlI5Yd7mZor7+SCMDJbU
+RhhfDMQa21gq5c5SBtnZ9v8qFr6p0M02DrqwnOc8+NoATpxwzSw/LcbgkxKiDzpV
+LdRWwZSG8t1fF3FLMYpMhwK+Cz6P//vP4Ll8pQpYphluUn/xNNKIVtc0d4E9/9In
+TBuLUqCnQEqYyMzTCyYx9rheNDPdcSPy5PuDkkqPVxhQrHuK0GCRy9KhDwZ3IIBg
+fhxz1Z9nnRL3t2QykboPZrdTwG9uPsHTHHF+sEkD0rjab9zM9pC+37AwnyWeql07
+8Dnw977Q8tO83YaFC4krIAi4Pr8=
+-----END CERTIFICATE-----
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDcoCTBspT3Sbn7
+798GAVljUbLGPMCE11+2V/7pXhyLtL9zPlvK3WHR8SrAcOxYI55ThECbeNdwDABT
+Sh9gGRYhQzeWUjE2QHFEajgxPk2ZCqZPSZD0Srbb3krEb+NEM4VhqGERvSPZWMqX
+ltsMEUwlWzVUG+KOTmIYUJbPdkuWwQ80y1R8qMvSGGKslz1ZKGTz2Gy3t2rX1XCD
+ZD2Hj9wl77XuNP5i475q/bESAgaiRkkIqUGRRgnu0j8WSVzUWxFaiEiKIoFwM5PS
+eULQC/DqglFU/xV7AD38pcJ2c/keco/uEOLrS7cBV6RfC3AFd9rUv1wdGVZD0w+c
+LJJAEm7DAgMBAAECggEANuxbGilV8lXVSgXGFJ2jXhlJkxYuqyNevWscl4YJPzJI
+8uyUN2VzY6tRBEVYYPzE/m+PEeU8ZLVoI1PxiZ/VJYSxQufFPoc+a1SyFng3IyFG
+k7hxsI8lBIlLPanI/wKAcvZS6/KVCReUn7Y7Y76uoSIanYyvktOqkfkGAjGl0apZ
+pWfm1hr75DkJcErO/mnM1cQD18QOKykXOVv1vXqGNje+uf39Kup7pO9Cl5pH0tBo
+dnJQSyE8fsigBu9Dev05iwI9MFHzta7mJsNDN8pQdsElSafKsST4GnZfhrPjEH9t
+rT/QdeUTlOro88FRY6UAAncinfIwnbrjwy9D70r7eQKBgQD47lhxYQ3FuwLsueTL
+ebzTZ3bkSffUk8BJg3cXzyF1gjTyAgbgltJ+DrV+QKOvvG2+8GMIo8hbo+1zrtxV
+2NUnNAlTHyvj/1X02fMZDyZaOuO+2CXbQ9Vmla6GL0zRFMVb2vt/atBUwGB+CsXn
+n9q2vkQheTMI5xGMs0F4bUGOrQKBgQDi5AaVX8U7m/UATox7zTzKHxxTYW63VuBg
+kC/xVjNwTy/KUB8A3UEtypeCN4T62jsXPQa9hqy2f6kz6dx9fkgRV9EBFU2zNcuA
+GJcUEU4mZkkSI0RUS4opLWdFHF9hli63AYiAxTNSqCx0UbGsaJbKoCsX6iLMVfgr
+3sgPkYbRLwKBgEIj6gHt/J/N6aAGYGEj7mTpN7C+aofj3hJqSMldspErUHAA09z2
+leUCeXybJiK6WRqDenzAL6sRqjSzgD2bCD+LVvLr9Fh27lrJUgsVeGY/AWYiviQ9
+KMVB/gYPKUTgVPPsAii/s6zhFIJI0H6TfaRSd33BWysBYitVMY8S0HldAoGASu6b
+7gEvFjyvcWZ/pvNlPCuCI1duRUZ/2+RCsSWS32iHk/1Z/+MSf5kE6JCFboNcOJSD
+AVzU9YC71NVLaHaoPnIOKJlsi98cUjXMz2fXs37caw0Sp/Q6sNa54G06YvRCgVYU
+StLV3FqGM/Q3BYWVJ142XTOKQomx6GsvD1U0fSkCgYEA3txxBL3NytgRDPbbrvTE
+zcBThd3k1y4qPPu2LHdIzrLfxRHmy8CF4+LwptJdZWlN9AuS6HzUi+GqVhL7GnE/
++23/MUmOxJKCi0PNpJ7KndPk1HL1ag23QKsQnueQjb8uasSx08BXUksnhXduZreD
+OmH5pyRTGx58Vhv+X+G/cvc=
+-----END PRIVATE KEY-----
diff --git a/jstests/ocsp/lib/mock_ocsp.js b/jstests/ocsp/lib/mock_ocsp.js
index 3795cf2fee2..00de55e3c86 100644
--- a/jstests/ocsp/lib/mock_ocsp.js
+++ b/jstests/ocsp/lib/mock_ocsp.js
@@ -2,6 +2,7 @@
* Starts a mock OCSP Server to test
* OCSP certificate revocation.
*/
+load("jstests/ocsp/lib/ocsp_helpers.js");
// These are a list of faults to match the list of faults
// in ocsp_mock.py.
@@ -25,9 +26,9 @@ class MockOCSPServer {
}
print("Using python interpreter: " + this.python);
- this.ca_file = "jstests/libs/ocsp/ca.crt";
- this.ocsp_cert_file = "jstests/libs/ocsp/ocsp_cert.crt";
- this.ocsp_cert_key = "jstests/libs/ocsp/ocsp_cert.key";
+ this.ca_file = OCSP_CA_CERT;
+ this.ocsp_cert_file = OCSP_RESPONDER_CERT;
+ this.ocsp_cert_key = OCSP_RESPONDER_KEY;
// The port must be hard coded to match the port of the
// responder in the certificates.
this.port = 8100;
@@ -45,15 +46,15 @@ class MockOCSPServer {
"--ocsp_responder_key=" + this.ocsp_cert_key
];
- this.pid = _startMongoProgram({args: args});
- assert(checkProgram(this.pid).alive);
-
if (this.fault_type) {
args.push("--fault=" + this.fault_type);
}
+ this.pid = _startMongoProgram({args: args});
+ assert(checkProgram(this.pid).alive);
+
assert.soon(function() {
- return rawMongoProgramOutput().search("Mock OCSP Responder is running") !== -1;
+ return rawMongoProgramOutput().search("Listening on") !== -1;
});
sleep(1000);
diff --git a/jstests/ocsp/lib/ocsp_helpers.js b/jstests/ocsp/lib/ocsp_helpers.js
new file mode 100644
index 00000000000..18066e441fb
--- /dev/null
+++ b/jstests/ocsp/lib/ocsp_helpers.js
@@ -0,0 +1,11 @@
+/**
+ * Helper variables and methods for OCSP
+ */
+
+load("jstests/ssl/libs/ssl_helpers.js");
+
+const OCSP_CA_CERT = "jstests/libs/ocsp/ca_ocsp.pem";
+const OCSP_SERVER_CERT = "jstests/libs/ocsp/server_ocsp.pem";
+const OCSP_CLIENT_CERT = "jstests/libs/ocsp/client_ocsp.pem";
+const OCSP_RESPONDER_CERT = "jstests/libs/ocsp/ocsp_responder.crt";
+const OCSP_RESPONDER_KEY = "jstests/libs/ocsp/ocsp_responder.key";
diff --git a/jstests/ocsp/lib/ocsp_mock.py b/jstests/ocsp/lib/ocsp_mock.py
index 8f7b625666d..3ad31193890 100644
--- a/jstests/ocsp/lib/ocsp_mock.py
+++ b/jstests/ocsp/lib/ocsp_mock.py
@@ -5,25 +5,12 @@ Python script to interface as a mock OCSP responder.
import argparse
import logging
-from datetime import datetime
-from ocspresponder import OCSPResponder, CertificateStatus
+import sys
+import os
-fault = None
+sys.path.append(os.path.join(os.getcwd() ,'src', 'third_party', 'mock_ocsp_responder'))
-def validate(serial: int):
- if fault == FAULT_REVOKED:
- return (CertificateStatus.revoked, datetime.today())
- elif fault == FAULT_UNKNOWN:
- return (CertificateStatus.unknown, None)
- return (CertificateStatus.good, None)
-
-def get_cert(serial: int):
- """
- Assume the certificates are stored in the ``certs`` directory with the
- serial as base filename.
- """
- with open('jstests/libs/ocsp/serial/server-%s.pem' % serial, 'r') as f:
- return f.read().strip()
+import mock_ocsp_responder
def main():
"""Main entry point"""
@@ -39,22 +26,14 @@ def main():
parser.add_argument('--ocsp_responder_key', type=str, required=True, help="OCSP Responder Keyfile")
- parser.add_argument('--fault', choices=[FAULT_REVOKED, FAULT_UNKNOWN], type=str, help="Specify a specific fault to test")
+ parser.add_argument('--fault', choices=[mock_ocsp_responder.FAULT_REVOKED, mock_ocsp_responder.FAULT_UNKNOWN], type=str, help="Specify a specific fault to test")
args = parser.parse_args()
if args.verbose:
logging.basicConfig(level=logging.DEBUG)
- global fault
- if args.fault:
- fault = args.fault
-
print('Initializing OCSP Responder')
- app = OCSPResponder(
- args.ca_file, args.ocsp_responder_cert, args.ocsp_responder_key,
- validate_func=validate,
- cert_retrieve_func=get_cert,
- )
+ app = mock_ocsp_responder.OCSPResponder(args.ca_file, args.ocsp_responder_cert, args.ocsp_responder_key, args.fault)
if args.verbose:
app.serve(args.port, debug=True)
diff --git a/jstests/ocsp/ocsp_basic.js b/jstests/ocsp/ocsp_basic.js
new file mode 100644
index 00000000000..eb7c4823d15
--- /dev/null
+++ b/jstests/ocsp/ocsp_basic.js
@@ -0,0 +1,39 @@
+// Check that OCSP verification works
+// @tags: [requires_http_client]
+
+load("jstests/ocsp/lib/mock_ocsp.js");
+
+(function() {
+"use strict";
+
+let mock_ocsp = new MockOCSPServer();
+mock_ocsp.start();
+
+const ocsp_options = {
+ sslMode: "requireSSL",
+ sslPEMKeyFile: OCSP_SERVER_CERT,
+ sslCAFile: OCSP_CA_CERT,
+ setParameter: {
+ ocspEnabled: "true",
+ },
+ sslAllowInvalidHostnames: "",
+};
+
+let conn = null;
+assert.doesNotThrow(() => {
+ conn = MongoRunner.runMongod(ocsp_options);
+});
+
+mock_ocsp.stop();
+
+// Test Scenario when Mock OCSP Server replies stating
+// that the OCSP status of the client cert is revoked.
+mock_ocsp = new MockOCSPServer(FAULT_REVOKED);
+mock_ocsp.start();
+assert.throws(() => {
+ new Mongo(conn.host);
+});
+
+mock_ocsp.stop();
+MongoRunner.stopMongod(conn);
+}()); \ No newline at end of file
diff --git a/jstests/ssl/x509/certs.yml b/jstests/ssl/x509/certs.yml
index af42b21c097..b889ee8ffea 100644
--- a/jstests/ssl/x509/certs.yml
+++ b/jstests/ssl/x509/certs.yml
@@ -324,6 +324,81 @@ certs:
extensions: {basicConstraints: {CA: true}}
###
+# OCSP Tree
+###
+
+- name: 'ca_ocsp.pem'
+ description: >-
+ Primary Root Certificate Authority
+ Most Certificates are issued by this CA.
+ Subject: {CN: 'Kernel Test CA'}
+ Issuer: self
+ include_header: false
+ output_path: 'jstests/libs/ocsp/'
+ extensions:
+ basicConstraints:
+ critical: true
+ CA: true
+
+- name: 'server_ocsp.pem'
+ description: >-
+ OCSP certificate for the mongodb server.
+ Subject:
+ CN: 'localhost'
+ C: US
+ ST: NY
+ L: OCSP-1
+ Issuer: 'ca_ocsp.pem'
+ include_header: false
+ output_path: 'jstests/libs/ocsp/'
+ extensions:
+ basicConstraints: {CA: false}
+ subjectAltName:
+ DNS: localhost
+ IP: 127.0.0.1
+ authorityInfoAccess: 'OCSP;URI:http://localhost:8100/status/'
+ subjectKeyIdentifier: hash
+ keyUsage: [digitalSignature, keyEncipherment]
+ extendedKeyUsage: [serverAuth, clientAuth]
+
+- name: 'client_ocsp.pem'
+ description: >-
+ OCSP certificate for the mongodb client.
+ Subject:
+ CN: 'localhost'
+ C: US
+ ST: NY
+ L: OCSP-2
+ Issuer: 'ca_ocsp.pem'
+ include_header: false
+ output_path: 'jstests/libs/ocsp/'
+ extensions:
+ basicConstraints: {CA: false}
+ subjectAltName:
+ DNS: localhost
+ IP: 127.0.0.1
+ authorityInfoAccess: 'OCSP;URI:http://localhost:8100/status/'
+ subjectKeyIdentifier: hash
+ keyUsage: [digitalSignature, keyEncipherment]
+ extendedKeyUsage: [clientAuth]
+
+- name: 'ocsp_responder.crt'
+ description: Certificate and key for the OCSP responder
+ Subject:
+ CN: 'localhost'
+ C: US
+ ST: NY
+ L: OCSP-3
+ Issuer: 'ca_ocsp.pem'
+ include_header: false
+ keyfile: 'ocsp_responder.key'
+ output_path: 'jstests/libs/ocsp/'
+ extensions:
+ basicConstraints: {CA: false}
+ keyUsage: [nonRepudiation, digitalSignature, keyEncipherment]
+ extendedKeyUsage: [OCSPSigning]
+
+###
# Rollover tree
###
diff --git a/jstests/ssl/x509/mkcert.py b/jstests/ssl/x509/mkcert.py
index f46bd12dd63..61defb35539 100755
--- a/jstests/ssl/x509/mkcert.py
+++ b/jstests/ssl/x509/mkcert.py
@@ -155,7 +155,7 @@ def set_general_dict_extension(x509, exts, cert, name, typed_values):
else:
value.append(key + ':' + val)
- exts.append(OpenSSL.crypto.X509Extension(b'basicConstraints', critical, ','.join(value).encode('utf-8'), subject=x509))
+ exts.append(OpenSSL.crypto.X509Extension(bytes(name, 'utf-8'), critical, ','.join(value).encode('utf-8'), subject=x509))
def set_general_list_extension(x509, exts, cert, name, values):
"""Set value elements for a given extension."""
@@ -177,6 +177,13 @@ def set_general_list_extension(x509, exts, cert, name, values):
exts.append(OpenSSL.crypto.X509Extension(name.encode('utf-8'), critical, ','.join(tags).encode('utf-8'), subject=x509))
+def set_ocsp_extension(x509, exts, cert):
+ """Set the OCSP extension"""
+ ocsp = cert.get('extensions', {}).get('authorityInfoAccess')
+ if not ocsp:
+ return
+ exts.append(OpenSSL.crypto.X509Extension(b'authorityInfoAccess', False, ocsp.encode('utf-8'), subject=x509))
+
def set_san_extension(x509, exts, cert):
"""Set the Subject Alternate Name extension."""
san = cert.get('extensions', {}).get('subjectAltName')
@@ -288,9 +295,10 @@ def set_extensions(x509, cert):
'keyAgreement', 'keyCertSign', 'cRLSign', 'encipherOnly', 'decipherOnly'])
set_general_list_extension(x509, exts, cert, 'extendedKeyUsage', [
'serverAuth', 'clientAuth', 'codeSigning', 'emailProtection', 'timeStamping',
- 'msCodeInd', 'msCodeCom', 'msCTLSign', 'msSGC', 'msEFS', 'nsSGC'])
+ 'msCodeInd', 'msCodeCom', 'msCTLSign', 'msSGC', 'msEFS', 'nsSGC', 'OCSPSigning'])
enable_subject_key_identifier_extension(x509, exts, cert)
enable_authority_key_identifier_extension(x509, exts, cert)
+ set_ocsp_extension(x509, exts, cert)
set_san_extension(x509, exts, cert)
set_mongo_roles_extension(exts, cert)
@@ -321,11 +329,14 @@ def sign_cert(x509, cert, key):
x509.sign(signing_key, sig)
def get_header_comment(cert):
+ if not cert.get('include_header', True):
+ return ''
"""Header comment for every generated file."""
comment = "# Autogenerated file, do not edit.\n"
comment = comment + '# Generate using jstests/ssl/x509/mkcert.py --config ' + CONFIGFILE
comment = comment + ' ' + cert['name'] + "\n#\n"
comment = comment + "# " + cert.get('description', '').replace("\n", "\n# ")
+ comment = comment + "\n"
return comment
def convert_cert_to_pkcs1(cert):
@@ -380,10 +391,25 @@ def create_cert(cert):
passphrase = passphrase.encode('utf-8')
cipher = 'aes256'
- open(make_filename(cert), 'wt').write(
- get_header_comment(cert) + "\n" +
- OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, x509).decode('ascii') +
- OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key, cipher=cipher, passphrase=passphrase).decode('ascii'))
+ header = get_header_comment(cert)
+ # The OCSP responder certificate needs to have the key and the pem file separated.
+ if cert.get('keyfile', False):
+ keyfile = cert['keyfile']
+ key_path_dict = {'output_path': cert['output_path'], 'name': keyfile}
+ open(make_filename(cert), 'wt').write(
+ header +
+ OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, x509).decode('ascii'))
+
+ open(make_filename(key_path_dict), 'wt').write(
+ header +
+ OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key, cipher=cipher, passphrase=passphrase).decode('ascii'))
+
+ else:
+ # OCSP certificates cannot have comments because the Mock OCSP responder cannot process comments in Certificates
+ open(make_filename(cert), 'wt').write(
+ header +
+ OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, x509).decode('ascii') +
+ OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key, cipher=cipher, passphrase=passphrase).decode('ascii'))
if cert.get('pkcs1'):
convert_cert_to_pkcs1(cert)
@@ -513,8 +539,9 @@ def process_cert(cert):
x509 = load_authority_file(append_cert)[0]
if not x509:
raise ValueError("Unable to find certificate '" + append_cert + "' to append")
+ header = "# Certificate from " + append_cert + "\n" if cert.get('include_header', True) else ""
open(make_filename(cert), 'at').write(
- "# Certificate from " + append_cert + "\n" +
+ header +
OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, x509).decode('ascii'))
def parse_command_line():
@@ -537,7 +564,7 @@ def validate_config():
if not CONFIG.get('certs'):
raise ValueError('No certificates defined')
- permissible = ['name', 'description', 'Subject', 'Issuer', 'append_cert', 'extensions', 'passphrase', 'output_path', 'hash', 'key_type', 'explicit_subject', 'serial', 'not_before', 'not_after', 'pkcs1', 'pkcs12']
+ permissible = ['name', 'description', 'Subject', 'Issuer', 'append_cert', 'extensions', 'passphrase', 'output_path', 'hash', 'include_header', 'key_type', 'keyfile', 'explicit_subject', 'serial', 'not_before', 'not_after', 'pkcs1', 'pkcs12']
for cert in CONFIG.get('certs', []):
keys = cert.keys()
if not 'name' in keys:
diff --git a/src/mongo/util/net/ocsp/ocsp_manager.cpp b/src/mongo/util/net/ocsp/ocsp_manager.cpp
index 62d88c4858f..1b3bf98115e 100644
--- a/src/mongo/util/net/ocsp/ocsp_manager.cpp
+++ b/src/mongo/util/net/ocsp/ocsp_manager.cpp
@@ -34,7 +34,7 @@
namespace mongo {
-StatusWith<std::vector<uint8_t>> ocspRequestStatus(ConstDataRange data, HostAndPort hostAndPort) {
+StatusWith<std::vector<uint8_t>> ocspRequestStatus(ConstDataRange data, StringData responderURI) {
auto client = HttpClient::create();
if (!client) {
return Status(ErrorCodes::InternalErrorNotSupported, "HTTP Client not supported");
@@ -42,7 +42,7 @@ StatusWith<std::vector<uint8_t>> ocspRequestStatus(ConstDataRange data, HostAndP
client->allowInsecureHTTP(true);
client->setTimeout(kOCSPRequestTimeoutSeconds);
client->setHeaders({"Content-Type: application/ocsp-request"});
- auto dataBuilder = client->post("http://" + hostAndPort.toString(), data);
+ auto dataBuilder = client->post("http://" + responderURI, data);
if (dataBuilder.size() == 0) {
return Status(ErrorCodes::SSLHandshakeFailed, "OCSP Validation Failed");
}
diff --git a/src/mongo/util/net/ocsp/ocsp_manager.h b/src/mongo/util/net/ocsp/ocsp_manager.h
index 82b96843d4a..5d2a3d1a9a5 100644
--- a/src/mongo/util/net/ocsp/ocsp_manager.h
+++ b/src/mongo/util/net/ocsp/ocsp_manager.h
@@ -41,6 +41,6 @@ constexpr Seconds kOCSPRequestTimeoutSeconds(5);
* Constructs the HTTP client and sends the OCSP request to the responder.
* Returns a vector of bytes to be constructed into a OCSP response.
*/
-StatusWith<std::vector<uint8_t>> ocspRequestStatus(ConstDataRange data, HostAndPort hostAndPort);
+StatusWith<std::vector<uint8_t>> ocspRequestStatus(ConstDataRange data, StringData responderURI);
} // namespace mongo \ No newline at end of file
diff --git a/src/mongo/util/net/ssl_manager_openssl.cpp b/src/mongo/util/net/ssl_manager_openssl.cpp
index 83bfb5a53db..3009d914996 100644
--- a/src/mongo/util/net/ssl_manager_openssl.cpp
+++ b/src/mongo/util/net/ssl_manager_openssl.cpp
@@ -1495,7 +1495,7 @@ struct OCSPRequestAndIDs {
*/
Status addOCSPUrlToMap(SSL* conn,
X509* cert,
- std::map<HostAndPort, OCSPRequestAndIDs>& ocspRequestMap,
+ std::map<std::string, OCSPRequestAndIDs>& ocspRequestMap,
OCSPCertIDSet& uniqueCertIds) {
UniqueOpenSSLStringStack aiaOCSP(X509_get1_ocsp(cert));
@@ -1549,7 +1549,8 @@ Status addOCSPUrlToMap(SSL* conn,
OCSPRequestAndIDs reqAndIDs{UniqueOCSPRequest(OCSP_REQUEST_new()), OCSPCertIDSet()};
- auto [mapIter, _] = ocspRequestMap.try_emplace(hostAndPort, std::move(reqAndIDs));
+ auto [mapIter, _] = ocspRequestMap.try_emplace(
+ str::stream() << host << ":" << port << path, std::move(reqAndIDs));
OCSP_request_add0_id(mapIter->second.request.get(), certID.release());
mapIter->second.certIDs.insert(std::move(certIDForArray));
@@ -1565,7 +1566,7 @@ Status addOCSPUrlToMap(SSL* conn,
}
struct OCSPContext {
- std::map<HostAndPort, OCSPRequestAndIDs> ocspRequestMap;
+ std::map<std::string, OCSPRequestAndIDs> ocspRequestMap;
OCSPCertIDSet uniqueCertIds;
};
@@ -1579,7 +1580,7 @@ StatusWith<OCSPContext> extractOcspUris(SSL* conn,
X509* peerCert,
STACK_OF(X509) * intermediateCerts) {
- std::map<HostAndPort, OCSPRequestAndIDs> ocspRequestMap;
+ std::map<std::string, OCSPRequestAndIDs> ocspRequestMap;
OCSPCertIDSet uniqueCertIds;
auto status = addOCSPUrlToMap(conn, peerCert, ocspRequestMap, uniqueCertIds);
diff --git a/src/third_party/mock_ocsp_responder/mock_ocsp_responder.py b/src/third_party/mock_ocsp_responder/mock_ocsp_responder.py
new file mode 100644
index 00000000000..64925b41e7b
--- /dev/null
+++ b/src/third_party/mock_ocsp_responder/mock_ocsp_responder.py
@@ -0,0 +1,611 @@
+#
+# This file has been modified in 2019 by MongoDB Inc.
+#
+
+# OCSPBuilder is derived from https://github.com/wbond/ocspbuilder
+# OCSPResponder is derived from https://github.com/threema-ch/ocspresponder
+
+# Copyright (c) 2015-2018 Will Bond <will@wbond.net>
+
+# 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.
+
+# Copyright 2016 Threema GmbH
+
+# 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.
+
+from __future__ import unicode_literals, division, absolute_import, print_function
+
+import logging
+import base64
+import inspect
+import re
+import enum
+import sys
+import textwrap
+from datetime import datetime, timezone, timedelta
+from typing import Callable, Tuple, Optional
+
+from asn1crypto import x509, keys, core, ocsp
+from asn1crypto.ocsp import OCSPRequest, OCSPResponse
+from oscrypto import asymmetric
+from bottle import Bottle, HTTPResponse, request
+
+__version__ = '0.10.2'
+__version_info__ = (0, 10, 2)
+
+logger = logging.getLogger(__name__)
+
+if sys.version_info < (3,):
+ byte_cls = str
+else:
+ byte_cls = bytes
+
+def _pretty_message(string, *params):
+ """
+ Takes a multi-line string and does the following:
+ - dedents
+ - converts newlines with text before and after into a single line
+ - strips leading and trailing whitespace
+ :param string:
+ The string to format
+ :param *params:
+ Params to interpolate into the string
+ :return:
+ The formatted string
+ """
+
+ output = textwrap.dedent(string)
+
+ # Unwrap lines, taking into account bulleted lists, ordered lists and
+ # underlines consisting of = signs
+ if output.find('\n') != -1:
+ output = re.sub('(?<=\\S)\n(?=[^ \n\t\\d\\*\\-=])', ' ', output)
+
+ if params:
+ output = output % params
+
+ output = output.strip()
+
+ return output
+
+
+def _type_name(value):
+ """
+ :param value:
+ A value to get the object name of
+ :return:
+ A unicode string of the object name
+ """
+
+ if inspect.isclass(value):
+ cls = value
+ else:
+ cls = value.__class__
+ if cls.__module__ in set(['builtins', '__builtin__']):
+ return cls.__name__
+ return '%s.%s' % (cls.__module__, cls.__name__)
+
+def _writer(func):
+ """
+ Decorator for a custom writer, but a default reader
+ """
+
+ name = func.__name__
+ return property(fget=lambda self: getattr(self, '_%s' % name), fset=func)
+
+
+class OCSPResponseBuilder(object):
+
+ _response_status = None
+ _certificate = None
+ _certificate_status = None
+ _revocation_date = None
+ _certificate_issuer = None
+ _hash_algo = None
+ _key_hash_algo = None
+ _nonce = None
+ _this_update = None
+ _next_update = None
+ _response_data_extensions = None
+ _single_response_extensions = None
+
+ def __init__(self, response_status, certificate_status_list=[], revocation_date=None):
+ """
+ Unless changed, responses will use SHA-256 for the signature,
+ and will be valid from the moment created for one week.
+ :param response_status:
+ A unicode string of OCSP response type:
+ - "successful" - when the response includes information about the certificate
+ - "malformed_request" - when the request could not be understood
+ - "internal_error" - when an internal error occured with the OCSP responder
+ - "try_later" - when the OCSP responder is temporarily unavailable
+ - "sign_required" - when the OCSP request must be signed
+ - "unauthorized" - when the responder is not the correct responder for the certificate
+ :param certificate_list:
+ A list of tuples with certificate serial number and certificate status objects.
+ certificate_status:
+ A unicode string of the status of the certificate. Only required if
+ the response_status is "successful".
+ - "good" - when the certificate is in good standing
+ - "revoked" - when the certificate is revoked without a reason code
+ - "key_compromise" - when a private key is compromised
+ - "ca_compromise" - when the CA issuing the certificate is compromised
+ - "affiliation_changed" - when the certificate subject name changed
+ - "superseded" - when the certificate was replaced with a new one
+ - "cessation_of_operation" - when the certificate is no longer needed
+ - "certificate_hold" - when the certificate is temporarily invalid
+ - "remove_from_crl" - only delta CRLs - when temporary hold is removed
+ - "privilege_withdrawn" - one of the usages for a certificate was removed
+ - "unknown" - the responder doesn't know about the certificate being requested
+ :param revocation_date:
+ A datetime.datetime object of when the certificate was revoked, if
+ the response_status is "successful" and the certificate status is
+ not "good" or "unknown".
+ """
+ self._response_status = response_status
+ self._certificate_status_list = certificate_status_list
+ self._revocation_date = revocation_date
+
+ self._key_hash_algo = 'sha1'
+ self._hash_algo = 'sha256'
+ self._response_data_extensions = {}
+ self._single_response_extensions = {}
+
+ @_writer
+ def nonce(self, value):
+ """
+ The nonce that was provided during the request.
+ """
+
+ if not isinstance(value, byte_cls):
+ raise TypeError(_pretty_message(
+ '''
+ nonce must be a byte string, not %s
+ ''',
+ _type_name(value)
+ ))
+
+ self._nonce = value
+
+ @_writer
+ def certificate_issuer(self, value):
+ """
+ An asn1crypto.x509.Certificate object of the issuer of the certificate.
+ This should only be set if the OCSP responder is not the issuer of
+ the certificate, but instead a special certificate only for OCSP
+ responses.
+ """
+
+ if value is not None:
+ is_oscrypto = isinstance(value, asymmetric.Certificate)
+ if not is_oscrypto and not isinstance(value, x509.Certificate):
+ raise TypeError(_pretty_message(
+ '''
+ certificate_issuer must be an instance of
+ asn1crypto.x509.Certificate or
+ oscrypto.asymmetric.Certificate, not %s
+ ''',
+ _type_name(value)
+ ))
+
+ if is_oscrypto:
+ value = value.asn1
+
+ self._certificate_issuer = value
+
+ @_writer
+ def next_update(self, value):
+ """
+ A datetime.datetime object of when the response may next change. This
+ should only be set if responses are cached. If responses are generated
+ fresh on every request, this should not be set.
+ """
+
+ if not isinstance(value, datetime):
+ raise TypeError(_pretty_message(
+ '''
+ next_update must be an instance of datetime.datetime, not %s
+ ''',
+ _type_name(value)
+ ))
+
+ self._next_update = value
+
+ def build(self, responder_private_key=None, responder_certificate=None):
+ """
+ Validates the request information, constructs the ASN.1 structure and
+ signs it.
+ The responder_private_key and responder_certificate parameters are onlystr
+ required if the response_status is "successful".
+ :param responder_private_key:
+ An asn1crypto.keys.PrivateKeyInfo or oscrypto.asymmetric.PrivateKey
+ object for the private key to sign the response with
+ :param responder_certificate:
+ An asn1crypto.x509.Certificate or oscrypto.asymmetric.Certificate
+ object of the certificate associated with the private key
+ :return:
+ An asn1crypto.ocsp.OCSPResponse object of the response
+ """
+ if self._response_status != 'successful':
+ return ocsp.OCSPResponse({
+ 'response_status': self._response_status
+ })
+
+ is_oscrypto = isinstance(responder_private_key, asymmetric.PrivateKey)
+ if not isinstance(responder_private_key, keys.PrivateKeyInfo) and not is_oscrypto:
+ raise TypeError(_pretty_message(
+ '''
+ responder_private_key must be an instance ofthe c
+ asn1crypto.keys.PrivateKeyInfo or
+ oscrypto.asymmetric.PrivateKey, not %s
+ ''',
+ _type_name(responder_private_key)
+ ))
+
+ cert_is_oscrypto = isinstance(responder_certificate, asymmetric.Certificate)
+ if not isinstance(responder_certificate, x509.Certificate) and not cert_is_oscrypto:
+ raise TypeError(_pretty_message(
+ '''
+ responder_certificate must be an instance of
+ asn1crypto.x509.Certificate or
+ oscrypto.asymmetric.Certificate, not %s
+ ''',
+ _type_name(responder_certificate)
+ ))
+
+ if cert_is_oscrypto:
+ responder_certificate = responder_certificate.asn1
+
+ if self._certificate_status_list is None:
+ raise ValueError(_pretty_message(
+ '''
+ certificate_status_list must be set if the response_status is
+ "successful"
+ '''
+ ))
+
+ def _make_extension(name, value):
+ return {
+ 'extn_id': name,
+ 'critical': False,
+ 'extn_value': value
+ }
+
+ responses = []
+ for serial, status in self._certificate_status_list:
+ response_data_extensions = []
+ single_response_extensions = []
+ for name, value in self._response_data_extensions.items():
+ response_data_extensions.append(_make_extension(name, value))
+ if self._nonce:
+ response_data_extensions.append(
+ _make_extension('nonce', self._nonce)
+ )
+
+ if not response_data_extensions:
+ response_data_extensions = None
+
+ for name, value in self._single_response_extensions.items():
+ single_response_extensions.append(_make_extension(name, value))
+
+ if self._certificate_issuer:
+ single_response_extensions.append(
+ _make_extension(
+ 'certificate_issuer',
+ [
+ x509.GeneralName(
+ name='directory_name',
+ value=self._certificate_issuer.subject
+ )
+ ]
+ )
+ )
+
+ if not single_response_extensions:
+ single_response_extensions = None
+
+ responder_key_hash = getattr(responder_certificate.public_key, self._key_hash_algo)
+
+ if status == 'good':
+ cert_status = ocsp.CertStatus(
+ name='good',
+ value=core.Null()
+ )
+ elif status == 'unknown':
+ cert_status = ocsp.CertStatus(
+ name='unknown',
+ value=core.Null()
+ )
+ else:
+ reason = status if status != 'revoked' else 'unspecified'
+ cert_status = ocsp.CertStatus(
+ name='revoked',
+ value={
+ 'revocation_time': self._revocation_date,
+ 'revocation_reason': reason,
+ }
+ )
+
+ issuer = self._certificate_issuer if self._certificate_issuer else responder_certificate
+
+ produced_at = datetime.now(timezone.utc)
+
+ if self._this_update is None:
+ self._this_update = produced_at
+
+ if self._next_update is None:
+ self._next_update = self._this_update + timedelta(days=7)
+
+ response = {
+ 'cert_id': {
+ 'hash_algorithm': {
+ 'algorithm': self._key_hash_algo
+ },
+ 'issuer_name_hash': getattr(issuer.subject, self._key_hash_algo),
+ 'issuer_key_hash': getattr(issuer.public_key, self._key_hash_algo),
+ 'serial_number': serial,
+ },
+ 'cert_status': cert_status,
+ 'this_update': self._this_update,
+ 'next_update': self._next_update,
+ 'single_extensions': single_response_extensions
+ }
+ responses.append(response)
+
+ response_data = ocsp.ResponseData({
+ 'responder_id': ocsp.ResponderId(name='by_key', value=responder_key_hash),
+ 'produced_at': produced_at,
+ 'responses': responses,
+ 'response_extensions': response_data_extensions
+ })
+
+ signature_algo = responder_private_key.algorithm
+ if signature_algo == 'ec':
+ signature_algo = 'ecdsa'
+
+ signature_algorithm_id = '%s_%s' % (self._hash_algo, signature_algo)
+
+ if responder_private_key.algorithm == 'rsa':
+ sign_func = asymmetric.rsa_pkcs1v15_sign
+ elif responder_private_key.algorithm == 'dsa':
+ sign_func = asymmetric.dsa_sign
+ elif responder_private_key.algorithm == 'ec':
+ sign_func = asymmetric.ecdsa_sign
+
+ if not is_oscrypto:
+ responder_private_key = asymmetric.load_private_key(responder_private_key)
+ signature_bytes = sign_func(responder_private_key, response_data.dump(), self._hash_algo)
+
+ certs = None
+ if self._certificate_issuer:
+ certs = [responder_certificate]
+
+ return ocsp.OCSPResponse({
+ 'response_status': self._response_status,
+ 'response_bytes': {
+ 'response_type': 'basic_ocsp_response',
+ 'response': {
+ 'tbs_response_data': response_data,
+ 'signature_algorithm': {'algorithm': signature_algorithm_id},
+ 'signature': signature_bytes,
+ }
+ }
+ })
+
+# Enums
+
+class ResponseStatus(enum.Enum):
+ successful = 'successful'
+ malformed_request = 'malformed_request'
+ internal_error = 'internal_error'
+ try_later = 'try_later'
+ sign_required = 'sign_required'
+ unauthorized = 'unauthorized'
+
+
+class CertificateStatus(enum.Enum):
+ good = 'good'
+ revoked = 'revoked'
+ key_compromise = 'key_compromise'
+ ca_compromise = 'ca_compromise'
+ affiliation_changed = 'affiliation_changed'
+ superseded = 'superseded'
+ cessation_of_operation = 'cessation_of_operation'
+ certificate_hold = 'certificate_hold'
+ remove_from_crl = 'remove_from_crl'
+ privilege_withdrawn = 'privilege_withdrawn'
+ unknown = 'unknown'
+
+
+# API endpoints
+FAULT_REVOKED = "revoked"
+FAULT_UNKNOWN = "unknown"
+
+class OCSPResponder:
+
+ def __init__(self, issuer_cert: str, responder_cert: str, responder_key: str,
+ fault: str = None, next_update_days: int = 7):
+ """
+ Create a new OCSPResponder instance.
+
+ :param issuer_cert: Path to the issuer certificate.
+ :param responder_cert: Path to the certificate of the OCSP responder
+ with the `OCSP Signing` extension.
+ :param responder_key: Path to the private key belonging to the
+ responder cert.
+ :param validate_func: A function that - given a certificate serial -
+ will return the appropriate :class:`CertificateStatus` and -
+ depending on the status - a revocation datetime.
+ :param cert_retrieve_func: A function that - given a certificate serial -
+ will return the corresponding certificate as a string.
+ :param next_update_days: The ``nextUpdate`` value that will be written
+ into the response. Default: 7 days.
+
+ """
+ # Certs and keys
+ self._issuer_cert = asymmetric.load_certificate(issuer_cert)
+ self._responder_cert = asymmetric.load_certificate(responder_cert)
+ self._responder_key = asymmetric.load_private_key(responder_key)
+
+ # Next update
+ self._next_update_days = next_update_days
+
+ self._fault = fault
+
+ # Bottle
+ self._app = Bottle()
+
+ # Initialize routing
+ self._route()
+
+ def _route(self):
+ self._app.get('/', callback=self._handle_root)
+ self._app.get('/status/<request_data>', callback=self._handle_get)
+ self._app.post('/status/', callback=self._handle_post)
+
+ def _handle_root(self):
+ return 'ocsp-responder'
+
+ def _handle_get(self, request_data):
+ """
+ An OCSP GET request contains the DER-in-base64 encoded OCSP request in the
+ HTTP request URL.
+ """
+ der = base64.b64decode(request_data)
+ ocsp_request = self._parse_ocsp_request(der)
+ return self._build_http_response(ocsp_request)
+
+ def _handle_post(self):
+ """
+ An OCSP POST request contains the DER encoded OCSP request in the HTTP
+ request body.
+ """
+ der = request.body.read()
+ ocsp_request = self._parse_ocsp_request(der)
+ return self._build_http_response(ocsp_request)
+
+ def _fail(self, status: ResponseStatus) -> OCSPResponse:
+ builder = OCSPResponseBuilder(response_status=status.value)
+ return builder.build()
+
+ def _parse_ocsp_request(self, request_der: bytes) -> OCSPRequest:
+ """
+ Parse the request bytes, return an ``OCSPRequest`` instance.
+ """
+ return OCSPRequest.load(request_der)
+
+ def validate(self):
+ time = datetime(2018, 1, 1, 1, 00, 00, 00, timezone.utc)
+ if self._fault == FAULT_REVOKED:
+ return (CertificateStatus.revoked, time)
+ elif self._fault == FAULT_UNKNOWN:
+ return (CertificateStatus.unknown, None)
+ elif self._fault != None:
+ raise NotImplemented('Fault type could not be found')
+ return (CertificateStatus.good, time)
+
+ def _build_ocsp_response(self, ocsp_request: OCSPRequest) -> OCSPResponse:
+ """
+ Create and return an OCSP response from an OCSP request.
+ """
+ # Get the certificate serial
+ tbs_request = ocsp_request['tbs_request']
+ request_list = tbs_request['request_list']
+ if len(request_list) < 1:
+ logger.warning('Received OCSP request with no requests')
+ raise NotImplemented('Empty requests not supported')
+
+ single_request = request_list[0] # TODO: Support more than one request
+ req_cert = single_request['req_cert']
+ serial = req_cert['serial_number'].native
+
+ # Check certificate status
+ try:
+ certificate_status, revocation_date = self.validate()
+ except Exception as e:
+ logger.exception('Could not determine certificate status: %s', e)
+ return self._fail(ResponseStatus.internal_error)
+
+ certificate_status_list = [(serial, certificate_status.value)]
+
+ # Build the response
+ builder = OCSPResponseBuilder(**{
+ 'response_status': ResponseStatus.successful.value,
+ 'certificate_status_list': certificate_status_list,
+ 'revocation_date': revocation_date,
+ })
+
+ # Parse extensions
+ for extension in tbs_request['request_extensions']:
+ extn_id = extension['extn_id'].native
+ critical = extension['critical'].native
+ value = extension['extn_value'].parsed
+
+ # This variable tracks whether any unknown extensions were encountered
+ unknown = False
+
+ # Handle nonce extension
+ if extn_id == 'nonce':
+ builder.nonce = value.native
+
+ # That's all we know
+ else:
+ unknown = True
+
+ # If an unknown critical extension is encountered (which should not
+ # usually happen, according to RFC 6960 4.1.2), we should throw our
+ # hands up in despair and run.
+ if unknown is True and critical is True:
+ logger.warning('Could not parse unknown critical extension: %r',
+ dict(extension.native))
+ return self._fail(ResponseStatus.internal_error)
+
+ # If it's an unknown non-critical extension, we can safely ignore it.
+ elif unknown is True:
+ logger.info('Ignored unknown non-critical extension: %r', dict(extension.native))
+
+ # Set certificate issuer
+ builder.certificate_issuer = self._issuer_cert
+
+ # Set next update date
+ builder.next_update = datetime.now(timezone.utc) + timedelta(days=self._next_update_days)
+
+ return builder.build(self._responder_key, self._responder_cert)
+
+ def _build_http_response(self, request_der: bytes) -> HTTPResponse:
+ response_der = self._build_ocsp_response(request_der).dump()
+ return HTTPResponse(
+ status=200,
+ body=response_der,
+ content_type='application/ocsp-response',
+ )
+
+ def serve(self, port=8080, debug=False):
+ logger.info('Launching %sserver on port %d', 'debug' if debug else '', port)
+ self._app.run(port=port, debug=debug)