File
Blob: src/workerd/api/node/tests/crypto_x509-test.js
| 1 | // Copyright (c) 2017-2022 Cloudflare, Inc. |
| 2 | // Licensed under the Apache 2.0 license found in the LICENSE file or at: |
| 3 | // https://opensource.org/licenses/Apache-2.0 |
| 4 | // |
| 5 | // Copyright Joyent, Inc. and other Node contributors. |
| 6 | // |
| 7 | // Permission is hereby granted, free of charge, to any person obtaining a |
| 8 | // copy of this software and associated documentation files (the |
| 9 | // "Software"), to deal in the Software without restriction, including |
| 10 | // without limitation the rights to use, copy, modify, merge, publish, |
| 11 | // distribute, sublicense, and/or sell copies of the Software, and to permit |
| 12 | // persons to whom the Software is furnished to do so, subject to the |
| 13 | // following conditions: |
| 14 | // |
| 15 | // The above copyright notice and this permission notice shall be included |
| 16 | // in all copies or substantial portions of the Software. |
| 17 | // |
| 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
| 19 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| 20 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN |
| 21 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, |
| 22 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT ORs |
| 23 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE |
| 24 | // USE OR OTHER DEALINGS IN THE SOFTWARE. |
| 25 | |
| 26 | import { deepStrictEqual, ok, strictEqual, throws } from 'node:assert'; |
| 27 | |
| 28 | import { Buffer } from 'node:buffer'; |
| 29 | import assert from 'node:assert'; |
| 30 | |
| 31 | import { X509Certificate, PublicKeyObject } from 'node:crypto'; |
| 32 | |
| 33 | const cert = Buffer.from(`-----BEGIN CERTIFICATE----- |
| 34 | MIID6DCCAtCgAwIBAgIUFH02wcL3Qgben6tfIibXitsApCYwDQYJKoZIhvcNAQEL |
| 35 | BQAwejELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQswCQYDVQQHDAJTRjEPMA0G |
| 36 | A1UECgwGSm95ZW50MRAwDgYDVQQLDAdOb2RlLmpzMQwwCgYDVQQDDANjYTExIDAe |
| 37 | BgkqhkiG9w0BCQEWEXJ5QHRpbnljbG91ZHMub3JnMCAXDTIyMDkwMzIxNDAzN1oY |
| 38 | DzIyOTYwNjE3MjE0MDM3WjB9MQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExCzAJ |
| 39 | BgNVBAcMAlNGMQ8wDQYDVQQKDAZKb3llbnQxEDAOBgNVBAsMB05vZGUuanMxDzAN |
| 40 | BgNVBAMMBmFnZW50MTEgMB4GCSqGSIb3DQEJARYRcnlAdGlueWNsb3Vkcy5vcmcw |
| 41 | ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDUVjIK+yDTgnCT3CxChO0E |
| 42 | 37q9VuHdrlKeKLeQzUJW2yczSfNzX/0zfHpjY+zKWie39z3HCJqWxtiG2wxiOI8c |
| 43 | 3WqWOvzVmdWADlh6EfkIlg+E7VC6JaKDA+zabmhPvnuu3JzogBMnsWl68lCXzuPx |
| 44 | deQAmEwNtqjrh74DtM+Ud0ulb//Ixjxo1q3rYKu+aaexSramuee6qJta2rjrB4l8 |
| 45 | B/bU+j1mDf9XQQfSjo9jRnp4hiTFdBl2k+lZzqE2L/rhu6EMjA2IhAq/7xA2MbLo |
| 46 | 9cObVUin6lfoo5+JKRgT9Fp2xEgDOit+2EA/S6oUfPNeLSVUqmXOSWlXlwlb9Nxr |
| 47 | AgMBAAGjYTBfMF0GCCsGAQUFBwEBBFEwTzAjBggrBgEFBQcwAYYXaHR0cDovL29j |
| 48 | c3Aubm9kZWpzLm9yZy8wKAYIKwYBBQUHMAKGHGh0dHA6Ly9jYS5ub2RlanMub3Jn |
| 49 | L2NhLmNlcnQwDQYJKoZIhvcNAQELBQADggEBAMM0mBBjLMt9pYXePtUeNO0VTw9y |
| 50 | FWCM8nAcAO2kRNwkJwcsispNpkcsHZ5o8Xf5mpCotdvziEWG1hyxwU6nAWyNOLcN |
| 51 | G0a0KUfbMO3B6ZYe1GwPDjXaQnv75SkAdxgX5zOzca3xnhITcjUUGjQ0fbDfwFV5 |
| 52 | ix8mnzvfXjDONdEznVa7PFcN6QliFUMwR/h8pCRHtE5+a10OSPeJSrGG+FtrGnRW |
| 53 | G1IJUv6oiGF/MvWCr84REVgc1j78xomGANJIu2hN7bnD1nEMON6em8IfnDOUtynV |
| 54 | 9wfWTqiQYD5Zifj6WcGa0aAHMuetyFG4lIfMAHmd3gaKpks7j9l26LwRPvI= |
| 55 | -----END CERTIFICATE----- |
| 56 | `); |
| 57 | |
| 58 | const ca = Buffer.from(`-----BEGIN CERTIFICATE----- |
| 59 | MIIDlDCCAnygAwIBAgIUSrFsjf1qfQ0t/KvfnEsOksatAikwDQYJKoZIhvcNAQEL |
| 60 | BQAwejELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQswCQYDVQQHDAJTRjEPMA0G |
| 61 | A1UECgwGSm95ZW50MRAwDgYDVQQLDAdOb2RlLmpzMQwwCgYDVQQDDANjYTExIDAe |
| 62 | BgkqhkiG9w0BCQEWEXJ5QHRpbnljbG91ZHMub3JnMCAXDTIyMDkwMzIxNDAzN1oY |
| 63 | DzIyOTYwNjE3MjE0MDM3WjB6MQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExCzAJ |
| 64 | BgNVBAcMAlNGMQ8wDQYDVQQKDAZKb3llbnQxEDAOBgNVBAsMB05vZGUuanMxDDAK |
| 65 | BgNVBAMMA2NhMTEgMB4GCSqGSIb3DQEJARYRcnlAdGlueWNsb3Vkcy5vcmcwggEi |
| 66 | MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDNvf4OGGep+ak+4DNjbuNgy0S/ |
| 67 | AZPxahEFp4gpbcvsi9YLOPZ31qpilQeQf7d27scIZ02Qx1YBAzljxELB8H/ZxuYS |
| 68 | cQK0s+DNP22xhmgwMWznO7TezkHP5ujN2UkbfbUpfUxGFgncXeZf9wR7yFWppeHi |
| 69 | RWNBOgsvY7sTrS12kXjWGjqntF7xcEDHc7h+KyF6ZjVJZJCnP6pJEQ+rUjd51eCZ |
| 70 | Xt4WjowLnQiCS1VKzXiP83a++Ma1BKKkUitTR112/Uwd5eGoiByhmLzb/BhxnHJN |
| 71 | 07GXjhlMItZRm/jfbZsx1mwnNOO3tx4r08l+DaqkinIadvazs+1ugCaKQn8xAgMB |
| 72 | AAGjEDAOMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAFqG0RXURDam |
| 73 | 56x5accdg9sY5zEGP5VQhkK3ZDc2NyNNa25rwvrjCpO+e0OSwKAmm4aX6iIf2woY |
| 74 | wF2f9swWYzxn9CG4fDlUA8itwlnHxupeL4fGMTYb72vf31plUXyBySRsTwHwBloc |
| 75 | F7KvAZpYYKN9EMH1S/267By6H2I33BT/Ethv//n8dSfmuCurR1kYRaiOC4PVeyFk |
| 76 | B3sj8TtolrN0y/nToWUhmKiaVFnDx3odQ00yhmxR3t21iB7yDkko6D8Vf2dVC4j/ |
| 77 | YYBVprXGlTP/hiYRLDoP20xKOYznx5cvHPJ9p+lVcOZUJsJj/Iy750+2n5UiBmXt |
| 78 | lz88C25ucKA= |
| 79 | -----END CERTIFICATE----- |
| 80 | `); |
| 81 | |
| 82 | const der = Buffer.from( |
| 83 | '308203e8308202d0a0030201020214147d36c1c2f74206de9fab5f2226d78adb00a42630' + |
| 84 | '0d06092a864886f70d01010b0500307a310b3009060355040613025553310b3009060355' + |
| 85 | '04080c024341310b300906035504070c025346310f300d060355040a0c064a6f79656e74' + |
| 86 | '3110300e060355040b0c074e6f64652e6a73310c300a06035504030c036361313120301e' + |
| 87 | '06092a864886f70d010901161172794074696e79636c6f7564732e6f72673020170d3232' + |
| 88 | '303930333231343033375a180f32323936303631373231343033375a307d310b30090603' + |
| 89 | '55040613025553310b300906035504080c024341310b300906035504070c025346310f30' + |
| 90 | '0d060355040a0c064a6f79656e743110300e060355040b0c074e6f64652e6a73310f300d' + |
| 91 | '06035504030c066167656e74313120301e06092a864886f70d010901161172794074696e' + |
| 92 | '79636c6f7564732e6f726730820122300d06092a864886f70d01010105000382010f0030' + |
| 93 | '82010a0282010100d456320afb20d3827093dc2c4284ed04dfbabd56e1ddae529e28b790' + |
| 94 | 'cd4256db273349f3735ffd337c7a6363ecca5a27b7f73dc7089a96c6d886db0c62388f1c' + |
| 95 | 'dd6a963afcd599d5800e587a11f908960f84ed50ba25a28303ecda6e684fbe7baedc9ce8' + |
| 96 | '801327b1697af25097cee3f175e400984c0db6a8eb87be03b4cf94774ba56fffc8c63c68' + |
| 97 | 'd6adeb60abbe69a7b14ab6a6b9e7baa89b5adab8eb07897c07f6d4fa3d660dff574107d2' + |
| 98 | '8e8f63467a788624c574197693e959cea1362ffae1bba10c8c0d88840abfef103631b2e8' + |
| 99 | 'f5c39b5548a7ea57e8a39f89291813f45a76c448033a2b7ed8403f4baa147cf35e2d2554' + |
| 100 | 'aa65ce49695797095bf4dc6b0203010001a361305f305d06082b06010505070101045130' + |
| 101 | '4f302306082b060105050730018617687474703a2f2f6f6373702e6e6f64656a732e6f72' + |
| 102 | '672f302806082b06010505073002861c687474703a2f2f63612e6e6f64656a732e6f7267' + |
| 103 | '2f63612e63657274300d06092a864886f70d01010b05000382010100c3349810632ccb7d' + |
| 104 | 'a585de3ed51e34ed154f0f7215608cf2701c00eda444dc2427072c8aca4da6472c1d9e68' + |
| 105 | 'f177f99a90a8b5dbf3884586d61cb1c14ea7016c8d38b70d1b46b42947db30edc1e9961e' + |
| 106 | 'd46c0f0e35da427bfbe52900771817e733b371adf19e12137235141a34347db0dfc05579' + |
| 107 | '8b1f269f3bdf5e30ce35d1339d56bb3c570de9096215433047f87ca42447b44e7e6b5d0e' + |
| 108 | '48f7894ab186f85b6b1a74561b520952fea888617f32f582afce1111581cd63efcc68986' + |
| 109 | '00d248bb684dedb9c3d6710c38de9e9bc21f9c3394b729d5f707d64ea890603e5989f8fa' + |
| 110 | '59c19ad1a00732e7adc851b89487cc00799dde068aa64b3b8fd976e8bc113ef2', |
| 111 | 'hex' |
| 112 | ); |
| 113 | |
| 114 | const _key = Buffer.from(`-----BEGIN RSA PRIVATE KEY----- |
| 115 | MIIEpAIBAAKCAQEA1FYyCvsg04Jwk9wsQoTtBN+6vVbh3a5Snii3kM1CVtsnM0nz |
| 116 | c1/9M3x6Y2Psylont/c9xwialsbYhtsMYjiPHN1qljr81ZnVgA5YehH5CJYPhO1Q |
| 117 | uiWigwPs2m5oT757rtyc6IATJ7FpevJQl87j8XXkAJhMDbao64e+A7TPlHdLpW// |
| 118 | yMY8aNat62CrvmmnsUq2prnnuqibWtq46weJfAf21Po9Zg3/V0EH0o6PY0Z6eIYk |
| 119 | xXQZdpPpWc6hNi/64buhDIwNiIQKv+8QNjGy6PXDm1VIp+pX6KOfiSkYE/RadsRI |
| 120 | AzorfthAP0uqFHzzXi0lVKplzklpV5cJW/TcawIDAQABAoIBAAvbtHfAhpjJVBgt |
| 121 | 15rvaX04MWmZjIugzKRgib/gdq/7FTlcC+iJl85kSUF7tyGl30n62MxgwqFhAX6m |
| 122 | hQ6HMhbelrFFIhGbwbyhEHfgwROlrcAysKt0pprCgVvBhrnNXYLqdyjU3jz9P3LK |
| 123 | TY3s0/YMK2uNFdI+PTjKH+Z9Foqn9NZUnUonEDepGyuRO7fLeccWJPv2L4CR4a/5 |
| 124 | ku4VbDgVpvVSVRG3PSVzbmxobnpdpl52og+T7tPx1cLnIknPtVljXPWtZdfekh2E |
| 125 | eAp2KxCCHOKzzG3ItBKsVu0woeqEpy8JcoO6LbgmEoVnZpgmtQClbBgef8+i+oGE |
| 126 | BgW9nmECgYEA8gA63QQuZOUC56N1QXURexN2PogF4wChPaCTFbQSJXvSBkQmbqfL |
| 127 | qRSD8P0t7GOioPrQK6pDwFf4BJB01AvkDf8Z6DxxOJ7cqIC7LOwDupXocWX7Q0Qk |
| 128 | O6cwclBVsrDZK00v60uRRpl/a39GW2dx7IiQDkKQndLh3/0TbMIWHNcCgYEA4J6r |
| 129 | yinZbLpKw2+ezhi4B4GT1bMLoKboJwpZVyNZZCzYR6ZHv+lS7HR/02rcYMZGoYbf |
| 130 | n7OHwF4SrnUS7vPhG4g2ZsOhKQnMvFSQqpGmK1ZTuoKGAevyvtouhK/DgtLWzGvX |
| 131 | 9fSahiq/UvfXs/z4M11q9Rv9ztPCmG1cwSEHlo0CgYEAogQNZJK8DMhVnYcNpXke |
| 132 | 7uskqtCeQE/Xo06xqkIYNAgloBRYNpUYAGa/vsOBz1UVN/kzDUi8ezVp0oRz8tLT |
| 133 | J5u2WIi+tE2HJTiqF3UbOfvK1sCT64DfUSCpip7GAQ/tFNRkVH8PD9kMOYfILsGe |
| 134 | v+DdsO5Xq5HXrwHb02BNNZkCgYBsl8lt33WiPx5OBfS8pu6xkk+qjPkeHhM2bKZs |
| 135 | nkZlS9j0KsudWGwirN/vkkYg8zrKdK5AQ0dqFRDrDuasZ3N5IA1M+V88u+QjWK7o |
| 136 | B6pSYVXxYZDv9OZSpqC+vUrEQLJf+fNakXrzSk9dCT1bYv2Lt6ox/epix7XYg2bI |
| 137 | Z/OHMQKBgQC2FUGhlndGeugTJaoJ8nhT/0VfRUX/h6sCgSerk5qFr/hNCBV4T022 |
| 138 | x0NDR2yLG6MXyqApJpG6rh3QIDElQoQCNlI3/KJ6JfEfmqrLLN2OigTvA5sE4fGU |
| 139 | Dp/ha8OQAx95EwXuaG7LgARduvOIK3x8qi8KsZoUGJcg2ywurUbkWA== |
| 140 | -----END RSA PRIVATE KEY----- |
| 141 | `); |
| 142 | |
| 143 | const subjectCheck = `C=US |
| 144 | ST=CA |
| 145 | L=SF |
| 146 | O=Joyent |
| 147 | OU=Node.js |
| 148 | CN=agent1 |
| 149 | emailAddress=ry@tinyclouds.org`; |
| 150 | |
| 151 | const issuerCheck = `C=US |
| 152 | ST=CA |
| 153 | L=SF |
| 154 | O=Joyent |
| 155 | OU=Node.js |
| 156 | CN=ca1 |
| 157 | emailAddress=ry@tinyclouds.org`; |
| 158 | |
| 159 | let infoAccessCheck = `OCSP - URI:http://ocsp.nodejs.org/ |
| 160 | CA Issuers - URI:http://ca.nodejs.org/ca.cert\n`; |
| 161 | |
| 162 | const legacyObjectCheck = { |
| 163 | subject: { |
| 164 | C: 'US', |
| 165 | ST: 'CA', |
| 166 | L: 'SF', |
| 167 | O: 'Joyent', |
| 168 | OU: 'Node.js', |
| 169 | CN: 'agent1', |
| 170 | emailAddress: 'ry@tinyclouds.org', |
| 171 | }, |
| 172 | issuer: { |
| 173 | C: 'US', |
| 174 | ST: 'CA', |
| 175 | L: 'SF', |
| 176 | O: 'Joyent', |
| 177 | OU: 'Node.js', |
| 178 | CN: 'ca1', |
| 179 | emailAddress: 'ry@tinyclouds.org', |
| 180 | }, |
| 181 | infoAccess: { |
| 182 | 'OCSP - URI': ['http://ocsp.nodejs.org/'], |
| 183 | 'CA Issuers - URI': ['http://ca.nodejs.org/ca.cert'], |
| 184 | }, |
| 185 | modulus: |
| 186 | 'D456320AFB20D3827093DC2C4284ED04DFBABD56E1DDAE529E28B790CD42' + |
| 187 | '56DB273349F3735FFD337C7A6363ECCA5A27B7F73DC7089A96C6D886DB0C' + |
| 188 | '62388F1CDD6A963AFCD599D5800E587A11F908960F84ED50BA25A28303EC' + |
| 189 | 'DA6E684FBE7BAEDC9CE8801327B1697AF25097CEE3F175E400984C0DB6A8' + |
| 190 | 'EB87BE03B4CF94774BA56FFFC8C63C68D6ADEB60ABBE69A7B14AB6A6B9E7' + |
| 191 | 'BAA89B5ADAB8EB07897C07F6D4FA3D660DFF574107D28E8F63467A788624' + |
| 192 | 'C574197693E959CEA1362FFAE1BBA10C8C0D88840ABFEF103631B2E8F5C3' + |
| 193 | '9B5548A7EA57E8A39F89291813F45A76C448033A2B7ED8403F4BAA147CF3' + |
| 194 | '5E2D2554AA65CE49695797095BF4DC6B', |
| 195 | bits: 2048, |
| 196 | exponent: '0x10001', |
| 197 | valid_from: 'Sep 3 21:40:37 2022 GMT', |
| 198 | valid_to: 'Jun 17 21:40:37 2296 GMT', |
| 199 | fingerprint: '8B:89:16:C4:99:87:D2:13:1A:64:94:36:38:A5:32:01:F0:95:3B:53', |
| 200 | fingerprint256: |
| 201 | '2C:62:59:16:91:89:AB:90:6A:3E:98:88:A6:D3:C5:58:58:6C:AE:FF:9C:33:' + |
| 202 | '22:7C:B6:77:D3:34:E7:53:4B:05', |
| 203 | fingerprint512: |
| 204 | '51:62:18:39:E2:E2:77:F5:86:11:E8:C0:CA:54:43:7C:76:83:19:05:D0:03:' + |
| 205 | '24:21:B8:EB:14:61:FB:24:16:EB:BD:51:1A:17:91:04:30:03:EB:68:5F:DC:' + |
| 206 | '86:E1:D1:7C:FB:AF:78:ED:63:5F:29:9C:32:AF:A1:8E:22:96:D1:02', |
| 207 | serialNumber: '147D36C1C2F74206DE9FAB5F2226D78ADB00A426', |
| 208 | }; |
| 209 | |
| 210 | export const test_ok = { |
| 211 | async test() { |
| 212 | const x509 = new X509Certificate(cert); |
| 213 | ok(!x509.ca); |
| 214 | strictEqual(x509.subject, subjectCheck); |
| 215 | strictEqual(x509.subjectAltName, undefined); |
| 216 | strictEqual(x509.issuer, issuerCheck); |
| 217 | strictEqual(x509.infoAccess, infoAccessCheck); |
| 218 | strictEqual(x509.validFrom, 'Sep 3 21:40:37 2022 GMT'); |
| 219 | strictEqual(x509.validTo, 'Jun 17 21:40:37 2296 GMT'); |
| 220 | strictEqual( |
| 221 | x509.fingerprint.length, |
| 222 | '8B:89:16:C4:99:87:D2:13:1A:64:94:36:38:A5:32:01:F0:95:3B:53'.length |
| 223 | ); |
| 224 | strictEqual( |
| 225 | x509.fingerprint256, |
| 226 | '2C:62:59:16:91:89:AB:90:6A:3E:98:88:A6:D3:C5:58:58:6C:AE:FF:9C:33:' + |
| 227 | '22:7C:B6:77:D3:34:E7:53:4B:05' |
| 228 | ); |
| 229 | strictEqual( |
| 230 | x509.fingerprint512, |
| 231 | '0B:6F:D0:4D:6B:22:53:99:66:62:51:2D:2C:96:F2:58:3F:95:1C:CC:4C:44:' + |
| 232 | '9D:B5:59:AA:AD:A8:F6:2A:24:8A:BB:06:A5:26:42:52:30:A3:37:61:30:A9:' + |
| 233 | '5A:42:63:E0:21:2F:D6:70:63:07:96:6F:27:A7:78:12:08:02:7A:8B' |
| 234 | ); |
| 235 | strictEqual(x509.keyUsage, undefined); |
| 236 | strictEqual(x509.serialNumber, '147D36C1C2F74206DE9FAB5F2226D78ADB00A426'); |
| 237 | |
| 238 | deepStrictEqual(x509.raw, der); |
| 239 | |
| 240 | ok(x509.publicKey instanceof PublicKeyObject); |
| 241 | ok(x509.publicKey.type === 'public'); |
| 242 | |
| 243 | strictEqual( |
| 244 | x509.toString().replaceAll('\r\n', '\n'), |
| 245 | cert.toString().replaceAll('\r\n', '\n') |
| 246 | ); |
| 247 | strictEqual(x509.toJSON(), x509.toString()); |
| 248 | |
| 249 | // TODO(soon): Need to implement createPrivateKey this check |
| 250 | // const privateKey = createPrivateKey(key); |
| 251 | // ok(x509.checkPrivateKey(privateKey)); |
| 252 | throws(() => x509.checkPrivateKey(x509.publicKey), { |
| 253 | code: 'ERR_INVALID_ARG_TYPE', |
| 254 | }); |
| 255 | |
| 256 | strictEqual(x509.checkIP('127.0.0.1'), undefined); |
| 257 | strictEqual(x509.checkIP('::'), undefined); |
| 258 | strictEqual(x509.checkHost('agent1'), 'agent1'); |
| 259 | strictEqual(x509.checkHost('agent2'), undefined); |
| 260 | strictEqual(x509.checkEmail('ry@tinyclouds.org'), 'ry@tinyclouds.org'); |
| 261 | strictEqual(x509.checkEmail('sally@example.com'), undefined); |
| 262 | ok(!x509.checkIssued(x509)); |
| 263 | |
| 264 | throws(() => x509.checkHost('agent\x001')); |
| 265 | throws(() => x509.checkIP('[::]')); |
| 266 | throws(() => x509.checkEmail('not\x00hing')); |
| 267 | |
| 268 | [1, false].forEach((i) => { |
| 269 | throws(() => x509.checkHost('agent1', i)); |
| 270 | throws(() => x509.checkHost('agent1', { subject: i })); |
| 271 | }); |
| 272 | |
| 273 | [ |
| 274 | 'wildcards', |
| 275 | 'partialWildcards', |
| 276 | 'multiLabelWildcards', |
| 277 | 'singleLabelSubdomains', |
| 278 | ].forEach((key) => { |
| 279 | [1, '', {}].forEach((i) => { |
| 280 | throws(() => x509.checkHost('agent1', { [key]: i }), { |
| 281 | code: 'ERR_INVALID_ARG_TYPE', |
| 282 | }); |
| 283 | }); |
| 284 | }); |
| 285 | |
| 286 | const ca_cert = new X509Certificate(ca); |
| 287 | ok(ca_cert.ca); |
| 288 | ok(x509.checkIssued(ca_cert)); |
| 289 | |
| 290 | ok(x509.verify(ca_cert.publicKey)); |
| 291 | |
| 292 | throws(() => x509.checkIssued({})); |
| 293 | throws(() => x509.checkIssued('')); |
| 294 | throws(() => x509.verify({})); |
| 295 | throws(() => x509.verify('')); |
| 296 | // eslint-disable-next-line no-undef |
| 297 | throws(() => x509.verify(privateKey)); |
| 298 | |
| 299 | const legacyObject = x509.toLegacyObject(); |
| 300 | deepStrictEqual(legacyObject.raw, x509.raw); |
| 301 | |
| 302 | deepStrictEqual(legacyObject.subject, legacyObjectCheck.subject); |
| 303 | deepStrictEqual(legacyObject.issuer, legacyObjectCheck.issuer); |
| 304 | deepStrictEqual(legacyObject.infoAccess, legacyObjectCheck.infoAccess); |
| 305 | strictEqual(legacyObject.modulus, legacyObjectCheck.modulus); |
| 306 | strictEqual(legacyObject.bits, legacyObjectCheck.bits); |
| 307 | strictEqual(legacyObject.exponent, legacyObjectCheck.exponent); |
| 308 | strictEqual(legacyObject.valid_from, legacyObjectCheck.valid_from); |
| 309 | strictEqual(legacyObject.valid_to, legacyObjectCheck.valid_to); |
| 310 | strictEqual(legacyObject.fingerprint, legacyObjectCheck.fingerprint); |
| 311 | strictEqual(legacyObject.fingerprint256, legacyObjectCheck.fingerprint256); |
| 312 | strictEqual(legacyObject.serialNumber, legacyObjectCheck.serialNumber); |
| 313 | }, |
| 314 | }; |
| 315 | |
| 316 | // This X.509 Certificate can be parsed by OpenSSL because it contains a |
| 317 | // structurally sound TBSCertificate structure. However, the SPKI field of the |
| 318 | // TBSCertificate contains the subjectPublicKey as a BIT STRING, and this bit |
| 319 | // sequence is not a valid public key. Ensure that X509Certificate.publicKey |
| 320 | // does not abort in this case. |
| 321 | // TODO(soon): F |
| 322 | // const badCert = `-----BEGIN CERTIFICATE----- |
| 323 | // MIIDpDCCAw0CFEc1OZ8g17q+PZnna3iQ/gfoZ7f3MA0GCSqGSIb3DQEBBQUAMIHX |
| 324 | // MRMwEQYLKwYBBAGCNzwCAQMTAkdJMR0wGwYDVQQPExRQcml2YXRlIE9yZ2FuaXph |
| 325 | // dGlvbjEOMAwGA1UEBRMFOTkxOTExCzAJBgNVBAYTAkdJMRIwEAYDVQQIFAlHaWJy |
| 326 | // YWx0YXIxEjAQBgNVBAcUCUdpYnJhbHRhcjEgMB4GA1UEChQXV0hHIChJbnRlcm5h |
| 327 | // dGlvbmFsKSBMdGQxHDAaBgNVBAsUE0ludGVyYWN0aXZlIEJldHRpbmcxHDAaBgNV |
| 328 | // BAMUE3d3dy53aWxsaWFtaGlsbC5jb20wIhgPMjAxNDAyMDcwMDAwMDBaGA8yMDE1 |
| 329 | // MDIyMTIzNTk1OVowgbAxCzAJBgNVBAYTAklUMQ0wCwYDVQQIEwRSb21lMRAwDgYD |
| 330 | // VQQHEwdQb21lemlhMRYwFAYDVQQKEw1UZWxlY29taXRhbGlhMRIwEAYDVQQrEwlB |
| 331 | // RE0uQVAuUE0xHTAbBgNVBAMTFHd3dy50ZWxlY29taXRhbGlhLml0MTUwMwYJKoZI |
| 332 | // hvcNAQkBFiZ2YXNlc2VyY2l6aW9wb3J0YWxpY29AdGVsZWNvbWl0YWxpYS5pdDCB |
| 333 | // nzANBgkqhkiG9w0BAQEFAAOBjQA4gYkCgYEA5m/Vf7PevH+inMfUJOc8GeR7WVhM |
| 334 | // CQwcMM5k46MSZo7kCk7VZuaq5G2JHGAGnLPaPUkeXlrf5qLpTxXXxHNtz+WrDlFt |
| 335 | // boAdnTcqpX3+72uBGOaT6Wi/9YRKuCs5D5/cAxAc3XjHfpRXMoXObj9Vy7mLndfV |
| 336 | // /wsnTfU9QVeBkgsCAwEAAaOBkjCBjzAdBgNVHQ4EFgQUfLjAjEiC83A+NupGrx5+ |
| 337 | // Qe6nhRMwbgYIKwYBBQUHAQwEYjBgoV6gXDBaMFgwVhYJaW1hZ2UvZ2lmMCEwHzAH |
| 338 | // BgUrDgMCGgQUS2u5KJYGDLvQUjibKaxLB4shBRgwJhYkaHR0cDovL2xvZ28udmVy |
| 339 | // aXNpZ24uY29tL3ZzbG9nbzEuZ2lmMA0GCSqGSIb3DQEBBQUAA4GBALLiAMX0cIMp |
| 340 | // +V/JgMRhMEUKbrt5lYKfv9dil/f22ezZaFafb070jGMMPVy9O3/PavDOkHtTv3vd |
| 341 | // tAt3hIKFD1bJt6c6WtMH2Su3syosWxmdmGk5ihslB00lvLpfj/wed8i3bkcB1doq |
| 342 | // UcXd/5qu2GhokrKU2cPttU+XAN2Om6a0 |
| 343 | // -----END CERTIFICATE-----`; |
| 344 | |
| 345 | // export const test_error = { |
| 346 | // test() { |
| 347 | // throws(() => new X509Certificate(badCert)); |
| 348 | // } |
| 349 | // }; |
| 350 | |
| 351 | // Verify that invalid certificate data throws a user-facing error (not an internal error). |
| 352 | export const test_invalid_cert = { |
| 353 | test() { |
| 354 | // Completely invalid data |
| 355 | throws(() => new X509Certificate('not a certificate'), { |
| 356 | code: 'ERR_INVALID_ARG_VALUE', |
| 357 | }); |
| 358 | |
| 359 | // Invalid base64 in PEM wrapper |
| 360 | throws( |
| 361 | () => |
| 362 | new X509Certificate( |
| 363 | '-----BEGIN CERTIFICATE-----\n!!!invalid!!!\n-----END CERTIFICATE-----' |
| 364 | ), |
| 365 | { |
| 366 | code: 'ERR_INVALID_ARG_VALUE', |
| 367 | } |
| 368 | ); |
| 369 | |
| 370 | // Empty buffer |
| 371 | throws(() => new X509Certificate(Buffer.alloc(0)), { |
| 372 | code: 'ERR_INVALID_ARG_VALUE', |
| 373 | }); |
| 374 | |
| 375 | // Random bytes (not a valid DER or PEM certificate) |
| 376 | throws(() => new X509Certificate(Buffer.from([0x01, 0x02, 0x03])), { |
| 377 | code: 'ERR_INVALID_ARG_VALUE', |
| 378 | }); |
| 379 | }, |
| 380 | }; |
| 381 | |
| 382 | // Test parsing a SHA384 ECDSA (P-384) certificate in DER (.cer) format |
| 383 | // |
| 384 | // Generated with: |
| 385 | // # CA key + self-signed CA cert |
| 386 | // openssl ecparam -name secp384r1 -genkey -noout -out ec384_ca_key.pem |
| 387 | // openssl req -new -x509 -key ec384_ca_key.pem -sha384 -days 100000 \ |
| 388 | // -out ec384_ca_cert.pem -subj "/C=US/ST=CA/L=SF/O=Test/OU=TestUnit/CN=TestCA" |
| 389 | // |
| 390 | // # End-entity key + CSR + cert signed by the CA |
| 391 | // openssl ecparam -name secp384r1 -genkey -noout -out ec384_key.pem |
| 392 | // openssl req -new -key ec384_key.pem -sha384 -out ec384_csr.pem \ |
| 393 | // -subj "/C=US/ST=CA/L=SF/O=Test/OU=TestUnit/CN=agent1/emailAddress=test@example.com" |
| 394 | // openssl x509 -req -in ec384_csr.pem -CA ec384_ca_cert.pem -CAkey ec384_ca_key.pem \ |
| 395 | // -CAcreateserial -out ec384_cert.pem -days 100000 -sha384 |
| 396 | const ecCert = Buffer.from(`-----BEGIN CERTIFICATE----- |
| 397 | MIICVzCCAd6gAwIBAgIUczuwDn2NET/tEfVmMXL4mPYyz4AwCgYIKoZIzj0EAwMw |
| 398 | WjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQswCQYDVQQHDAJTRjENMAsGA1UE |
| 399 | CgwEVGVzdDERMA8GA1UECwwIVGVzdFVuaXQxDzANBgNVBAMMBlRlc3RDQTAgFw0y |
| 400 | NjA0MDgwNTE1MzZaGA8yMzAwMDEyMjA1MTUzNlowezELMAkGA1UEBhMCVVMxCzAJ |
| 401 | BgNVBAgMAkNBMQswCQYDVQQHDAJTRjENMAsGA1UECgwEVGVzdDERMA8GA1UECwwI |
| 402 | VGVzdFVuaXQxDzANBgNVBAMMBmFnZW50MTEfMB0GCSqGSIb3DQEJARYQdGVzdEBl |
| 403 | eGFtcGxlLmNvbTB2MBAGByqGSM49AgEGBSuBBAAiA2IABP66lBV0gd87DBgeGtRG |
| 404 | c+nKyL5u9snb0ByLhcw+8CL+YGGv6SWOtVa5LFTiAEP95yBl861RBYExDzSbLGfb |
| 405 | Oifr+9EmUxKsahSaMNHIRXxoALnseTOOnDYcn2B66fck+qNCMEAwHQYDVR0OBBYE |
| 406 | FEefA0NT2iIPiO8NQ3eBUeUIrAQIMB8GA1UdIwQYMBaAFGMNBZGlbYnBCXgHLTrU |
| 407 | KRDLfkFOMAoGCCqGSM49BAMDA2cAMGQCME8lAYbgOeO10lqvwUGx2xR29G13pouH |
| 408 | k4dEBxJF5hiUaOyRHcv+57tm8aBL/nWmEQIwf+G5sVuUJGcou41ick7RXM+fDg4t |
| 409 | mnbgde9KSx8Ogi9u2RimDFan8BMiJWbJIwBj |
| 410 | -----END CERTIFICATE----- |
| 411 | `); |
| 412 | |
| 413 | const ecCa = Buffer.from(`-----BEGIN CERTIFICATE----- |
| 414 | MIICSTCCAc6gAwIBAgIUJbLH/BA5dfjCxho12s8bw8BjxlcwCgYIKoZIzj0EAwMw |
| 415 | WjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQswCQYDVQQHDAJTRjENMAsGA1UE |
| 416 | CgwEVGVzdDERMA8GA1UECwwIVGVzdFVuaXQxDzANBgNVBAMMBlRlc3RDQTAgFw0y |
| 417 | NjA0MDgwNTE1MTVaGA8yMzAwMDEyMjA1MTUxNVowWjELMAkGA1UEBhMCVVMxCzAJ |
| 418 | BgNVBAgMAkNBMQswCQYDVQQHDAJTRjENMAsGA1UECgwEVGVzdDERMA8GA1UECwwI |
| 419 | VGVzdFVuaXQxDzANBgNVBAMMBlRlc3RDQTB2MBAGByqGSM49AgEGBSuBBAAiA2IA |
| 420 | BPA7rTck1nzyMF+VHeP17ih9EYcwDkSJ5MtWr4ftLLcgPD6gPxMosNv+ykPLpw0P |
| 421 | V8IycMa4gFceYGp8NFPE5tavX2Y1loRrmQbduo5ctox11so+/4TisC/Kl3Ovile2 |
| 422 | /aNTMFEwHQYDVR0OBBYEFGMNBZGlbYnBCXgHLTrUKRDLfkFOMB8GA1UdIwQYMBaA |
| 423 | FGMNBZGlbYnBCXgHLTrUKRDLfkFOMA8GA1UdEwEB/wQFMAMBAf8wCgYIKoZIzj0E |
| 424 | AwMDaQAwZgIxAN5cy8aaW3zyozwFRVqR6k7vorwpuemQnqYSocfmuQI5CxbXgYEy |
| 425 | 2cVrA4j32pZD5wIxAJBE4gfYmjJt4OG/YdOaslrnn84Jk1O21cTzxtivtTxxNOcQ |
| 426 | rd0vE+2ygF1pCs5aEw== |
| 427 | -----END CERTIFICATE----- |
| 428 | `); |
| 429 | |
| 430 | // Convert the PEM to DER for testing the raw property and to ensure that the same data is being parsed in both cases. |
| 431 | // openssl x509 -in ec384_cert.pem -outform DER | python3 -c "import sys; print(sys.stdin.buffer.read().hex())" |
| 432 | const ecDer = Buffer.from( |
| 433 | '30820257308201dea0030201020214733bb00e7d8d113fed11f5663172f898f632cf80' + |
| 434 | '300a06082a8648ce3d040303305a310b3009060355040613025553310b300906035504' + |
| 435 | '080c024341310b300906035504070c025346310d300b060355040a0c04546573743111' + |
| 436 | '300f060355040b0c0854657374556e6974310f300d06035504030c06546573744341' + |
| 437 | '3020170d3236303430383035313533365a180f32333030303132323035313533365a' + |
| 438 | '307b310b3009060355040613025553310b300906035504080c024341310b3009060355' + |
| 439 | '04070c025346310d300b060355040a0c04546573743111300f060355040b0c08546573' + |
| 440 | '74556e6974310f300d06035504030c066167656e7431311f301d06092a864886f70d01' + |
| 441 | '0901161074657374406578616d706c652e636f6d3076301006072a8648ce3d02010605' + |
| 442 | '2b8104002203620004feba94157481df3b0c181e1ad44673e9cac8be6ef6c9dbd01c8b' + |
| 443 | '85cc3ef022fe6061afe9258eb556b92c54e20043fde72065f3ad510581310f349b2c67' + |
| 444 | 'db3a27ebfbd1265312ac6a149a30d1c8457c6800b9ec79338e9c361c9f607ae9f724fa' + |
| 445 | 'a3423040301d0603551d0e04160414479f034353da220f88ef0d43778151e508ac0408' + |
| 446 | '301f0603551d23041830168014630d0591a56d89c10978072d3ad42910cb7e414e300a' + |
| 447 | '06082a8648ce3d040303036700306402304f250186e039e3b5d25aafc141b1db1476f4' + |
| 448 | '6d77a68b87938744071245e6189468ec911dcbfee7bb66f1a04bfe75a61102307fe1b9' + |
| 449 | 'b15b94246728bb8d62724ed15ccf9f0e0e2d9a76e075ef4a4b1f0e822f6ed918a60c56' + |
| 450 | 'a7f013222566c9230063', |
| 451 | 'hex' |
| 452 | ); |
| 453 | |
| 454 | const ecSubjectCheck = `C=US |
| 455 | ST=CA |
| 456 | L=SF |
| 457 | O=Test |
| 458 | OU=TestUnit |
| 459 | CN=agent1 |
| 460 | emailAddress=test@example.com`; |
| 461 | |
| 462 | const ecIssuerCheck = `C=US |
| 463 | ST=CA |
| 464 | L=SF |
| 465 | O=Test |
| 466 | OU=TestUnit |
| 467 | CN=TestCA`; |
| 468 | |
| 469 | export const test_sha384_ecdsa = { |
| 470 | async test() { |
| 471 | // Test parsing a SHA384 ECDSA certificate (P-384 curve) |
| 472 | const x509 = new X509Certificate(ecCert); |
| 473 | ok(!x509.ca); |
| 474 | strictEqual(x509.subject, ecSubjectCheck); |
| 475 | strictEqual(x509.subjectAltName, undefined); |
| 476 | strictEqual(x509.issuer, ecIssuerCheck); |
| 477 | strictEqual(x509.validFrom, 'Apr 8 05:15:36 2026 GMT'); |
| 478 | strictEqual(x509.validTo, 'Jan 22 05:15:36 2300 GMT'); |
| 479 | strictEqual( |
| 480 | x509.fingerprint, |
| 481 | 'F5:CD:67:3C:C2:41:ED:A0:E9:7B:1E:F9:8D:CD:72:36:FB:BC:27:E6' |
| 482 | ); |
| 483 | strictEqual( |
| 484 | x509.fingerprint256, |
| 485 | '97:87:A6:43:9C:54:D4:F7:5D:42:58:2C:E0:D7:F1:2E:08:DF:08:BF:' + |
| 486 | '18:F4:62:4D:2E:52:E1:C4:EC:41:6B:77' |
| 487 | ); |
| 488 | strictEqual( |
| 489 | x509.fingerprint512, |
| 490 | '28:02:30:8A:87:86:07:5E:C6:FC:33:3C:D3:D9:C1:35:A5:72:F3:F2:' + |
| 491 | 'D4:D8:33:D2:9C:F4:73:7B:A5:33:6B:9B:3E:23:D6:84:70:B5:37:F8:' + |
| 492 | 'C2:D7:A3:03:9C:61:F1:40:92:AC:43:44:E6:6C:43:2D:98:AE:CF:0B:' + |
| 493 | '9A:99:90:6C' |
| 494 | ); |
| 495 | strictEqual(x509.serialNumber, '733BB00E7D8D113FED11F5663172F898F632CF80'); |
| 496 | |
| 497 | // Verify DER round-trip |
| 498 | deepStrictEqual(x509.raw, ecDer); |
| 499 | |
| 500 | // Public key should be an EC key |
| 501 | ok(x509.publicKey instanceof PublicKeyObject); |
| 502 | ok(x509.publicKey.type === 'public'); |
| 503 | |
| 504 | // PEM round-trip |
| 505 | strictEqual( |
| 506 | x509.toString().replaceAll('\r\n', '\n'), |
| 507 | ecCert.toString().replaceAll('\r\n', '\n') |
| 508 | ); |
| 509 | strictEqual(x509.toJSON(), x509.toString()); |
| 510 | |
| 511 | // Test parsing from DER (.cer) format directly |
| 512 | const x509FromDer = new X509Certificate(ecDer); |
| 513 | strictEqual( |
| 514 | x509FromDer.serialNumber, |
| 515 | '733BB00E7D8D113FED11F5663172F898F632CF80' |
| 516 | ); |
| 517 | strictEqual(x509FromDer.subject, ecSubjectCheck); |
| 518 | strictEqual(x509FromDer.issuer, ecIssuerCheck); |
| 519 | deepStrictEqual(x509FromDer.raw, ecDer); |
| 520 | |
| 521 | // Host/email checks |
| 522 | strictEqual(x509.checkHost('agent1'), 'agent1'); |
| 523 | strictEqual(x509.checkHost('agent2'), undefined); |
| 524 | strictEqual(x509.checkEmail('test@example.com'), 'test@example.com'); |
| 525 | strictEqual(x509.checkEmail('other@example.com'), undefined); |
| 526 | |
| 527 | // CA certificate verification |
| 528 | const caCert = new X509Certificate(ecCa); |
| 529 | ok(caCert.ca); |
| 530 | ok(x509.checkIssued(caCert)); |
| 531 | ok(x509.verify(caCert.publicKey)); |
| 532 | ok(!x509.checkIssued(x509)); |
| 533 | |
| 534 | // Legacy object format |
| 535 | const legacyObject = x509.toLegacyObject(); |
| 536 | deepStrictEqual(legacyObject.raw, x509.raw); |
| 537 | deepStrictEqual(legacyObject.subject, { |
| 538 | C: 'US', |
| 539 | ST: 'CA', |
| 540 | L: 'SF', |
| 541 | O: 'Test', |
| 542 | OU: 'TestUnit', |
| 543 | CN: 'agent1', |
| 544 | emailAddress: 'test@example.com', |
| 545 | }); |
| 546 | deepStrictEqual(legacyObject.issuer, { |
| 547 | C: 'US', |
| 548 | ST: 'CA', |
| 549 | L: 'SF', |
| 550 | O: 'Test', |
| 551 | OU: 'TestUnit', |
| 552 | CN: 'TestCA', |
| 553 | }); |
| 554 | strictEqual(legacyObject.valid_from, 'Apr 8 05:15:36 2026 GMT'); |
| 555 | strictEqual(legacyObject.valid_to, 'Jan 22 05:15:36 2300 GMT'); |
| 556 | strictEqual( |
| 557 | legacyObject.serialNumber, |
| 558 | '733BB00E7D8D113FED11F5663172F898F632CF80' |
| 559 | ); |
| 560 | }, |
| 561 | }; |
| 562 | |
| 563 | // Ref: https://github.com/unjs/unenv/pull/310 |
| 564 | export const shouldImportCertificate = { |
| 565 | test() { |
| 566 | // This is allowed by Node.js as well. |
| 567 | assert(process.getBuiltinModule('crypto').Certificate); |
| 568 | }, |
| 569 | }; |