From fbca3fa710d8068e39eee574813c35f1d60fabe5 Mon Sep 17 00:00:00 2001 From: John Jiang Date: Tue, 11 Feb 2020 08:36:02 +0800 Subject: [PATCH] 8238677: java/net/httpclient/ssltest/CertificateTest.java should not specify TLS version Reviewed-by: dfuchs --- .../jdk/java/net/httpclient/ssltest/Cert.java | 231 ++++++++++++++++++ .../httpclient/ssltest/CertificateTest.java | 80 +++--- .../java/net/httpclient/ssltest/Server.java | 34 +-- .../java/net/httpclient/ssltest/bad.keystore | Bin 2329 -> 0 bytes .../java/net/httpclient/ssltest/gen-certs.sh | 53 ++++ .../java/net/httpclient/ssltest/good.keystore | Bin 2321 -> 0 bytes .../net/httpclient/ssltest/loopback.keystore | Bin 2343 -> 0 bytes 7 files changed, 337 insertions(+), 61 deletions(-) create mode 100644 test/jdk/java/net/httpclient/ssltest/Cert.java delete mode 100644 test/jdk/java/net/httpclient/ssltest/bad.keystore create mode 100644 test/jdk/java/net/httpclient/ssltest/gen-certs.sh delete mode 100644 test/jdk/java/net/httpclient/ssltest/good.keystore delete mode 100644 test/jdk/java/net/httpclient/ssltest/loopback.keystore diff --git a/test/jdk/java/net/httpclient/ssltest/Cert.java b/test/jdk/java/net/httpclient/ssltest/Cert.java new file mode 100644 index 00000000000..dc3d1c9a4fa --- /dev/null +++ b/test/jdk/java/net/httpclient/ssltest/Cert.java @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * The certificates used by this test. + * They are generated by script gen-certs.sh. + */ +public enum Cert { + + /* + * Version: 3 (0x2) + * Serial Number: + * 65:24:13:3c:7a:98:0c:16:a2:91:9c:8e:42:84:cf:be:be:d2:f1:42 + * Signature Algorithm: sha256WithRSAEncryption + * Issuer: CN = evil + * Validity + * Not Before: Feb 8 03:59:27 2020 GMT + * Not After : Feb 5 03:59:27 2030 GMT + * Subject: CN = evil + * X509v3 extensions: + * X509v3 Subject Key Identifier: + * 09:D0:E8:51:6C:0F:88:59:47:D1:FD:05:C2:00:10:D6:A4:80:04:07 + * X509v3 Authority Key Identifier: + * keyid:09:D0:E8:51:6C:0F:88:59:47:D1:FD:05:C2:00:10:D6:A4:80:04:07 + */ + BAD_CERT( + "RSA", + "-----BEGIN CERTIFICATE-----\n" + + "MIIC7jCCAdagAwIBAgIUZSQTPHqYDBaikZyOQoTPvr7S8UIwDQYJKoZIhvcNAQEL\n" + + "BQAwDzENMAsGA1UEAwwEZXZpbDAeFw0yMDAyMDgwMzU5MjdaFw0zMDAyMDUwMzU5\n" + + "MjdaMA8xDTALBgNVBAMMBGV2aWwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK\n" + + "AoIBAQCmirsTOW1G+LoI/Aj59lMk3KLywAbXASeTdnBoWkchuJ0QJWO/5b5kgf6Q\n" + + "VFfe9lXof9psGIKaCGq6KsI0uqj7+7y++//l+E6GB8UshVB8MXc1SLFe8AxPYhWC\n" + + "TXaKWyWGl7PXvugzbByFrf4IwE9+6phYkvl/zHvaMKqdwnkpXuyuBgT3BiYTSNsx\n" + + "k1Ma+s5rqiwsOODSzwhadwmU9T4z11KypYb/DixJgHvUET4gTB+i3ll+PllVdQtX\n" + + "zBLpEuj5HadK0PsqlOIok3eoSU+MpRqsz0gFEQ95y+Les3MlBeQ7fVKBz8GbrFDB\n" + + "Atzca+iknEh8fkLIUUuCjTjUtLvfAgMBAAGjQjBAMB0GA1UdDgQWBBQJ0OhRbA+I\n" + + "WUfR/QXCABDWpIAEBzAfBgNVHSMEGDAWgBQJ0OhRbA+IWUfR/QXCABDWpIAEBzAN\n" + + "BgkqhkiG9w0BAQsFAAOCAQEAQMfPfYfVSSdsiEUOlVg6M5D90HRONzqlg/v0RqQI\n" + + "fb3uufXJs20dg8iamVORXIIeUpGv1OQ2Rx4ndnV3bRLK6ep3gswIkOnD8z/CeNgl\n" + + "odZPvWyklHTMenGqU2TR3ceFep/DvQkrP4aZWyr3e2fjatKR/s4pXgBwHs/hR76O\n" + + "vDYLRDyCG/+MtUClFsc9HLedbU4Wp8JyaafFZ63/VjaIcvdHoDGNILRu5AIN/JVM\n" + + "Sgz4blkWJxS1dlqBYwxvbpJWrHUcktsa3Bzw2zWOkTVGQJi3pMvzRBkgliNaXPi3\n" + + "qcPViqgzVoB4QdOQBnvDtQ9+8Nt/dQY1VJFSBLxZQIefiQ==\n" + + "-----END CERTIFICATE-----", + "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCmirsTOW1G+LoI\n" + + "/Aj59lMk3KLywAbXASeTdnBoWkchuJ0QJWO/5b5kgf6QVFfe9lXof9psGIKaCGq6\n" + + "KsI0uqj7+7y++//l+E6GB8UshVB8MXc1SLFe8AxPYhWCTXaKWyWGl7PXvugzbByF\n" + + "rf4IwE9+6phYkvl/zHvaMKqdwnkpXuyuBgT3BiYTSNsxk1Ma+s5rqiwsOODSzwha\n" + + "dwmU9T4z11KypYb/DixJgHvUET4gTB+i3ll+PllVdQtXzBLpEuj5HadK0PsqlOIo\n" + + "k3eoSU+MpRqsz0gFEQ95y+Les3MlBeQ7fVKBz8GbrFDBAtzca+iknEh8fkLIUUuC\n" + + "jTjUtLvfAgMBAAECggEATyu2QS5Un5+QOMMvtTx/TA/DOulElyNKYBS23TTFiedM\n" + + "ayeLIuehuf/+NziRSUILlupouGhyda04p2W6SvzNZnTGxnffr8B5+8dn2YFKwK93\n" + + "PxJel4ZAI+C53ubaSm2IClLFwPNVSVTEvlv3XsulPu1hHQJJr5JS8meeRD72AE8G\n" + + "brKbLlq6OGey6u9teao0m4Wo05MzaEoOx4fztPP4BiJJobuPYrdthUwfXJ2mQYeg\n" + + "fJKl+JeLUnAXmq8e+6Zs88NzGK8Gmd2TvGnUahxSDtXHuRkB2lOrGFrEJKkAXDBx\n" + + "2q8r3vvcay6+k95fS2HOvggFDALS37BGckWg4+HYuQKBgQDXkxw0u2G7rCYbF691\n" + + "jil++DLNUzejZonAvA/cceVHShfAMlWCBf58cLNsY33rkRsFazhrdDoDAFqKxejB\n" + + "xWM8U7UHiHZSznuXTL0YbUombfz+0lp/KwXcirnB7O3AdIW4lfMo/ozeMMIuEzsL\n" + + "G/MDvbNSdawEso/qtxFvz87ctQKBgQDFxcCSyWb/SQVr3RkZkO3BW2efuANxNeUh\n" + + "35L4inWTa8ml8UL4SrTrkfUHzu5TnBGbSb2n8CdkPnInA81dKagX6LXuONeLkX/e\n" + + "RXyWIwWRiBkpYSaw2OGApl49DRvk2kCzwoVRWwh8qfhpC0P6AClFRaVAovYcTxm3\n" + + "vhCJL3jmwwKBgGMLvTbhLStMEgn6nOwXECu9H6JE7NhPgVUjUupHDj/t4/GzbqQZ\n" + + "2u4T3ewb3jwAZHjd5YNBWHIOlIsUGTgGV+zczN0ULsEnC5Pddzgk5p+3gzkVLu0k\n" + + "uEG3H1fhYu882j+P7bPVGKXxoxYGUedtxP7gBucJF6rk28jMqd9EjFfNAoGBAKcc\n" + + "ASwGodDzknEh0SOZIkxPP6/lfIMcVw/YKgd4dwCqAykEQuIpvdWO7sw6PYbISNg9\n" + + "5tMQSTiayznMLKqbmD0blR5FSVvVBYZ6kFsMHJhrt1cPj/G+UEy0RsyvVvJ4uFMr\n" + + "+hpUIUe1FwErU7TajgTKZGfJSsuAyupG3xIL2syhAoGALv+ulZAY/gUKH8NigsXo\n" + + "pFPTpiXMyTD/O4RUH/5LcxDELVZ8cnV2q3qEX+ep24y0AtNiBx4oHpZ/vIxtwBCR\n" + + "JKU2xmIGC6NyQMRSzfmNgi0X450rgKbTAxn/LAU8syXmNpBUrFZ8+02pQvWzxqfU\n" + + "zGaMEK3+f1sq8Byzau/qhKU="), + + /* + * Version: 3 (0x2) + * Serial Number: + * 70:41:2f:71:43:d1:67:b5:29:c6:3e:ce:62:ba:d5:aa:4a:f1:f7:f0 + * Signature Algorithm: sha256WithRSAEncryption + * Issuer: CN = localhost + * Validity + * Not Before: Feb 8 03:59:18 2020 GMT + * Not After : Feb 5 03:59:18 2030 GMT + * Subject: CN = localhost + * X509v3 extensions: + * X509v3 Subject Key Identifier: + * 12:65:C7:4B:D8:77:D8:55:6E:2D:AF:C4:F8:09:FE:08:F4:22:EA:D5 + * X509v3 Authority Key Identifier: + * keyid:12:65:C7:4B:D8:77:D8:55:6E:2D:AF:C4:F8:09:FE:08:F4:22:EA:D5 + */ + GOOD_CERT( + "RSA", + "-----BEGIN CERTIFICATE-----\n" + + "MIIC+DCCAeCgAwIBAgIUcEEvcUPRZ7Upxj7OYrrVqkrx9/AwDQYJKoZIhvcNAQEL\n" + + "BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTIwMDIwODAzNTkxOFoXDTMwMDIw\n" + + "NTAzNTkxOFowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF\n" + + "AAOCAQ8AMIIBCgKCAQEAtSOmfkF0zjPeZ4DDsJZO3OaDq+XHtPLB+xvri1iuL9b+\n" + + "dZDXOqPZ5+koWM9NzDR6Um+IN46oTU+8eJw+hYcZaE9tzS9kH+6qOBk/827yEyVa\n" + + "jh9Wqw164xj16QPyQJuHEeeDJ7elNfaOQXRu2UqZB9suKbolqsHe42hbg0/tbln7\n" + + "C8C6qEJOpnEaapFHi3/3AeoQQ57zywqrzopeiiuUDWmBhXY30ve33RrJl/OIM1sB\n" + + "QSoVCPcaF0mXaDwUTYIksxelon1K9PJa76p9ybGnsxkYfCAGZ8O+fTjJfQONU+Gu\n" + + "zOmcyXL5D5O/nI8lxN8hbZwVIAYXLYRUonECIOJ/iQIDAQABo0IwQDAdBgNVHQ4E\n" + + "FgQUEmXHS9h32FVuLa/E+An+CPQi6tUwHwYDVR0jBBgwFoAUEmXHS9h32FVuLa/E\n" + + "+An+CPQi6tUwDQYJKoZIhvcNAQELBQADggEBAFatzXsT9YZ0TF66G6apSbbs6mH9\n" + + "PMVE9IuE4yv2zyKofSMmDHFdmfNdkMHWkIxcZKuiL00IPFL76LAb9DWNQVy4otq6\n" + + "3+n0CCi808gDNUMYMQLlXVooZsByXuMuokyg29F5mWEH4rswU6ru33lAB7CT7BuN\n" + + "z5/eUhxTcXcJV6pLgcEM68NIc755PULevmqmd8SrVgcFjkxAFOsYd9L86wYLdiPO\n" + + "uXfN/EjLMGHG2gpEqHEzQpEEAA/IsCJ1HQ8vvGkeggUIXPrwlIMbQcz/8WBSDel5\n" + + "hvVRmADJCLf/0IwxKsSOMWZ4OMmcXMjxnae3lWPQomlzWHMZlFraG2rE/Vo=\n" + + "-----END CERTIFICATE-----", + "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC1I6Z+QXTOM95n\n" + + "gMOwlk7c5oOr5ce08sH7G+uLWK4v1v51kNc6o9nn6ShYz03MNHpSb4g3jqhNT7x4\n" + + "nD6FhxloT23NL2Qf7qo4GT/zbvITJVqOH1arDXrjGPXpA/JAm4cR54Mnt6U19o5B\n" + + "dG7ZSpkH2y4puiWqwd7jaFuDT+1uWfsLwLqoQk6mcRpqkUeLf/cB6hBDnvPLCqvO\n" + + "il6KK5QNaYGFdjfS97fdGsmX84gzWwFBKhUI9xoXSZdoPBRNgiSzF6WifUr08lrv\n" + + "qn3JsaezGRh8IAZnw759OMl9A41T4a7M6ZzJcvkPk7+cjyXE3yFtnBUgBhcthFSi\n" + + "cQIg4n+JAgMBAAECggEAD2O4AYIOKna9ro2CEr6ydJIhHbmn/feiA3Obz3r5UZcy\n" + + "h0qG/rRtDwcAJot2UKMkwVw4dn/oTKk5mgWsSivwPKyC56vfFddxHtMGW+hRKM9D\n" + + "ok+HTYEXr7OvMNzk+Bg+oYbJ3dX8c1k/PNBnmo578e7tPR5TlO5jwW5cWAuyYG2f\n" + + "+YUCqMNe02yZvvlvK1kOSSgqlNH0S14/hVZTYkyxXMCCrkxPFXh5j8w6ZUzVipXg\n" + + "99EYcRdq7dA3XVBSgQQ4m5772FIIzlBn8LdIIfw3VQrtZ9HapowLk6QdcHSHBKMK\n" + + "0rqb1PlG2ynD2n8hKn4MssJ+tkzvbGrQcLjL/+XHAQKBgQDmiOIke90T8puq3FkN\n" + + "NlgdBA9Zem5U2t3TvtQa36cuO/paYrLaVK5U0ucNZ9d4i6avdyp8TyKJrUHDcazi\n" + + "QkDpjxb0KBhisutDZ4o1JFW4ZtB3zwIGIYWBBIE1kRIc0ucYoAurSdOmAsKq6XJQ\n" + + "B0CQYBJPrTHq5niCl0tKPtrISwKBgQDJJfNcKSz46zdnqsNZAmL+q+cMQf4txiCS\n" + + "v0JefOeKKlwNcYWxRgf1yTNENamKKh8wyqOhc/OkxXjglRo9BFMt6BFFARzDddWE\n" + + "Wo18cyLc2WvTTv2FCZ0J/eF1jPTGJsTpCU6Prbt4XPjZpzSTF2cQR7CxLp15FsJm\n" + + "2LMcQ8ma+wKBgQC72So8hFme2X+S+D3wECo4aoh/Zs3kgvtigQqgY0H84I6de/M1\n" + + "CO+M2tW/DLB8336RV87cwDbqbK07rrMrIsV2C0yu4sUMF7Kwl/v8VYEr40tXdOy3\n" + + "RjVc7ejDV1Sk/A2m+TLI/j1h9rndPqARKfeoLUB+gCg+ulHUR6fn9dOchQKBgByx\n" + + "uj6qbQzxWQ0D0iwvZ/nWgfZAr8bN3bWxbQFXphwSoOEWEbFRQS9xzUtssEvSaHKo\n" + + "ZaFRji8yMGUxP/X2WPtSgKwsVXMYqyXfWRGoxw9kQLp7KTVCQtG7Et+XBRADVdG8\n" + + "jyV17ilkcedyr9BP5VbwMyeDc9ljQsYzIZHlpavjAoGAct8Wktj0hegCoTxSukU1\n" + + "SkJ7t4376sSfxVbbUrH86Y1Z55le1O+VkGtqETmk+Q8qf5Ymnal3W9zZ0O9mOE04\n" + + "otFbiB3ifUbpBAipyxS06SIFwMctmSk2EqBcXa3nZ9eUGqx0JhoQahfyDkFzfwJY\n" + + "hiBTWnlMjCiJ40yRYAWDzZg="), + + /* + * Version: 3 (0x2) + * Serial Number: + * 3f:62:91:39:7e:02:e9:77:20:61:ce:7e:a2:3c:c0:6c:3f:2e:08:49 + * Signature Algorithm: sha256WithRSAEncryption + * Issuer: CN = UNKOWN + * Validity + * Not Before: Feb 8 04:00:04 2020 GMT + * Not After : Feb 5 04:00:04 2030 GMT + * Subject: CN = unknown + * X509v3 extensions: + * X509v3 Subject Key Identifier: + * F7:D7:AE:80:DF:EC:7A:60:5A:E8:62:60:70:03:B6:BD:23:05:19:62 + * X509v3 Authority Key Identifier: + * keyid:F7:D7:AE:80:DF:EC:7A:60:5A:E8:62:60:70:03:B6:BD:23:05:19:62 + * X509v3 Subject Alternative Name: + * IP Address:127.0.0.1 + */ + LOOPBACK_CERT( + "RSA", + "-----BEGIN CERTIFICATE-----\n" + + "MIIDBTCCAe2gAwIBAgIUP2KROX4C6XcgYc5+ojzAbD8uCEkwDQYJKoZIhvcNAQEL\n" + + "BQAwEjEQMA4GA1UEAwwHdW5rbm93bjAeFw0yMDAyMDgwNDAwMDRaFw0zMDAyMDUw\n" + + "NDAwMDRaMBIxEDAOBgNVBAMMB3Vua25vd24wggEiMA0GCSqGSIb3DQEBAQUAA4IB\n" + + "DwAwggEKAoIBAQC8dBwc+nhzuGOcqmeQkcms6JrUPDPcvq6gEEH3dxorzngfxrsl\n" + + "lfM6SPJBV4A7HVEcsGhcMoPzzpFVISi3XyLkGuw2WnEW6nKcB2QgaS0Ub8PoDZ7P\n" + + "erWGOIjHF1slKxX40tZBiEp1oJANDq7CzSGWiyTorCjbX6OiWZCbhQkw+SpXrAdD\n" + + "fzjEAr3y8cgsC7qqTxoz/T9C1+UMmzc88kpAqih7jj2L/i6387dBmV+zrMsNyO0Q\n" + + "UPGACzMiSZV3tiwYA6cvDY3WS3fCwLSYUWdHi1orerHQuGOHLK4eyPVDcvuQdUJ/\n" + + "T0+jbNZa51scqrBUT/aDlCMCxFUY3vquz2xfAgMBAAGjUzBRMB0GA1UdDgQWBBT3\n" + + "166A3+x6YFroYmBwA7a9IwUZYjAfBgNVHSMEGDAWgBT3166A3+x6YFroYmBwA7a9\n" + + "IwUZYjAPBgNVHREECDAGhwR/AAABMA0GCSqGSIb3DQEBCwUAA4IBAQBcfcv2J73T\n" + + "nHFsCPU3WM6UW2uE8BIM/s/VbjkV1nalFyHi/TU6CN01sDymTABhzIlx5N6PW0HP\n" + + "Z0q1C7l1nsoQHwmJO+avOHu3ZjDrLMpU6wTQLEemTd3R5HTyA3/I/FUVFHeuLwJg\n" + + "L7OLNc8ouT1hkiIZD+xKwfCEdT3o+ldB+9L4WYRJPt2W3bf3W/yM8JmwW8uf6+U3\n" + + "V46xiE5GoOKoIkeAkBAaIbepsZH9rPb7alBSgYgwQYDft9wuGMeNcvPvgVsXjA7I\n" + + "RafJVdxVinVMEaOjckIZ5WlrR5667aIJapZH1r7/tiSQCRaJcILx7pL4x8C+x34z\n" + + "dPHbbyP/Rdq9\n" + + "-----END CERTIFICATE-----", + "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC8dBwc+nhzuGOc\n" + + "qmeQkcms6JrUPDPcvq6gEEH3dxorzngfxrsllfM6SPJBV4A7HVEcsGhcMoPzzpFV\n" + + "ISi3XyLkGuw2WnEW6nKcB2QgaS0Ub8PoDZ7PerWGOIjHF1slKxX40tZBiEp1oJAN\n" + + "Dq7CzSGWiyTorCjbX6OiWZCbhQkw+SpXrAdDfzjEAr3y8cgsC7qqTxoz/T9C1+UM\n" + + "mzc88kpAqih7jj2L/i6387dBmV+zrMsNyO0QUPGACzMiSZV3tiwYA6cvDY3WS3fC\n" + + "wLSYUWdHi1orerHQuGOHLK4eyPVDcvuQdUJ/T0+jbNZa51scqrBUT/aDlCMCxFUY\n" + + "3vquz2xfAgMBAAECggEAEcYNpLoGxDs+wdbcf6kQUUt61st6xLYDODtwTUuhX0JQ\n" + + "2AZhPjE/SF764ijDgk/Ih6EnppJpGYSA9ntzIKBLZSIY5yNuiQ/BkW+tBNWGl+fW\n" + + "nTszoDPdjPQmCkjsorvGjbos1O9qvl9PVrvsxZidM1qaN4uNKuuBPl2eItzQOhsM\n" + + "YFbmw1nqSX31gukv9a6yM2VgDUiGMlEGwkOphutbqt+wTO+9hEopGZHB7mNc5NO9\n" + + "foWVVI1rzS2yR2d85lsG4YBqBMDp2s2cBofIAe/SSSpBYPR4RfEBDpSaVceR4+cL\n" + + "Lq52DhLVe/zgVj7LEGdyTZTQxw414sRBIz8KXcRIkQKBgQDon26R0/vToZcxgnpr\n" + + "ososGh+iTov883DCxX30sntl/GbyUhg50q7Tq5mXGyzodoFHGTL+bOcU3g5O4MOV\n" + + "6HlTFe1dUjyC7M0ah6NaCSsl4SPTxtWjeHMBMhNisInDAO+ju4MJAhgoHuYL6p39\n" + + "NDmKSDtpaegFz1Q64C1Ea9fsFwKBgQDPZFvQNjSCm06ObsfXLZKS6IEqgGbihMfM\n" + + "cv/HjIpAKXNp/Y6Y/YmdFBpdHDkOJ9BXwJqTuMuM69BuldvNXkkY7zrhPFPawWyF\n" + + "O/N1aMNCT89AreBwXMYmgG9yLm1EF1FOuz2oAnWWpcUHBups+cZQikYSQxcOSqrL\n" + + "bNTEWffG+QKBgDTk+8lhAGQQ3EY/uwJ6k6oPjp3jamVsHXnMWmWnp/N6vxXeoO+U\n" + + "/nfXDyeS4FVDjQXTrwq3TJwsGejJpu+RWvUPiVes+WFz4vdjXDt+1jbYyMLA9Zck\n" + + "LlJZRpssNUcIEXWTj6oetct5qymOgbovg93zqr6/fCjGCgsRKnniY8ilAoGAcWGH\n" + + "hGQt/v1TTDEqVexXRrOP8iFyngJDjPWN+pVN+9ftfhOeAuwRcOvNofvNAX0ovODS\n" + + "YVJVDfzZ3atWGIekZNpdEUg++8hlQM3OwvB8V2N0hgLJQgSmW+Q5iW3yVJh+3hEl\n" + + "mxWFHdAQ0E+ql9tR3TRLLK67CxgtGbus8o/RE1kCgYAuf9o6Q++l8H0vNZTnzBNu\n" + + "bt0QnLxyh7RuViYuCkzLK+jGftgadVfsRgnOKvxQkMzcXfBgpV5JcVKXtaxDhPxM\n" + + "xHwblgOEGlrD4tAwvtPw3GLhmD4Shy8zcT0Lwto81fquskA5yyDGJxbq9CMzWk3w\n" + + "dSOT2C7lwW+hkycUio/fTQ=="); + + public final String keyAlgo; + public final String certStr; + public final String keyStr; + + private Cert(String keyAlgo, String certStr, String keyStr) { + this.keyAlgo = keyAlgo; + this.certStr = certStr; + this.keyStr = keyStr; + } +} diff --git a/test/jdk/java/net/httpclient/ssltest/CertificateTest.java b/test/jdk/java/net/httpclient/ssltest/CertificateTest.java index a269dd071a5..96ba5c05ed3 100644 --- a/test/jdk/java/net/httpclient/ssltest/CertificateTest.java +++ b/test/jdk/java/net/httpclient/ssltest/CertificateTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,36 +21,41 @@ * questions. */ -import java.io.File; +import static java.net.http.HttpClient.Builder.NO_PROXY; + import java.io.IOException; import java.net.URI; import java.net.http.HttpClient; -import java.net.http.HttpResponse.BodyHandlers; import java.net.http.HttpRequest; import java.net.http.HttpResponse; +import java.net.http.HttpResponse.BodyHandlers; + import javax.net.ssl.SSLContext; import javax.net.ssl.SSLException; -import javax.net.ssl.SSLParameters; -import static java.net.http.HttpClient.Builder.NO_PROXY; + +import jdk.test.lib.security.KeyEntry; +import jdk.test.lib.security.KeyStoreUtils; +import jdk.test.lib.security.SSLContextBuilder; /* * @test + * @library /test/lib * @build Server CertificateTest - * @run main/othervm CertificateTest good.keystore expectSuccess - * @run main/othervm CertificateTest bad.keystore expectFailure + * @run main/othervm CertificateTest GOOD_CERT expectSuccess + * @run main/othervm CertificateTest BAD_CERT expectFailure * @run main/othervm * -Djdk.internal.httpclient.disableHostnameVerification - * CertificateTest bad.keystore expectSuccess + * CertificateTest BAD_CERT expectSuccess * @run main/othervm * -Djdk.internal.httpclient.disableHostnameVerification=true - * CertificateTest bad.keystore expectSuccess + * CertificateTest BAD_CERT expectSuccess * @run main/othervm * -Djdk.internal.httpclient.disableHostnameVerification=false - * CertificateTest bad.keystore expectFailure + * CertificateTest BAD_CERT expectFailure * @run main/othervm * -Djdk.internal.httpclient.disableHostnameVerification=xxyyzz - * CertificateTest bad.keystore expectFailure - * @run main/othervm CertificateTest loopback.keystore expectSuccess + * CertificateTest BAD_CERT expectFailure + * @run main/othervm CertificateTest LOOPBACK_CERT expectSuccess */ /** @@ -59,25 +64,24 @@ import static java.net.http.HttpClient.Builder.NO_PROXY; * by the server for its own identity. Two servers on two different ports are used * on the remote end. * - * For the "good" run the cert contains the correct hostname of the target server + * The GOOD_CERT cert contains the correct hostname of the target server * and therefore should be accepted by the cert checking code in the client. - * For the "bad" run, the cert contains an invalid hostname, and should be rejected. + * The BAD_CERT cert contains an invalid hostname, and should be rejected. + * The LOOPBACK_CERT cert contains an invalid hostname, but it also contains a + * subject alternative name for IP address 127.0.0.1, so it should be accepted + * for this address. */ public class CertificateTest { - static SSLContext ctx; - static SSLParameters params; + + private static Cert cert; static boolean expectSuccess; - static String trustStoreProp; static Server server; static int port; - static String TESTSRC = System.getProperty("test.src"); public static void main(String[] args) throws Exception { try { - String keystore = args[0]; - trustStoreProp = TESTSRC + File.separatorChar + keystore; - + String certName = args[0]; String passOrFail = args[1]; if (passOrFail.equals("expectSuccess")) { @@ -85,38 +89,44 @@ public class CertificateTest { } else { expectSuccess = false; } - server = new Server(trustStoreProp); + + cert = Cert.valueOf(certName); + server = new Server(getSSLContext(cert)); port = server.getPort(); - System.setProperty("javax.net.ssl.trustStore", trustStoreProp); - System.setProperty("javax.net.ssl.trustStorePassword", "passphrase"); - init(); - test(args); + test(cert); } finally { - server.stop(); + if (server != null) { + server.stop(); + } } } - static void init() throws Exception - { - ctx = SSLContext.getDefault(); - params = ctx.getDefaultSSLParameters(); - //params.setProtocols(new String[] { "TLSv1.2" }); + private static SSLContext getSSLContext(Cert cert) throws Exception { + SSLContextBuilder builder = SSLContextBuilder.builder(); + builder.trustStore( + KeyStoreUtils.createTrustStore(new String[] { cert.certStr })); + builder.keyStore(KeyStoreUtils.createKeyStore( + new KeyEntry[] { new KeyEntry(cert.keyAlgo, + cert.keyStr, new String[] { cert.certStr }) })); + return builder.build(); } - static void test(String[] args) throws Exception + static void test(Cert cert) throws Exception { String uri_s; - if (args[0].equals("loopback.keystore")) + if (cert == Cert.LOOPBACK_CERT) uri_s = "https://127.0.0.1:" + Integer.toString(port) + "/foo"; else uri_s = "https://localhost:" + Integer.toString(port) + "/foo"; String error = null; Exception exception = null; System.out.println("Making request to " + uri_s); + + SSLContext ctx = getSSLContext(cert); HttpClient client = HttpClient.newBuilder() .proxy(NO_PROXY) .sslContext(ctx) - .sslParameters(params) + .sslParameters(ctx.getDefaultSSLParameters()) .build(); HttpRequest request = HttpRequest.newBuilder(new URI(uri_s)) diff --git a/test/jdk/java/net/httpclient/ssltest/Server.java b/test/jdk/java/net/httpclient/ssltest/Server.java index 6d841be9015..fc954016991 100644 --- a/test/jdk/java/net/httpclient/ssltest/Server.java +++ b/test/jdk/java/net/httpclient/ssltest/Server.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,12 +22,10 @@ */ import com.sun.net.httpserver.*; + import java.io.*; import java.net.InetAddress; import java.net.InetSocketAddress; -import java.net.URI; -import java.security.*; -import java.util.*; import java.util.logging.*; import java.util.concurrent.Executors; import java.util.concurrent.ExecutorService; @@ -44,17 +42,17 @@ public class Server { // assuming the TLS handshake succeeds, the server returns a 200 OK // response with a short text string. - public Server(String certfile) throws Exception { + public Server(SSLContext ctx) throws Exception { initLogger(); - SSLContext ctx = getContext("TLSv1.2", certfile); Configurator cfg = new Configurator(ctx); - InetSocketAddress addr = new InetSocketAddress(InetAddress.getLoopbackAddress(),0); + InetSocketAddress addr = new InetSocketAddress( + InetAddress.getLoopbackAddress(), 0); server = HttpsServer.create(addr, 10); server.setHttpsConfigurator(cfg); server.createContext("/", new MyHandler()); - server.setExecutor((exec=Executors.newCachedThreadPool())); + server.setExecutor((exec = Executors.newCachedThreadPool())); port = server.getAddress().getPort(); - System.out.println ("Listening on port " + port); + System.out.println("Listening on port " + port); server.start(); } @@ -67,22 +65,6 @@ public class Server { exec.shutdownNow(); } - SSLContext getContext(String protocol, String certfile) throws Exception { - char[] passphrase = "passphrase".toCharArray(); - KeyStore ks = KeyStore.getInstance("JKS"); - ks.load(new FileInputStream(certfile), passphrase); - - KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); - kmf.init(ks, passphrase); - - TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); - tmf.init(ks); - - SSLContext ssl = SSLContext.getInstance(protocol); - ssl.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); - return ssl; - } - Logger logger; void initLogger() { @@ -120,7 +102,7 @@ public class Server { SSLParameters p = getSSLContext().getDefaultSSLParameters(); for (String cipher : p.getCipherSuites()) System.out.println("Cipher: " + cipher); - System.err.println("PArams = " + p); + System.err.println("Params = " + p); params.setSSLParameters(p); } } diff --git a/test/jdk/java/net/httpclient/ssltest/bad.keystore b/test/jdk/java/net/httpclient/ssltest/bad.keystore deleted file mode 100644 index acbb7517ec93dcd3239a032b23cc1497e9295917..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2329 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 openssl.conf +echo "distinguished_name = dn" >> openssl.conf +echo "x509_extensions = v3_ext" >> openssl.conf +echo "[dn]" >> openssl.conf +echo "[v3_ext]" >> openssl.conf +echo "subjectKeyIdentifier = hash" >> openssl.conf +echo "authorityKeyIdentifier = keyid" >> openssl.conf +echo "basicConstraints = critical,CA:FALSE" >> openssl.conf + +# Generate X.509 version 3 extension file +echo "subjectKeyIdentifier = hash" > v3.ext +echo "authorityKeyIdentifier = keyid,issuer" >> v3.ext + +# Generate good cert +openssl genpkey -algorithm rsa -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 -out good.key +openssl req -config openssl.conf -new -key good.key -subj "/CN=localhost" -sha256 -out good.csr +openssl x509 -extfile v3.ext -req -CAcreateserial -days 3650 -in good.csr -sha256 -signkey good.key -out good.cer + +# Generate bad cert +openssl genpkey -algorithm rsa -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 -out bad.key +openssl req -config openssl.conf -new -key bad.key -subj "/CN=evil" -sha256 -out bad.csr +openssl x509 -extfile v3.ext -req -CAcreateserial -days 3650 -in bad.csr -sha256 -signkey bad.key -out bad.cer + +# Generate loopback cert with subject alternative name +echo "subjectAltName = @alt_names" >> v3.ext +echo "[alt_names]" >> v3.ext +echo "IP.1 = 127.0.0.1" >> v3.ext + +openssl genpkey -algorithm rsa -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 -out loopback.key +openssl req -config openssl.conf -new -key loopback.key -subj "/CN=unknown" -sha256 -out loopback.csr +openssl x509 -extfile v3.ext -req -CAcreateserial -days 3650 -in loopback.csr -sha256 -signkey loopback.key -out loopback.cer diff --git a/test/jdk/java/net/httpclient/ssltest/good.keystore b/test/jdk/java/net/httpclient/ssltest/good.keystore deleted file mode 100644 index 02988c752af315257b78ee4d6adacfc796f26eef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2321 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 E1^`7*)&Kwi