8383608: Make BinaryEncodable non-exhaustive

Reviewed-by: mullan
This commit is contained in:
Anthony Scarpino 2026-06-10 17:35:43 +00:00
parent 132072077a
commit dc4bb5acbe
5 changed files with 212 additions and 18 deletions

View File

@ -32,15 +32,35 @@ import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import jdk.internal.javac.PreviewFeature;
import sun.security.internal.InternalBinaryEncodable;
/**
* This interface is implemented by security API classes that contain
* binary-encodable cryptographic material.
* This interface identifies the cryptographic objects that can be converted
* to and from binary data, and thereby encoded and decoded as PEM text.
*
* <p> This sealed interface may evolve. When using {@code switch}, always include a
* {@code default} case rather than relying on the classes specified in the
* {@code permits} clause to remain fixed. An exhaustive {@code switch} may
* result in a {@link MatchException}.
* <p> The APIs for cryptographic objects such as public keys, private keys,
* certificates, and certificate revocation lists all provide the means to
* convert their instances to and from standardized binary representations.
* Other kinds of cryptographic objects, such as certificate requests, have
* no corresponding API but can still be expressed as standardized binary
* representations. The {@code BinaryEncodable} interface allows the
* {@link PEMEncoder} and {@link PEMDecoder} classes to operate uniformly on
* binary representations of key or certificate material.
*
* <p> The permitted subtype {@code PEM} is notable for supporting the encoding
* and decoding of PEM text that represents cryptographic objects for which no
* API exists. In future releases, other permitted subtypes may be added to
* support the encoding and decoding of such cryptographic objects.
*
* <p> The list of permitted subtypes shown after {@code permits} is not
* exhaustive. This means if application code switches over a
* {@code BinaryEncodable} value, the {@code switch} cannot be made exhaustive
* simply by providing a {@code case} label for every permitted subtype shown
* in the list; there also must be a {@code default} or
* {@code case BinaryEncodable} label to handle additional subtypes. This
* allows the list of permitted subtypes to change over time without causing
* pre-existing switches to fail because of an unrecognized subtype.
*
* @see AsymmetricKey
* @see KeyPair
@ -57,5 +77,5 @@ import jdk.internal.javac.PreviewFeature;
@PreviewFeature(feature = PreviewFeature.Feature.PEM_API)
public sealed interface BinaryEncodable permits AsymmetricKey, KeyPair,
PKCS8EncodedKeySpec, X509EncodedKeySpec, EncryptedPrivateKeyInfo,
X509Certificate, X509CRL, PEM {
X509Certificate, X509CRL, PEM, InternalBinaryEncodable {
}

View File

@ -48,10 +48,10 @@ import java.util.Objects;
* PEM is a textual encoding used to store and transfer cryptographic
* objects, such as asymmetric keys, certificates, and certificate revocation
* lists (CRLs). It is defined in RFC 1421 and RFC 7468. PEM consists of
* Base64-encoded content enclosed by a type-identifying header
* and footer.
* Base64-encoded content enclosed by a header and footer that identify the
* type of the content.
*
* <p>The {@link #decode(String)} and {@link #decode(InputStream)} methods
* <p> The {@link #decode(String)} and {@link #decode(InputStream)} methods
* return an instance of a class that matches the PEM type and implements
* {@link BinaryEncodable}, as follows:
* <ul>
@ -70,11 +70,18 @@ import java.util.Objects;
* </ul>
*
* <p> If the PEM type has no corresponding class, {@code decode(String)} and
* {@code decode(InputStream)} will return a {@code PEM} object.
* {@code decode(InputStream)} return a {@code PEM} object.
*
* <p> If application code switches over the {@code BinaryEncodable} result of
* {@link #decode(String)} or {@link #decode(InputStream)}, the {@code switch} cannot
* be made exhaustive simply by providing a {@code case} label for every permitted
* subtype listed for {@code BinaryEncodable}; there also must be a {@code default}
* or {@code case BinaryEncodable} label to handle additional subtypes that
* might be added in the future.
*
* <p> The {@link #decode(String, Class)} and {@link #decode(InputStream, Class)}
* methods accept a class parameter specifying the desired {@code BinaryEncodable}
* type. These methods avoid the need for casting and are useful when multiple
* methods accept a parameter specifying the desired {@code BinaryEncodable}
* result. These methods avoid the need for casting and are useful when multiple
* representations are possible. For example, if the PEM contains both public and
* private keys, specifying {@code PrivateKey.class} returns only the private key.
* If {@code X509EncodedKeySpec.class} is provided, the public key encoding is
@ -109,11 +116,6 @@ import java.util.Objects;
* for decryption, an {@link EncryptedPrivateKeyInfo} is returned.
* A {@code PEMDecoder} configured for decryption can also decode unencrypted PEM.
*
* <p> The {@code BinaryEncodable} interface may evolve. When using a decode method
* with {@code switch}, always include a {@code default} case rather than
* relying on the classes specified in the permits clause to remain fixed.
* An exhaustive {@code switch} may result in a {@link MatchException}.
*
* <p> This class is immutable and thread-safe.
*
* <p> Example: decode a private key:
@ -136,6 +138,7 @@ import java.util.Objects;
* @see PEMEncoder
* @see PEM
* @see EncryptedPrivateKeyInfo
* @see BinaryEncodable
*
* @spec https://www.rfc-editor.org/info/rfc1421
* RFC 1421: Privacy Enhancement for Internet Electronic Mail

View File

@ -0,0 +1,39 @@
/*
* Copyright (c) 2026, 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.
*/
package sun.security.internal;
import java.security.BinaryEncodable;
/**
* This class is a non-public subtype of BinaryEncodable. This type
* allows the BinaryEncodable list of permitted subtypes to change
* over time without causing pre-existing switches to fail because of an
* unrecognized subtype.
*/
public final class InternalBinaryEncodable implements BinaryEncodable {
private InternalBinaryEncodable() {}
}

View File

@ -0,0 +1,67 @@
/*
* Copyright (c) 2026, 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.
*
* 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 8383608
* @summary check that InternalBinaryEncodable exists
* @enablePreview
* @modules java.base/sun.security.internal
* @run main CheckIBE
*/
import javax.crypto.EncryptedPrivateKeyInfo;
import java.security.AsymmetricKey;
import java.security.BinaryEncodable;
import java.security.KeyPair;
import java.security.PEM;
import java.security.cert.X509CRL;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import sun.security.internal.InternalBinaryEncodable;
/*
* This test verifies that BinaryEncodable has the expected set of permitted
* subtypes, including InternalBinaryEncodable. If this switch stops compiling,
* update the cases to match the BinaryEncodable permits list.
*/
public class CheckIBE {
public static void main(String[] args) {
BinaryEncodable be = new PEM("TEST", "TEST");
switch (be) {
case AsymmetricKey ignored -> {}
case KeyPair ignored -> {}
case PKCS8EncodedKeySpec ignored -> {}
case X509EncodedKeySpec ignored -> {}
case EncryptedPrivateKeyInfo ignored -> {}
case X509Certificate ignored -> {}
case X509CRL ignored -> {}
case PEM ignored -> {}
case InternalBinaryEncodable ignored -> {}
}
}
}

View File

@ -0,0 +1,65 @@
/*
* Copyright (c) 2026, 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.
*
* 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 8383608
* @summary verify switches over BinaryEncodable are not exhaustive
* @enablePreview
* @compile/fail ExhaustiveBE.java
*/
import javax.crypto.EncryptedPrivateKeyInfo;
import java.security.AsymmetricKey;
import java.security.BinaryEncodable;
import java.security.KeyPair;
import java.security.PEM;
import java.security.cert.X509CRL;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
/*
* This test verifies that application code cannot exhaustively switch over
* BinaryEncodable by naming only the public permitted subtypes. Compilation
* must fail because application code needs a default case, or a
* BinaryEncodable case, to cover the internal permitted subtype
* InternalBinaryEncodable.
*/
public class ExhaustiveBE {
public static void main(String[] args) {
BinaryEncodable be = new PEM("TEST", "TEST");
switch (be) {
case AsymmetricKey ignored -> {}
case KeyPair ignored -> {}
case PKCS8EncodedKeySpec ignored -> {}
case X509EncodedKeySpec ignored -> {}
case EncryptedPrivateKeyInfo ignored -> {}
case X509Certificate ignored -> {}
case X509CRL ignored -> {}
case PEM ignored -> {}
}
}
}