Skip to content

Commit ad3dfaf

Browse files
author
Anthony Scarpino
committed
8360564: Implement JEP 524: PEM Encodings of Cryptographic Objects (Second Preview)
Reviewed-by: weijun, mullan
1 parent cc05530 commit ad3dfaf

File tree

22 files changed

+1382
-813
lines changed

22 files changed

+1382
-813
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,13 @@
4747
* @see EncryptedPrivateKeyInfo
4848
* @see X509Certificate
4949
* @see X509CRL
50-
* @see PEMRecord
50+
* @see PEM
5151
*
5252
* @since 25
5353
*/
5454

5555
@PreviewFeature(feature = PreviewFeature.Feature.PEM_API)
5656
public sealed interface DEREncodable permits AsymmetricKey, KeyPair,
5757
PKCS8EncodedKeySpec, X509EncodedKeySpec, EncryptedPrivateKeyInfo,
58-
X509Certificate, X509CRL, PEMRecord {
58+
X509Certificate, X509CRL, PEM {
5959
}
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
/*
2+
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
26+
package java.security;
27+
28+
import jdk.internal.javac.PreviewFeature;
29+
30+
import sun.security.util.Pem;
31+
32+
import java.io.InputStream;
33+
import java.util.Base64;
34+
import java.util.Objects;
35+
36+
/**
37+
* {@code PEM} is a {@link DEREncodable} that represents Privacy-Enhanced
38+
* Mail (PEM) data by its type and Base64-encoded content.
39+
*
40+
* <p> The {@link PEMDecoder#decode(String)} and
41+
* {@link PEMDecoder#decode(InputStream)} methods return a {@code PEM} object
42+
* when the data type cannot be represented by a cryptographic object.
43+
* If you need access to the leading data of a PEM text, or want to
44+
* handle the text content directly, use the decoding methods
45+
* {@link PEMDecoder#decode(String, Class)} or
46+
* {@link PEMDecoder#decode(InputStream, Class)} with {@code PEM.class} as an
47+
* argument type.
48+
*
49+
* <p> A {@code PEM} object can be encoded back to its textual format by calling
50+
* {@link #toString()} or by using the encode methods in {@link PEMEncoder}.
51+
*
52+
* <p> When constructing a {@code PEM} instance, both {@code type} and
53+
* {@code content} must not be {@code null}.
54+
*
55+
* <p>No validation is performed during instantiation to ensure that
56+
* {@code type} conforms to RFC 7468 or other legacy formats, that
57+
* {@code content} is valid Base64 data, or that {@code content} matches the
58+
* {@code type}.
59+
60+
* <p> Common {@code type} values include, but are not limited to:
61+
* CERTIFICATE, CERTIFICATE REQUEST, ATTRIBUTE CERTIFICATE, X509 CRL, PKCS7,
62+
* CMS, PRIVATE KEY, ENCRYPTED PRIVATE KEY, and PUBLIC KEY.
63+
*
64+
* <p> {@code leadingData} is {@code null} if there is no data preceding the PEM
65+
* header during decoding. {@code leadingData} can be useful for reading
66+
* metadata that accompanies the PEM data. Because the value may represent a large
67+
* amount of data, it is not defensively copied by the constructor, and the
68+
* {@link #leadingData()} method does not return a clone. Modification of the
69+
* passed-in or returned array changes the value stored in this record.
70+
*
71+
* @param type the type identifier from the PEM header, without PEM syntax
72+
* labels; for example, for a public key, {@code type} would be
73+
* "PUBLIC KEY"
74+
* @param content the Base64-encoded data, excluding the PEM header and footer
75+
* @param leadingData any non-PEM data that precedes the PEM header during
76+
* decoding. This value may be {@code null}.
77+
*
78+
* @spec https://www.rfc-editor.org/info/rfc7468
79+
* RFC 7468: Textual Encodings of PKIX, PKCS, and CMS Structures
80+
*
81+
* @see PEMDecoder
82+
* @see PEMEncoder
83+
*
84+
* @since 26
85+
*/
86+
@PreviewFeature(feature = PreviewFeature.Feature.PEM_API)
87+
public record PEM(String type, String content, byte[] leadingData)
88+
implements DEREncodable {
89+
90+
/**
91+
* Creates a {@code PEM} instance with the specified parameters.
92+
*
93+
* @param type the PEM type identifier
94+
* @param content the Base64-encoded data, excluding the PEM header and footer
95+
* @param leadingData any non-PEM data read during the decoding process
96+
* before the PEM header. This value may be {@code null}.
97+
* @throws IllegalArgumentException if {@code type} is incorrectly formatted
98+
* @throws NullPointerException if {@code type} or {@code content} is {@code null}
99+
*/
100+
public PEM {
101+
Objects.requireNonNull(type, "\"type\" cannot be null.");
102+
Objects.requireNonNull(content, "\"content\" cannot be null.");
103+
104+
// With no validity checking on `type`, the constructor accept anything
105+
// including lowercase. The onus is on the caller.
106+
if (type.startsWith("-") || type.startsWith("BEGIN ") ||
107+
type.startsWith("END ")) {
108+
throw new IllegalArgumentException("PEM syntax labels found. " +
109+
"Only the PEM type identifier is allowed.");
110+
}
111+
}
112+
113+
/**
114+
* Creates a {@code PEM} instance with the specified type and content. This
115+
* constructor sets {@code leadingData} to {@code null}.
116+
*
117+
* @param type the PEM type identifier
118+
* @param content the Base64-encoded data, excluding the PEM header and footer
119+
* @throws IllegalArgumentException if {@code type} is incorrectly formatted
120+
* @throws NullPointerException if {@code type} or {@code content} is {@code null}
121+
*/
122+
public PEM(String type, String content) {
123+
this(type, content, null);
124+
}
125+
126+
/**
127+
* Returns the PEM formatted string containing the {@code type} and
128+
* Base64-encoded {@code content}. {@code leadingData} is not included.
129+
*
130+
* @return the PEM text representation
131+
*/
132+
@Override
133+
final public String toString() {
134+
return Pem.pemEncoded(this);
135+
}
136+
137+
/**
138+
* Returns a Base64-decoded byte array of {@code content}, using
139+
* {@link Base64#getMimeDecoder()}.
140+
*
141+
* @return a decoded byte array
142+
* @throws IllegalArgumentException if decoding fails
143+
*/
144+
final public byte[] decode() {
145+
return Base64.getMimeDecoder().decode(content);
146+
}
147+
}

0 commit comments

Comments
 (0)