From 6a677d466aaa23eb2fb4f3e983a0c0dc3ec281cf Mon Sep 17 00:00:00 2001 From: Fernando Guallini Date: Fri, 9 May 2025 11:10:25 +0200 Subject: [PATCH 1/7] updated tests --- test/jdk/java/security/PEM/PEMData.java | 152 +++++++++++++++--- .../jdk/java/security/PEM/PEMDecoderTest.java | 54 ++++++- .../jdk/java/security/PEM/PEMEncoderTest.java | 103 +++++++++++- .../jdk/test/lib/security/SecurityUtils.java | 4 + 4 files changed, 278 insertions(+), 35 deletions(-) diff --git a/test/jdk/java/security/PEM/PEMData.java b/test/jdk/java/security/PEM/PEMData.java index 4c2bd5c8f981e..5f123cc0e27fa 100644 --- a/test/jdk/java/security/PEM/PEMData.java +++ b/test/jdk/java/security/PEM/PEMData.java @@ -27,6 +27,7 @@ import java.security.DEREncodable; import java.security.KeyPair; import java.security.PEMRecord; +import java.security.cert.X509CRL; import java.security.cert.X509Certificate; import java.security.interfaces.*; import java.util.ArrayList; @@ -45,7 +46,7 @@ class PEMData { KwDdi3cNwu7YYD/QtJ+9+AEBdoqhRANCAASL+REY4vvAI9M3gonaml5K3lRgHq5w +OO4oO0VNduC44gUN1nrk7/wdNSpL+xXNEX52Dsff+2RD/fop224ANvB -----END PRIVATE KEY----- - """, KeyPair.class); + """, KeyPair.class, "SunEC"); public static final Entry rsapriv = new Entry("rsapriv", """ @@ -65,7 +66,39 @@ class PEMData { 6onPAs4hkm+63dfzCojvEkALevO8J3OVX7YS5q9J1r75wDn60Ob0Zh+iiorpx8Ob WqcWcoJqfdLEyBT+ -----END PRIVATE KEY----- - """, RSAPrivateKey.class); + """, RSAPrivateKey.class, "SunRsaSign"); + + public static final Entry rsapsspriv = new Entry("rsapsspriv", + """ + -----BEGIN PRIVATE KEY----- + MIIEugIBADALBgkqhkiG9w0BAQoEggSmMIIEogIBAAKCAQEAn3qFQtvj9JVqVPRh + mMMRyT17GiUY+NWOwUHx5bHqfhlHJoCllctSU/YXzrH4Da1w7sSeaMmAKYMW4X5k + rn9hnKOhgHnm2nkZBaVNQeyrseuDnfwWtLXjnj8rEKpgf9UPRUeXGRSoAb1qpwBf + epFtLSKZrzswZY2u2UEUGJERABi6Qp+cIZ8uXzBkIsMgrhb50xsdysZ9+qq95z0i + N1vh/V+Yi2fYpSYVDE8aMWdpvs0oWGvoLQjRgElJx/SknndAfLD42HPYZyyXevyJ + RgUf+NP0V7c+UtE7m7pgMs1hhxHSmUNdfH9GnOSg9m3+L3WqhaNNWB4aKMqFyhlM + EsAuawIDAQABAoIBAAMJ9yXIgeYEaN54j67fsg7nJIQ228HLc/5xGWvFQaYofidu + K87twm0GKKWlf4gR24U5QEQtPst2YNu9fav+PRJv4yEgcYqNOdxWg4veN4Fa7wr2 + JZ/zmARJHjLMRFgl67YSwKmCMBdkZXa24PA5et6cJQM7+gFIELe5b+lt7j3VsxQ7 + JpTJyp3I73lXcJrzcb/OCTxobFPLtkVSgKUNwae26xlNqXX4fQfLp99LHGnkmG3k + Wlzjs6dUi4fl4yLAJYMxEwxQbSbmY66ZKnM4SkT/YHx67gyJw2CMRp4FQDg94Sor + 0IDDKiSMGzcjuCuUl27/qTuv+iMgCqNB7CSPXtECgYEAvqN8ZuZXeUKz4tn6wxJk + k1utCl3pSM5PLMF4J43uvX49HF1i3svXrwxzJqug6kPXvB7cuB6U2DFIM3lh2jNE + u2w0U/5zVz2yEI9EaRjnOXePLsSWjOiC+5MGTafJWy5vZ8+zaWL9tjtUH5hsg1cB + ZMlXtWrI+AmAUAv6FFDZaHECgYEA1igXzRMGgXAT+YX0wdZQcRMy9jD5WIYIiCex + 6/WRE2LDM855ZBwTU5gyqZC2MWC3yn1ASDraxKdH1m3872Q0NVJfsOaLvBk1HuPk + dlSHRKO2GeO9m5/YzrZm9jpGs0IH6DKOah/t0aCd2CFxt6qef2wOUmXTCK6tyCXN + EiUmEpsCgYAMue85E1Ftl+VYVILn+Ndb+ve/RGupX5RrgXLa+R+h6MZ9mUJbazI3 + zlX1k+mHGgZR2aGUbP40vH18ajL9FQUWme+YV9ktTsIPVvETLwVokbGuRpNiTrdH + whXeoz/O5Xesb3Ijq+cR/j3sagl8bxd5ufMv+jP2UvQM4+/K4WbSEQKBgGuZeVvw + UzR1u5ODWpaJt6EYpGJN+PohXegLCbokh9/Vn35IH3XNJWi677mCnAfzMGTsyX+B + Eqn74nw6hvtAvXqNCMc5DrxTbf03Q3KwxcYW+0fGxV2L0sMJonHUlfE7G/3uaN+p + azQIH0aYhypg74HWKNv9jSqvmWEWnRKg16BBAoGAGLAqCRCP8wxqRVZZjhUJ+5JN + b6PrykDxCKhlA6JqZKuCYzvXhLABq/7miNDg0CmRREl+yKXlkEoFl4g9LtP7wfjX + n4us/WNmh+GPZYxlCJSNRTjgk7pm5TjVH5YWURDEnjIHZ7yxbAFlvNUhI1mF5g97 + KVcB4fjBitP1h8P+MoY= + -----END PRIVATE KEY----- + """, RSAPrivateKey.class, "SunRsaSign"); public static final Entry rsaprivbc = new Entry("rsaprivbc", """ @@ -85,14 +118,14 @@ class PEMData { 6onPAs4hkm+63dfzCojvEkALevO8J3OVX7YS5q9J1r75wDn60Ob0Zh+iiorpx8Ob WqcWcoJqfdLEyBT+ -----END PRIVATE KEY----- - """, RSAPrivateKey.class); + """, RSAPrivateKey.class, "SunRsaSign"); public static final Entry ec25519priv = new Entry("ed25519priv", """ -----BEGIN PRIVATE KEY----- MC4CAQAwBQYDK2VwBCIEIFFZsmD+OKk67Cigc84/2fWtlKsvXWLSoMJ0MHh4jI4I -----END PRIVATE KEY----- - """, EdECPrivateKey.class); + """, EdECPrivateKey.class, "SunEC"); public static final Entry rsapub = new Entry("rsapub", """ @@ -102,7 +135,20 @@ class PEMData { MtJpIPIXynEqRy2mIw2GrKTtu3dqrW+ndarbD6D4yRY1hWHluiuOtzhxuueCuf9h XCYEHZS1cqd8wokFPwIDAQAB -----END PUBLIC KEY----- - """, RSAPublicKey.class); + """, RSAPublicKey.class, "SunRsaSign"); + + public static final Entry rsapsspub = new Entry("rsapsspub", + """ + -----BEGIN PUBLIC KEY----- + MIIBIDALBgkqhkiG9w0BAQoDggEPADCCAQoCggEBAJ96hULb4/SValT0YZjDEck9 + exolGPjVjsFB8eWx6n4ZRyaApZXLUlP2F86x+A2tcO7EnmjJgCmDFuF+ZK5/YZyj + oYB55tp5GQWlTUHsq7Hrg538FrS1454/KxCqYH/VD0VHlxkUqAG9aqcAX3qRbS0i + ma87MGWNrtlBFBiREQAYukKfnCGfLl8wZCLDIK4W+dMbHcrGffqqvec9Ijdb4f1f + mItn2KUmFQxPGjFnab7NKFhr6C0I0YBJScf0pJ53QHyw+Nhz2Gcsl3r8iUYFH/jT + 9Fe3PlLRO5u6YDLNYYcR0plDXXx/RpzkoPZt/i91qoWjTVgeGijKhcoZTBLALmsC + AwEAAQ== + -----END PUBLIC KEY----- + """, RSAPublicKey.class, "SunRsaSign"); public static final Entry rsapubbc = new Entry("rsapubbc", """ @@ -112,14 +158,14 @@ class PEMData { MtJpIPIXynEqRy2mIw2GrKTtu3dqrW+ndarbD6D4yRY1hWHluiuOtzhxuueCuf9h XCYEHZS1cqd8wokFPwIDAQAB -----END PUBLIC KEY----- - """, RSAPublicKey.class); + """, RSAPublicKey.class, "SunRsaSign"); public static final Entry ecsecp256pub = new Entry("ecsecp256pub", """ -----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEi/kRGOL7wCPTN4KJ2ppeSt5UYB6u cPjjuKDtFTXbguOIFDdZ65O/8HTUqS/sVzRF+dg7H3/tkQ/36KdtuADbwQ== -----END PUBLIC KEY----- - """, ECPublicKey.class); + """, ECPublicKey.class, "SunEC"); // EC key with explicit parameters -- Not currently supported by SunEC public static final String pubec_explicit = """ @@ -152,7 +198,7 @@ class PEMData { P4c4mySRy5N3plFQUp3pIB7wqshi1t6hkdg7gRGjMtJpIPIXynEqRy2mIw2GrKTtu3dqrW+ndarb D6D4yRY1hWHluiuOtzhxuueCuf9hXCYEHZS1cqd8wokFPwIDAQAB -----END PRIVATE KEY----- - """, KeyPair.class); + """, KeyPair.class, "SunRsaSign"); public static final Entry oasrfc8410 = new Entry("oasrfc8410", """ @@ -161,7 +207,24 @@ class PEMData { oB8wHQYKKoZIhvcNAQkJFDEPDA1DdXJkbGUgQ2hhaXJzgSEAGb9ECWmEzf6FQbrB Z9w7lshQhqowtrbLDFw4rXAxZuE= -----END PRIVATE KEY----- - """, KeyPair.class); + """, KeyPair.class, "SunEC"); + + public static final Entry oasxdh = new Entry("oasxdh", + """ + -----BEGIN PRIVATE KEY----- + MFECAQEwBQYDK2VuBCIEIIrMS7w5YxuBTyPFiaFvp6ILiGET7wY9ybk7Qqhe3hSq + gSEAB7ODPxRePrPnJMaj3f47blVx6c5bfxcllQzLp4bW5x4= + -----END PRIVATE KEY----- + """, KeyPair.class, "SunEC"); + + public static final Entry oasec = new Entry("oasec", + """ + -----BEGIN PRIVATE KEY----- + MIGFAgEBMBMGByqGSM49AgEGCCqGSM49AwEHBCcwJQIBAQQgkGEVbZE1yAiO11Ya + eepcrBQL+HpVE4fy0V6jbpJcmkiBQgAERCqYYmN9uNT9Z1O2Z2VC3Zag9eUAhz7G + p8DqC21VrIgpqVQ4BrcWsieNg9fSd4N2hgfMpk9PCQwJQ8ifCMiBVQ== + -----END PRIVATE KEY----- + """, KeyPair.class, "SunEC"); public static final Entry rsaOpenSSL = new Entry("rsaOpenSSL", """ @@ -192,7 +255,7 @@ class PEMData { EcgIOtkvoTrJ9Cquvuj+O7/d2yNoH0SZQ4IYJKq47/Z4kKhwXzJnBCCCBKgkjfub RTQSNnSEgTaBD29l7FrhNRHX9lIKFZ23caCTBS6o3q3+KgPbq7ao -----END RSA PRIVATE KEY----- - """, RSAPrivateKey.class); + """, RSAPrivateKey.class, "SunRsaSign"); static final Entry ed25519ep8 = new Entry("ed25519ep8", """ @@ -202,7 +265,7 @@ class PEMData { vdMyi46+Dw7cOjwEQLtx5ME0NOOo7vlCGm3H/4j+Tf5UXrMb1UrkPjqc8OiLbC0n IycFtI70ciPjgwDSjtCcPxR8fSxJPrm2yOJsRVo= -----END ENCRYPTED PRIVATE KEY----- - """, EdECPrivateKey.class, "fish".toCharArray()); + """, EdECPrivateKey.class, "SunEC", "fish".toCharArray()); // This is not meant to be decrypted and to stay as an EKPI static final Entry ed25519ekpi = new Entry("ed25519ekpi", @@ -237,7 +300,7 @@ class PEMData { 8gOYV33zkPhziWJt4uFMFIi7N2DLEk5UVZv1KTLZlfPl55DRs7j/Sb4vKHpB17AO meVknxVvifDVY0TIz57t28Accsk6ClBCxNPluPU/8YLGAZJYsdDXjGcndQ13s5G7 -----END CERTIFICATE----- - """, X509Certificate.class); + """, X509Certificate.class, "SUN"); static final Entry ecCert = new Entry("ecCert", """ @@ -249,7 +312,36 @@ class PEMData { lU3G9QAwCgYIKoZIzj0EAwIDSAAwRQIgMwYld7aBzkcRt9mn27YOed5+n0xN1y8Q VEcFjLI/tBYCIQDU3szDZ/PK2mUZwtgQxLqHdh+f1JY0UwQS6M8QUvoDHw== -----END CERTIFICATE----- - """, X509Certificate.class); + """, X509Certificate.class, "SUN"); + + private static final Entry rsaCrl = new Entry("rsaCrl", + """ + -----BEGIN X509 CRL----- + MIIBRTCBrwIBATANBgkqhkiG9w0BAQUFADBMMQswCQYDVQQGEwJVUzEaMBgGA1UE + ChMRVGVzdCBDZXJ0aWZpY2F0ZXMxITAfBgNVBAMTGEJhc2ljIEhUVFAgVVJJIFBl + ZXIgMSBDQRcNMDUwNjAzMjE0NTQ3WhcNMTUwNjAxMjE0NTQ3WqAvMC0wHwYDVR0j + BBgwFoAUa+bxcvx1zVdUhvIEd9hcfbmFdw4wCgYDVR0UBAMCAQEwDQYJKoZIhvcN + AQEFBQADgYEAZ+21yt1pJn2FU6vBwpFtAKVeBCCCqJVFiRxT84XbUw0BpLrCFvlk + FOo6tC95aoV7vPGwOEyUNbKJJOCzLliIwV1PPfgZQV20xohSIPISHdUjmlyttglv + AuEvltGnbP7ENxw18HxvM20XmHz+akuFu6npI6MkBjfoxvlq1bcdTrI= + -----END X509 CRL----- + """, X509CRL.class, "SUN"); + + private static final Entry invalidPEM = new Entry("invalidPEM", """ + -----BEGIN INVALID PEM----- + MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDBVS52ZSKZ0oES7twD2 + GGwRIVu3uHlGIwlu0xzFe7sgIPntca2bHfYMhgGxrlCm0q+hZANiAAQNWgwWfLX8 + 8pYVjvwbfvDF9f+Oa9w6JjrfpWwFAUI6b1OPgrNUh+yXtUXnQNXnfUcIu0Os53bM + """, DEREncodable.class, null); + + private static final Entry incorrectFooter = new Entry("incorrectFooter", """ + -----BEGIN PRIVATE KEY----- + MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDBVS52ZSKZ0oES7twD2 + GGwRIVu3uHlGIwlu0xzFe7sgIPntca2bHfYMhgGxrlCm0q+hZANiAAQNWgwWfLX8 + 8pYVjvwbfvDF9f+Oa9w6JjrfpWwFAUI6b1OPgrNUh+yXtUXnQNXnfUcIu0Os53bM + 8fTqPkQl6RyWEDHeXqJK8zTBHMeBq9nLfDPSbzQgLDyC64Orn0D8exM= + -----END PUBLIC KEY----- + """, DEREncodable.class, null); // EC cert with explicit parameters -- Not currently supported by SunEC static final String ecCertEX = """ @@ -280,7 +372,7 @@ class PEMData { 8pYVjvwbfvDF9f+Oa9w6JjrfpWwFAUI6b1OPgrNUh+yXtUXnQNXnfUcIu0Os53bM 8fTqPkQl6RyWEDHeXqJK8zTBHMeBq9nLfDPSbzQgLDyC64Orn0D8exM= -----END PRIVATE KEY----- - """, KeyPair.class); + """, KeyPair.class, "SunEC"); public static final Entry ecCSR = new Entry("ecCSR", """ @@ -297,7 +389,7 @@ class PEMData { MQYMBGZpc2gwCgYIKoZIzj0EAwIDRwAwRAIgUBTdrMDE4BqruYRh1rRyKQBf48WR kIX8R4dBK9h1VRcCIEBR2Mzvku/huTbWTwKVlXBZeEmwIlxKwpRepPtViXcW -----END CERTIFICATE REQUEST----- - """, PEMRecord.class); + """, PEMRecord.class, "SunEC"); public static final String preData = "TEXT BLAH TEXT BLAH" + System.lineSeparator(); @@ -318,16 +410,17 @@ class PEMData { MQYMBGZpc2gwCgYIKoZIzj0EAwIDRwAwRAIgUBTdrMDE4BqruYRh1rRyKQBf48WR kIX8R4dBK9h1VRcCIEBR2Mzvku/huTbWTwKVlXBZeEmwIlxKwpRepPtViXcW -----END CERTIFICATE REQUEST----- - """ + postData, PEMRecord.class); + """ + postData, PEMRecord.class, "SunEC"); - public record Entry(String name, String pem, Class clazz, char[] password, + public record Entry(String name, String pem, Class clazz, String provider, char[] password, byte[] der) { - public Entry(String name, String pem, Class clazz, char[] password, + public Entry(String name, String pem, Class clazz, String provider, char[] password, byte[] der) { this.name = name; this.pem = pem; this.clazz = clazz; + this.provider = provider; this.password = password; if (pem != null && pem.length() > 0) { String[] pemtext = pem.split("-----"); @@ -336,16 +429,16 @@ public Entry(String name, String pem, Class clazz, char[] password, this.der = null; } } - Entry(String name, String pem, Class clazz, char[] password) { - this(name, pem, clazz, password, null); + Entry(String name, String pem, Class clazz, String provider, char[] password) { + this(name, pem, clazz,provider, password, null); } - Entry(String name, String pem, Class clazz) { - this(name, pem, clazz, null, null); + Entry(String name, String pem, Class clazz, String provider) { + this(name, pem, clazz, provider, null); } public Entry newClass(String name, Class c) { - return new Entry(name, pem, c, password); + return new Entry(name, pem, c, provider, password); } public Entry newClass(Class c) { @@ -355,19 +448,19 @@ public Entry newClass(Class c) { Entry makeCRLF(String name) { return new Entry(name, Pattern.compile(System.lineSeparator()).matcher(pem).replaceAll("\r\n"), - clazz, password()); + clazz, provider, password()); } Entry makeCR(String name) { return new Entry(name, Pattern.compile(System.lineSeparator()).matcher(pem).replaceAll("\r"), - clazz, password()); + clazz, provider, password()); } Entry makeNoCRLF(String name) { return new Entry(name, Pattern.compile(System.lineSeparator()).matcher(pem).replaceAll(""), - clazz, password()); + clazz, provider, password()); } } @@ -395,10 +488,12 @@ static public Entry getEntry(List list, String varname) { static { pubList.add(rsapub); + pubList.add(rsapsspub); pubList.add(rsapubbc); pubList.add(ecsecp256pub.makeCR("ecsecp256pub-r")); pubList.add(ecsecp256pub.makeCRLF("ecsecp256pub-rn")); privList.add(rsapriv); + privList.add(rsapsspriv); privList.add(rsaprivbc); privList.add(ecsecp256); privList.add(ecsecp384); @@ -407,9 +502,12 @@ static public Entry getEntry(List list, String varname) { privList.add(rsaOpenSSL); oasList.add(oasrfc8410); oasList.add(oasbcpem); + oasList.add(oasec); + oasList.add(oasxdh); certList.add(rsaCert); certList.add(ecCert); + certList.add(rsaCrl); entryList.addAll(pubList); entryList.addAll(privList); @@ -423,5 +521,7 @@ static public Entry getEntry(List list, String varname) { failureEntryList.add(new Entry("emptyPEM", "", DEREncodable.class, null)); failureEntryList.add(new Entry("nullPEM", null, DEREncodable.class, null)); + failureEntryList.add(incorrectFooter); + failureEntryList.add(invalidPEM); } } \ No newline at end of file diff --git a/test/jdk/java/security/PEM/PEMDecoderTest.java b/test/jdk/java/security/PEM/PEMDecoderTest.java index dbbfac6154fb5..db266d75563a3 100644 --- a/test/jdk/java/security/PEM/PEMDecoderTest.java +++ b/test/jdk/java/security/PEM/PEMDecoderTest.java @@ -26,6 +26,7 @@ /* * @test * @bug 8298420 + * @library /test/lib * @modules java.base/sun.security.pkcs * java.base/sun.security.util * @summary Testing basic PEM API decoding @@ -39,20 +40,22 @@ import java.security.cert.X509CRL; import java.security.cert.X509Certificate; import java.security.interfaces.*; -import java.security.spec.PKCS8EncodedKeySpec; -import java.security.spec.X509EncodedKeySpec; +import java.security.spec.*; import java.util.*; import java.util.Arrays; +import sun.security.pkcs.PKCS8Key; import sun.security.util.Pem; public class PEMDecoderTest { static HexFormat hex = HexFormat.of(); - public static void main(String[] args) throws IOException { + public static void main(String[] args) throws Exception { System.out.println("Decoder test:"); - PEMData.entryList.forEach(PEMDecoderTest::test); + PEMData.entryList.forEach(entry -> test(entry, false)); + System.out.println("Decoder test withFactory:"); + PEMData.entryList.forEach(entry -> test(entry, true)); System.out.println("Decoder test returning DEREncodable class:"); PEMData.entryList.forEach(entry -> test(entry, DEREncodable.class)); System.out.println("Decoder test with encrypted PEM:"); @@ -95,8 +98,11 @@ public static void main(String[] args) throws IOException { System.out.println("Check a Signature/Verify op is successful:"); PEMData.privList.forEach(PEMDecoderTest::testSignature); - PEMData.oasList.forEach(PEMDecoderTest::testSignature); + PEMData.oasList.stream().filter(e -> !e.name().endsWith("xdh")) + .forEach(PEMDecoderTest::testSignature); + System.out.println("Checking if decode() returns a PKCS8Key and can generate a pub"); + PEMData.oasList.forEach(PEMDecoderTest::testPKCS8Key); // PEMRecord tests System.out.println("Checking if ecCSR:"); @@ -257,13 +263,25 @@ static DEREncodable testEncrypted(PEMData.Entry entry) { // Change the Entry to use the given class as the expected class returned static DEREncodable test(PEMData.Entry entry, Class c) { - return test(entry.newClass(c)); + return test(entry.newClass(c), false); } // Run test with a given Entry static DEREncodable test(PEMData.Entry entry) { + return test(entry, false); + } + + // Run test with a given Entry + static DEREncodable test(PEMData.Entry entry, boolean withFactory) { try { - DEREncodable r = test(entry.pem(), entry.clazz(), PEMDecoder.of()); + PEMDecoder pemDecoder; + if (withFactory) { + Provider provider = Security.getProvider(entry.provider()); + pemDecoder = PEMDecoder.of().withFactory(provider); + } else { + pemDecoder = PEMDecoder.of(); + } + DEREncodable r = test(entry.pem(), entry.clazz(), pemDecoder); System.out.println("PASS (" + entry.name() + ")"); return r; } catch (Exception | AssertionError e) { @@ -340,6 +358,19 @@ static void testTwoKeys() throws IOException { } } + private static void testPKCS8Key(PEMData.Entry entry) { + try { + PKCS8Key key = PEMDecoder.of().decode(entry.pem(), PKCS8Key.class); + PKCS8EncodedKeySpec spec = + new PKCS8EncodedKeySpec(key.getEncoded()); + + KeyFactory kf = KeyFactory.getInstance(key.getAlgorithm()); + kf.generatePublic(spec); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + static void testClass(PEMData.Entry entry, Class clazz) throws IOException { var pk = PEMDecoder.of().decode(entry.pem(), clazz); } @@ -400,9 +431,15 @@ static void testSignature(PEMData.Entry entry) { "should not be null"); } + AlgorithmParameterSpec spec = null; String algorithm = switch(privateKey.getAlgorithm()) { case "EC" -> "SHA256withECDSA"; case "EdDSA" -> "EdDSA"; + case "RSASSA-PSS" -> { + spec = new PSSParameterSpec( + "SHA-256", "MGF1", MGF1ParameterSpec.SHA256, 32, 1); + yield "RSASSA-PSS"; + } case null -> { System.out.println("Algorithm is null " + entry.name()); @@ -415,6 +452,9 @@ static void testSignature(PEMData.Entry entry) { try { if (d instanceof PrivateKey) { s = Signature.getInstance(algorithm); + if (spec != null) { + s.setParameter(spec); + } s.initSign(privateKey); s.update(data); s.sign(); diff --git a/test/jdk/java/security/PEM/PEMEncoderTest.java b/test/jdk/java/security/PEM/PEMEncoderTest.java index 8aef5be4b9da0..d1adc0b4648a0 100644 --- a/test/jdk/java/security/PEM/PEMEncoderTest.java +++ b/test/jdk/java/security/PEM/PEMEncoderTest.java @@ -26,11 +26,13 @@ /* * @test * @bug 8298420 + * @library /test/lib * @summary Testing basic PEM API encoding * @enablePreview * @modules java.base/sun.security.util */ +import jdk.test.lib.Asserts; import sun.security.util.Pem; import javax.crypto.EncryptedPrivateKeyInfo; @@ -39,9 +41,18 @@ import java.nio.charset.StandardCharsets; import java.security.*; import java.security.spec.InvalidParameterSpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; import java.util.*; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; +import jdk.test.lib.security.SecurityUtils; +import static jdk.test.lib.Asserts.assertThrows; + public class PEMEncoderTest { static Map keymap; @@ -68,7 +79,12 @@ public static void main(String[] args) throws Exception { System.out.println("New instance re-encode testToString:"); keymap.keySet().stream().forEach(key -> testToString(key, PEMEncoder.of())); - + System.out.println("Same instance Encoder testEncodedKeySpec:"); + testEncodedKeySpec(encoder); + System.out.println("New instance Encoder testEncodedKeySpec:"); + testEncodedKeySpec(PEMEncoder.of()); + System.out.println("Same instance Encoder testEmptyKey:"); + testEmptyAndNullKey(encoder); keymap = generateObjKeyMap(PEMData.encryptedList); System.out.println("Same instance Encoder match test:"); keymap.keySet().stream().forEach(key -> testEncryptedMatch(key, encoder)); @@ -86,6 +102,9 @@ public static void main(String[] args) throws Exception { throw new Exception("Should have been a NullPointerException thrown"); } } + + System.out.println("Same instance testThreadSafety:"); + testThreadSafety(encoder); } static Map generateObjKeyMap(List list) { @@ -196,6 +215,49 @@ static void testEncryptedMatch(String key, PEMEncoder encoder) { System.out.println("PASS: " + entry.name()); } + // this test that keys can be encoded and decoded safely in a concurrent environment + static void testThreadSafety(PEMEncoder pemEncoder) throws Exception { + try(ExecutorService ex = Executors.newFixedThreadPool(5)) { + List keys = new ArrayList<>(); + int count = 50; + List encoded = Collections.synchronizedList(new ArrayList<>()); + List decoded = Collections.synchronizedList(new ArrayList<>()); + final CountDownLatch encodingComplete = new CountDownLatch(count); + final CountDownLatch decodingComplete = new CountDownLatch(count); + + for (int i = 0 ; i < count ; i++) { + KeyPair kp = getKeyPair(); + keys.add(kp.getPublic()); + ex.submit(() -> { + encoded.add(pemEncoder.encodeToString(kp.getPublic())); + encodingComplete.countDown(); + }); + } + encodingComplete.await(); + + PEMDecoder decoder = PEMDecoder.of(); + for (String pem : encoded) { + ex.submit(() -> { + decoded.add(decoder.decode(pem, PublicKey.class).toString()); + decodingComplete.countDown(); + }); + } + + decodingComplete.await(); + + // verify all keys were properly encoded and decoded comparing with the original list + for (PublicKey kp : keys) { + if (!decoded.contains(kp.toString())) { + throw new RuntimeException("a key was not properly encoded and decoded: " + decoded); + } + // to avoid duplication + decoded.remove(kp.toString()); + } + } + + System.out.println("PASS: testThreadSafety"); + } + static void checkResults(PEMData.Entry entry, String result) { String pem = new String(entry.pem()); // The below matches the \r\n generated PEM with the PEM passed @@ -230,5 +292,42 @@ static void indexDiff(String a, String b) { } } } -} + static void testEncodedKeySpec(PEMEncoder encoder) throws NoSuchAlgorithmException { + KeyPair kp = getKeyPair(); + encoder.encodeToString(new X509EncodedKeySpec(kp.getPublic().getEncoded())); + encoder.encodeToString((new PKCS8EncodedKeySpec(kp.getPrivate().getEncoded()))); + System.out.println("PASS: testEncodedKeySpec"); + } + + private static void testEmptyAndNullKey(PEMEncoder encoder) throws NoSuchAlgorithmException { + KeyPair kp = getKeyPair(); + assertThrows(IllegalArgumentException.class,() -> encoder.encode( + new KeyPair(kp.getPublic(), new EmptyKey()))); + assertThrows(IllegalArgumentException.class,() -> encoder.encode( + new KeyPair(kp.getPublic(), null))); + + assertThrows(IllegalArgumentException.class,() -> encoder.encode( + new KeyPair(new EmptyKey(), kp.getPrivate()))); + assertThrows(IllegalArgumentException.class,() -> encoder.encode( + new KeyPair(null, kp.getPrivate()))); + System.out.println("PASS: testEmptyKey"); + } + private static KeyPair getKeyPair() throws NoSuchAlgorithmException { + Provider provider = Security.getProvider("SunRsaSign"); + KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", provider); + kpg.initialize(SecurityUtils.getTestKeySize("RSA")); + return kpg.generateKeyPair(); + } + + private static class EmptyKey implements PublicKey, PrivateKey { + @Override + public String getAlgorithm() { return "Test"; } + + @Override + public String getFormat() { return "Test"; } + + @Override + public byte[] getEncoded() { return new byte[0]; } + } +} diff --git a/test/lib/jdk/test/lib/security/SecurityUtils.java b/test/lib/jdk/test/lib/security/SecurityUtils.java index 7509488225eae..f21b6178e05f0 100644 --- a/test/lib/jdk/test/lib/security/SecurityUtils.java +++ b/test/lib/jdk/test/lib/security/SecurityUtils.java @@ -45,6 +45,8 @@ public final class SecurityUtils { private enum KeySize{ RSA(2048), DSA(2048), + Ed25519(256), + EC(256), DH(2048); private final int keySize; @@ -145,6 +147,8 @@ public static int getTestKeySize(String algo) { return switch (algo) { case "RSA" -> KeySize.RSA.keySize; case "DSA" -> KeySize.DSA.keySize; + case "Ed25519" -> KeySize.Ed25519.keySize; + case "EC" -> KeySize.EC.keySize; case "DH", "DiffieHellman" -> KeySize.DH.keySize; default -> throw new RuntimeException("Test key size not defined for " + algo); }; From 3579483b6e7b1835f5ca9c6f9ff5a103e4c1d294 Mon Sep 17 00:00:00 2001 From: Fernando Guallini Date: Mon, 12 May 2025 17:24:59 +0200 Subject: [PATCH 2/7] multi threaded in new file, added test for jdk.epkcs8.defaultAlgorithm --- .../jdk/java/security/PEM/PEMEncoderTest.java | 62 +++--------- .../java/security/PEM/PEMMultiThreadTest.java | 96 +++++++++++++++++++ .../jdk/java/security/PEM/java.security-extra | 1 + 3 files changed, 112 insertions(+), 47 deletions(-) create mode 100644 test/jdk/java/security/PEM/PEMMultiThreadTest.java create mode 100644 test/jdk/java/security/PEM/java.security-extra diff --git a/test/jdk/java/security/PEM/PEMEncoderTest.java b/test/jdk/java/security/PEM/PEMEncoderTest.java index d1adc0b4648a0..fcb48058e6bd3 100644 --- a/test/jdk/java/security/PEM/PEMEncoderTest.java +++ b/test/jdk/java/security/PEM/PEMEncoderTest.java @@ -30,6 +30,9 @@ * @summary Testing basic PEM API encoding * @enablePreview * @modules java.base/sun.security.util + * @run main PEMEncoderTest PBEWithHmacSHA256AndAES_128 + * @run main/othervm -Djava.security.properties=${test.src}/java.security-extra + * PEMEncoderTest PBEWithHmacSHA512AndAES_256 */ import jdk.test.lib.Asserts; @@ -51,17 +54,21 @@ import java.util.regex.Pattern; import jdk.test.lib.security.SecurityUtils; + +import static jdk.test.lib.Asserts.assertEquals; import static jdk.test.lib.Asserts.assertThrows; public class PEMEncoderTest { static Map keymap; + static String pkcs8DefaultAlgExpect; final static Pattern CR = Pattern.compile("\r"); final static Pattern LF = Pattern.compile("\n"); final static Pattern LSDEFAULT = Pattern.compile(System.lineSeparator()); public static void main(String[] args) throws Exception { + pkcs8DefaultAlgExpect = args[0]; PEMEncoder encoder = PEMEncoder.of(); // These entries are removed @@ -102,9 +109,6 @@ public static void main(String[] args) throws Exception { throw new Exception("Should have been a NullPointerException thrown"); } } - - System.out.println("Same instance testThreadSafety:"); - testThreadSafety(encoder); } static Map generateObjKeyMap(List list) { @@ -164,10 +168,12 @@ static void testToString(String key, PEMEncoder encoder) { static void testEncrypted(String key, PEMEncoder encoder) { PEMData.Entry entry = PEMData.getEntry(key); try { - encoder.withEncryption( + String pem = encoder.withEncryption( (entry.password() != null ? entry.password() : "fish".toCharArray())) .encodeToString(keymap.get(key)); + + verifyEncriptionAlg(pem); } catch (RuntimeException e) { throw new AssertionError("Encrypted encoder failed with " + entry.name(), e); @@ -176,6 +182,11 @@ static void testEncrypted(String key, PEMEncoder encoder) { System.out.println("PASS: " + entry.name()); } + private static void verifyEncriptionAlg(String pem) { + var epki = PEMDecoder.of().decode(pem, EncryptedPrivateKeyInfo.class); + assertEquals(epki.getAlgName(), pkcs8DefaultAlgExpect); + } + /* Test cannot verify PEM was the same as known PEM because we have no public access to the AlgoritmID.params and PBES2Parameters. @@ -215,49 +226,6 @@ static void testEncryptedMatch(String key, PEMEncoder encoder) { System.out.println("PASS: " + entry.name()); } - // this test that keys can be encoded and decoded safely in a concurrent environment - static void testThreadSafety(PEMEncoder pemEncoder) throws Exception { - try(ExecutorService ex = Executors.newFixedThreadPool(5)) { - List keys = new ArrayList<>(); - int count = 50; - List encoded = Collections.synchronizedList(new ArrayList<>()); - List decoded = Collections.synchronizedList(new ArrayList<>()); - final CountDownLatch encodingComplete = new CountDownLatch(count); - final CountDownLatch decodingComplete = new CountDownLatch(count); - - for (int i = 0 ; i < count ; i++) { - KeyPair kp = getKeyPair(); - keys.add(kp.getPublic()); - ex.submit(() -> { - encoded.add(pemEncoder.encodeToString(kp.getPublic())); - encodingComplete.countDown(); - }); - } - encodingComplete.await(); - - PEMDecoder decoder = PEMDecoder.of(); - for (String pem : encoded) { - ex.submit(() -> { - decoded.add(decoder.decode(pem, PublicKey.class).toString()); - decodingComplete.countDown(); - }); - } - - decodingComplete.await(); - - // verify all keys were properly encoded and decoded comparing with the original list - for (PublicKey kp : keys) { - if (!decoded.contains(kp.toString())) { - throw new RuntimeException("a key was not properly encoded and decoded: " + decoded); - } - // to avoid duplication - decoded.remove(kp.toString()); - } - } - - System.out.println("PASS: testThreadSafety"); - } - static void checkResults(PEMData.Entry entry, String result) { String pem = new String(entry.pem()); // The below matches the \r\n generated PEM with the PEM passed diff --git a/test/jdk/java/security/PEM/PEMMultiThreadTest.java b/test/jdk/java/security/PEM/PEMMultiThreadTest.java new file mode 100644 index 0000000000000..44b48b2d36841 --- /dev/null +++ b/test/jdk/java/security/PEM/PEMMultiThreadTest.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2025, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +/* + * @test + * @bug 8298420 + * @library /test/lib + * @summary Testing basic PEM API encoding + * @enablePreview + * @modules java.base/sun.security.util + * @run main PEMMultiThreadTest + */ + +import java.security.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class PEMMultiThreadTest { + static final int THREAD_COUNT = 5; + static final int KEYS_COUNT = 50; + + public static void main(String[] args) throws Exception { + PEMEncoder encoder = PEMEncoder.of(); + try(ExecutorService ex = Executors.newFixedThreadPool(THREAD_COUNT)) { + List keys = new ArrayList<>(); + List encoded = Collections.synchronizedList(new ArrayList<>()); + List decoded = Collections.synchronizedList(new ArrayList<>()); + final CountDownLatch encodingComplete = new CountDownLatch(KEYS_COUNT); + final CountDownLatch decodingComplete = new CountDownLatch(KEYS_COUNT); + + for (int i = 0 ; i < KEYS_COUNT ; i++) { + KeyPair kp = getKeyPair(); + keys.add(kp.getPublic()); + ex.submit(() -> { + encoded.add(encoder.encodeToString(kp.getPublic())); + encodingComplete.countDown(); + }); + } + encodingComplete.await(); + + PEMDecoder decoder = PEMDecoder.of(); + for (String pem : encoded) { + ex.submit(() -> { + decoded.add(decoder.decode(pem, PublicKey.class).toString()); + decodingComplete.countDown(); + }); + } + + decodingComplete.await(); + + // verify all keys were properly encoded and decoded comparing with the original list + for (PublicKey kp : keys) { + if (!decoded.contains(kp.toString())) { + throw new RuntimeException("a key was not properly encoded and decoded: " + decoded); + } + // to avoid duplication + decoded.remove(kp.toString()); + } + } + + System.out.println("PASS: testThreadSafety"); + } + + private static KeyPair getKeyPair() throws NoSuchAlgorithmException { + String alg = "RSA"; + KeyPairGenerator kpg = KeyPairGenerator.getInstance(alg); + kpg.initialize(jdk.test.lib.security.SecurityUtils.getTestKeySize(alg)); + return kpg.generateKeyPair(); + } +} diff --git a/test/jdk/java/security/PEM/java.security-extra b/test/jdk/java/security/PEM/java.security-extra new file mode 100644 index 0000000000000..e6acd81169df5 --- /dev/null +++ b/test/jdk/java/security/PEM/java.security-extra @@ -0,0 +1 @@ +jdk.epkcs8.defaultAlgorithm=PBEWithHmacSHA512AndAES_256 \ No newline at end of file From 389e60067711e616941a15be2067260deb5a94d6 Mon Sep 17 00:00:00 2001 From: Fernando Guallini Date: Tue, 13 May 2025 10:24:27 +0200 Subject: [PATCH 3/7] using EC --- test/jdk/java/security/PEM/PEMMultiThreadTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/jdk/java/security/PEM/PEMMultiThreadTest.java b/test/jdk/java/security/PEM/PEMMultiThreadTest.java index 44b48b2d36841..038db10902628 100644 --- a/test/jdk/java/security/PEM/PEMMultiThreadTest.java +++ b/test/jdk/java/security/PEM/PEMMultiThreadTest.java @@ -88,7 +88,7 @@ public static void main(String[] args) throws Exception { } private static KeyPair getKeyPair() throws NoSuchAlgorithmException { - String alg = "RSA"; + String alg = "EC"; KeyPairGenerator kpg = KeyPairGenerator.getInstance(alg); kpg.initialize(jdk.test.lib.security.SecurityUtils.getTestKeySize(alg)); return kpg.generateKeyPair(); From 6b1897ccca0c0efdf85c0e84e57dc027849d6209 Mon Sep 17 00:00:00 2001 From: Fernando Guallini Date: Wed, 14 May 2025 20:07:51 +0200 Subject: [PATCH 4/7] more coverage --- test/jdk/java/security/PEM/PEMData.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/test/jdk/java/security/PEM/PEMData.java b/test/jdk/java/security/PEM/PEMData.java index 5f123cc0e27fa..69ff16f22732b 100644 --- a/test/jdk/java/security/PEM/PEMData.java +++ b/test/jdk/java/security/PEM/PEMData.java @@ -334,6 +334,18 @@ class PEMData { 8pYVjvwbfvDF9f+Oa9w6JjrfpWwFAUI6b1OPgrNUh+yXtUXnQNXnfUcIu0Os53bM """, DEREncodable.class, null); + private static final Entry invalidHeader = new Entry("invalidHeader", """ + ---BEGIN PRIVATE KEY--- + MC4CAQAwBQYDK2VwBCIEIFFZsmD+OKk67Cigc84/2fWtlKsvXWLSoMJ0MHh4jI4I + -----END PRIVATE KEY----- + """, DEREncodable.class, null); + + private static final Entry invalidFooter = new Entry("invalidFooter", """ + -----BEGIN PRIVATE KEY----- + MC4CAQAwBQYDK2VwBCIEIFFZsmD+OKk67Cigc84/2fWtlKsvXWLSoMJ0MHh4jI4I + ---END PRIVATE KEY--- + """, DEREncodable.class, null); + private static final Entry incorrectFooter = new Entry("incorrectFooter", """ -----BEGIN PRIVATE KEY----- MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDBVS52ZSKZ0oES7twD2 @@ -422,7 +434,8 @@ public Entry(String name, String pem, Class clazz, String provider, char[] passw this.clazz = clazz; this.provider = provider; this.password = password; - if (pem != null && pem.length() > 0) { + if (pem != null && pem.length() > 0 && + !name.contains("incorrect") && !name.contains("invalid")) { String[] pemtext = pem.split("-----"); this.der = Base64.getMimeDecoder().decode(pemtext[2]); } else { @@ -523,5 +536,7 @@ static public Entry getEntry(List list, String varname) { failureEntryList.add(new Entry("nullPEM", null, DEREncodable.class, null)); failureEntryList.add(incorrectFooter); failureEntryList.add(invalidPEM); + failureEntryList.add(invalidHeader); + failureEntryList.add(invalidFooter); } } \ No newline at end of file From 402bc872d33c326eda359f8fd2291f9eb910be34 Mon Sep 17 00:00:00 2001 From: Fernando Guallini Date: Fri, 16 May 2025 15:51:53 +0200 Subject: [PATCH 5/7] added coverage for EncryptedPrivateKeyInfo --- .../jdk/java/security/PEM/PEMDecoderTest.java | 4 +++ .../jdk/java/security/PEM/PEMEncoderTest.java | 25 +++++++++++++------ 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/test/jdk/java/security/PEM/PEMDecoderTest.java b/test/jdk/java/security/PEM/PEMDecoderTest.java index a771315d10c02..d432ecfac49d6 100644 --- a/test/jdk/java/security/PEM/PEMDecoderTest.java +++ b/test/jdk/java/security/PEM/PEMDecoderTest.java @@ -44,6 +44,7 @@ import java.util.*; import java.util.Arrays; +import jdk.test.lib.Asserts; import sun.security.pkcs.PKCS8Key; import sun.security.util.Pem; @@ -206,6 +207,9 @@ static void testPEMRecord(PEMData.Entry entry) { "does not match."); } + // binary encodings must be equal + Asserts.assertEqualsByteArray(entry.der(), r.getEncoded()); + boolean result = switch(r.type()) { case Pem.PRIVATE_KEY -> PrivateKey.class.isAssignableFrom(entry.clazz()); diff --git a/test/jdk/java/security/PEM/PEMEncoderTest.java b/test/jdk/java/security/PEM/PEMEncoderTest.java index fcb48058e6bd3..161025c039af2 100644 --- a/test/jdk/java/security/PEM/PEMEncoderTest.java +++ b/test/jdk/java/security/PEM/PEMEncoderTest.java @@ -93,8 +93,13 @@ public static void main(String[] args) throws Exception { System.out.println("Same instance Encoder testEmptyKey:"); testEmptyAndNullKey(encoder); keymap = generateObjKeyMap(PEMData.encryptedList); - System.out.println("Same instance Encoder match test:"); - keymap.keySet().stream().forEach(key -> testEncryptedMatch(key, encoder)); + System.out.println("Same instance Encoder match test, no provider and with Algo :"); + keymap.keySet().stream().forEach(key -> testEncryptedMatch(key, encoder, false, true)); + System.out.println("Same instance Encoder match test, no provider and no algo :"); + keymap.keySet().stream().forEach(key -> testEncryptedMatch(key, encoder, false, false)); + System.out.println("Same instance Encoder match test, with provider :"); + keymap.keySet().stream().filter(key -> "SUN".equals( PEMData.getEntry(key).provider())) + .forEach(key -> testEncryptedMatch(key, encoder, true, false)); System.out.println("Same instance Encoder new withEnc test:"); keymap.keySet().stream().forEach(key -> testEncrypted(key, encoder)); System.out.println("New instance Encoder and withEnc test:"); @@ -203,18 +208,24 @@ static void testSameEncryptor(String key, PEMEncoder encoder) { System.out.println("PASS: " + entry.name()); } - static void testEncryptedMatch(String key, PEMEncoder encoder) { + static void testEncryptedMatch(String key, PEMEncoder encoder, boolean withProvider, boolean withAlgo) { String result; PEMData.Entry entry = PEMData.getEntry(key); + Provider provider = withProvider ? Security.getProvider(entry.provider()) : null; + try { PrivateKey pkey = (PrivateKey) keymap.get(key); EncryptedPrivateKeyInfo ekpi = PEMDecoder.of().decode(entry.pem(), EncryptedPrivateKeyInfo.class); if (entry.password() != null) { - EncryptedPrivateKeyInfo.encryptKey(pkey, entry.password(), - Pem.DEFAULT_ALGO, ekpi.getAlgParameters(). - getParameterSpec(PBEParameterSpec.class), - null); + if (withAlgo) { + EncryptedPrivateKeyInfo.encryptKey(pkey, entry.password(), + Pem.DEFAULT_ALGO, ekpi.getAlgParameters(). + getParameterSpec(PBEParameterSpec.class), + provider); + } else { + EncryptedPrivateKeyInfo.encryptKey(pkey, entry.password()); + } } result = encoder.encodeToString(ekpi); } catch (RuntimeException | InvalidParameterSpecException e) { From c466ee583422b53642cd0ec14d7e1d7ae477f8b6 Mon Sep 17 00:00:00 2001 From: Fernando Guallini Date: Mon, 19 May 2025 13:45:18 +0200 Subject: [PATCH 6/7] refactored multi thread --- .../jdk/java/security/PEM/PEMEncoderTest.java | 5 ---- .../java/security/PEM/PEMMultiThreadTest.java | 29 +++++++++---------- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/test/jdk/java/security/PEM/PEMEncoderTest.java b/test/jdk/java/security/PEM/PEMEncoderTest.java index 161025c039af2..7c95e91456759 100644 --- a/test/jdk/java/security/PEM/PEMEncoderTest.java +++ b/test/jdk/java/security/PEM/PEMEncoderTest.java @@ -35,7 +35,6 @@ * PEMEncoderTest PBEWithHmacSHA512AndAES_256 */ -import jdk.test.lib.Asserts; import sun.security.util.Pem; import javax.crypto.EncryptedPrivateKeyInfo; @@ -47,10 +46,6 @@ import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.*; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; import jdk.test.lib.security.SecurityUtils; diff --git a/test/jdk/java/security/PEM/PEMMultiThreadTest.java b/test/jdk/java/security/PEM/PEMMultiThreadTest.java index 038db10902628..01c279f3a7cf9 100644 --- a/test/jdk/java/security/PEM/PEMMultiThreadTest.java +++ b/test/jdk/java/security/PEM/PEMMultiThreadTest.java @@ -34,9 +34,7 @@ */ import java.security.*; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; +import java.util.*; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -48,39 +46,40 @@ public class PEMMultiThreadTest { public static void main(String[] args) throws Exception { PEMEncoder encoder = PEMEncoder.of(); try(ExecutorService ex = Executors.newFixedThreadPool(THREAD_COUNT)) { - List keys = new ArrayList<>(); - List encoded = Collections.synchronizedList(new ArrayList<>()); - List decoded = Collections.synchronizedList(new ArrayList<>()); + Map keys = new HashMap<>(); + Map encoded = Collections.synchronizedMap(new HashMap<>()); + Map decoded = Collections.synchronizedMap(new HashMap<>()); final CountDownLatch encodingComplete = new CountDownLatch(KEYS_COUNT); final CountDownLatch decodingComplete = new CountDownLatch(KEYS_COUNT); for (int i = 0 ; i < KEYS_COUNT ; i++) { + final int finalI = i; KeyPair kp = getKeyPair(); - keys.add(kp.getPublic()); + keys.put(finalI, kp.getPublic()); + ex.submit(() -> { - encoded.add(encoder.encodeToString(kp.getPublic())); + encoded.put(finalI, encoder.encodeToString(kp.getPublic())); encodingComplete.countDown(); }); } encodingComplete.await(); PEMDecoder decoder = PEMDecoder.of(); - for (String pem : encoded) { + for (Map.Entry entry : encoded.entrySet()) { ex.submit(() -> { - decoded.add(decoder.decode(pem, PublicKey.class).toString()); + decoded.put(entry.getKey(), decoder.decode(entry.getValue(), PublicKey.class) + .toString()); decodingComplete.countDown(); }); } decodingComplete.await(); - // verify all keys were properly encoded and decoded comparing with the original list - for (PublicKey kp : keys) { - if (!decoded.contains(kp.toString())) { + // verify all keys were properly encoded and decoded comparing with the original key map + for (Map.Entry kp : keys.entrySet()) { + if (!decoded.get(kp.getKey()).equals(kp.getValue().toString())) { throw new RuntimeException("a key was not properly encoded and decoded: " + decoded); } - // to avoid duplication - decoded.remove(kp.toString()); } } From ac55413b287827090f176ef0a59196c1ac2a5e7f Mon Sep 17 00:00:00 2001 From: Fernando Guallini Date: Mon, 19 May 2025 13:50:10 +0200 Subject: [PATCH 7/7] updated summary --- test/jdk/java/security/PEM/PEMMultiThreadTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/jdk/java/security/PEM/PEMMultiThreadTest.java b/test/jdk/java/security/PEM/PEMMultiThreadTest.java index 01c279f3a7cf9..138461bd5f42e 100644 --- a/test/jdk/java/security/PEM/PEMMultiThreadTest.java +++ b/test/jdk/java/security/PEM/PEMMultiThreadTest.java @@ -27,7 +27,7 @@ * @test * @bug 8298420 * @library /test/lib - * @summary Testing basic PEM API encoding + * @summary Testing PEM API is thread safe * @enablePreview * @modules java.base/sun.security.util * @run main PEMMultiThreadTest