Skip to content

Commit b89f364

Browse files
author
Anthony Scarpino
committed
8358099: PEM spec updates
Reviewed-by: weijun Backport-of: 78158f3
1 parent 0694cc1 commit b89f364

File tree

6 files changed

+48
-67
lines changed

6 files changed

+48
-67
lines changed

src/java.base/share/classes/java/security/PEMDecoder.java

Lines changed: 18 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -81,24 +81,24 @@
8181
* {@link PEMRecord}.
8282
*
8383
* <p> The {@linkplain #decode(String, Class)} and
84-
* {@linkplain #decode(InputStream, Class)} methods take a Class parameter
84+
* {@linkplain #decode(InputStream, Class)} methods take a class parameter
8585
* which determines the type of {@code DEREncodable} that is returned. These
8686
* methods are useful when extracting or changing the return class.
8787
* For example, if the PEM contains both public and private keys, the
88-
* Class parameter can specify which to return. Use
88+
* class parameter can specify which to return. Use
8989
* {@code PrivateKey.class} to return only the private key.
90-
* If the Class parameter is set to {@code X509EncodedKeySpec.class}, the
90+
* If the class parameter is set to {@code X509EncodedKeySpec.class}, the
9191
* public key will be returned in that format. Any type of PEM data can be
9292
* decoded into a {@code PEMRecord} by specifying {@code PEMRecord.class}.
93-
* If the Class parameter doesn't match the PEM content, an
94-
* {@code IllegalArgumentException} will be thrown.
93+
* If the class parameter doesn't match the PEM content, a
94+
* {@linkplain ClassCastException} will be thrown.
9595
*
9696
* <p> A new {@code PEMDecoder} instance is created when configured
9797
* with {@linkplain #withFactory(Provider)} and/or
9898
* {@linkplain #withDecryption(char[])}. {@linkplain #withFactory(Provider)}
9999
* configures the decoder to use only {@linkplain KeyFactory} and
100100
* {@linkplain CertificateFactory} instances from the given {@code Provider}.
101-
* {@link#withDecryption(char[])} configures the decoder to decrypt all
101+
* {@linkplain #withDecryption(char[])} configures the decoder to decrypt all
102102
* encrypted private key PEM data using the given password.
103103
* Configuring an instance for decryption does not prevent decoding with
104104
* unencrypted PEM. Any encrypted PEM that fails decryption
@@ -117,15 +117,15 @@
117117
* <p> Here is an example of a {@code PEMDecoder} configured with decryption
118118
* and a factory provider:
119119
* {@snippet lang = java:
120-
* PEMDecoder pe = PEMDecoder.of().withDecryption(password).
120+
* PEMDecoder pd = PEMDecoder.of().withDecryption(password).
121121
* withFactory(provider);
122-
* byte[] pemData = pe.decode(privKey);
122+
* byte[] pemData = pd.decode(privKey);
123123
* }
124124
*
125125
* @implNote An implementation may support other PEM types and
126-
* {@code DEREncodables}. This implementation additionally supports PEM types:
127-
* {@code X509 CERTIFICATE}, {@code X.509 CERTIFICATE}, {@code CRL},
128-
* and {@code RSA PRIVATE KEY}.
126+
* {@code DEREncodable} objects. This implementation additionally supports
127+
* the following PEM types: {@code X509 CERTIFICATE},
128+
* {@code X.509 CERTIFICATE}, {@code CRL}, and {@code RSA PRIVATE KEY}.
129129
*
130130
* @see PEMEncoder
131131
* @see PEMRecord
@@ -179,13 +179,13 @@ private DEREncodable decode(PEMRecord pem) {
179179
return switch (pem.type()) {
180180
case Pem.PUBLIC_KEY -> {
181181
X509EncodedKeySpec spec =
182-
new X509EncodedKeySpec(decoder.decode(pem.pem()));
182+
new X509EncodedKeySpec(decoder.decode(pem.content()));
183183
yield getKeyFactory(
184184
KeyUtil.getAlgorithm(spec.getEncoded())).
185185
generatePublic(spec);
186186
}
187187
case Pem.PRIVATE_KEY -> {
188-
PKCS8Key p8key = new PKCS8Key(decoder.decode(pem.pem()));
188+
PKCS8Key p8key = new PKCS8Key(decoder.decode(pem.content()));
189189
String algo = p8key.getAlgorithm();
190190
KeyFactory kf = getKeyFactory(algo);
191191
DEREncodable d = kf.generatePrivate(
@@ -216,27 +216,27 @@ yield new KeyPair(getKeyFactory(algo).
216216
case Pem.ENCRYPTED_PRIVATE_KEY -> {
217217
if (password == null) {
218218
yield new EncryptedPrivateKeyInfo(decoder.decode(
219-
pem.pem()));
219+
pem.content()));
220220
}
221-
yield new EncryptedPrivateKeyInfo(decoder.decode(pem.pem())).
221+
yield new EncryptedPrivateKeyInfo(decoder.decode(pem.content())).
222222
getKey(password.getPassword());
223223
}
224224
case Pem.CERTIFICATE, Pem.X509_CERTIFICATE,
225225
Pem.X_509_CERTIFICATE -> {
226226
CertificateFactory cf = getCertFactory("X509");
227227
yield (X509Certificate) cf.generateCertificate(
228-
new ByteArrayInputStream(decoder.decode(pem.pem())));
228+
new ByteArrayInputStream(decoder.decode(pem.content())));
229229
}
230230
case Pem.X509_CRL, Pem.CRL -> {
231231
CertificateFactory cf = getCertFactory("X509");
232232
yield (X509CRL) cf.generateCRL(
233-
new ByteArrayInputStream(decoder.decode(pem.pem())));
233+
new ByteArrayInputStream(decoder.decode(pem.content())));
234234
}
235235
case Pem.RSA_PRIVATE_KEY -> {
236236
KeyFactory kf = getKeyFactory("RSA");
237237
yield kf.generatePrivate(
238238
RSAPrivateCrtKeyImpl.getKeySpec(decoder.decode(
239-
pem.pem())));
239+
pem.content())));
240240
}
241241
default -> pem;
242242
};
@@ -271,7 +271,6 @@ yield new EncryptedPrivateKeyInfo(decoder.decode(pem.pem())).
271271
*/
272272
public DEREncodable decode(String str) {
273273
Objects.requireNonNull(str);
274-
DEREncodable de;
275274
try {
276275
return decode(new ByteArrayInputStream(
277276
str.getBytes(StandardCharsets.UTF_8)));
@@ -483,9 +482,6 @@ private CertificateFactory getCertFactory(String algorithm) {
483482
* from the specified {@link Provider} to produce cryptographic objects.
484483
* Any errors using the {@code Provider} will occur during decoding.
485484
*
486-
* <p>If {@code provider} is {@code null}, a new instance is returned with
487-
* the default provider configuration.
488-
*
489485
* @param provider the factory provider
490486
* @return a new PEMEncoder instance configured to the {@code Provider}.
491487
* @throws NullPointerException if {@code provider} is null

src/java.base/share/classes/java/security/PEMEncoder.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@
7171
* OneAsymmetricKey structure using the "PRIVATE KEY" type.
7272
*
7373
* <p> When encoding a {@link PEMRecord}, the API surrounds the
74-
* {@linkplain PEMRecord#pem()} with the PEM header and footer
74+
* {@linkplain PEMRecord#content()} with the PEM header and footer
7575
* from {@linkplain PEMRecord#type()}. {@linkplain PEMRecord#leadingData()} is
7676
* not included in the encoding. {@code PEMRecord} will not perform
7777
* validity checks on the data.
@@ -108,7 +108,8 @@
108108
* byte[] pemData = pe.encode(privKey);
109109
* }
110110
*
111-
* @implNote An implementation may support other PEM types and DEREncodables.
111+
* @implNote An implementation may support other PEM types and
112+
* {@code DEREncodable} objects.
112113
*
113114
*
114115
* @see PEMDecoder
@@ -287,7 +288,7 @@ private String buildKey(byte[] privateBytes, byte[] publicBytes) {
287288
}
288289

289290
// If `keySpec` is non-null, then `key` hasn't been established.
290-
// Setting a `key' prevents repeated key generations operations.
291+
// Setting a `key` prevents repeated key generation operations.
291292
// withEncryption() is a configuration method and cannot throw an
292293
// exception; therefore generation is delayed.
293294
if (keySpec != null) {

src/java.base/share/classes/java/security/PEMRecord.java

Lines changed: 20 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929

3030
import sun.security.util.Pem;
3131

32-
import java.util.Base64;
3332
import java.util.Objects;
3433

3534
/**
@@ -39,20 +38,20 @@
3938
* cryptographic object is not desired or the type has no
4039
* {@code DEREncodable}.
4140
*
42-
* <p> {@code type} and {@code pem} may not be {@code null}.
41+
* <p> {@code type} and {@code content} may not be {@code null}.
4342
* {@code leadingData} may be null if no non-PEM data preceded PEM header
4443
* during decoding. {@code leadingData} may be useful for reading metadata
4544
* that accompanies PEM data.
4645
*
4746
* <p> No validation is performed during instantiation to ensure that
48-
* {@code type} conforms to {@code RFC 7468}, that {@code pem} is valid Base64,
49-
* or that {@code pem} matches the {@code type}. {@code leadingData} is not
50-
* defensively copied and does not return a clone when
51-
* {@linkplain #leadingData()} is called.
47+
* {@code type} conforms to {@code RFC 7468}, that {@code content} is valid
48+
* Base64, or that {@code content} matches the {@code type}.
49+
* {@code leadingData} is not defensively copied and does not return a
50+
* clone when {@linkplain #leadingData()} is called.
5251
*
5352
* @param type the type identifier in the PEM header without PEM syntax labels.
5453
* For a public key, {@code type} would be "PUBLIC KEY".
55-
* @param pem any data between the PEM header and footer.
54+
* @param content the Base64-encoded data, excluding the PEM header and footer
5655
* @param leadingData any non-PEM data preceding the PEM header when decoding.
5756
*
5857
* @spec https://www.rfc-editor.org/info/rfc7468
@@ -64,25 +63,25 @@
6463
* @since 25
6564
*/
6665
@PreviewFeature(feature = PreviewFeature.Feature.PEM_API)
67-
public record PEMRecord(String type, String pem, byte[] leadingData)
66+
public record PEMRecord(String type, String content, byte[] leadingData)
6867
implements DEREncodable {
6968

7069
/**
7170
* Creates a {@code PEMRecord} instance with the given parameters.
7271
*
7372
* @param type the type identifier
74-
* @param pem the Base64-encoded data encapsulated by the PEM header and
75-
* footer.
73+
* @param content the Base64-encoded data, excluding the PEM header and
74+
* footer
7675
* @param leadingData any non-PEM data read during the decoding process
7776
* before the PEM header. This value maybe {@code null}.
78-
* @throws IllegalArgumentException if the {@code type} is incorrectly
77+
* @throws IllegalArgumentException if {@code type} is incorrectly
7978
* formatted.
80-
* @throws NullPointerException if {@code type} and/or {@code pem} are
79+
* @throws NullPointerException if {@code type} and/or {@code content} are
8180
* {@code null}.
8281
*/
83-
public PEMRecord(String type, String pem, byte[] leadingData) {
82+
public PEMRecord {
8483
Objects.requireNonNull(type, "\"type\" cannot be null.");
85-
Objects.requireNonNull(pem, "\"pem\" cannot be null.");
84+
Objects.requireNonNull(content, "\"content\" cannot be null.");
8685

8786
// With no validity checking on `type`, the constructor accept anything
8887
// including lowercase. The onus is on the caller.
@@ -92,37 +91,22 @@ public PEMRecord(String type, String pem, byte[] leadingData) {
9291
"Only the PEM type identifier is allowed");
9392
}
9493

95-
this.type = type;
96-
this.pem = pem;
97-
this.leadingData = leadingData;
9894
}
9995

10096
/**
10197
* Creates a {@code PEMRecord} instance with a given {@code type} and
102-
* {@code pem} data in String form. {@code leadingData} is set to null.
98+
* {@code content} data in String form. {@code leadingData} is set to null.
10399
*
104100
* @param type the PEM type identifier
105-
* @param pem the Base64-encoded data encapsulated by the PEM header and
106-
* footer.
107-
* @throws IllegalArgumentException if the {@code type} is incorrectly
101+
* @param content the Base64-encoded data, excluding the PEM header and
102+
* footer
103+
* @throws IllegalArgumentException if {@code type} is incorrectly
108104
* formatted.
109-
* @throws NullPointerException if {@code type} and/or {@code pem} are
105+
* @throws NullPointerException if {@code type} and/or {@code content} are
110106
* {@code null}.
111107
*/
112-
public PEMRecord(String type, String pem) {
113-
this(type, pem, null);
114-
}
115-
116-
/**
117-
* Returns the binary encoding from the Base64 data contained in
118-
* {@code pem}.
119-
*
120-
* @throws IllegalArgumentException if {@code pem} cannot be decoded.
121-
* @return a new array of the binary encoding each time this
122-
* method is called.
123-
*/
124-
public byte[] getEncoded() {
125-
return Base64.getMimeDecoder().decode(pem);
108+
public PEMRecord(String type, String content) {
109+
this(type, content, null);
126110
}
127111

128112
/**

src/java.base/share/classes/sun/security/provider/X509Factory.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -565,7 +565,7 @@ private static byte[] readOneBlock(InputStream is) throws IOException {
565565
} catch (EOFException e) {
566566
return null;
567567
}
568-
return Base64.getDecoder().decode(rec.pem());
568+
return Base64.getDecoder().decode(rec.content());
569569
} catch (IllegalArgumentException e) {
570570
throw new IOException(e);
571571
}

src/java.base/share/classes/sun/security/util/Pem.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ public static String pemEncoded(String type, byte[] der) {
343343
* @return PEM in a string
344344
*/
345345
public static String pemEncoded(PEMRecord pem) {
346-
String p = pem.pem().replaceAll("(.{64})", "$1\r\n");
346+
String p = pem.content().replaceAll("(.{64})", "$1\r\n");
347347
return pemEncoded(pem.type(), p);
348348
}
349349
}

test/jdk/java/security/PEM/PEMDecoderTest.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,8 @@ public static void main(String[] args) throws Exception {
116116
System.err.println("received: " + new String(rec.leadingData()));
117117
throw new AssertionError("ecCSRWithData preData wrong");
118118
}
119-
if (rec.pem().lastIndexOf("F") > rec.pem().length() - 5) {
120-
System.err.println("received: " + rec.pem());
119+
if (rec.content().lastIndexOf("F") > rec.content().length() - 5) {
120+
System.err.println("received: " + rec.content());
121121
throw new AssertionError("ecCSRWithData: " +
122122
"End of PEM data has an unexpected character");
123123
}
@@ -266,10 +266,10 @@ static void testPEMRecord(PEMData.Entry entry) {
266266
PEMRecord r = PEMDecoder.of().decode(entry.pem(), PEMRecord.class);
267267
String expected = entry.pem().split("-----")[2].replace(System.lineSeparator(), "");
268268
try {
269-
PEMData.checkResults(expected, r.pem());
269+
PEMData.checkResults(expected, r.content());
270270
} catch (AssertionError e) {
271271
System.err.println("expected:\n" + expected);
272-
System.err.println("received:\n" + r.pem());
272+
System.err.println("received:\n" + r.content());
273273
throw e;
274274
}
275275

0 commit comments

Comments
 (0)