8358171: Additional code coverage for PEM API

Reviewed-by: ascarpino
This commit is contained in:
Fernando Guallini 2025-06-06 09:53:25 +00:00
parent 65fda5c02a
commit b2e7cda6a0
10 changed files with 533 additions and 43 deletions

View File

@ -27,6 +27,7 @@ import javax.crypto.EncryptedPrivateKeyInfo;
import java.security.DEREncodable; import java.security.DEREncodable;
import java.security.KeyPair; import java.security.KeyPair;
import java.security.PEMRecord; import java.security.PEMRecord;
import java.security.cert.X509CRL;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.security.interfaces.*; import java.security.interfaces.*;
import java.util.ArrayList; import java.util.ArrayList;
@ -45,7 +46,7 @@ class PEMData {
KwDdi3cNwu7YYD/QtJ+9+AEBdoqhRANCAASL+REY4vvAI9M3gonaml5K3lRgHq5w KwDdi3cNwu7YYD/QtJ+9+AEBdoqhRANCAASL+REY4vvAI9M3gonaml5K3lRgHq5w
+OO4oO0VNduC44gUN1nrk7/wdNSpL+xXNEX52Dsff+2RD/fop224ANvB +OO4oO0VNduC44gUN1nrk7/wdNSpL+xXNEX52Dsff+2RD/fop224ANvB
-----END PRIVATE KEY----- -----END PRIVATE KEY-----
""", KeyPair.class); """, KeyPair.class, "SunEC");
public static final Entry rsapriv = new Entry("rsapriv", public static final Entry rsapriv = new Entry("rsapriv",
""" """
@ -65,7 +66,68 @@ class PEMData {
6onPAs4hkm+63dfzCojvEkALevO8J3OVX7YS5q9J1r75wDn60Ob0Zh+iiorpx8Ob 6onPAs4hkm+63dfzCojvEkALevO8J3OVX7YS5q9J1r75wDn60Ob0Zh+iiorpx8Ob
WqcWcoJqfdLEyBT+ WqcWcoJqfdLEyBT+
-----END PRIVATE KEY----- -----END PRIVATE KEY-----
""", RSAPrivateKey.class); """, RSAPrivateKey.class, "SunRsaSign");
public static final Entry rsaCrtCoefZeroPriv = new Entry("rsaCrtCoefZeroPriv",
"""
-----BEGIN RSA PRIVATE KEY-----
MIIEIwIBAAKCAQEAuZAPiPMlA5R0oOIbxbq+gOmBRcvptIT+0pmG6rZ6H//r7A/Z
MRwen0iO2GuhlyUgOk9Fja/TMBrNX99boVDEZtL4oxRTJibdLNjfQqPeFhs3NNvv
CJJEGD91Dq/fGbWv1TMZcEywqqYSdERDEA7yluw87I7YZc9kXVBwRw5AedvoXOL/
z5yPjK8W7FTCLHSVKiD/X3P3ZX9TmFjTIbRH15Do5sRxsPdrZczYjWdXFXNQEUuF
sVFGHFbB/AJiZlGYqMU+hEIErE35sHrKpZYkj9YovYQe0SBJyuROPl8wmz0Cd69s
rhg142Qf23RPhclBuCNAQQOkefeCppg4IFFh7QIDAQABAoIBADlKHlm4Q7m2uElB
dbSWwqEXNnefjJBUrT3E84/8dXjysNppTDNqzJN9uchcdn+tESWfeshTO97yr2yF
j4se3fwm72ed60vwnMFvVYKECBmIHoO90S8yxT49PT0jFDyiSN6IT7bJnpOZAUKP
HqtTCheJaQfZ1DqejIx4vKlbX5GfShwPQV0Q7KeKnfxhryhAbM5Y5UT8grQGBQU7
aQUZuasQV10APVRaz39VU8/hzBc081LR3O0tjnZcrMAQ7ANsP9Gu3My04cnQ5WBo
P8uCCaSPSkrzCvjd9YYkdnwXMbVCfALOa+MxBddMi4IQG0qI28Bpw6dkKziPxage
KcAQnAsCgYEA0/CwzUlyrG6f+FF3uAvPVn/jEhWatJb7Me7lkhO3suV92brb1pQo
1W1jHdx0OqMuCVfIJ4bXkTwKuvLQGcWJzNWUVh4Tij/ZAV0Ssw2cKbBSCQGNIFsx
Ux0V9tDSYJsEdk+Y5grloDNJokYYCCpF5bz/6QYmX+t3yzjyVSvcNeMCgYEA4COU
ezUXKLSWD+jJZXpS5yopB7oXAg7mmonlc1DRuaIxspwBrbXPLQ/neN8Sefpz9Nxn
4ksPxGbLnJh0wGCnxSu0Qfn4eNVSsulag4IQmbCO2UBsdXNCJB5KlDpRlVhvvviH
Zpz2Dkdx+itLf1EV841MCtPAHZJwrs6i6JntEe8CgYEAgJYdjs+rJXcQ04YKDr4L
k72PtR8qd7rKuObqnhAcegvGqV03mB7YD3WIl0tzsUfj3INHysOC8njtQbOkEp7J
Fl/W2dDxpgVK0gr4F26AesKhYxlv2Fu7t2OEOfVETpx+vpFYgOnHm8TCPhQs7HdJ
ZTOgSG8UxUmFquToEkjEGGUCgYB6AMP8wKxHeuzH4iVl+EySCa/lxdRqSWQasH7V
4yMVkYTNvP9o57LKy4Jql7n97Wca3LIrSkJd3LpuFcpPQQ1xVNW8p+0pEK0AN+cN
+ElC7wkCln+y+rcA5AAiaRApY8cHw04oe72vjhIrY0+oEKILPVkr95D2R9TQQigI
xmh1vwIBAA==
-----END RSA PRIVATE KEY-----
""", 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", public static final Entry rsaprivbc = new Entry("rsaprivbc",
""" """
@ -85,14 +147,14 @@ class PEMData {
6onPAs4hkm+63dfzCojvEkALevO8J3OVX7YS5q9J1r75wDn60Ob0Zh+iiorpx8Ob 6onPAs4hkm+63dfzCojvEkALevO8J3OVX7YS5q9J1r75wDn60Ob0Zh+iiorpx8Ob
WqcWcoJqfdLEyBT+ WqcWcoJqfdLEyBT+
-----END PRIVATE KEY----- -----END PRIVATE KEY-----
""", RSAPrivateKey.class); """, RSAPrivateKey.class, "SunRsaSign");
public static final Entry ec25519priv = new Entry("ed25519priv", public static final Entry ec25519priv = new Entry("ed25519priv",
""" """
-----BEGIN PRIVATE KEY----- -----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIFFZsmD+OKk67Cigc84/2fWtlKsvXWLSoMJ0MHh4jI4I MC4CAQAwBQYDK2VwBCIEIFFZsmD+OKk67Cigc84/2fWtlKsvXWLSoMJ0MHh4jI4I
-----END PRIVATE KEY----- -----END PRIVATE KEY-----
""", EdECPrivateKey.class); """, EdECPrivateKey.class, "SunEC");
public static final Entry rsapub = new Entry("rsapub", public static final Entry rsapub = new Entry("rsapub",
""" """
@ -102,7 +164,20 @@ class PEMData {
MtJpIPIXynEqRy2mIw2GrKTtu3dqrW+ndarbD6D4yRY1hWHluiuOtzhxuueCuf9h MtJpIPIXynEqRy2mIw2GrKTtu3dqrW+ndarbD6D4yRY1hWHluiuOtzhxuueCuf9h
XCYEHZS1cqd8wokFPwIDAQAB XCYEHZS1cqd8wokFPwIDAQAB
-----END PUBLIC KEY----- -----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", public static final Entry rsapubbc = new Entry("rsapubbc",
""" """
@ -112,14 +187,14 @@ class PEMData {
MtJpIPIXynEqRy2mIw2GrKTtu3dqrW+ndarbD6D4yRY1hWHluiuOtzhxuueCuf9h MtJpIPIXynEqRy2mIw2GrKTtu3dqrW+ndarbD6D4yRY1hWHluiuOtzhxuueCuf9h
XCYEHZS1cqd8wokFPwIDAQAB XCYEHZS1cqd8wokFPwIDAQAB
-----END PUBLIC KEY----- -----END PUBLIC KEY-----
""", RSAPublicKey.class); """, RSAPublicKey.class, "SunRsaSign");
public static final Entry ecsecp256pub = new Entry("ecsecp256pub", """ public static final Entry ecsecp256pub = new Entry("ecsecp256pub", """
-----BEGIN PUBLIC KEY----- -----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEi/kRGOL7wCPTN4KJ2ppeSt5UYB6u MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEi/kRGOL7wCPTN4KJ2ppeSt5UYB6u
cPjjuKDtFTXbguOIFDdZ65O/8HTUqS/sVzRF+dg7H3/tkQ/36KdtuADbwQ== cPjjuKDtFTXbguOIFDdZ65O/8HTUqS/sVzRF+dg7H3/tkQ/36KdtuADbwQ==
-----END PUBLIC KEY----- -----END PUBLIC KEY-----
""", ECPublicKey.class); """, ECPublicKey.class, "SunEC");
// EC key with explicit parameters -- Not currently supported by SunEC // EC key with explicit parameters -- Not currently supported by SunEC
public static final String pubec_explicit = """ public static final String pubec_explicit = """
@ -152,7 +227,7 @@ class PEMData {
P4c4mySRy5N3plFQUp3pIB7wqshi1t6hkdg7gRGjMtJpIPIXynEqRy2mIw2GrKTtu3dqrW+ndarb P4c4mySRy5N3plFQUp3pIB7wqshi1t6hkdg7gRGjMtJpIPIXynEqRy2mIw2GrKTtu3dqrW+ndarb
D6D4yRY1hWHluiuOtzhxuueCuf9hXCYEHZS1cqd8wokFPwIDAQAB D6D4yRY1hWHluiuOtzhxuueCuf9hXCYEHZS1cqd8wokFPwIDAQAB
-----END PRIVATE KEY----- -----END PRIVATE KEY-----
""", KeyPair.class); """, KeyPair.class, "SunRsaSign");
public static final Entry oasrfc8410 = new Entry("oasrfc8410", public static final Entry oasrfc8410 = new Entry("oasrfc8410",
""" """
@ -161,7 +236,24 @@ class PEMData {
oB8wHQYKKoZIhvcNAQkJFDEPDA1DdXJkbGUgQ2hhaXJzgSEAGb9ECWmEzf6FQbrB oB8wHQYKKoZIhvcNAQkJFDEPDA1DdXJkbGUgQ2hhaXJzgSEAGb9ECWmEzf6FQbrB
Z9w7lshQhqowtrbLDFw4rXAxZuE= Z9w7lshQhqowtrbLDFw4rXAxZuE=
-----END PRIVATE KEY----- -----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", public static final Entry rsaOpenSSL = new Entry("rsaOpenSSL",
""" """
@ -192,7 +284,7 @@ class PEMData {
EcgIOtkvoTrJ9Cquvuj+O7/d2yNoH0SZQ4IYJKq47/Z4kKhwXzJnBCCCBKgkjfub EcgIOtkvoTrJ9Cquvuj+O7/d2yNoH0SZQ4IYJKq47/Z4kKhwXzJnBCCCBKgkjfub
RTQSNnSEgTaBD29l7FrhNRHX9lIKFZ23caCTBS6o3q3+KgPbq7ao RTQSNnSEgTaBD29l7FrhNRHX9lIKFZ23caCTBS6o3q3+KgPbq7ao
-----END RSA PRIVATE KEY----- -----END RSA PRIVATE KEY-----
""", RSAPrivateKey.class); """, RSAPrivateKey.class, "SunRsaSign");
static final Entry ed25519ep8 = new Entry("ed25519ep8", static final Entry ed25519ep8 = new Entry("ed25519ep8",
""" """
@ -202,11 +294,11 @@ class PEMData {
vdMyi46+Dw7cOjwEQLtx5ME0NOOo7vlCGm3H/4j+Tf5UXrMb1UrkPjqc8OiLbC0n vdMyi46+Dw7cOjwEQLtx5ME0NOOo7vlCGm3H/4j+Tf5UXrMb1UrkPjqc8OiLbC0n
IycFtI70ciPjgwDSjtCcPxR8fSxJPrm2yOJsRVo= IycFtI70ciPjgwDSjtCcPxR8fSxJPrm2yOJsRVo=
-----END ENCRYPTED PRIVATE KEY----- -----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 // This is not meant to be decrypted and to stay as an EKPI
static final Entry ed25519ekpi = new Entry("ed25519ekpi", static final Entry ed25519ekpi = new Entry("ed25519ekpi",
ed25519ep8.pem(), EncryptedPrivateKeyInfo.class, null); ed25519ep8.pem(), EncryptedPrivateKeyInfo.class, "SunEC", null);
static final Entry rsaCert = new Entry("rsaCert", static final Entry rsaCert = new Entry("rsaCert",
""" """
@ -237,7 +329,7 @@ class PEMData {
8gOYV33zkPhziWJt4uFMFIi7N2DLEk5UVZv1KTLZlfPl55DRs7j/Sb4vKHpB17AO 8gOYV33zkPhziWJt4uFMFIi7N2DLEk5UVZv1KTLZlfPl55DRs7j/Sb4vKHpB17AO
meVknxVvifDVY0TIz57t28Accsk6ClBCxNPluPU/8YLGAZJYsdDXjGcndQ13s5G7 meVknxVvifDVY0TIz57t28Accsk6ClBCxNPluPU/8YLGAZJYsdDXjGcndQ13s5G7
-----END CERTIFICATE----- -----END CERTIFICATE-----
""", X509Certificate.class); """, X509Certificate.class, "SUN");
static final Entry ecCert = new Entry("ecCert", static final Entry ecCert = new Entry("ecCert",
""" """
@ -249,7 +341,68 @@ class PEMData {
lU3G9QAwCgYIKoZIzj0EAwIDSAAwRQIgMwYld7aBzkcRt9mn27YOed5+n0xN1y8Q lU3G9QAwCgYIKoZIzj0EAwIDSAAwRQIgMwYld7aBzkcRt9mn27YOed5+n0xN1y8Q
VEcFjLI/tBYCIQDU3szDZ/PK2mUZwtgQxLqHdh+f1JY0UwQS6M8QUvoDHw== VEcFjLI/tBYCIQDU3szDZ/PK2mUZwtgQxLqHdh+f1JY0UwQS6M8QUvoDHw==
-----END CERTIFICATE----- -----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");
// Few random manipulated Base64 characters in PEM content
private static final Entry invalidDer = new Entry("invalidDER", """
-----BEGIN PRIVATE KEY-----
MIICeAIBADANBhkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAOtjMnCzPy4jCeZb
OdOvmvU3jl7+cvPFgL5MfqDCM5a8yI0yImg/hzibJJHLk3emUVBSnekgHvCqyGLW
3qGR2DuBEaMy0mkg8hfKcSpHLaYjDYaspO27d2qtb6d1qtsPoPjJFjWFYeW6K463
OHG654K5/2FcJgQdlLVyp3zCiQU/AgMBAAECgYEAwNkDkTv5rlX8nWLuLJV5kh/T
H9a93SRZxw8qy5Bv7bZ7ZNfHP7uUkHbi7iPojKWRhwo43692SdzR0dCSk7LGgN9q
CYvndsYR6gifVGBi0WF+St4+NdtcQ3VlNdsojy2BdIx0oC+r7i3bn+zc968O/kI+
EgdgrMcjjFqyx6tMHpECQQD8TYPKGHyN7Jdy28llCoUX/sL/yZ2vIi5mnDAFE5ae
KZQSkNAXG+8i9Qbs/Wdd5S3oZDqu+6DBn9gib80pYY05AkEA7tY59Oy8ka7nBlGP
g6Wo1usF2bKqk8vjko9ioZQay7f86aB10QFcAjCr+cCUm16Lc9DwzWl02nNggRZa
Jz8eNwJBAO+1zfLjFOPb14F/JHdlaVKE8EwKCFDuztsapd0M4Vtf8Zk6ERsDpU63
Ml9T2zOwnM9g+whpdjDAZ59ATdJ1JrECQQDReJQ2SxeL0lGPCiOLu9RcQp7L81aF
79G1bgp8WlAyEjlAkloiqEWRKiz7DDuKFR7Lwhognng9S+n87aS+PS57AkBh75t8
6onPAs4hkm+63dfzCojvEkALevO8J3OVX7YS5q9J1r75wDn60Ob0Zh+iiorpx8Ob
WqcWcoJqfdLEyBT+
-----END PRIVATE KEY-----
""", DEREncodable.class, null);
private static final Entry invalidPEM = new Entry("invalidPEM", """
-----BEGIN INVALID PEM-----
MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDBVS52ZSKZ0oES7twD2
GGwRIVu3uHlGIwlu0xzFe7sgIPntca2bHfYMhgGxrlCm0q+hZANiAAQNWgwWfLX8
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
GGwRIVu3uHlGIwlu0xzFe7sgIPntca2bHfYMhgGxrlCm0q+hZANiAAQNWgwWfLX8
8pYVjvwbfvDF9f+Oa9w6JjrfpWwFAUI6b1OPgrNUh+yXtUXnQNXnfUcIu0Os53bM
8fTqPkQl6RyWEDHeXqJK8zTBHMeBq9nLfDPSbzQgLDyC64Orn0D8exM=
-----END PUBLIC KEY-----
""", DEREncodable.class, null);
// EC cert with explicit parameters -- Not currently supported by SunEC // EC cert with explicit parameters -- Not currently supported by SunEC
static final String ecCertEX = """ static final String ecCertEX = """
@ -280,7 +433,7 @@ class PEMData {
8pYVjvwbfvDF9f+Oa9w6JjrfpWwFAUI6b1OPgrNUh+yXtUXnQNXnfUcIu0Os53bM 8pYVjvwbfvDF9f+Oa9w6JjrfpWwFAUI6b1OPgrNUh+yXtUXnQNXnfUcIu0Os53bM
8fTqPkQl6RyWEDHeXqJK8zTBHMeBq9nLfDPSbzQgLDyC64Orn0D8exM= 8fTqPkQl6RyWEDHeXqJK8zTBHMeBq9nLfDPSbzQgLDyC64Orn0D8exM=
-----END PRIVATE KEY----- -----END PRIVATE KEY-----
""", KeyPair.class); """, KeyPair.class, "SunEC");
public static final Entry ecCSR = new Entry("ecCSR", public static final Entry ecCSR = new Entry("ecCSR",
""" """
@ -297,7 +450,7 @@ class PEMData {
MQYMBGZpc2gwCgYIKoZIzj0EAwIDRwAwRAIgUBTdrMDE4BqruYRh1rRyKQBf48WR MQYMBGZpc2gwCgYIKoZIzj0EAwIDRwAwRAIgUBTdrMDE4BqruYRh1rRyKQBf48WR
kIX8R4dBK9h1VRcCIEBR2Mzvku/huTbWTwKVlXBZeEmwIlxKwpRepPtViXcW kIX8R4dBK9h1VRcCIEBR2Mzvku/huTbWTwKVlXBZeEmwIlxKwpRepPtViXcW
-----END CERTIFICATE REQUEST----- -----END CERTIFICATE REQUEST-----
""", PEMRecord.class); """, PEMRecord.class, "SunEC");
public static final String preData = "TEXT BLAH TEXT BLAH" + public static final String preData = "TEXT BLAH TEXT BLAH" +
System.lineSeparator(); System.lineSeparator();
@ -318,39 +471,41 @@ class PEMData {
MQYMBGZpc2gwCgYIKoZIzj0EAwIDRwAwRAIgUBTdrMDE4BqruYRh1rRyKQBf48WR MQYMBGZpc2gwCgYIKoZIzj0EAwIDRwAwRAIgUBTdrMDE4BqruYRh1rRyKQBf48WR
kIX8R4dBK9h1VRcCIEBR2Mzvku/huTbWTwKVlXBZeEmwIlxKwpRepPtViXcW kIX8R4dBK9h1VRcCIEBR2Mzvku/huTbWTwKVlXBZeEmwIlxKwpRepPtViXcW
-----END CERTIFICATE REQUEST----- -----END CERTIFICATE REQUEST-----
""" + postData, PEMRecord.class); """ + postData, PEMRecord.class, "SunEC");
final static Pattern CR = Pattern.compile("\r"); final static Pattern CR = Pattern.compile("\r");
final static Pattern LF = Pattern.compile("\n"); final static Pattern LF = Pattern.compile("\n");
final static Pattern LSDEFAULT = Pattern.compile(System.lineSeparator()); final static Pattern LSDEFAULT = Pattern.compile(System.lineSeparator());
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) { 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) { byte[] der) {
this.name = name; this.name = name;
this.pem = pem; this.pem = pem;
this.clazz = clazz; this.clazz = clazz;
this.provider = provider;
this.password = password; 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("-----"); String[] pemtext = pem.split("-----");
this.der = Base64.getMimeDecoder().decode(pemtext[2]); this.der = Base64.getMimeDecoder().decode(pemtext[2]);
} else { } else {
this.der = null; this.der = null;
} }
} }
Entry(String name, String pem, Class clazz, char[] password) { Entry(String name, String pem, Class clazz, String provider, char[] password) {
this(name, pem, clazz, password, null); this(name, pem, clazz, provider, password, null);
} }
Entry(String name, String pem, Class clazz) { Entry(String name, String pem, Class clazz, String provider) {
this(name, pem, clazz, null, null); this(name, pem, clazz, provider, null, null);
} }
public Entry newClass(String name, Class c) { 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) { public Entry newClass(Class c) {
@ -360,20 +515,20 @@ class PEMData {
Entry makeCRLF(String name) { Entry makeCRLF(String name) {
return new Entry(name, return new Entry(name,
Pattern.compile(System.lineSeparator()).matcher(pem).replaceAll("\r\n"), Pattern.compile(System.lineSeparator()).matcher(pem).replaceAll("\r\n"),
clazz, password()); clazz, provider, password());
} }
Entry makeCR(String name) { Entry makeCR(String name) {
return new Entry(name, return new Entry(name,
Pattern.compile(System.lineSeparator()).matcher(pem).replaceAll("\r"), Pattern.compile(System.lineSeparator()).matcher(pem).replaceAll("\r"),
clazz, password()); clazz, provider, password());
} }
Entry makeNoCRLF(String name) { Entry makeNoCRLF(String name) {
return new Entry(name, return new Entry(name,
LF.matcher(CR.matcher(pem).replaceAll("")). LF.matcher(CR.matcher(pem).replaceAll("")).
replaceAll(""), replaceAll(""),
clazz, password()); clazz, provider, password());
} }
} }
@ -401,10 +556,12 @@ class PEMData {
static { static {
pubList.add(rsapub); pubList.add(rsapub);
pubList.add(rsapsspub);
pubList.add(rsapubbc); pubList.add(rsapubbc);
pubList.add(ecsecp256pub.makeCR("ecsecp256pub-r")); pubList.add(ecsecp256pub.makeCR("ecsecp256pub-r"));
pubList.add(ecsecp256pub.makeCRLF("ecsecp256pub-rn")); pubList.add(ecsecp256pub.makeCRLF("ecsecp256pub-rn"));
privList.add(rsapriv); privList.add(rsapriv);
privList.add(rsapsspriv);
privList.add(rsaprivbc); privList.add(rsaprivbc);
privList.add(ecsecp256); privList.add(ecsecp256);
privList.add(ecsecp384); privList.add(ecsecp384);
@ -413,9 +570,12 @@ class PEMData {
privList.add(rsaOpenSSL); privList.add(rsaOpenSSL);
oasList.add(oasrfc8410); oasList.add(oasrfc8410);
oasList.add(oasbcpem); oasList.add(oasbcpem);
oasList.add(oasec);
oasList.add(oasxdh);
certList.add(rsaCert); certList.add(rsaCert);
certList.add(ecCert); certList.add(ecCert);
certList.add(rsaCrl);
entryList.addAll(pubList); entryList.addAll(pubList);
entryList.addAll(privList); entryList.addAll(privList);
@ -429,6 +589,11 @@ class PEMData {
failureEntryList.add(new Entry("emptyPEM", "", DEREncodable.class, null)); failureEntryList.add(new Entry("emptyPEM", "", DEREncodable.class, null));
failureEntryList.add(new Entry("nullPEM", null, DEREncodable.class, null)); failureEntryList.add(new Entry("nullPEM", null, DEREncodable.class, null));
failureEntryList.add(incorrectFooter);
failureEntryList.add(invalidPEM);
failureEntryList.add(invalidDer);
failureEntryList.add(invalidHeader);
failureEntryList.add(invalidFooter);
} }
static void checkResults(PEMData.Entry entry, String result) { static void checkResults(PEMData.Entry entry, String result) {

View File

@ -26,6 +26,7 @@
/* /*
* @test * @test
* @bug 8298420 * @bug 8298420
* @library /test/lib
* @modules java.base/sun.security.pkcs * @modules java.base/sun.security.pkcs
* java.base/sun.security.util * java.base/sun.security.util
* @summary Testing basic PEM API decoding * @summary Testing basic PEM API decoding
@ -37,23 +38,27 @@ import java.io.*;
import java.lang.Class; import java.lang.Class;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.*; import java.security.*;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509CRL; import java.security.cert.X509CRL;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.security.interfaces.*; import java.security.interfaces.*;
import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.*;
import java.security.spec.X509EncodedKeySpec;
import java.util.*; import java.util.*;
import java.util.Arrays; import java.util.Arrays;
import jdk.test.lib.Asserts;
import sun.security.pkcs.PKCS8Key;
import sun.security.util.Pem; import sun.security.util.Pem;
public class PEMDecoderTest { public class PEMDecoderTest {
static HexFormat hex = HexFormat.of(); 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:"); 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:"); System.out.println("Decoder test returning DEREncodable class:");
PEMData.entryList.forEach(entry -> test(entry, DEREncodable.class)); PEMData.entryList.forEach(entry -> test(entry, DEREncodable.class));
System.out.println("Decoder test with encrypted PEM:"); System.out.println("Decoder test with encrypted PEM:");
@ -95,7 +100,11 @@ public class PEMDecoderTest {
System.out.println("Check a Signature/Verify op is successful:"); System.out.println("Check a Signature/Verify op is successful:");
PEMData.privList.forEach(PEMDecoderTest::testSignature); 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);
System.out.println("Checking if ecCSR:"); System.out.println("Checking if ecCSR:");
test(PEMData.ecCSR); test(PEMData.ecCSR);
@ -182,6 +191,10 @@ public class PEMDecoderTest {
} catch (Exception e) { } catch (Exception e) {
throw new AssertionError("error getting key", e); throw new AssertionError("error getting key", e);
} }
testCertTypeConverter(PEMData.ecCert);
System.out.println("Decoder test testCoefZero:");
testCoefZero(PEMData.rsaCrtCoefZeroPriv);
} }
static void testInputStream() throws IOException { static void testInputStream() throws IOException {
@ -231,6 +244,24 @@ public class PEMDecoderTest {
throw new AssertionError("Failed"); throw new AssertionError("Failed");
} }
// test that X509 CERTIFICATE is converted to CERTIFICATE in PEM
static void testCertTypeConverter(PEMData.Entry entry) throws CertificateEncodingException {
String certPem = entry.pem().replace("CERTIFICATE", "X509 CERTIFICATE");
Asserts.assertEqualsByteArray(entry.der(),
PEMDecoder.of().decode(certPem, X509Certificate.class).getEncoded());
certPem = entry.pem().replace("CERTIFICATE", "X.509 CERTIFICATE");
Asserts.assertEqualsByteArray(entry.der(),
PEMDecoder.of().decode(certPem, X509Certificate.class).getEncoded());
}
// test that when the crtCoeff is zero, the key is decoded but only the modulus and private
// exponent are used resulting in a different der
static void testCoefZero(PEMData.Entry entry) {
RSAPrivateKey decoded = PEMDecoder.of().decode(entry.pem(), RSAPrivateKey.class);
Asserts.assertNotEqualsByteArray(decoded.getEncoded(), entry.der());
}
static void testPEMRecord(PEMData.Entry entry) { static void testPEMRecord(PEMData.Entry entry) {
PEMRecord r = PEMDecoder.of().decode(entry.pem(), PEMRecord.class); PEMRecord r = PEMDecoder.of().decode(entry.pem(), PEMRecord.class);
String expected = entry.pem().split("-----")[2].replace(System.lineSeparator(), ""); String expected = entry.pem().split("-----")[2].replace(System.lineSeparator(), "");
@ -333,13 +364,26 @@ public class PEMDecoderTest {
// Change the Entry to use the given class as the expected class returned // Change the Entry to use the given class as the expected class returned
static DEREncodable test(PEMData.Entry entry, Class c) { 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 // Run test with a given Entry
static DEREncodable test(PEMData.Entry 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) {
System.out.printf("Testing %s %s%n", entry.name(), entry.provider());
try { 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() + ")"); System.out.println("PASS (" + entry.name() + ")");
return r; return r;
} catch (Exception | AssertionError e) { } catch (Exception | AssertionError e) {
@ -412,6 +456,19 @@ public class PEMDecoderTest {
} }
} }
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 { static void testClass(PEMData.Entry entry, Class clazz) throws IOException {
var pk = PEMDecoder.of().decode(entry.pem(), clazz); var pk = PEMDecoder.of().decode(entry.pem(), clazz);
} }
@ -472,9 +529,15 @@ public class PEMDecoderTest {
"should not be null"); "should not be null");
} }
AlgorithmParameterSpec spec = null;
String algorithm = switch(privateKey.getAlgorithm()) { String algorithm = switch(privateKey.getAlgorithm()) {
case "EC" -> "SHA256withECDSA"; case "EC" -> "SHA256withECDSA";
case "EdDSA" -> "EdDSA"; case "EdDSA" -> "EdDSA";
case "RSASSA-PSS" -> {
spec = new PSSParameterSpec(
"SHA-256", "MGF1", MGF1ParameterSpec.SHA256, 32, 1);
yield "RSASSA-PSS";
}
case null -> { case null -> {
System.out.println("Algorithm is null " + System.out.println("Algorithm is null " +
entry.name()); entry.name());
@ -487,6 +550,9 @@ public class PEMDecoderTest {
try { try {
if (d instanceof PrivateKey) { if (d instanceof PrivateKey) {
s = Signature.getInstance(algorithm); s = Signature.getInstance(algorithm);
if (spec != null) {
s.setParameter(spec);
}
s.initSign(privateKey); s.initSign(privateKey);
s.update(data); s.update(data);
s.sign(); s.sign();

View File

@ -26,9 +26,15 @@
/* /*
* @test * @test
* @bug 8298420 * @bug 8298420
* @library /test/lib
* @summary Testing basic PEM API encoding * @summary Testing basic PEM API encoding
* @enablePreview * @enablePreview
* @modules java.base/sun.security.util * @modules java.base/sun.security.util
* @run main PEMEncoderTest PBEWithHmacSHA256AndAES_128
* @run main/othervm -Djava.security.properties=${test.src}/java.security-anotherAlgo
* PEMEncoderTest PBEWithHmacSHA512AndAES_256
* @run main/othervm -Djava.security.properties=${test.src}/java.security-emptyAlgo
* PEMEncoderTest PBEWithHmacSHA256AndAES_128
*/ */
import sun.security.util.Pem; import sun.security.util.Pem;
@ -39,13 +45,22 @@ import javax.crypto.spec.PBEParameterSpec;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.*; import java.security.*;
import java.security.spec.InvalidParameterSpecException; import java.security.spec.InvalidParameterSpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.*; import java.util.*;
import jdk.test.lib.security.SecurityUtils;
import static jdk.test.lib.Asserts.assertEquals;
import static jdk.test.lib.Asserts.assertThrows;
public class PEMEncoderTest { public class PEMEncoderTest {
static Map<String, DEREncodable> keymap; static Map<String, DEREncodable> keymap;
static String pkcs8DefaultAlgExpect;
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
pkcs8DefaultAlgExpect = args[0];
PEMEncoder encoder = PEMEncoder.of(); PEMEncoder encoder = PEMEncoder.of();
// These entries are removed // These entries are removed
@ -63,7 +78,12 @@ public class PEMEncoderTest {
System.out.println("New instance re-encode testToString:"); System.out.println("New instance re-encode testToString:");
keymap.keySet().stream().forEach(key -> testToString(key, keymap.keySet().stream().forEach(key -> testToString(key,
PEMEncoder.of())); 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); keymap = generateObjKeyMap(PEMData.encryptedList);
System.out.println("Same instance Encoder match test:"); System.out.println("Same instance Encoder match test:");
keymap.keySet().stream().forEach(key -> testEncryptedMatch(key, encoder)); keymap.keySet().stream().forEach(key -> testEncryptedMatch(key, encoder));
@ -86,6 +106,13 @@ public class PEMEncoderTest {
PEMRecord pemRecord = PEMRecord pemRecord =
d.decode(PEMData.ed25519ep8.pem(), PEMRecord.class); d.decode(PEMData.ed25519ep8.pem(), PEMRecord.class);
PEMData.checkResults(PEMData.ed25519ep8, pemRecord.toString()); PEMData.checkResults(PEMData.ed25519ep8, pemRecord.toString());
// test PemRecord is encapsulated with PEM header and footer on encoding
String[] pemLines = PEMData.ed25519ep8.pem().split("\n");
String[] pemNoHeaderFooter = Arrays.copyOfRange(pemLines, 1, pemLines.length - 1);
PEMRecord pemR = new PEMRecord("ENCRYPTED PRIVATE KEY", String.join("\n",
pemNoHeaderFooter));
PEMData.checkResults(PEMData.ed25519ep8.pem(), encoder.encodeToString(pemR));
} }
static Map generateObjKeyMap(List<PEMData.Entry> list) { static Map generateObjKeyMap(List<PEMData.Entry> list) {
@ -145,10 +172,12 @@ public class PEMEncoderTest {
static void testEncrypted(String key, PEMEncoder encoder) { static void testEncrypted(String key, PEMEncoder encoder) {
PEMData.Entry entry = PEMData.getEntry(key); PEMData.Entry entry = PEMData.getEntry(key);
try { try {
encoder.withEncryption( String pem = encoder.withEncryption(
(entry.password() != null ? entry.password() : (entry.password() != null ? entry.password() :
"fish".toCharArray())) "fish".toCharArray()))
.encodeToString(keymap.get(key)); .encodeToString(keymap.get(key));
verifyEncriptionAlg(pem);
} catch (RuntimeException e) { } catch (RuntimeException e) {
throw new AssertionError("Encrypted encoder failed with " + throw new AssertionError("Encrypted encoder failed with " +
entry.name(), e); entry.name(), e);
@ -157,6 +186,11 @@ public class PEMEncoderTest {
System.out.println("PASS: " + entry.name()); 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 Test cannot verify PEM was the same as known PEM because we have no
public access to the AlgoritmID.params and PBES2Parameters. public access to the AlgoritmID.params and PBES2Parameters.
@ -195,5 +229,42 @@ public class PEMEncoderTest {
PEMData.checkResults(entry, result); PEMData.checkResults(entry, result);
System.out.println("PASS: " + entry.name()); System.out.println("PASS: " + entry.name());
} }
}
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]; }
}
}

View File

@ -0,0 +1,97 @@
/*
* 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 PEM API is thread safe
* @enablePreview
* @modules java.base/sun.security.util
*/
import java.security.*;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import jdk.test.lib.security.SecurityUtils;
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)) {
Map<Integer, PublicKey> keys = new HashMap<>();
Map<Integer, String> encoded = Collections.synchronizedMap(new HashMap<>());
Map<Integer, String> decoded = Collections.synchronizedMap(new HashMap<>());
final CountDownLatch encodingComplete = new CountDownLatch(KEYS_COUNT);
final CountDownLatch decodingComplete = new CountDownLatch(KEYS_COUNT);
// Generate keys and encode them in parallel
for (int i = 0; i < KEYS_COUNT; i++) {
final int finalI = i;
KeyPair kp = getKeyPair();
keys.put(finalI, kp.getPublic());
ex.submit(() -> {
encoded.put(finalI, encoder.encodeToString(kp.getPublic()));
encodingComplete.countDown();
});
}
encodingComplete.await();
// Decode keys in parallel
PEMDecoder decoder = PEMDecoder.of();
for (Map.Entry<Integer, String> entry : encoded.entrySet()) {
ex.submit(() -> {
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 key map
for (Map.Entry<Integer, PublicKey> kp : keys.entrySet()) {
if (!decoded.get(kp.getKey()).equals(kp.getValue().toString())) {
throw new RuntimeException("a key was not properly encoded and decoded: " + decoded);
}
}
}
System.out.println("PASS: testThreadSafety");
}
private static KeyPair getKeyPair() throws NoSuchAlgorithmException {
String alg = "EC";
KeyPairGenerator kpg = KeyPairGenerator.getInstance(alg);
kpg.initialize(SecurityUtils.getTestKeySize(alg));
return kpg.generateKeyPair();
}
}

View File

@ -0,0 +1 @@
jdk.epkcs8.defaultAlgorithm=PBEWithHmacSHA512AndAES_256

View File

@ -0,0 +1 @@
jdk.epkcs8.defaultAlgorithm=

View File

@ -25,11 +25,15 @@
/** /**
* @test * @test
* @library /test/lib
* @modules java.base/sun.security.util
* @bug 8298420 * @bug 8298420
* @summary Testing encryptKey * @summary Testing encryptKey
* @enablePreview * @enablePreview
*/ */
import sun.security.util.Pem;
import javax.crypto.EncryptedPrivateKeyInfo; import javax.crypto.EncryptedPrivateKeyInfo;
import javax.crypto.SecretKey; import javax.crypto.SecretKey;
import javax.crypto.spec.PBEParameterSpec; import javax.crypto.spec.PBEParameterSpec;
@ -37,8 +41,13 @@ import javax.crypto.spec.SecretKeySpec;
import java.security.AlgorithmParameters; import java.security.AlgorithmParameters;
import java.security.PEMDecoder; import java.security.PEMDecoder;
import java.security.PrivateKey; import java.security.PrivateKey;
import java.security.Provider;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Arrays; import java.util.Arrays;
import static jdk.test.lib.Asserts.assertEquals;
public class EncryptKey { public class EncryptKey {
private static final String encEdECKey = private static final String encEdECKey =
@ -56,6 +65,8 @@ public class EncryptKey {
passwdText.getBytes(), "PBE"); passwdText.getBytes(), "PBE");
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
Provider p = Security.getProvider(System.getProperty("test.provider.name", "SunJCE"));
EncryptedPrivateKeyInfo ekpi = PEMDecoder.of().decode(encEdECKey, EncryptedPrivateKeyInfo ekpi = PEMDecoder.of().decode(encEdECKey,
EncryptedPrivateKeyInfo.class); EncryptedPrivateKeyInfo.class);
PrivateKey priKey = PEMDecoder.of().withDecryption(password). PrivateKey priKey = PEMDecoder.of().withDecryption(password).
@ -71,6 +82,19 @@ public class EncryptKey {
" with expected."); " with expected.");
} }
// Test encryptKey(PrivateKey, char[], String, ...) with provider
e = EncryptedPrivateKeyInfo.encryptKey(priKey, password, ekpi.getAlgName(),
ap.getParameterSpec(PBEParameterSpec.class), p);
if (!Arrays.equals(ekpi.getEncryptedData(), e.getEncryptedData())) {
throw new AssertionError("encryptKey() didn't match" +
" with expected.");
}
// Test encryptKey(PrivateKey, char[], String, ...) with provider and null algorithm
e = EncryptedPrivateKeyInfo.encryptKey(priKey, password, null, null,
p);
assertEquals(e.getAlgName(), Pem.DEFAULT_ALGO);
// Test encryptKey(PrivateKey, Key, String, ...) // Test encryptKey(PrivateKey, Key, String, ...)
e = EncryptedPrivateKeyInfo.encryptKey(priKey, key, ekpi.getAlgName(), e = EncryptedPrivateKeyInfo.encryptKey(priKey, key, ekpi.getAlgName(),
ap.getParameterSpec(PBEParameterSpec.class),null, null); ap.getParameterSpec(PBEParameterSpec.class),null, null);
@ -78,5 +102,26 @@ public class EncryptKey {
throw new AssertionError("encryptKey() didn't match" + throw new AssertionError("encryptKey() didn't match" +
" with expected."); " with expected.");
} }
// Test encryptKey(PrivateKey, Key, String, ...) with provider and null random
e = EncryptedPrivateKeyInfo.encryptKey(priKey, key, ekpi.getAlgName(),
ap.getParameterSpec(PBEParameterSpec.class), p, null);
if (!Arrays.equals(ekpi.getEncryptedData(), e.getEncryptedData())) {
throw new AssertionError("encryptKey() didn't match" +
" with expected.");
}
// Test encryptKey(PrivateKey, Key, String, ...) with provider and SecureRandom
e = EncryptedPrivateKeyInfo.encryptKey(priKey, key, ekpi.getAlgName(),
ap.getParameterSpec(PBEParameterSpec.class), p, new SecureRandom());
if (!Arrays.equals(ekpi.getEncryptedData(), e.getEncryptedData())) {
throw new AssertionError("encryptKey() didn't match" +
" with expected.");
}
// Test encryptKey(PrivateKey, Key, String, ...) with provider and null algorithm
e = EncryptedPrivateKeyInfo.encryptKey(priKey, key, null, null,
p, new SecureRandom());
assertEquals(e.getAlgName(), Pem.DEFAULT_ALGO);
} }
} }

View File

@ -35,6 +35,8 @@ import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.SecretKeySpec;
import java.security.PEMDecoder; import java.security.PEMDecoder;
import java.security.PrivateKey; import java.security.PrivateKey;
import java.security.Provider;
import java.security.Security;
import java.util.Arrays; import java.util.Arrays;
public class GetKey { public class GetKey {
@ -48,12 +50,29 @@ public class GetKey {
IycFtI70ciPjgwDSjtCcPxR8fSxJPrm2yOJsRVo= IycFtI70ciPjgwDSjtCcPxR8fSxJPrm2yOJsRVo=
-----END ENCRYPTED PRIVATE KEY----- -----END ENCRYPTED PRIVATE KEY-----
"""; """;
private static final String encDHECKey =
"""
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIBvDBmBgkqhkiG9w0BBQ0wWTA4BgkqhkiG9w0BBQwwKwQUN8pkErJx7aqH0fJF
BcOadPKiuRoCAhAAAgEQMAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAECBBAT1Vwd
gU4rTd6zy7lKr4wmBIIBUMe+2+O0AG6t4CMSHcDVceRg2jvbs5PmPjW4Ka5mDich
hVEsjSpJLbUyJdbji6UaiUpuWgvYSMLZ10pfhOFw/ssXwCw+JrlXUqDpQGLaW8ZR
zSL3CoozTI2Y6EBdWt53KbySwtZMoTpW/W3vPi98bJXtR635msf6gYXmSUP7DyoJ
79dxz3pRYsnOuBe0yZ2wTq9iMgTMudzLJAFX2qyi+3KOb1g5Va9DYAqJmzCYOd74
+I+0gGNFtSc1vGQYr3cAfcKT8AZ1RHE4IkpnpgFD5HsZ8f4hy0yK8juk9NE9Gzuy
B929LBXk6V3L0MKzIABS3QvAlhWETM6XtGBDugzAgsooo9lEHLwYRldvOlL+QYyE
CtqDmXOrgEMWvxWGEFCTKYhKkqMKjU3y3GiozEEdb9j2okW1s30yHQjIoj0OR4nB
D8GeP0QnY73NfbOw7z81TA==
-----END ENCRYPTED PRIVATE KEY-----
""";
private static final String passwdText = "fish"; private static final String passwdText = "fish";
private static final char[] password = passwdText.toCharArray(); private static final char[] password = passwdText.toCharArray();
private static final SecretKey key = new SecretKeySpec( private static final SecretKey key = new SecretKeySpec(
passwdText.getBytes(), "PBE"); passwdText.getBytes(), "PBE");
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
Provider p = Security.getProvider(System.getProperty("test.provider.name", "SunJCE"));
EncryptedPrivateKeyInfo ekpi = PEMDecoder.of().decode(encEdECKey, EncryptedPrivateKeyInfo ekpi = PEMDecoder.of().decode(encEdECKey,
EncryptedPrivateKeyInfo.class); EncryptedPrivateKeyInfo.class);
PrivateKey priKey = PEMDecoder.of().withDecryption(password). PrivateKey priKey = PEMDecoder.of().withDecryption(password).
@ -66,11 +85,23 @@ public class GetKey {
+ "match with expected."); + "match with expected.");
} }
// Test getKey(key, provider) // Test getKey(key, provider) provider null
if (!Arrays.equals(priKey.getEncoded(), if (!Arrays.equals(priKey.getEncoded(),
ekpi.getKey(key, null).getEncoded())) { ekpi.getKey(key, null).getEncoded())) {
throw new AssertionError("getKey(key, provider) " + throw new AssertionError("getKey(key, provider) " +
"didn't match with expected."); "didn't match with expected.");
} }
// Test getKey(key, provider) with provider
EncryptedPrivateKeyInfo ekpiDH = PEMDecoder.of().decode(encDHECKey,
EncryptedPrivateKeyInfo.class);
PrivateKey priKeyDH = PEMDecoder.of().withDecryption(password).
decode(encDHECKey, PrivateKey.class);
if (!Arrays.equals(priKeyDH.getEncoded(),
ekpiDH.getKey(key, p).getEncoded())) {
throw new AssertionError("getKey(key, provider) " +
"didn't match with expected.");
}
} }
} }

View File

@ -31,10 +31,13 @@
* java.base/sun.security.provider * java.base/sun.security.provider
* java.base/sun.security.x509 * java.base/sun.security.x509
* @run main PKCS8Test * @run main PKCS8Test
* @run main/othervm -Dtest.provider.name=SunJCE PKCS8Test
*/ */
import java.math.BigInteger; import java.math.BigInteger;
import java.security.InvalidKeyException; import java.security.InvalidKeyException;
import java.security.Provider;
import java.security.Security;
import java.util.Arrays; import java.util.Arrays;
import java.util.HexFormat; import java.util.HexFormat;
@ -45,6 +48,7 @@ import sun.security.provider.DSAPrivateKey;
public class PKCS8Test { public class PKCS8Test {
static Provider provider;
static final String FORMAT = "PKCS#8"; static final String FORMAT = "PKCS#8";
static final String EXPECTED_ALG_ID_CHRS = "DSA, \n" + static final String EXPECTED_ALG_ID_CHRS = "DSA, \n" +
"\tp: 02\n\tq: 03\n\tg: 04\n"; "\tp: 02\n\tq: 03\n\tg: 04\n";
@ -59,7 +63,7 @@ public class PKCS8Test {
"0403020101"); // PrivateKey OCTET int x = 1 "0403020101"); // PrivateKey OCTET int x = 1
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
provider = Security.getProvider(System.getProperty("test.provider.name"));
byte[] encodedKey = new DSAPrivateKey( byte[] encodedKey = new DSAPrivateKey(
BigInteger.valueOf(1), BigInteger.valueOf(1),
BigInteger.valueOf(2), BigInteger.valueOf(2),
@ -73,7 +77,8 @@ public class PKCS8Test {
.toString(encodedKey)); .toString(encodedKey));
} }
PKCS8Key decodedKey = (PKCS8Key)PKCS8Key.parseKey(encodedKey); PKCS8Key decodedKey = provider == null ? (PKCS8Key)PKCS8Key.parseKey(encodedKey) :
(PKCS8Key)PKCS8Key.parseKey(encodedKey, provider);
assert(ALGORITHM.equalsIgnoreCase(decodedKey.getAlgorithm())); assert(ALGORITHM.equalsIgnoreCase(decodedKey.getAlgorithm()));
assert(FORMAT.equalsIgnoreCase(decodedKey.getFormat())); assert(FORMAT.equalsIgnoreCase(decodedKey.getFormat()));
@ -126,7 +131,11 @@ public class PKCS8Test {
original[1] = (byte) (length - 2); // the length field inside DER original[1] = (byte) (length - 2); // the length field inside DER
original[4] = (byte) newVersion; // the version inside DER original[4] = (byte) newVersion; // the version inside DER
try { try {
if (provider == null) {
PKCS8Key.parseKey(original); PKCS8Key.parseKey(original);
} else {
PKCS8Key.parseKey(original, provider);
}
} catch (InvalidKeyException e) { } catch (InvalidKeyException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -45,6 +45,8 @@ public final class SecurityUtils {
private enum KeySize{ private enum KeySize{
RSA(2048), RSA(2048),
DSA(2048), DSA(2048),
Ed25519(256),
EC(256),
DH(2048); DH(2048);
private final int keySize; private final int keySize;
@ -145,6 +147,8 @@ public final class SecurityUtils {
return switch (algo) { return switch (algo) {
case "RSA" -> KeySize.RSA.keySize; case "RSA" -> KeySize.RSA.keySize;
case "DSA" -> KeySize.DSA.keySize; case "DSA" -> KeySize.DSA.keySize;
case "Ed25519" -> KeySize.Ed25519.keySize;
case "EC" -> KeySize.EC.keySize;
case "DH", "DiffieHellman" -> KeySize.DH.keySize; case "DH", "DiffieHellman" -> KeySize.DH.keySize;
default -> throw new RuntimeException("Test key size not defined for " + algo); default -> throw new RuntimeException("Test key size not defined for " + algo);
}; };