Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 18 additions & 22 deletions src/java.base/share/classes/java/security/PEMDecoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,24 +81,24 @@
* {@link PEMRecord}.
*
* <p> The {@linkplain #decode(String, Class)} and
* {@linkplain #decode(InputStream, Class)} methods take a Class parameter
* {@linkplain #decode(InputStream, Class)} methods take a class parameter
* which determines the type of {@code DEREncodable} that is returned. These
* methods are useful when extracting or changing the return class.
* For example, if the PEM contains both public and private keys, the
* Class parameter can specify which to return. Use
* class parameter can specify which to return. Use
* {@code PrivateKey.class} to return only the private key.
* If the Class parameter is set to {@code X509EncodedKeySpec.class}, the
* If the class parameter is set to {@code X509EncodedKeySpec.class}, the
* public key will be returned in that format. Any type of PEM data can be
* decoded into a {@code PEMRecord} by specifying {@code PEMRecord.class}.
* If the Class parameter doesn't match the PEM content, an
* {@code IllegalArgumentException} will be thrown.
* If the class parameter doesn't match the PEM content, a
* {@linkplain ClassCastException} will be thrown.
*
* <p> A new {@code PEMDecoder} instance is created when configured
* with {@linkplain #withFactory(Provider)} and/or
* {@linkplain #withDecryption(char[])}. {@linkplain #withFactory(Provider)}
* configures the decoder to use only {@linkplain KeyFactory} and
* {@linkplain CertificateFactory} instances from the given {@code Provider}.
* {@link#withDecryption(char[])} configures the decoder to decrypt all
* {@linkplain #withDecryption(char[])} configures the decoder to decrypt all
* encrypted private key PEM data using the given password.
* Configuring an instance for decryption does not prevent decoding with
* unencrypted PEM. Any encrypted PEM that fails decryption
Expand All @@ -117,15 +117,15 @@
* <p> Here is an example of a {@code PEMDecoder} configured with decryption
* and a factory provider:
* {@snippet lang = java:
* PEMDecoder pe = PEMDecoder.of().withDecryption(password).
* PEMDecoder pd = PEMDecoder.of().withDecryption(password).
* withFactory(provider);
* byte[] pemData = pe.decode(privKey);
* byte[] pemData = pd.decode(privKey);
* }
*
* @implNote An implementation may support other PEM types and
* {@code DEREncodables}. This implementation additionally supports PEM types:
* {@code X509 CERTIFICATE}, {@code X.509 CERTIFICATE}, {@code CRL},
* and {@code RSA PRIVATE KEY}.
* {@code DEREncodable} objects. This implementation additionally supports
* the following PEM types: {@code X509 CERTIFICATE},
* {@code X.509 CERTIFICATE}, {@code CRL}, and {@code RSA PRIVATE KEY}.
*
* @see PEMEncoder
* @see PEMRecord
Expand Down Expand Up @@ -179,13 +179,13 @@ private DEREncodable decode(PEMRecord pem) {
return switch (pem.type()) {
case Pem.PUBLIC_KEY -> {
X509EncodedKeySpec spec =
new X509EncodedKeySpec(decoder.decode(pem.pem()));
new X509EncodedKeySpec(decoder.decode(pem.content()));
yield getKeyFactory(
KeyUtil.getAlgorithm(spec.getEncoded())).
generatePublic(spec);
}
case Pem.PRIVATE_KEY -> {
PKCS8Key p8key = new PKCS8Key(decoder.decode(pem.pem()));
PKCS8Key p8key = new PKCS8Key(decoder.decode(pem.content()));
String algo = p8key.getAlgorithm();
KeyFactory kf = getKeyFactory(algo);
DEREncodable d = kf.generatePrivate(
Expand Down Expand Up @@ -216,27 +216,27 @@ yield new KeyPair(getKeyFactory(algo).
case Pem.ENCRYPTED_PRIVATE_KEY -> {
if (password == null) {
yield new EncryptedPrivateKeyInfo(decoder.decode(
pem.pem()));
pem.content()));
}
yield new EncryptedPrivateKeyInfo(decoder.decode(pem.pem())).
yield new EncryptedPrivateKeyInfo(decoder.decode(pem.content())).
getKey(password.getPassword());
}
case Pem.CERTIFICATE, Pem.X509_CERTIFICATE,
Pem.X_509_CERTIFICATE -> {
CertificateFactory cf = getCertFactory("X509");
yield (X509Certificate) cf.generateCertificate(
new ByteArrayInputStream(decoder.decode(pem.pem())));
new ByteArrayInputStream(decoder.decode(pem.content())));
}
case Pem.X509_CRL, Pem.CRL -> {
CertificateFactory cf = getCertFactory("X509");
yield (X509CRL) cf.generateCRL(
new ByteArrayInputStream(decoder.decode(pem.pem())));
new ByteArrayInputStream(decoder.decode(pem.content())));
}
case Pem.RSA_PRIVATE_KEY -> {
KeyFactory kf = getKeyFactory("RSA");
yield kf.generatePrivate(
RSAPrivateCrtKeyImpl.getKeySpec(decoder.decode(
pem.pem())));
pem.content())));
}
default -> pem;
};
Expand Down Expand Up @@ -271,7 +271,6 @@ yield new EncryptedPrivateKeyInfo(decoder.decode(pem.pem())).
*/
public DEREncodable decode(String str) {
Objects.requireNonNull(str);
DEREncodable de;
try {
return decode(new ByteArrayInputStream(
str.getBytes(StandardCharsets.UTF_8)));
Expand Down Expand Up @@ -483,9 +482,6 @@ private CertificateFactory getCertFactory(String algorithm) {
* from the specified {@link Provider} to produce cryptographic objects.
* Any errors using the {@code Provider} will occur during decoding.
*
* <p>If {@code provider} is {@code null}, a new instance is returned with
* the default provider configuration.
*
* @param provider the factory provider
* @return a new PEMEncoder instance configured to the {@code Provider}.
* @throws NullPointerException if {@code provider} is null
Expand Down
7 changes: 4 additions & 3 deletions src/java.base/share/classes/java/security/PEMEncoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
* OneAsymmetricKey structure using the "PRIVATE KEY" type.
*
* <p> When encoding a {@link PEMRecord}, the API surrounds the
* {@linkplain PEMRecord#pem()} with the PEM header and footer
* {@linkplain PEMRecord#content()} with the PEM header and footer
* from {@linkplain PEMRecord#type()}. {@linkplain PEMRecord#leadingData()} is
* not included in the encoding. {@code PEMRecord} will not perform
* validity checks on the data.
Expand Down Expand Up @@ -108,7 +108,8 @@
* byte[] pemData = pe.encode(privKey);
* }
*
* @implNote An implementation may support other PEM types and DEREncodables.
* @implNote An implementation may support other PEM types and
* {@code DEREncodable} objects.
*
*
* @see PEMDecoder
Expand Down Expand Up @@ -287,7 +288,7 @@ private String buildKey(byte[] privateBytes, byte[] publicBytes) {
}

// If `keySpec` is non-null, then `key` hasn't been established.
// Setting a `key' prevents repeated key generations operations.
// Setting a `key` prevents repeated key generation operations.
// withEncryption() is a configuration method and cannot throw an
// exception; therefore generation is delayed.
if (keySpec != null) {
Expand Down
56 changes: 20 additions & 36 deletions src/java.base/share/classes/java/security/PEMRecord.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@

import sun.security.util.Pem;

import java.util.Base64;
import java.util.Objects;

/**
Expand All @@ -39,20 +38,20 @@
* cryptographic object is not desired or the type has no
* {@code DEREncodable}.
*
* <p> {@code type} and {@code pem} may not be {@code null}.
* <p> {@code type} and {@code content} may not be {@code null}.
* {@code leadingData} may be null if no non-PEM data preceded PEM header
* during decoding. {@code leadingData} may be useful for reading metadata
* that accompanies PEM data.
*
* <p> No validation is performed during instantiation to ensure that
* {@code type} conforms to {@code RFC 7468}, that {@code pem} is valid Base64,
* or that {@code pem} matches the {@code type}. {@code leadingData} is not
* defensively copied and does not return a clone when
* {@linkplain #leadingData()} is called.
* {@code type} conforms to {@code RFC 7468}, that {@code content} is valid
* Base64, or that {@code content} matches the {@code type}.
* {@code leadingData} is not defensively copied and does not return a
* clone when {@linkplain #leadingData()} is called.
*
* @param type the type identifier in the PEM header without PEM syntax labels.
* For a public key, {@code type} would be "PUBLIC KEY".
* @param pem any data between the PEM header and footer.
* @param content the Base64-encoded data, excluding the PEM header and footer
* @param leadingData any non-PEM data preceding the PEM header when decoding.
*
* @spec https://www.rfc-editor.org/info/rfc7468
Expand All @@ -64,25 +63,25 @@
* @since 25
*/
@PreviewFeature(feature = PreviewFeature.Feature.PEM_API)
public record PEMRecord(String type, String pem, byte[] leadingData)
public record PEMRecord(String type, String content, byte[] leadingData)
implements DEREncodable {

/**
* Creates a {@code PEMRecord} instance with the given parameters.
*
* @param type the type identifier
* @param pem the Base64-encoded data encapsulated by the PEM header and
* footer.
* @param content the Base64-encoded data, excluding the PEM header and
* footer
* @param leadingData any non-PEM data read during the decoding process
* before the PEM header. This value maybe {@code null}.
* @throws IllegalArgumentException if the {@code type} is incorrectly
* @throws IllegalArgumentException if {@code type} is incorrectly
* formatted.
* @throws NullPointerException if {@code type} and/or {@code pem} are
* @throws NullPointerException if {@code type} and/or {@code content} are
* {@code null}.
*/
public PEMRecord(String type, String pem, byte[] leadingData) {
public PEMRecord {
Objects.requireNonNull(type, "\"type\" cannot be null.");
Objects.requireNonNull(pem, "\"pem\" cannot be null.");
Objects.requireNonNull(content, "\"content\" cannot be null.");

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

this.type = type;
this.pem = pem;
this.leadingData = leadingData;
}

/**
* Creates a {@code PEMRecord} instance with a given {@code type} and
* {@code pem} data in String form. {@code leadingData} is set to null.
* {@code content} data in String form. {@code leadingData} is set to null.
*
* @param type the PEM type identifier
* @param pem the Base64-encoded data encapsulated by the PEM header and
* footer.
* @throws IllegalArgumentException if the {@code type} is incorrectly
* @param content the Base64-encoded data, excluding the PEM header and
* footer
* @throws IllegalArgumentException if {@code type} is incorrectly
* formatted.
* @throws NullPointerException if {@code type} and/or {@code pem} are
* @throws NullPointerException if {@code type} and/or {@code content} are
* {@code null}.
*/
public PEMRecord(String type, String pem) {
this(type, pem, null);
}

/**
* Returns the binary encoding from the Base64 data contained in
* {@code pem}.
*
* @throws IllegalArgumentException if {@code pem} cannot be decoded.
* @return a new array of the binary encoding each time this
* method is called.
*/
public byte[] getEncoded() {
return Base64.getMimeDecoder().decode(pem);
public PEMRecord(String type, String content) {
this(type, content, null);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -565,7 +565,7 @@ private static byte[] readOneBlock(InputStream is) throws IOException {
} catch (EOFException e) {
return null;
}
return Base64.getDecoder().decode(rec.pem());
return Base64.getDecoder().decode(rec.content());
} catch (IllegalArgumentException e) {
throw new IOException(e);
}
Expand Down
2 changes: 1 addition & 1 deletion src/java.base/share/classes/sun/security/util/Pem.java
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ public static String pemEncoded(String type, byte[] der) {
* @return PEM in a string
*/
public static String pemEncoded(PEMRecord pem) {
String p = pem.pem().replaceAll("(.{64})", "$1\r\n");
String p = pem.content().replaceAll("(.{64})", "$1\r\n");
return pemEncoded(pem.type(), p);
}
}
8 changes: 4 additions & 4 deletions test/jdk/java/security/PEM/PEMDecoderTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,8 @@ public static void main(String[] args) throws IOException {
System.err.println("received: " + new String(rec.leadingData()));
throw new AssertionError("ecCSRWithData preData wrong");
}
if (rec.pem().lastIndexOf("F") > rec.pem().length() - 5) {
System.err.println("received: " + rec.pem());
if (rec.content().lastIndexOf("F") > rec.content().length() - 5) {
System.err.println("received: " + rec.content());
throw new AssertionError("ecCSRWithData: " +
"End of PEM data has an unexpected character");
}
Expand Down Expand Up @@ -235,10 +235,10 @@ static void testPEMRecord(PEMData.Entry entry) {
PEMRecord r = PEMDecoder.of().decode(entry.pem(), PEMRecord.class);
String expected = entry.pem().split("-----")[2].replace(System.lineSeparator(), "");
try {
PEMData.checkResults(expected, r.pem());
PEMData.checkResults(expected, r.content());
} catch (AssertionError e) {
System.err.println("expected:\n" + expected);
System.err.println("received:\n" + r.pem());
System.err.println("received:\n" + r.content());
throw e;
}

Expand Down