From 9414cdaca5562c050c56ded8d3ad798d44f0bfca Mon Sep 17 00:00:00 2001 From: Nizar Benalla Date: Fri, 15 Nov 2024 00:01:22 +0100 Subject: [PATCH] revert changes pulled from PEM PR --- .../classes/java/security/AsymmetricKey.java | 2 +- .../classes/java/security/DEREncodable.java | 58 --- .../share/classes/java/security/KeyPair.java | 4 +- .../classes/java/security/PEMDecoder.java | 387 ------------------ .../classes/java/security/PEMEncoder.java | 358 ---------------- .../classes/java/security/cert/X509CRL.java | 2 +- .../java/security/cert/X509Certificate.java | 6 +- .../java/security/spec/EncodedKeySpec.java | 15 +- .../security/spec/PKCS8EncodedKeySpec.java | 6 +- .../security/spec/X509EncodedKeySpec.java | 7 +- .../javax/crypto/EncryptedPrivateKeyInfo.java | 213 ++-------- .../jdk/internal/javac/PreviewFeature.java | 4 - .../classes/sun/security/ec/ECKeyFactory.java | 43 +- .../sun/security/ec/ECPrivateKeyImpl.java | 22 +- .../sun/security/ec/XDHKeyFactory.java | 20 +- .../sun/security/ec/XDHPrivateKeyImpl.java | 7 +- .../sun/security/ec/ed/EdDSAKeyFactory.java | 24 +- .../security/ec/ed/EdDSAPrivateKeyImpl.java | 10 +- .../sun/security/pkcs/NamedPKCS8Key.java | 6 +- .../classes/sun/security/pkcs/PKCS8Key.java | 183 ++------- .../sun/security/provider/DSAPrivateKey.java | 6 +- .../sun/security/provider/KeyProtector.java | 6 +- .../sun/security/provider/X509Factory.java | 112 ++++- .../sun/security/rsa/RSAKeyFactory.java | 12 +- .../security/rsa/RSAPrivateCrtKeyImpl.java | 36 +- .../sun/security/rsa/RSAPrivateKeyImpl.java | 4 +- .../sun/security/rsa/RSAPublicKeyImpl.java | 8 +- .../classes/sun/security/util/DerValue.java | 9 +- .../classes/sun/security/util/KeyUtil.java | 54 +-- .../classes/sun/security/util/PEMRecord.java | 77 ---- .../share/classes/sun/security/util/Pem.java | 239 +---------- .../classes/sun/security/x509/X509Key.java | 32 +- .../share/conf/security/java.security | 8 - .../KeyFactoryGetKeySpecForInvalidSpec.java | 8 +- test/jdk/java/security/PEM/PEMCerts.java | 358 ---------------- .../jdk/java/security/PEM/PEMDecoderTest.java | 210 ---------- .../jdk/java/security/PEM/PEMEncoderTest.java | 227 ---------- .../sun/security/pkcs/pkcs8/PKCS8Test.java | 74 ++-- 38 files changed, 316 insertions(+), 2541 deletions(-) delete mode 100644 src/java.base/share/classes/java/security/DEREncodable.java delete mode 100644 src/java.base/share/classes/java/security/PEMDecoder.java delete mode 100644 src/java.base/share/classes/java/security/PEMEncoder.java delete mode 100644 src/java.base/share/classes/sun/security/util/PEMRecord.java delete mode 100644 test/jdk/java/security/PEM/PEMCerts.java delete mode 100644 test/jdk/java/security/PEM/PEMDecoderTest.java delete mode 100644 test/jdk/java/security/PEM/PEMEncoderTest.java diff --git a/src/java.base/share/classes/java/security/AsymmetricKey.java b/src/java.base/share/classes/java/security/AsymmetricKey.java index 53b61af4a5bc7..e96aeb4d84c71 100644 --- a/src/java.base/share/classes/java/security/AsymmetricKey.java +++ b/src/java.base/share/classes/java/security/AsymmetricKey.java @@ -34,7 +34,7 @@ * * @since 22 */ -public non-sealed interface AsymmetricKey extends Key, DEREncodable { +public interface AsymmetricKey extends Key { /** * Returns the parameters associated with this key. * The parameters are optional and may be either diff --git a/src/java.base/share/classes/java/security/DEREncodable.java b/src/java.base/share/classes/java/security/DEREncodable.java deleted file mode 100644 index 4d0e4620f2712..0000000000000 --- a/src/java.base/share/classes/java/security/DEREncodable.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2024, 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 java.security; - -import jdk.internal.javac.PreviewFeature; - -import javax.crypto.EncryptedPrivateKeyInfo; -import java.security.cert.X509CRL; -import java.security.cert.X509Certificate; -import java.security.spec.EncodedKeySpec; -import java.security.spec.PKCS8EncodedKeySpec; -import java.security.spec.X509EncodedKeySpec; - -/** - * This is a top-level interface for security classes that contain cryptographic - * data which may not be related or have a common class hierarchy. These - * security objects provide standard binary encoding, like ASN.1, and type - * formats, like X.509 and PKCS#8. These encodings are used in some form with - * {@link KeyFactory} and {@link java.security.cert.CertificateFactory}, - * - * @see Key - * @see KeyPair - * @see EncodedKeySpec - * @see EncryptedPrivateKeyInfo - * @see X509Certificate - * @see X509CRL - * - * @since 24 - */ - -@PreviewFeature(feature = PreviewFeature.Feature.PEM_API) -public sealed interface DEREncodable permits AsymmetricKey, KeyPair, X509CRL, - X509Certificate, PKCS8EncodedKeySpec, X509EncodedKeySpec, - EncryptedPrivateKeyInfo { -} diff --git a/src/java.base/share/classes/java/security/KeyPair.java b/src/java.base/share/classes/java/security/KeyPair.java index a9d048204145e..61c1121bdee4a 100644 --- a/src/java.base/share/classes/java/security/KeyPair.java +++ b/src/java.base/share/classes/java/security/KeyPair.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2022, 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 @@ -37,7 +37,7 @@ * @since 1.1 */ -public final class KeyPair implements java.io.Serializable, DEREncodable { +public final class KeyPair implements java.io.Serializable { @java.io.Serial private static final long serialVersionUID = -7565189502268009837L; diff --git a/src/java.base/share/classes/java/security/PEMDecoder.java b/src/java.base/share/classes/java/security/PEMDecoder.java deleted file mode 100644 index 1a99618eef12c..0000000000000 --- a/src/java.base/share/classes/java/security/PEMDecoder.java +++ /dev/null @@ -1,387 +0,0 @@ -/* - * Copyright (c) 2024, 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 java.security; - -import jdk.internal.javac.PreviewFeature; - -import sun.security.pkcs.PKCS8Key; -import sun.security.rsa.RSAPrivateCrtKeyImpl; -import sun.security.util.PEMRecord; -import sun.security.util.Pem; - -import javax.crypto.EncryptedPrivateKeyInfo; -import java.io.*; -import java.security.cert.*; -import java.security.spec.*; -import java.util.Base64; -import java.util.Objects; - -/** - * {@code PEMDecoder} is an immutable Privacy-Enhanced Mail (PEM) decoding class. - * PEM is a textual encoding used for storing and transferring security - * objects, such as asymmetric keys, certificates, and certificate revocation - * lists (CRL). Defined in RFC 1421 and RFC 7468, PEM consists of a - * Base64-formatted binary encoding surrounded by a type identifying header - * and footer. - *

- * Decoding methods return a class that matches the data type and implements - * {@link DEREncodable}. - * If a return class is specified, an {@link IllegalArgumentException} - * is thrown if data is not valid for the class. - *

- * When passing input data into any {@code decode} methods, any non-PEM data - * prior to the PEM header will be ignored. If that data is important to the - * application, it should be parsed before decoding. - *

- * A new immutable {@code PEMDecoder} instance is returned by - * {@linkplain #withFactory} and/or {@linkplain #withDecryption}. Configuring - * an instance for decryption does not prevent decoding with unencrypted PEM. - * Any encrypted PEM that does not use the configured password will cause an - * exception. A decoder instance not configured with decryption will return an - * {@link EncryptedPrivateKeyInfo} with encrypted PEM. EncryptedPrivateKeyInfo - * methods must be used to retrieve the {@link PrivateKey}. - *

- * {@code PEMDecoder} supports the follow types: - *

- *     PRIVATE KEY, RSA PRIVATE KEY, PUBLIC KEY, CERTIFICATE, CRL, and
- *     ENCRYPTED PRIVATE KEY.
- * 
- * @apiNote - * Here is an example of encoding a PrivateKey object: - *
- *     PEMDecoder pd = PEMDecoder.of();
- *     PrivateKey priKey = pd.decode(PriKeyPEM);
- * 
- * - * @since 24 - */ - -@PreviewFeature(feature = PreviewFeature.Feature.PEM_API) -public final class PEMDecoder { - private final Provider factory; - private final char[] password; - - // Singleton instance for PEMDecoder - private final static PEMDecoder PEM_DECODER = new PEMDecoder(null, null); - - /** - * Creates an immutable instance with a specific KeyFactory and/or - * password. - * @param withFactory KeyFactory provider - * @param withPassword char[] password for EncryptedPrivateKeyInfo - * decryption - */ - private PEMDecoder(Provider withFactory, char[] withPassword) { - password = withPassword != null ? withPassword.clone() : null; - factory = withFactory; - } - - /** - * Returns an instance of {@code PEMDecoder}. This instance may be repeatedly used - * to decode different PEM text. - * - * @return returns a {@code PEMDecoder} - */ - public static PEMDecoder of() { - return PEM_DECODER; - } - - /** - * After the header, footer, and base64 have been separated, identify the - * header and footer and proceed with decoding the base64 for the - * appropriate type. - */ - private DEREncodable decode(PEMRecord pem) throws IOException { - Base64.Decoder decoder = Base64.getMimeDecoder(); - - try { - return switch (pem.id()) { - case PEMRecord.PUBLIC_KEY -> { - X509EncodedKeySpec spec = - new X509EncodedKeySpec(decoder.decode(pem.pem())); - yield (getKeyFactory(spec.getAlgorithm())). - generatePublic(spec); - - } - case PEMRecord.PRIVATE_KEY -> { - PKCS8Key p8key = new PKCS8Key(decoder.decode(pem.pem())); - PrivateKey priKey; - KeyFactory kf = getKeyFactory(p8key.getAlgorithm()); - priKey = kf.generatePrivate( - new PKCS8EncodedKeySpec(p8key.getEncoded(), - p8key.getAlgorithm())); - // If there is a public key, it's an OAS. - if (p8key.getPubKeyEncoded() != null) { - X509EncodedKeySpec spec = new X509EncodedKeySpec( - p8key.getPubKeyEncoded(), p8key.getAlgorithm()); - yield new KeyPair(getKeyFactory(p8key.getAlgorithm()). - generatePublic(spec), priKey); - } - yield priKey; - } - case PEMRecord.ENCRYPTED_PRIVATE_KEY -> { - if (password == null) { - yield new EncryptedPrivateKeyInfo(decoder.decode(pem.pem())); - } - yield new EncryptedPrivateKeyInfo(decoder.decode(pem.pem())). - getKey(password); - } - case PEMRecord.CERTIFICATE, - PEMRecord.X509_CERTIFICATE -> { - CertificateFactory cf = getCertFactory("X509"); - yield (X509Certificate) cf.generateCertificate( - new ByteArrayInputStream(decoder.decode(pem.pem()))); - } - case PEMRecord.X509_CRL -> { - CertificateFactory cf = getCertFactory("X509"); - yield (X509CRL) cf.generateCRL( - new ByteArrayInputStream(decoder.decode(pem.pem()))); - } - case PEMRecord.RSA_PRIVATE_KEY -> { - KeyFactory kf = getKeyFactory("RSA"); - yield kf.generatePrivate( - RSAPrivateCrtKeyImpl.getKeySpec(decoder.decode(pem.pem()))); - } - default -> - throw new IllegalArgumentException("Unsupported type or " + - "not properly formatted PEM"); - }; - } catch (GeneralSecurityException e) { - throw new IllegalArgumentException(e); - } - } - - /** - * Decodes and returns {@link DEREncodable} from the given string. - * - * @param str PEM data in a String. - * @return an DEREncodable generated from the PEM data. - * @throws IllegalArgumentException on error in decoding or if the PEM is - * unsupported. - */ - public DEREncodable decode(String str) { - Objects.requireNonNull(str); - try { - return decode(new ByteArrayInputStream(str.getBytes())); - } catch (IOException e) { - // With all data contained in the String, there are no IO ops. - throw new IllegalArgumentException(e); - } - } - - /** - * Decodes and returns a {@link DEREncodable} from the given - * {@code InputStream}. - * - *

The method will read the {@code InputStream} until PEM data is - * found or until the end of the stream. Non-PEM data in the - * {@code InputStream} before the PEM header will be ignored by the decoder. - * - * @param is InputStream containing PEM data. - * @return an DEREncodable generated from the PEM data. - * @throws IOException on IO error with the InputStream. - * @throws IllegalArgumentException on error in decoding or if the PEM is - * unsupported. - */ - public DEREncodable decode(InputStream is) throws IOException { - Objects.requireNonNull(is); - PEMRecord pem = Pem.readPEM(is); - if (pem == null) { - throw new IllegalArgumentException("No PEM data found."); - } - return decode(pem); - } - - /** - * Decodes and returns the specified class for the given PEM string. The - * class must extend {@link DEREncodable} and be the appropriate class for - * the PEM type. - * - *

With the {@code tClass} argument, the returned object may be cast to a - * subclass or converted to a different return class, if - * appropriate for that PEM data. Using EC public key PEM as an example, - * {@code tClass} may be set to {@code PublicKey.class}, - * {@code ECPublicKey}, or a {@code X509EncodedKeySpec}. {@code PublicKey} - * is useful for algorithm-agnostic methods, {@code ECPublicKey} for - * algorithm-specific operations, or {@code X509EncodedKeySpec} if the - * X.509 binary encoding is desired instead of a Key object. An IOException - * will be thrown if the class is incorrect for the given PEM data. - * - * @param Class type parameter that extends {@link DEREncodable} - * @param string the String containing PEM data. - * @param tClass the returned object class that implementing - * {@link DEREncodable}. - * @return The DEREncodable typecast to tClass. - * @throws IllegalArgumentException on error in decoding or if the PEM is - * unsupported. - * @throws ClassCastException if the given class is invalid for the PEM . - */ - public S decode(String string, Class tClass) { - Objects.requireNonNull(string); - try { - return decode(new ByteArrayInputStream(string.getBytes()), tClass); - } catch (IOException e) { - // With all data contained in the String, there are no IO ops. - throw new IllegalArgumentException(e); - } - } - - /** - * Decodes and returns the specified class for the given PEM stream. The - * class must extend {@link DEREncodable} and be an appropriate class for - * the PEM type. - * - *

See {@link PEMDecoder#decode(String, Class)} for details about - * {@code tClass}. - *
See {@link PEMDecoder#decode(InputStream)} for details on using an - * {@code InputStream}. - * - * @param Class type parameter that extends {@code DEREncodable} - * @param is an InputStream containing PEM data. - * @param tClass the returned object class that implementing - * {@code DEREncodable}. - * @return tClass. - * @throws IOException on IO error with the InputStream. - * @throws IllegalArgumentException on error in decoding or if the PEM is - * unsupported. - * @throws ClassCastException if the given class is invalid for the PEM . - * - */ - public S decode(InputStream is, Class tClass) - throws IOException { - Objects.requireNonNull(is); - Objects.requireNonNull(tClass); - PEMRecord pem = Pem.readPEM(is); - if (pem == null) { - throw new IllegalArgumentException("No PEM data found."); - } - - DEREncodable so = decode(pem); - - /* - * If the object is a KeyPair, check if the tClass is set to class - * specific to a private or public key. Because PKCS8v2 can be a - * KeyPair, it is possible for someone to assume all their PEM private - * keys are only PrivateKey and not KeyPair. - */ - if (so instanceof KeyPair kp) { - if ((PrivateKey.class).isAssignableFrom(tClass) || - (PKCS8EncodedKeySpec.class).isAssignableFrom(tClass)) { - so = kp.getPrivate(); - } - if ((PublicKey.class).isAssignableFrom(tClass) || - (X509EncodedKeySpec.class).isAssignableFrom(tClass)) { - so = kp.getPublic(); - } - } - - /* - * KeySpec use getKeySpec after the Key has been generated. Even though - * returning a binary encoding after the Base64 decoding is ok when the - * user wants PKCS8EncodedKeySpec, generating the key verifies the - * binary encoding and allows the KeyFactory to use the provider's - * KeySpec() - */ - - if ((EncodedKeySpec.class).isAssignableFrom(tClass) && - so instanceof Key key) { - try { - // unchecked suppressed as we know tClass comes from KeySpec - // KeyType not relevant here. We just want KeyFactory - if ((PKCS8EncodedKeySpec.class).isAssignableFrom(tClass)) { - getKeyFactory(key.getAlgorithm()). - getKeySpec(key, PKCS8EncodedKeySpec.class); - } else if ((X509EncodedKeySpec.class).isAssignableFrom(tClass)) { - getKeyFactory(key.getAlgorithm()) - .getKeySpec(key, X509EncodedKeySpec.class); - } else { - throw new IllegalArgumentException("Invalid KeySpec."); - } - } catch (InvalidKeySpecException e) { - throw new IllegalArgumentException("Invalid KeySpec " + - "specified (" + tClass.getName() +") for key (" + - key.getClass().getName() +")", e); - } - } - - return tClass.cast(so); - } - - - private KeyFactory getKeyFactory(String algorithm) { - try { - if (factory == null) { - return KeyFactory.getInstance(algorithm); - } - return KeyFactory.getInstance(algorithm, factory); - } catch(GeneralSecurityException e){ - throw new IllegalArgumentException(e); - } - } - - // Convenience method to avoid provider getInstance checks clutter - private CertificateFactory getCertFactory(String algorithm) { - try { - if (factory == null) { - return CertificateFactory.getInstance(algorithm); - } - return CertificateFactory.getInstance(algorithm, factory); - } catch (GeneralSecurityException e) { - throw new IllegalArgumentException(e); - } - } - - /** - * Configures and returns a new {@code PEMDecoder} instance from the - * current instance that will use Factory classes from the specified - * {@link Provider}. Any errors using the {@code provider} will occur - * during decoding. - * - *

If {@code params} is {@code null}, a new instance is returned with - * the default provider configuration. - * - * @param provider the Factory provider. - * @return a new PEM decoder instance. - */ - public PEMDecoder withFactory(Provider provider) { - return new PEMDecoder(provider, password); - } - - /** - * Returns a new {@code PEMDecoder} instance from the current instance - * configured to decrypt encrypted PEM data with given password. - * Non-encrypted PEM may still be decoded from this instance. - * - * @param password the password to decrypt encrypted PEM data. This array - * is cloned and stored in the new instance. - * @return the decoder - * @throws NullPointerException if password is null. - */ - public PEMDecoder withDecryption(char[] password) { - char[] pwd = password.clone(); - return new PEMDecoder(factory, pwd); - } -} diff --git a/src/java.base/share/classes/java/security/PEMEncoder.java b/src/java.base/share/classes/java/security/PEMEncoder.java deleted file mode 100644 index 177976d2888a3..0000000000000 --- a/src/java.base/share/classes/java/security/PEMEncoder.java +++ /dev/null @@ -1,358 +0,0 @@ -/* - * Copyright (c) 2024, 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 java.security; - -import jdk.internal.javac.PreviewFeature; - -import sun.security.pkcs.PKCS8Key; -import sun.security.util.DerOutputStream; -import sun.security.util.DerValue; -import sun.security.util.PEMRecord; -import sun.security.util.Pem; -import sun.security.x509.AlgorithmId; - -import javax.crypto.*; -import javax.crypto.spec.PBEKeySpec; -import java.io.*; -import java.nio.charset.StandardCharsets; -import java.security.cert.*; -import java.security.spec.AlgorithmParameterSpec; -import java.security.spec.PKCS8EncodedKeySpec; -import java.security.spec.X509EncodedKeySpec; -import java.util.Base64; -import java.util.Objects; -import java.util.concurrent.locks.ReentrantLock; - -/** - * PEMEncoder is an immutable Privacy-Enhanced Mail (PEM) encoding class. - * PEM is a textual encoding used for storing and transferring security - * objects, such as asymmetric keys, certificates, and certificate revocation - * lists (CRL). Defined in RFC 1421 and RFC 7468, PEM consists of a - * Base64-formatted binary encoding surrounded by a type identifying header - * and footer. - *

- * Encoding may be performed on objects that implement {@link DEREncodable}. - *

- * Encrypted private key PEM data can be built by calling the encode methods - * on a PEMEncoder instance returned by {@link #withEncryption(char[])} or - * by passing an {@link EncryptedPrivateKeyInfo} object into the encode methods. - *

- * PKCS8 v2.0 allows OneAsymmetric encoding, which is a private and public - * key in the same PEM. This is supported by using the {@link KeyPair} class - * with the encode methods. - *

- * PEMEncoder supports the follow types: - *

- *     PRIVATE KEY, PUBLIC KEY, CERTIFICATE, CRL, and ENCRYPTED PRIVATE KEY.
- * 
- * - * @apiNote - * Here is an example of encoding a PrivateKey object: - *
- *     PEMEncoder pe = PEMEncoder.of();
- *     byte[] pemData = pe.encode(privKey);
- * 
- * - * @since 24 - */ -@PreviewFeature(feature = PreviewFeature.Feature.PEM_API) -public final class PEMEncoder { - - // Singleton instance of PEMEncoder - private static final PEMEncoder PEM_ENCODER = new PEMEncoder(null); - - // Stores the password for an encrypted encoder that isn't setup yet. - private PBEKeySpec keySpec; - // Stores the key after the encoder is ready to encrypt. The prevents - // repeated SecretKeyFactory calls if the encoder is used on multiple keys. - private SecretKey key; - // Makes SecretKeyFactory generation thread-safe. - private final ReentrantLock lock; - // Lazy initialize singleton encoder. - private static Base64.Encoder b64Encoder; - - /** - * Instantiate a new PEMEncoder for Encrypted Private Keys. - * - * @param pbe contains the password spec used for encryption. - */ - private PEMEncoder(PBEKeySpec pbe) { - keySpec = pbe; - lock = new ReentrantLock(); - } - - /** - * Returns an instance of PEMEncoder. - * - * @return PEMEncoder instance - */ - static public PEMEncoder of() { - return PEM_ENCODER; - } - - /** - * Construct a String-based encoding based off the id type. - * @return the string - */ - private String pemEncoded(PEMRecord pem) { - StringBuilder sb = new StringBuilder(1024); - sb.append("-----BEGIN ").append(pem.id()).append("-----"); - sb.append(System.lineSeparator()); - if (b64Encoder == null) { - b64Encoder = Base64.getMimeEncoder(64, - System.lineSeparator().getBytes()); - } - sb.append(b64Encoder.encodeToString( - pem.pem().getBytes(StandardCharsets.ISO_8859_1))); - sb.append(System.lineSeparator()); - sb.append("-----END ").append(pem.id()).append("-----"); - sb.append(System.lineSeparator()); - return sb.toString(); - } - - /** - * Encoded a given {@code DEREncodable} and return the PEM encoding in a - * String - * - * @param de a cryptographic object to be PEM encoded that implements - * DEREncodable. - * @return PEM encoding in a String - * @throws IllegalArgumentException when the passed object returns a null - * binary encoding. An exception is thrown when PEMEncoder is - * configured for encryption while encoding a DEREncodable that does - * not support encryption. - * @throws NullPointerException when object passed is null. - * @see #withEncryption(char[]) - */ - public String encodeToString(DEREncodable de) { - Objects.requireNonNull(de); - return switch (de) { - case PublicKey pu -> buildKey(null, pu.getEncoded()); - case PrivateKey pr -> buildKey(pr.getEncoded(), null); - case KeyPair kp -> { - if (kp.getPublic() == null) { - throw new IllegalArgumentException("KeyPair does not " + - "contain PublicKey."); - } - - if (kp.getPrivate() == null) { - throw new IllegalArgumentException("KeyPair does not " + - "contain PrivateKey."); - } - yield buildKey(kp.getPrivate().getEncoded(), - kp.getPublic().getEncoded()); - } - case X509EncodedKeySpec x -> - buildKey(null, x.getEncoded()); - case PKCS8EncodedKeySpec p -> - buildKey(p.getEncoded(), null); - case EncryptedPrivateKeyInfo epki -> { - try { - yield pemEncoded(new PEMRecord( - PEMRecord.ENCRYPTED_PRIVATE_KEY, epki.getEncoded())); - } catch (IOException e) { - throw new SecurityException(e); - } - } - case X509Certificate c -> { - try { - if (isEncrypted()) { - throw new IllegalArgumentException("Certificates " + - "cannot be encrypted"); - } - yield pemEncoded(new PEMRecord(PEMRecord.CERTIFICATE, - c.getEncoded())); - } catch (CertificateEncodingException e) { - throw new IllegalArgumentException(e); - } - } - case X509CRL crl -> { - try { - if (isEncrypted()) { - throw new IllegalArgumentException("CRLs cannot be " + - "encrypted"); - } - yield pemEncoded(new PEMRecord(PEMRecord.X509_CRL, - crl.getEncoded())); - } catch (CRLException e) { - throw new IllegalArgumentException(e); - } - } - default -> throw new IllegalArgumentException("PEM does not " + - "support " + de.getClass().getCanonicalName()); - }; - } - - /** - * Encodes a given {@code DEREncodable} into PEM. - * - * @param de the object that implements DEREncodable. - * @return a PEM encoded byte[] of the given DEREncodable. - * @throws IllegalArgumentException when the passed object returns a null - * binary encoding. An exception is thrown when PEMEncoder is - * configured for encryption while encoding a DEREncodable that does - * not support encryption. - * @throws NullPointerException when object passed is null. - * @see #withEncryption(char[]) - */ - public byte[] encode(DEREncodable de) { - return encodeToString(de).getBytes(StandardCharsets.ISO_8859_1); - } - - /** - * Returns a new immutable PEMEncoder instance configured to the default - * encryption algorithm and a given password. - * - *

Only {@link PrivateKey} will be encrypted with this newly configured - * instance. Other {@link DEREncodable} classes that do not support - * encrypted PEM will cause encode() to throw an IllegalArgumentException. - * - * @implNote Default algorithm defined by Security Property {@code - * jdk.epkcs8.defaultAlgorithm}. To configure all the encryption options - * see {@link EncryptedPrivateKeyInfo#encryptKey(PrivateKey, char[], String, - * AlgorithmParameterSpec, Provider)} and use the returned object with - * {@link #encode(DEREncodable)}. - * - * @param password sets the encryption password. The array is cloned and - * stored in the new instance. - * @return a new PEMEncoder - * @throws NullPointerException if password is null. - */ - public PEMEncoder withEncryption(char[] password) { - // PBEKeySpec clones the password - Objects.requireNonNull(password); - return new PEMEncoder(new PBEKeySpec(password)); - } - - /** - * Build PEM encoding. - */ - private String buildKey(byte[] privateBytes, byte[] publicBytes) { - DerOutputStream out = new DerOutputStream(); - Cipher cipher; - - if (privateBytes == null && publicBytes == null) { - throw new IllegalArgumentException("No encoded data given by the " + - "DEREncodable."); - } - - // If `keySpec` is non-null, then `key` hasn't been established. - // Setting a `key' prevents repeated key generations operations. - // withEncryption() is a configuration method and cannot throw an - // exception; therefore generation is delayed. - if (keySpec != null) { - // For thread safety - lock.lock(); - if (key == null) { - try { - key = SecretKeyFactory.getInstance(Pem.DEFAULT_ALGO). - generateSecret(keySpec); - keySpec.clearPassword(); - keySpec = null; - } catch (GeneralSecurityException e) { - throw new SecurityException("Security property " + - "\"jdk.epkcs8.defaultAlgorithm\" may not specify a " + - "valid algorithm. Operation cannot be performed.", e); - } finally { - lock.unlock(); - } - } else { - lock.unlock(); - } - } - - // If `key` is non-null, this is an encoder ready to encrypt. - if (key != null) { - if (privateBytes == null || publicBytes != null) { - throw new IllegalArgumentException("Can only encrypt a " + - "PrivateKey."); - } - - try { - cipher = Cipher.getInstance(Pem.DEFAULT_ALGO); - cipher.init(Cipher.ENCRYPT_MODE, key); - } catch (GeneralSecurityException e) { - throw new SecurityException("Security property " + - "\"jdk.epkcs8.defaultAlgorithm\" may not specify a " + - "valid algorithm. Operation cannot be performed.", e); - } - - new AlgorithmId(Pem.getPBEID(Pem.DEFAULT_ALGO), - cipher.getParameters()).encode(out); - - try { - out.putOctetString(cipher.doFinal(privateBytes)); - return pemEncoded(new PEMRecord(PEMRecord.ENCRYPTED_PRIVATE_KEY, - DerValue.wrap(DerValue.tag_Sequence, out).toByteArray())); - } catch (GeneralSecurityException e) { - throw new IllegalArgumentException(e); - } - } - - // X509 only - if (publicBytes != null && privateBytes == null) { - if (publicBytes.length == 0) { - throw new IllegalArgumentException("No public key encoding " + - "given by the DEREncodable."); - } - - return pemEncoded(new PEMRecord(PEMRecord.PUBLIC_KEY, publicBytes)); - } - - // PKCS8 only - if (publicBytes == null && privateBytes != null) { - if (privateBytes.length == 0) { - throw new IllegalArgumentException("No private key encoding " + - "given by the DEREncodable."); - } - - return pemEncoded(new PEMRecord(PEMRecord.PRIVATE_KEY, - privateBytes)); - } - - // OAS - try { - if (privateBytes.length == 0) { - throw new IllegalArgumentException("No private key encoding " + - "given by the DEREncodable."); - } - - if (publicBytes.length == 0) { - throw new IllegalArgumentException("No public key encoding " + - "given by the DEREncodable."); - } - - return pemEncoded(new PEMRecord(PEMRecord.PRIVATE_KEY, - PKCS8Key.getEncoded(publicBytes, privateBytes))); - } catch (IOException e) { - throw new IllegalArgumentException(e); - } - } - - private boolean isEncrypted() { - return (key != null || keySpec != null); - } -} diff --git a/src/java.base/share/classes/java/security/cert/X509CRL.java b/src/java.base/share/classes/java/security/cert/X509CRL.java index 1703ec2e73473..111de1daf0e43 100644 --- a/src/java.base/share/classes/java/security/cert/X509CRL.java +++ b/src/java.base/share/classes/java/security/cert/X509CRL.java @@ -107,7 +107,7 @@ * @see X509Extension */ -public abstract non-sealed class X509CRL extends CRL implements X509Extension, DEREncodable { +public abstract class X509CRL extends CRL implements X509Extension { private transient X500Principal issuerPrincipal; diff --git a/src/java.base/share/classes/java/security/cert/X509Certificate.java b/src/java.base/share/classes/java/security/cert/X509Certificate.java index 60b9bfae95948..79f37a82d47bb 100644 --- a/src/java.base/share/classes/java/security/cert/X509Certificate.java +++ b/src/java.base/share/classes/java/security/cert/X509Certificate.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2022, 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 @@ -107,8 +107,8 @@ * @see X509Extension */ -public abstract non-sealed class X509Certificate extends Certificate -implements X509Extension, DEREncodable { +public abstract class X509Certificate extends Certificate +implements X509Extension { @java.io.Serial private static final long serialVersionUID = -2491127588187038216L; diff --git a/src/java.base/share/classes/java/security/spec/EncodedKeySpec.java b/src/java.base/share/classes/java/security/spec/EncodedKeySpec.java index bce31371f5489..8bb23d08df35b 100644 --- a/src/java.base/share/classes/java/security/spec/EncodedKeySpec.java +++ b/src/java.base/share/classes/java/security/spec/EncodedKeySpec.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2022, 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 @@ -26,9 +26,7 @@ package java.security.spec; import jdk.internal.access.SharedSecrets; -import sun.security.util.KeyUtil; -import java.io.IOException; import java.util.Arrays; /** @@ -52,14 +50,12 @@ public abstract class EncodedKeySpec implements KeySpec { private String algorithmName; static { - SharedSecrets.setJavaSecuritySpecAccess(EncodedKeySpec::clear); + SharedSecrets.setJavaSecuritySpecAccess( + EncodedKeySpec::clear); } /** * Creates a new {@code EncodedKeySpec} with the given encoded key. - * This constructor extracts the algorithm name from the encoded bytes, - * which may be an OID if no standard algorithm name is defined. If the - * algorithm name cannot be extracted, it is set to null. * * @param encodedKey the encoded key. The contents of the * array are copied to protect against subsequent modification. @@ -68,11 +64,6 @@ public abstract class EncodedKeySpec implements KeySpec { */ public EncodedKeySpec(byte[] encodedKey) { this.encodedKey = encodedKey.clone(); - try { - algorithmName = KeyUtil.getAlgorithm(this.encodedKey).getName(); - } catch (IOException e) { - // On error leave algorithmName as null. - } } /** diff --git a/src/java.base/share/classes/java/security/spec/PKCS8EncodedKeySpec.java b/src/java.base/share/classes/java/security/spec/PKCS8EncodedKeySpec.java index 4823b0d6ebbad..73d79a413216b 100644 --- a/src/java.base/share/classes/java/security/spec/PKCS8EncodedKeySpec.java +++ b/src/java.base/share/classes/java/security/spec/PKCS8EncodedKeySpec.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2017, 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 @@ -25,8 +25,6 @@ package java.security.spec; -import java.security.DEREncodable; - /** * This class represents the ASN.1 encoding of a private key, * encoded according to the ASN.1 type {@code PrivateKeyInfo}. @@ -61,7 +59,7 @@ * @since 1.2 */ -public final class PKCS8EncodedKeySpec extends EncodedKeySpec implements DEREncodable { +public class PKCS8EncodedKeySpec extends EncodedKeySpec { /** * Creates a new {@code PKCS8EncodedKeySpec} with the given encoded key. diff --git a/src/java.base/share/classes/java/security/spec/X509EncodedKeySpec.java b/src/java.base/share/classes/java/security/spec/X509EncodedKeySpec.java index d72894dcf401b..7de4a2a14179a 100644 --- a/src/java.base/share/classes/java/security/spec/X509EncodedKeySpec.java +++ b/src/java.base/share/classes/java/security/spec/X509EncodedKeySpec.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2017, 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 @@ -25,8 +25,6 @@ package java.security.spec; -import java.security.DEREncodable; - /** * This class represents the ASN.1 encoding of a public key, * encoded according to the ASN.1 type {@code SubjectPublicKeyInfo}. @@ -51,8 +49,7 @@ * @since 1.2 */ -public final class X509EncodedKeySpec extends EncodedKeySpec implements - DEREncodable { +public class X509EncodedKeySpec extends EncodedKeySpec { /** * Creates a new {@code X509EncodedKeySpec} with the given encoded key. diff --git a/src/java.base/share/classes/javax/crypto/EncryptedPrivateKeyInfo.java b/src/java.base/share/classes/javax/crypto/EncryptedPrivateKeyInfo.java index 85de6632d95be..698548d373f80 100644 --- a/src/java.base/share/classes/javax/crypto/EncryptedPrivateKeyInfo.java +++ b/src/java.base/share/classes/javax/crypto/EncryptedPrivateKeyInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2022, 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 @@ -25,17 +25,13 @@ package javax.crypto; -import jdk.internal.javac.PreviewFeature; - -import sun.security.pkcs.PKCS8Key; -import sun.security.util.*; -import sun.security.x509.AlgorithmId; - -import javax.crypto.spec.PBEKeySpec; -import java.io.IOException; +import java.io.*; import java.security.*; import java.security.spec.*; -import java.util.Objects; +import sun.security.x509.AlgorithmId; +import sun.security.util.DerValue; +import sun.security.util.DerInputStream; +import sun.security.util.DerOutputStream; /** * This class implements the {@code EncryptedPrivateKeyInfo} type @@ -54,19 +50,19 @@ * * @author Valerie Peng * - * @see PKCS8EncodedKeySpec + * @see java.security.spec.PKCS8EncodedKeySpec * * @since 1.4 */ -public final class EncryptedPrivateKeyInfo implements DEREncodable { +public class EncryptedPrivateKeyInfo { // The "encryptionAlgorithm" is stored in either the algid or // the params field. Precisely, if this object is created by // {@link #EncryptedPrivateKeyInfo(AlgorithmParameters, byte[])} // with an uninitialized AlgorithmParameters, the AlgorithmParameters // object is stored in the params field and algid is set to null. - // In all other cases, algid is non-null and params is null. + // In all other cases, algid is non null and params is null. private final AlgorithmId algid; private final AlgorithmParameters params; @@ -77,14 +73,19 @@ public final class EncryptedPrivateKeyInfo implements DEREncodable { private final byte[] encoded; /** - * Constructs an {@code EncryptedPrivateKeyInfo} from a given Encrypted - * PKCS#8 ASN.1 encoding. - * @param encoded the ASN.1 encoding to be parsed. - * @throws NullPointerException if {@code encoded} is {@code null}. - * @throws IOException if error occurs when parsing the ASN.1 encoding. + * Constructs (i.e., parses) an {@code EncryptedPrivateKeyInfo} from + * its ASN.1 encoding. + * @param encoded the ASN.1 encoding of this object. The contents of + * the array are copied to protect against subsequent modification. + * @exception NullPointerException if the {@code encoded} is + * {@code null}. + * @exception IOException if error occurs when parsing the ASN.1 encoding. */ public EncryptedPrivateKeyInfo(byte[] encoded) throws IOException { - Objects.requireNonNull(encoded); + if (encoded == null) { + throw new NullPointerException("the encoded parameter " + + "must be non-null"); + } this.encoded = encoded.clone(); DerValue val = DerValue.wrap(this.encoded); @@ -187,7 +188,6 @@ public EncryptedPrivateKeyInfo(AlgorithmParameters algParams, if (algParams == null) { throw new NullPointerException("algParams must be non-null"); } - AlgorithmId tmp; try { tmp = AlgorithmId.get(algParams); @@ -201,7 +201,7 @@ public EncryptedPrivateKeyInfo(AlgorithmParameters algParams, tmp = null; } - // one and only one is non-null + // one and only one is non null this.algid = tmp; this.params = this.algid != null ? null : algParams; @@ -219,17 +219,6 @@ public EncryptedPrivateKeyInfo(AlgorithmParameters algParams, this.encoded = null; } - /** - * Create an EncryptedPrivateKeyInfo object from the given components - */ - private EncryptedPrivateKeyInfo(byte[] encoded, byte[] eData, - AlgorithmId id, AlgorithmParameters p) { - this.encoded = encoded; - encryptedData = eData; - algid = id; - params = p; - } - /** * Returns the encryption algorithm. *

Note: Standard name is returned instead of the specified one @@ -319,167 +308,15 @@ private PKCS8EncodedKeySpec getKeySpecImpl(Key decryptKey, } } - /** - * Returns an {@code EncryptedPrivateKeyInfo} from a given PrivateKey. - * A valid password-based encryption (PBE) algorithm and password must be - * specified. AlgorithmParameterSpec, {@code params}, will use the - * provider default if {@code null} is passed. If {@code provider} is - * {@code null}, the provider will be selected through the default - * provider list. - *

- * The PBE algorithm string format details can be found in the - * - * Cipher section of the Java Security Standard Algorithm Names - * Specification. - * - * @param key the PrivateKey object to encrypt. - * @param password the password used for generating the PBE key. - * @param algorithm the PBE encryption algorithm. - * @param params the parameters used with the PBE encryption. - * @param provider the Provider that will perform the encryption. - * @return an EncryptedPrivateKeyInfo. - * @throws IllegalArgumentException when an argument causes an - * initialization error. - * @throws SecurityException on a cryptographic errors. - * @throws NullPointerException if the key, password, or algorithm are null. - * null. - * - * @since 24 - */ - @PreviewFeature(feature = PreviewFeature.Feature.PEM_API) - public static EncryptedPrivateKeyInfo encryptKey(PrivateKey key, - char[] password, String algorithm, AlgorithmParameterSpec params, - Provider provider) { - - PBEKeySpec keySpec = new PBEKeySpec(password); - Objects.requireNonNull(key); - Objects.requireNonNull(algorithm); - Cipher cipher; - SecretKey skey; - - try { - SecretKeyFactory factory; - if (provider == null) { - factory = SecretKeyFactory.getInstance(algorithm); - cipher = Cipher.getInstance(algorithm); - } else { - factory = SecretKeyFactory.getInstance(algorithm, provider); - cipher = Cipher.getInstance(algorithm, provider); - } - skey = factory.generateSecret(keySpec); - } catch (NoSuchAlgorithmException | NoSuchPaddingException | - InvalidKeySpecException e) { - throw new IllegalArgumentException(e); - } - - AlgorithmId algId; - byte[] encryptedData; - DerOutputStream out = new DerOutputStream(); - try { - cipher.init(Cipher.ENCRYPT_MODE, skey, params); - encryptedData = cipher.doFinal(key.getEncoded()); - algId = new AlgorithmId(Pem.getPBEID(algorithm), - cipher.getParameters()); - algId.encode(out); - out.putOctetString(encryptedData); - } catch (InvalidAlgorithmParameterException e) { - throw new IllegalArgumentException(e); - } catch (IllegalBlockSizeException | BadPaddingException | - InvalidKeyException e) { - throw new SecurityException(e); - } - return new EncryptedPrivateKeyInfo( - DerValue.wrap(DerValue.tag_Sequence, out).toByteArray(), - encryptedData, algId, cipher.getParameters()); - } - - /** - * Creates and encrypts an `EncryptedPrivateKeyInfo` from a given PrivateKey - * and password. - * - * @implNote The encryption uses the algorithm set by `jdk.epkcs8.defaultAlgorithm` - * Security Property by the default provider and default the - * AlgorithmParameterSpec of that provider. - * - * @param key The PrivateKey object to encrypt. - * @param password the password used in the PBE encryption. This array is - * cloned before being used. - * @return an EncryptedPrivateKeyInfo. - * @throws IllegalArgumentException on initialization errors based on the - * arguments passed to the method. - * @throws SecurityException on a encryption errors. - * @throws NullPointerException when the password is null. - * - * @since 24 - */ - @PreviewFeature(feature = PreviewFeature.Feature.PEM_API) - public static EncryptedPrivateKeyInfo encryptKey(PrivateKey key, - char[] password) { - char[] pass = password.clone(); - if (Pem.DEFAULT_ALGO == null || Pem.DEFAULT_ALGO.length() == 0) { - throw new SecurityException("Security property " + - "\"jdk.epkcs8.defaultAlgorithm\" may not specify a " + - "valid algorithm. Operation cannot be performed."); - } - return encryptKey(key, pass, Pem.DEFAULT_ALGO, - null, null); - } - - /** - * Return a PrivateKey from the encrypted data in the object. - * - * @param password the password used in the PBE encryption. - * @return a PrivateKey - * @throws InvalidKeyException if an error occurs during parsing of the - * encrypted data or creation of the key object. - * - * @since 24 - */ - @PreviewFeature(feature = PreviewFeature.Feature.PEM_API) - public PrivateKey getKey(char[] password) throws InvalidKeyException { - return getKey(password, null); - } - - /** - * Return a PrivateKey from the object's encrypted data with a KeyFactory - * from the given Provider. - * - * @param password the password - * @param provider the KeyFactory provider used to generate the key. - * @return a PrivateKey - * @throws InvalidKeyException if an error occurs during parsing of the - * encrypted data or creation of the key object. - * - * @since 24 - */ - @PreviewFeature(feature = PreviewFeature.Feature.PEM_API) - public PrivateKey getKey(char[] password, Provider provider) - throws InvalidKeyException { - try { - PBEKeySpec pks = new PBEKeySpec(password); - SecretKeyFactory skf; - PKCS8EncodedKeySpec keySpec; - if (provider == null) { - skf = SecretKeyFactory.getInstance(getAlgName()); - keySpec = getKeySpec(skf.generateSecret(pks)); - } else { - skf = SecretKeyFactory.getInstance(getAlgName(), provider); - keySpec = getKeySpec(skf.generateSecret(pks), provider); - } - return PKCS8Key.parseKey(keySpec.getEncoded()); - } catch (GeneralSecurityException e) { - throw new InvalidKeyException(e); - } - } - /** * Extract the enclosed PKCS8EncodedKeySpec object from the * encrypted data and return it. * @param decryptKey key used for decrypting the encrypted data. * @return the PKCS8EncodedKeySpec object. - * @exception NullPointerException if {@code decryptKey} is {@code null}. - * @exception NoSuchAlgorithmException Cannot find appropriate cipher to - * decrypt. + * @exception NullPointerException if {@code decryptKey} + * is {@code null}. + * @exception NoSuchAlgorithmException if cannot find appropriate + * cipher to decrypt the encrypted data. * @exception InvalidKeyException if {@code decryptKey} * cannot be used to decrypt the encrypted data or the decryption * result is not a valid PKCS8KeySpec. diff --git a/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java b/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java index 198bae1d99a92..4b2a0629706c1 100644 --- a/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java +++ b/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java @@ -76,15 +76,11 @@ public enum Feature { STRUCTURED_CONCURRENCY, @JEP(number=466, title="ClassFile API", status="Second Preview") CLASSFILE_API, - @JEP(number=473, title="Stream Gatherers", status="Second Preview") STREAM_GATHERERS, @JEP(number=476, title="Module Import Declarations", status="Preview") MODULE_IMPORTS, @JEP(number=478, title="Key Derivation Function API", status="Preview") KEY_DERIVATION, - //XXX Number will change when assigned - @JEP(number=999, title="PEM API", status="Preview") - PEM_API, LANGUAGE_MODEL, /** * A key for testing. diff --git a/src/java.base/share/classes/sun/security/ec/ECKeyFactory.java b/src/java.base/share/classes/sun/security/ec/ECKeyFactory.java index a73bc8013f21d..85d4d0bf2639b 100644 --- a/src/java.base/share/classes/sun/security/ec/ECKeyFactory.java +++ b/src/java.base/share/classes/sun/security/ec/ECKeyFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2022, 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 @@ -25,9 +25,6 @@ package sun.security.ec; -import sun.security.pkcs.PKCS8Key; - -import java.io.IOException; import java.security.*; import java.security.interfaces.*; import java.security.spec.*; @@ -220,23 +217,16 @@ private PrivateKey implTranslatePrivateKey(PrivateKey key) // internal implementation of generatePublic. See JCA doc private PublicKey implGeneratePublic(KeySpec keySpec) - throws GeneralSecurityException { + throws GeneralSecurityException { if (keySpec instanceof X509EncodedKeySpec) { - return new ECPublicKeyImpl(((X509EncodedKeySpec)keySpec).getEncoded()); - - } else if (keySpec instanceof ECPublicKeySpec ecSpec) { - return new ECPublicKeyImpl(ecSpec.getW(), ecSpec.getParams()); - - } else if (keySpec instanceof PKCS8EncodedKeySpec) { - PKCS8Key p8key; - try { - p8key = new ECPrivateKeyImpl( - ((PKCS8EncodedKeySpec)keySpec).getEncoded()); - } catch (Exception e) { - throw new GeneralSecurityException(e); - } - return new ECPublicKeyImpl(p8key.getPubKeyEncoded()); - + X509EncodedKeySpec x509Spec = (X509EncodedKeySpec)keySpec; + return new ECPublicKeyImpl(x509Spec.getEncoded()); + } else if (keySpec instanceof ECPublicKeySpec) { + ECPublicKeySpec ecSpec = (ECPublicKeySpec)keySpec; + return new ECPublicKeyImpl( + ecSpec.getW(), + ecSpec.getParams() + ); } else { throw new InvalidKeySpecException("Only ECPublicKeySpec " + "and X509EncodedKeySpec supported for EC public keys"); @@ -247,20 +237,19 @@ private PublicKey implGeneratePublic(KeySpec keySpec) private PrivateKey implGeneratePrivate(KeySpec keySpec) throws GeneralSecurityException { if (keySpec instanceof PKCS8EncodedKeySpec) { - byte[] encoded = ((PKCS8EncodedKeySpec) keySpec).getEncoded(); + PKCS8EncodedKeySpec pkcsSpec = (PKCS8EncodedKeySpec)keySpec; + byte[] encoded = pkcsSpec.getEncoded(); try { return new ECPrivateKeyImpl(encoded); } finally { Arrays.fill(encoded, (byte) 0); } - - } else if (keySpec instanceof ECPrivateKeySpec ecSpec) { + } else if (keySpec instanceof ECPrivateKeySpec) { + ECPrivateKeySpec ecSpec = (ECPrivateKeySpec)keySpec; return new ECPrivateKeyImpl(ecSpec.getS(), ecSpec.getParams()); - } else { - throw new InvalidKeySpecException("Only ECPrivateKeySpec " + - "and PKCS8EncodedKeySpec supported for EC private keys. " + - keySpec.getClass().getName() + " provided."); + throw new InvalidKeySpecException("Only ECPrivateKeySpec " + + "and PKCS8EncodedKeySpec supported for EC private keys"); } } diff --git a/src/java.base/share/classes/sun/security/ec/ECPrivateKeyImpl.java b/src/java.base/share/classes/sun/security/ec/ECPrivateKeyImpl.java index 3b20c0f7bfeb3..b1b8b2d188f2c 100644 --- a/src/java.base/share/classes/sun/security/ec/ECPrivateKeyImpl.java +++ b/src/java.base/share/classes/sun/security/ec/ECPrivateKeyImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2023, 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 @@ -111,7 +111,7 @@ private void makeEncoding(byte[] s) throws InvalidKeyException { out.putOctetString(privBytes); Arrays.fill(privBytes, (byte) 0); DerValue val = DerValue.wrap(DerValue.tag_Sequence, out); - privKeyMaterial = val.toByteArray(); + key = val.toByteArray(); val.clear(); } @@ -133,7 +133,7 @@ private void makeEncoding(BigInteger s) throws InvalidKeyException { out.putOctetString(sOctets); Arrays.fill(sOctets, (byte) 0); DerValue val = DerValue.wrap(DerValue.tag_Sequence, out); - privKeyMaterial = val.toByteArray(); + key = val.toByteArray(); val.clear(); } @@ -153,11 +153,15 @@ public BigInteger getS() { return s; } - public byte[] getArrayS() { + private byte[] getArrayS0() { if (arrayS == null) { arrayS = ECUtil.sArray(getS(), params); } - return arrayS.clone(); + return arrayS; + } + + public byte[] getArrayS() { + return getArrayS0().clone(); } // see JCA doc @@ -166,9 +170,8 @@ public ECParameterSpec getParams() { } private void parseKeyBits() throws InvalidKeyException { - // Parse private key material from PKCS8Key.decode() try { - DerInputStream in = new DerInputStream(privKeyMaterial); + DerInputStream in = new DerInputStream(key); DerValue derValue = in.getDerValue(); if (derValue.tag != DerValue.tag_Sequence) { throw new IOException("Not a SEQUENCE"); @@ -191,8 +194,6 @@ private void parseKeyBits() throws InvalidKeyException { throw new InvalidKeyException("Unexpected value: " + value); } } - - // Validate parameters stored from PKCS8Key.decode() AlgorithmParameters algParams = this.algid.getParameters(); if (algParams == null) { throw new InvalidKeyException("EC domain parameters must be " @@ -204,11 +205,12 @@ private void parseKeyBits() throws InvalidKeyException { } } + @Override public PublicKey calculatePublicKey() { ECParameterSpec ecParams = getParams(); ECOperations ops = ECOperations.forParameters(ecParams) .orElseThrow(ProviderException::new); - MutablePoint pub = ops.multiply(ecParams.getGenerator(), getArrayS()); + MutablePoint pub = ops.multiply(ecParams.getGenerator(), getArrayS0()); AffinePoint affPub = pub.asAffine(); ECPoint w = new ECPoint(affPub.getX().asBigInteger(), affPub.getY().asBigInteger()); diff --git a/src/java.base/share/classes/sun/security/ec/XDHKeyFactory.java b/src/java.base/share/classes/sun/security/ec/XDHKeyFactory.java index feb61d23b597f..3e2ced4123644 100644 --- a/src/java.base/share/classes/sun/security/ec/XDHKeyFactory.java +++ b/src/java.base/share/classes/sun/security/ec/XDHKeyFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2021, 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 @@ -25,10 +25,12 @@ package sun.security.ec; -import sun.security.pkcs.PKCS8Key; - -import java.io.IOException; -import java.security.*; +import java.security.KeyFactorySpi; +import java.security.Key; +import java.security.PublicKey; +import java.security.PrivateKey; +import java.security.InvalidKeyException; +import java.security.ProviderException; import java.security.interfaces.XECKey; import java.security.interfaces.XECPrivateKey; import java.security.interfaces.XECPublicKey; @@ -158,14 +160,6 @@ private PublicKey generatePublicImpl(KeySpec keySpec) InvalidKeySpecException::new, publicKeySpec.getParams()); checkLockedParams(InvalidKeySpecException::new, params); return new XDHPublicKeyImpl(params, publicKeySpec.getU()); - } else if (keySpec instanceof PKCS8EncodedKeySpec) { - PKCS8Key p8key = (PKCS8Key) XDHPrivateKeyImpl.parseKey(( - (PKCS8EncodedKeySpec)keySpec).getEncoded()); - XDHPublicKeyImpl result = - new XDHPublicKeyImpl(p8key.getPubKeyEncoded()); - checkLockedParams(InvalidKeySpecException::new, - result.getParams()); - return result; } else { throw new InvalidKeySpecException( "Only X509EncodedKeySpec and XECPublicKeySpec are supported"); diff --git a/src/java.base/share/classes/sun/security/ec/XDHPrivateKeyImpl.java b/src/java.base/share/classes/sun/security/ec/XDHPrivateKeyImpl.java index 4ae38787c5529..dfc0d0f6cd349 100644 --- a/src/java.base/share/classes/sun/security/ec/XDHPrivateKeyImpl.java +++ b/src/java.base/share/classes/sun/security/ec/XDHPrivateKeyImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2023, 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 @@ -54,7 +54,7 @@ public final class XDHPrivateKeyImpl extends PKCS8Key implements XECPrivateKey { DerValue val = new DerValue(DerValue.tag_OctetString, k); try { - this.privKeyMaterial = val.toByteArray(); + this.key = val.toByteArray(); } finally { val.clear(); } @@ -67,7 +67,7 @@ public final class XDHPrivateKeyImpl extends PKCS8Key implements XECPrivateKey { InvalidKeyException::new, algid); paramSpec = new NamedParameterSpec(params.getName()); try { - DerInputStream derStream = new DerInputStream(privKeyMaterial); + DerInputStream derStream = new DerInputStream(key); k = derStream.getOctetString(); } catch (IOException ex) { throw new InvalidKeyException(ex); @@ -102,6 +102,7 @@ public Optional getScalar() { return Optional.of(getK()); } + @Override public PublicKey calculatePublicKey() { XECParameters params = paramSpec.getName().equalsIgnoreCase("X25519") ? XECParameters.X25519 diff --git a/src/java.base/share/classes/sun/security/ec/ed/EdDSAKeyFactory.java b/src/java.base/share/classes/sun/security/ec/ed/EdDSAKeyFactory.java index 8fd4c40314bd2..c282a34ef846f 100644 --- a/src/java.base/share/classes/sun/security/ec/ed/EdDSAKeyFactory.java +++ b/src/java.base/share/classes/sun/security/ec/ed/EdDSAKeyFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2021, 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 @@ -25,9 +25,12 @@ package sun.security.ec.ed; -import sun.security.pkcs.PKCS8Key; - -import java.security.*; +import java.security.KeyFactorySpi; +import java.security.Key; +import java.security.PublicKey; +import java.security.PrivateKey; +import java.security.InvalidKeyException; +import java.security.ProviderException; import java.security.interfaces.*; import java.security.spec.*; import java.util.Arrays; @@ -150,18 +153,9 @@ private PublicKey generatePublicImpl(KeySpec keySpec) InvalidKeySpecException::new, publicKeySpec.getParams()); checkLockedParams(InvalidKeySpecException::new, params); return new EdDSAPublicKeyImpl(params, publicKeySpec.getPoint()); - } else if (keySpec instanceof PKCS8EncodedKeySpec) { - PKCS8Key p8key = null; - try { - p8key = new EdDSAPrivateKeyImpl( - ((PKCS8EncodedKeySpec)keySpec).getEncoded()); - } catch (Exception e) { - throw new InvalidKeyException(e); - } - return new EdDSAPublicKeyImpl(p8key.getPubKeyEncoded()); } else { - throw new InvalidKeySpecException(keySpec.getClass().getName() + - "not supported"); + throw new InvalidKeySpecException( + "Only X509EncodedKeySpec and EdECPublicKeySpec are supported"); } } diff --git a/src/java.base/share/classes/sun/security/ec/ed/EdDSAPrivateKeyImpl.java b/src/java.base/share/classes/sun/security/ec/ed/EdDSAPrivateKeyImpl.java index fc31e87c6137b..661ec9ed1b7b5 100644 --- a/src/java.base/share/classes/sun/security/ec/ed/EdDSAPrivateKeyImpl.java +++ b/src/java.base/share/classes/sun/security/ec/ed/EdDSAPrivateKeyImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, 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 @@ -56,7 +56,7 @@ public final class EdDSAPrivateKeyImpl DerValue val = new DerValue(DerValue.tag_OctetString, h); try { - privKeyMaterial = val.toByteArray(); + this.key = val.toByteArray(); } finally { val.clear(); } @@ -71,7 +71,7 @@ public final class EdDSAPrivateKeyImpl paramSpec = new NamedParameterSpec(params.getName()); try { - DerInputStream derStream = new DerInputStream(privKeyMaterial); + DerInputStream derStream = new DerInputStream(key); h = derStream.getOctetString(); } catch (IOException ex) { throw new InvalidKeyException(ex); @@ -81,8 +81,8 @@ public final class EdDSAPrivateKeyImpl void checkLength(EdDSAParameters params) throws InvalidKeyException { - if (params.getKeyLength() != h.length) { - throw new InvalidKeyException("key length is " + h.length + + if (params.getKeyLength() != this.h.length) { + throw new InvalidKeyException("key length is " + this.h.length + ", key length must be " + params.getKeyLength()); } } diff --git a/src/java.base/share/classes/sun/security/pkcs/NamedPKCS8Key.java b/src/java.base/share/classes/sun/security/pkcs/NamedPKCS8Key.java index 50b39db098758..88a2909cfac1c 100644 --- a/src/java.base/share/classes/sun/security/pkcs/NamedPKCS8Key.java +++ b/src/java.base/share/classes/sun/security/pkcs/NamedPKCS8Key.java @@ -75,7 +75,7 @@ public NamedPKCS8Key(String fname, String pname, byte[] rawBytes) { DerValue val = new DerValue(DerValue.tag_OctetString, rawBytes); try { - this.privKeyMaterial = val.toByteArray(); + this.key = val.toByteArray(); } finally { val.clear(); } @@ -90,7 +90,7 @@ public NamedPKCS8Key(String fname, byte[] encoded) throws InvalidKeyException { if (algid.getEncodedParams() != null) { throw new InvalidKeyException("algorithm identifier has params"); } - rawBytes = new DerInputStream(privKeyMaterial).getOctetString(); + rawBytes = new DerInputStream(key).getOctetString(); } catch (IOException e) { throw new InvalidKeyException("Cannot parse input", e); } @@ -129,7 +129,7 @@ private void readObject(ObjectInputStream stream) @Override public void destroy() throws DestroyFailedException { Arrays.fill(rawBytes, (byte)0); - Arrays.fill(privKeyMaterial, (byte)0); + Arrays.fill(key, (byte)0); if (encodedKey != null) { Arrays.fill(encodedKey, (byte)0); } diff --git a/src/java.base/share/classes/sun/security/pkcs/PKCS8Key.java b/src/java.base/share/classes/sun/security/pkcs/PKCS8Key.java index f5b0787db9caf..4cb277b43a830 100644 --- a/src/java.base/share/classes/sun/security/pkcs/PKCS8Key.java +++ b/src/java.base/share/classes/sun/security/pkcs/PKCS8Key.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2023, 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 @@ -25,22 +25,25 @@ package sun.security.pkcs; -import jdk.internal.access.SharedSecrets; -import sun.security.util.*; -import sun.security.x509.AlgorithmId; -import sun.security.x509.X509Key; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.security.*; +import java.io.*; +import java.security.Key; +import java.security.KeyRep; +import java.security.PrivateKey; +import java.security.KeyFactory; +import java.security.MessageDigest; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.util.Arrays; -import java.util.Optional; + +import jdk.internal.access.SharedSecrets; +import sun.security.x509.*; +import sun.security.util.*; /** * Holds a PKCS#8 key, for example a private key - *

+ * * According to https://tools.ietf.org/html/rfc5958: * * OneAsymmetricKey ::= SEQUENCE { @@ -52,7 +55,7 @@ * [[2: publicKey [1] PublicKey OPTIONAL ]], * ... * } - *

+ * * We support this format but do not parse attributes and publicKey now. */ public class PKCS8Key implements PrivateKey, InternalPrivateKey { @@ -64,25 +67,20 @@ public class PKCS8Key implements PrivateKey, InternalPrivateKey { /* The algorithm information (name, parameters, etc). */ protected AlgorithmId algid; - /* The key bytes in octet form for the algorithm subclasses to decode */ - protected byte[] privKeyMaterial; + /* The key bytes, without the algorithm information */ + protected byte[] key; - /* The pkcs8 encoding of this key(s). Created on demand. */ + /* The encoded for the key. Created on demand by encode(). */ protected byte[] encodedKey; - /* The encoded x509 public key for v2 */ - protected byte[] pubKeyEncoded = null; - /* The version for this key */ private static final int V1 = 0; private static final int V2 = 1; - private byte[] attributes; - private int version; /** * Default constructor. Constructors in subclasses that create a new key * from its components require this. These constructors must initialize - * {@link #algid} and {@link #privKeyMaterial}. + * {@link #algid} and {@link #key}. */ protected PKCS8Key() { } @@ -93,7 +91,7 @@ protected PKCS8Key() { } * * This method is also used by {@link #parseKey} to create a raw key. */ - public PKCS8Key(byte[] input) throws InvalidKeyException { + protected PKCS8Key(byte[] input) throws InvalidKeyException { try { decode(new DerValue(input)); } catch (IOException e) { @@ -101,70 +99,39 @@ public PKCS8Key(byte[] input) throws InvalidKeyException { } } - private PKCS8Key(byte[] privEncoding, byte[] pubEncoding) - throws InvalidKeyException { - this(privEncoding); - pubKeyEncoded = pubEncoding; - version = V2; - } - - public int getVersion() { - return version; - } - - /** - * Method for decoding PKCS8 v1 and v2 formats. Decoded values are stored - * in this class, key material remains in DER format for algorithm - * subclasses to decode. - */ private void decode(DerValue val) throws InvalidKeyException { try { if (val.tag != DerValue.tag_Sequence) { throw new InvalidKeyException("invalid key format"); } - // Support check for V1, aka 0, and V2, aka 1. - version = val.data.getInteger(); + int version = val.data.getInteger(); if (version != V1 && version != V2) { throw new InvalidKeyException("unknown version: " + version); } - // Parse and store AlgorithmID - algid = AlgorithmId.parse(val.data.getDerValue()); + algid = AlgorithmId.parse (val.data.getDerValue ()); + key = val.data.getOctetString(); - // Store key material for subclasses to parse - privKeyMaterial = val.data.getOctetString(); - - // PKCS8 v1 typically ends here + DerValue next; if (val.data.available() == 0) { return; } - - // OPTIONAL Context tag 0 for Attributes for PKCS8 v1 & v2 - // Uses 0xA0 constructed define-length or 0x80 constructed - // indefinite. - DerValue v = val.data.getDerValue(); - if (v.isContextSpecific((byte)0)) { - attributes = v.getDataBytes(); // Save DER sequence + next = val.data.getDerValue(); + if (next.isContextSpecific((byte)0)) { if (val.data.available() == 0) { return; } - v = val.data.getDerValue(); + next = val.data.getDerValue(); } - // OPTIONAL context tag 1 for Public Key for PKCS8 v2 only - if (version == V2) { - if (v.isContextSpecific((byte)1)) { - DerValue bits = v.withTag(DerValue.tag_BitString); - pubKeyEncoded = new X509Key(algid, - bits.getUnalignedBitString()).getEncoded(); - } else { - throw new InvalidKeyException("Invalid context tag"); + if (next.isContextSpecific((byte)1)) { + if (version == V1) { + throw new InvalidKeyException("publicKey seen in v1"); } if (val.data.available() == 0) { return; } } - throw new InvalidKeyException("Extra bytes"); } catch (IOException e) { throw new InvalidKeyException("Unable to decode key", e); @@ -189,13 +156,11 @@ private void decode(DerValue val) throws InvalidKeyException { * @param encoded the DER-encoded SubjectPublicKeyInfo value * @exception IOException on data format errors */ - public static PrivateKey parseKey(byte[] encoded) - throws InvalidKeyException { + public static PrivateKey parseKey(byte[] encoded) throws IOException { try { PKCS8Key rawKey = new PKCS8Key(encoded); - byte[] internal = rawKey.generateEncoding(); - PKCS8EncodedKeySpec pkcs8KeySpec = - new PKCS8EncodedKeySpec(internal); + byte[] internal = rawKey.getEncodedInternal(); + PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(internal); PrivateKey result = null; try { result = KeyFactory.getInstance(rawKey.algid.getName()) @@ -211,8 +176,8 @@ public static PrivateKey parseKey(byte[] encoded) .clearEncodedKeySpec(pkcs8KeySpec); } return result; - } catch (IOException e) { - throw new InvalidKeyException(e); + } catch (InvalidKeyException e) { + throw new IOException("corrupt private key", e); } } @@ -223,22 +188,10 @@ public String getAlgorithm() { return algid.getName(); } - public byte[] getPubKeyEncoded() { - return pubKeyEncoded; - } - - public byte[] getPrivKeyMaterial() { - return privKeyMaterial; - } - - public boolean hasPublicKey() { - return (pubKeyEncoded == null ? false : true); - } - /** * Returns the algorithm ID to be used with this key. */ - public AlgorithmId getAlgorithmId () { + public AlgorithmId getAlgorithmId () { return algid; } @@ -257,26 +210,6 @@ public String getFormat() { return "PKCS#8"; } - /** - * With a given encoded Public and Private key, generate and return a - * PKCS8v2 DER-encoded byte[]. - * - * @param pubKeyEncoded DER-encoded PublicKey - * @param privKeyEncoded DER-encoded PrivateKey - * @return DER-encoded byte array - * @throws IOException thrown on encoding failure - */ - public static byte[] getEncoded(byte[] pubKeyEncoded, byte[] privKeyEncoded) - throws IOException { - PKCS8Key privKey; - try { - privKey = new PKCS8Key(privKeyEncoded, pubKeyEncoded); - } catch (InvalidKeyException e) { - throw new IOException(e); - } - return privKey.generateEncoding(); - } - /** * DER-encodes this key as a byte array stored inside this object * and return it. @@ -285,45 +218,17 @@ public static byte[] getEncoded(byte[] pubKeyEncoded, byte[] privKeyEncoded) */ private synchronized byte[] getEncodedInternal() { if (encodedKey == null) { - try { - encodedKey = generateEncoding(); - } catch (IOException e) { - e.printStackTrace(); - // encodedKey is still null - } + DerOutputStream tmp = new DerOutputStream(); + tmp.putInteger(V1); + algid.encode(tmp); + tmp.putOctetString(key); + DerValue out = DerValue.wrap(DerValue.tag_Sequence, tmp); + encodedKey = out.toByteArray(); + out.clear(); } return encodedKey; } - private byte[] generateEncoding() throws IOException { - DerOutputStream out = new DerOutputStream(); - out.putInteger((pubKeyEncoded == null) ? 0 : 1); - algid.encode(out); - out.putOctetString(privKeyMaterial); - - if (attributes != null) { - out.writeImplicit( - DerValue.createTag((byte) (DerValue.TAG_CONTEXT | - DerValue.TAG_CONSTRUCT), false, (byte) 0), - new DerOutputStream().putOctetString(attributes)); - - } - - if (pubKeyEncoded != null) { - X509Key x = (X509Key) X509Key.parseKey(pubKeyEncoded); - DerOutputStream pubOut = new DerOutputStream(); - pubOut.putUnalignedBitString(x.getKey()); - out.writeImplicit( - DerValue.createTag(DerValue.TAG_CONTEXT, false, (byte) 1), - pubOut); - } - - DerValue val = DerValue.wrap(DerValue.tag_Sequence, out); - encodedKey = val.toByteArray(); - val.clear(); - return encodedKey; - } - @java.io.Serial protected Object writeReplace() throws java.io.ObjectStreamException { return new KeyRep(KeyRep.Type.PRIVATE, @@ -393,6 +298,6 @@ public void clear() { if (encodedKey != null) { Arrays.fill(encodedKey, (byte)0); } - Arrays.fill(privKeyMaterial, (byte)0); + Arrays.fill(key, (byte)0); } } diff --git a/src/java.base/share/classes/sun/security/provider/DSAPrivateKey.java b/src/java.base/share/classes/sun/security/provider/DSAPrivateKey.java index 586b0ea65b31c..e34fd3a6af145 100644 --- a/src/java.base/share/classes/sun/security/provider/DSAPrivateKey.java +++ b/src/java.base/share/classes/sun/security/provider/DSAPrivateKey.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2022, 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 @@ -70,7 +70,7 @@ public DSAPrivateKey(BigInteger x, BigInteger p, byte[] xbytes = x.toByteArray(); DerValue val = new DerValue(DerValue.tag_Integer, xbytes); - privKeyMaterial = val.toByteArray(); + key = val.toByteArray(); val.clear(); Arrays.fill(xbytes, (byte)0); } @@ -81,7 +81,7 @@ public DSAPrivateKey(BigInteger x, BigInteger p, public DSAPrivateKey(byte[] encoded) throws InvalidKeyException { super(encoded); try { - DerInputStream in = new DerInputStream(privKeyMaterial); + DerInputStream in = new DerInputStream(key); x = in.getBigInteger(); } catch (IOException e) { throw new InvalidKeyException(e.getMessage(), e); diff --git a/src/java.base/share/classes/sun/security/provider/KeyProtector.java b/src/java.base/share/classes/sun/security/provider/KeyProtector.java index cafbe148898c4..0faed9db73737 100644 --- a/src/java.base/share/classes/sun/security/provider/KeyProtector.java +++ b/src/java.base/share/classes/sun/security/provider/KeyProtector.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2022, 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 @@ -300,8 +300,8 @@ public Key recover(EncryptedPrivateKeyInfo encrInfo) // which in turn parses the key material. try { return PKCS8Key.parseKey(plainKey); - } catch (InvalidKeyException e) { - throw new UnrecoverableKeyException(e.getMessage()); + } catch (IOException ioe) { + throw new UnrecoverableKeyException(ioe.getMessage()); } finally { Arrays.fill(plainKey, (byte)0); } diff --git a/src/java.base/share/classes/sun/security/provider/X509Factory.java b/src/java.base/share/classes/sun/security/provider/X509Factory.java index 47472b1730133..ad48c0c598d02 100644 --- a/src/java.base/share/classes/sun/security/provider/X509Factory.java +++ b/src/java.base/share/classes/sun/security/provider/X509Factory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2022, 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 @@ -36,8 +36,6 @@ import sun.security.provider.certpath.X509CertificatePair; import sun.security.util.Cache; import sun.security.util.DerValue; -import sun.security.util.PEMRecord; -import sun.security.util.Pem; import sun.security.x509.X509CRLImpl; import sun.security.x509.X509CertImpl; @@ -558,18 +556,118 @@ private static byte[] readOneBlock(InputStream is) throws IOException { readBERInternal(is, bout, c); return bout.toByteArray(); } else { - try { - PEMRecord pem = Pem.readPEM(is, (c == '-' ? true : false)); - if (pem == null) { + // Read BASE64 encoded data, might skip info at the beginning + ByteArrayOutputStream data = new ByteArrayOutputStream(); + + // Step 1: Read until header is found + int hyphen = (c=='-') ? 1: 0; // count of consequent hyphens + int last = (c=='-') ? -1: c; // the char before hyphen + while (true) { + int next = is.read(); + if (next == -1) { + // We accept useless data after the last block, + // say, empty lines. return null; } - return Base64.getDecoder().decode(pem.pem()); + if (next == '-') { + hyphen++; + } else { + hyphen = 0; + last = next; + } + if (hyphen == 5 && (last == -1 || last == '\r' || last == '\n')) { + break; + } + } + + // Step 2: Read the rest of header, determine the line end + int end; + StringBuilder header = new StringBuilder("-----"); + while (true) { + int next = is.read(); + if (next == -1) { + throw new IOException("Incomplete data"); + } + if (next == '\n') { + end = '\n'; + break; + } + if (next == '\r') { + next = is.read(); + if (next == -1) { + throw new IOException("Incomplete data"); + } + if (next == '\n') { + end = '\n'; + } else { + end = '\r'; + // Skip all white space chars + if (next != 9 && next != 10 && next != 13 && next != 32) { + data.write(next); + } + } + break; + } + header.append((char)next); + } + + // Step 3: Read the data + while (true) { + int next = is.read(); + if (next == -1) { + throw new IOException("Incomplete data"); + } + if (next != '-') { + // Skip all white space chars + if (next != 9 && next != 10 && next != 13 && next != 32) { + data.write(next); + } + } else { + break; + } + } + + // Step 4: Consume the footer + StringBuilder footer = new StringBuilder("-"); + while (true) { + int next = is.read(); + // Add next == '\n' for maximum safety, in case endline + // is not consistent. + if (next == -1 || next == end || next == '\n') { + break; + } + if (next != '\r') footer.append((char)next); + } + + checkHeaderFooter(header.toString().stripTrailing(), + footer.toString().stripTrailing()); + + try { + return Base64.getDecoder().decode(data.toByteArray()); } catch (IllegalArgumentException e) { throw new IOException(e); } } } + private static void checkHeaderFooter(String header, + String footer) throws IOException { + if (header.length() < 16 || !header.startsWith("-----BEGIN ") || + !header.endsWith("-----")) { + throw new IOException("Illegal header: " + header); + } + if (footer.length() < 14 || !footer.startsWith("-----END ") || + !footer.endsWith("-----")) { + throw new IOException("Illegal footer: " + footer); + } + String headerType = header.substring(11, header.length()-5); + String footerType = footer.substring(9, footer.length()-5); + if (!headerType.equals(footerType)) { + throw new IOException("Header and footer do not match: " + + header + " " + footer); + } + } + /** * Read one BER data block. This method is aware of indefinite-length BER * encoding and will read all the subsections in a recursive way diff --git a/src/java.base/share/classes/sun/security/rsa/RSAKeyFactory.java b/src/java.base/share/classes/sun/security/rsa/RSAKeyFactory.java index 2795f54bc3f84..6a9cf6edbf8e7 100644 --- a/src/java.base/share/classes/sun/security/rsa/RSAKeyFactory.java +++ b/src/java.base/share/classes/sun/security/rsa/RSAKeyFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2022, 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 @@ -33,7 +33,6 @@ import java.util.Arrays; import sun.security.action.GetPropertyAction; -import sun.security.pkcs.PKCS8Key; import sun.security.rsa.RSAUtil.KeyType; /** @@ -334,15 +333,6 @@ private PublicKey generatePublic(KeySpec keySpec) } catch (ProviderException e) { throw new InvalidKeySpecException(e); } - } else if (keySpec instanceof PKCS8EncodedKeySpec) { - PKCS8Key p8key; - try { - p8key = new PKCS8Key(((PKCS8EncodedKeySpec)keySpec).getEncoded()); - } catch (Exception e) { - throw new GeneralSecurityException(e); - } - return RSAPublicKeyImpl.newKey(type, "X.509", - p8key.getPubKeyEncoded()); } else { throw new InvalidKeySpecException("Only RSAPublicKeySpec " + "and X509EncodedKeySpec supported for RSA public keys"); diff --git a/src/java.base/share/classes/sun/security/rsa/RSAPrivateCrtKeyImpl.java b/src/java.base/share/classes/sun/security/rsa/RSAPrivateCrtKeyImpl.java index 7d85eb7361e46..218b1f6f4ba16 100644 --- a/src/java.base/share/classes/sun/security/rsa/RSAPrivateCrtKeyImpl.java +++ b/src/java.base/share/classes/sun/security/rsa/RSAPrivateCrtKeyImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2023, 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 @@ -69,7 +69,6 @@ public final class RSAPrivateCrtKeyImpl private BigInteger qe; // prime exponent q private BigInteger coeff; // CRT coefficient - // RSA or RSS-PSS KeyType private final transient KeyType type; // Optional parameters associated with this RSA key @@ -102,7 +101,7 @@ public static RSAPrivateKey newKey(KeyType type, String format, } case "PKCS#1": try { - BigInteger[] comps = parsePKCS1(encoded); + BigInteger[] comps = parseASN1(encoded); if ((comps[1].signum() == 0) || (comps[3].signum() == 0) || (comps[4].signum() == 0) || (comps[5].signum() == 0) || (comps[6].signum() == 0) || (comps[7].signum() == 0)) { @@ -238,7 +237,7 @@ private RSAPrivateCrtKeyImpl(byte[] encoded) throws InvalidKeyException { Arrays.fill(nbytes[6], (byte) 0); Arrays.fill(nbytes[7], (byte) 0); DerValue val = DerValue.wrap(DerValue.tag_Sequence, out); - privKeyMaterial = val.toByteArray(); + key = val.toByteArray(); val.clear(); } @@ -305,7 +304,7 @@ public AlgorithmParameterSpec getParams() { // utility method for parsing DER encoding of RSA private keys in PKCS#1 // format as defined in RFC 8017 Appendix A.1.2, i.e. SEQ of version, n, // e, d, p, q, pe, qe, and coeff, and return the parsed components. - private static BigInteger[] parsePKCS1(byte[] raw) throws IOException { + private static BigInteger[] parseASN1(byte[] raw) throws IOException { DerValue derValue = new DerValue(raw); try { if (derValue.tag != DerValue.tag_Sequence) { @@ -338,7 +337,7 @@ private static BigInteger[] parsePKCS1(byte[] raw) throws IOException { private void parseKeyBits() throws InvalidKeyException { try { - BigInteger[] comps = parsePKCS1(privKeyMaterial); + BigInteger[] comps = parseASN1(key); n = comps[0]; e = comps[1]; d = comps[2]; @@ -352,31 +351,6 @@ private void parseKeyBits() throws InvalidKeyException { } } - /** - * With a given PKCS#1/slleay/OpenSSL old default RSA binary encoding, - * decode and return the proper RSA encoded KeySpec - * @param encoded RSA binary encoding - * @return KeySpec - * @throws InvalidKeyException on decoding failure - */ - - public static KeySpec getKeySpec(byte[] encoded) throws - InvalidKeyException { - try { - BigInteger[] comps = parsePKCS1(encoded); - if ((comps[1].signum() == 0) || (comps[3].signum() == 0) || - (comps[4].signum() == 0) || (comps[5].signum() == 0) || - (comps[6].signum() == 0) || (comps[7].signum() == 0)) { - return new RSAPrivateKeySpec(comps[0], comps[2]); - } else { - return new RSAPrivateCrtKeySpec(comps[0], - comps[1], comps[2], comps[3], comps[4], comps[5], - comps[6], comps[7]); - } - } catch (IOException ioe) { - throw new InvalidKeyException("Invalid PKCS#1 encoding", ioe); - } - } /** * Restores the state of this object from the stream. *

diff --git a/src/java.base/share/classes/sun/security/rsa/RSAPrivateKeyImpl.java b/src/java.base/share/classes/sun/security/rsa/RSAPrivateKeyImpl.java index 010a1a871ce83..aa5297b0040cb 100644 --- a/src/java.base/share/classes/sun/security/rsa/RSAPrivateKeyImpl.java +++ b/src/java.base/share/classes/sun/security/rsa/RSAPrivateKeyImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2023, 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 @@ -110,7 +110,7 @@ public final class RSAPrivateKeyImpl extends PKCS8Key implements RSAPrivateKey { out.putInteger(0); out.putInteger(0); DerValue val = DerValue.wrap(DerValue.tag_Sequence, out); - privKeyMaterial = val.toByteArray(); + key = val.toByteArray(); val.clear(); } diff --git a/src/java.base/share/classes/sun/security/rsa/RSAPublicKeyImpl.java b/src/java.base/share/classes/sun/security/rsa/RSAPublicKeyImpl.java index 73a3e94d2401e..5a0745604d2d6 100644 --- a/src/java.base/share/classes/sun/security/rsa/RSAPublicKeyImpl.java +++ b/src/java.base/share/classes/sun/security/rsa/RSAPublicKeyImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2023, 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 @@ -82,7 +82,7 @@ public static RSAPublicKey newKey(KeyType type, String format, break; case "PKCS#1": try { - BigInteger[] comps = parsePKCS1(encoded); + BigInteger[] comps = parseASN1(encoded); key = new RSAPublicKeyImpl(type, null, comps[0], comps[1]); } catch (IOException ioe) { throw new InvalidKeyException("Invalid PKCS#1 encoding", ioe); @@ -199,7 +199,7 @@ public AlgorithmParameterSpec getParams() { // utility method for parsing DER encoding of RSA public keys in PKCS#1 // format as defined in RFC 8017 Appendix A.1.1, i.e. SEQ of n and e. - private static BigInteger[] parsePKCS1(byte[] raw) throws IOException { + private static BigInteger[] parseASN1(byte[] raw) throws IOException { DerValue derValue = new DerValue(raw); if (derValue.tag != DerValue.tag_Sequence) { throw new IOException("Not a SEQUENCE"); @@ -218,7 +218,7 @@ private static BigInteger[] parsePKCS1(byte[] raw) throws IOException { */ protected void parseKeyBits() throws InvalidKeyException { try { - BigInteger[] comps = parsePKCS1(getKey().toByteArray()); + BigInteger[] comps = parseASN1(getKey().toByteArray()); n = comps[0]; e = comps[1]; } catch (IOException e) { diff --git a/src/java.base/share/classes/sun/security/util/DerValue.java b/src/java.base/share/classes/sun/security/util/DerValue.java index 010014e2f4292..f2fcf350b3947 100644 --- a/src/java.base/share/classes/sun/security/util/DerValue.java +++ b/src/java.base/share/classes/sun/security/util/DerValue.java @@ -67,7 +67,6 @@ public class DerValue { /** The tag class types */ public static final byte TAG_UNIVERSAL = (byte)0x000; - public static final byte TAG_CONSTRUCT = (byte)0x020; public static final byte TAG_APPLICATION = (byte)0x040; public static final byte TAG_CONTEXT = (byte)0x080; public static final byte TAG_PRIVATE = (byte)0x0c0; @@ -192,13 +191,13 @@ public class DerValue { public boolean isApplication() { return ((tag & 0x0c0) == 0x040); } /** - * Returns true if the CONTEXT SPECIFIC bit is set in the type tag. + * Returns true iff the CONTEXT SPECIFIC bit is set in the type tag. * This is associated with the ASN.1 "DEFINED BY" syntax. */ public boolean isContextSpecific() { return ((tag & 0x0c0) == 0x080); } /** - * Returns true if the CONTEXT SPECIFIC TAG matches the passed tag. + * Returns true iff the CONTEXT SPECIFIC TAG matches the passed tag. */ public boolean isContextSpecific(byte cntxtTag) { if (!isContextSpecific()) { @@ -209,11 +208,11 @@ public boolean isContextSpecific(byte cntxtTag) { boolean isPrivate() { return ((tag & 0x0c0) == 0x0c0); } - /** Returns true if the CONSTRUCTED bit is set in the type tag. */ + /** Returns true iff the CONSTRUCTED bit is set in the type tag. */ public boolean isConstructed() { return ((tag & 0x020) == 0x020); } /** - * Returns true if the CONSTRUCTED TAG matches the passed tag. + * Returns true iff the CONSTRUCTED TAG matches the passed tag. */ public boolean isConstructed(byte constructedTag) { if (!isConstructed()) { diff --git a/src/java.base/share/classes/sun/security/util/KeyUtil.java b/src/java.base/share/classes/sun/security/util/KeyUtil.java index 11fc3214322da..680962e450a52 100644 --- a/src/java.base/share/classes/sun/security/util/KeyUtil.java +++ b/src/java.base/share/classes/sun/security/util/KeyUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2023, 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 @@ -38,7 +38,6 @@ import javax.crypto.spec.DHPublicKeySpec; import sun.security.jca.JCAUtil; -import sun.security.x509.AlgorithmId; /** * A utility class to get key length, validate keys, etc. @@ -446,55 +445,4 @@ public static String hashAlgFromHSS(PublicKey publicKey) throw new NoSuchAlgorithmException("Cannot decode public key", e); } } - - /** - * With a given DER encoded bytes, read through and return the AlgorithmID - * stored if it can be found. - * - * @param encoded DER encoded bytes - * @return AlgorithmID stored in the DER encoded bytes - * @throws IOException - */ - public static AlgorithmId getAlgorithm(byte[] encoded) throws IOException { - DerInputStream is = new DerInputStream(encoded); - DerValue value = is.getDerValue(); - if (value.tag != DerValue.tag_Sequence) { - throw new IOException("Unknown DER Format: Value 1 not a Sequence"); - } - - is = value.data; - value = is.getDerValue(); - // This route is for: RSAPublic, Encrypted RSAPrivate, EC Public, - // Encrypted EC Private, - if (value.tag == DerValue.tag_Sequence) { - return AlgorithmId.parse(value); - } else if (value.tag == DerValue.tag_Integer) { - // RSAPrivate, ECPrivate - // current value is version, which can be ignored - value = is.getDerValue(); - if (value.tag == DerValue.tag_OctetString) { - value = is.getDerValue(); - if (value.tag == DerValue.tag_Sequence) { - return AlgorithmId.parse(value); - } else { - // OpenSSL/X9.62 (0xA0) - ObjectIdentifier oid = value.data.getOID(); - AlgorithmId algo = new AlgorithmId(oid, (AlgorithmParameters) null); - if (CurveDB.lookup(algo.getName()) != null) { - return new AlgorithmId(AlgorithmId.EC_oid); - } - - } - - } else if (value.tag == DerValue.tag_Sequence) { - // Public Key - return AlgorithmId.parse(value); - } - - } - throw new IOException("No algorithm detected"); - } - - } - diff --git a/src/java.base/share/classes/sun/security/util/PEMRecord.java b/src/java.base/share/classes/sun/security/util/PEMRecord.java deleted file mode 100644 index eff28d16396a8..0000000000000 --- a/src/java.base/share/classes/sun/security/util/PEMRecord.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2024, 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.util; - -import java.nio.charset.StandardCharsets; -import java.util.Objects; - -/** - * A Record for PEM - * - * @param id The PEM header and footer value that identifies the data. - * @param pem The Base64 encoded data only in byte[] format - */ -public record PEMRecord(String id, String pem) { - - public static final String PUBLIC_KEY = "PUBLIC KEY"; - public static final String ENCRYPTED_PRIVATE_KEY = "ENCRYPTED PRIVATE KEY"; - public static final String PRIVATE_KEY = "PRIVATE KEY"; - public static final String RSA_PRIVATE_KEY = "RSA PRIVATE KEY"; - public static final String CERTIFICATE = "CERTIFICATE"; - public static final String X509_CERTIFICATE = "X509 CERTIFICATE"; - public static final String X509_CRL = "X509 CRL"; - - /** - * Get an instance of a PEMRecord. - * - * @param id The PEM header and footer value that identifies the data. - * @param pem The Base64 encoded data only in byte[] format - */ - public PEMRecord(String id, byte[] pem) { - this(id, new String(Objects.requireNonNull(pem), - StandardCharsets.ISO_8859_1)); - } - - /** - * Instantiates a new Pem record. - * - * @param id The PEM header and footer value that identifies the data. - * @param pem The Base64 encoded data only in byte[] format - */ - public PEMRecord(String id, String pem) { - Objects.requireNonNull(pem); - if (id.startsWith("-----")) { - // decode id in the - this.id = id.substring(11, id.lastIndexOf('-') - 4); - } else { - this.id = id; - } - - this.pem = pem; - } - - -} diff --git a/src/java.base/share/classes/sun/security/util/Pem.java b/src/java.base/share/classes/sun/security/util/Pem.java index d58565b33a8c7..ba01d91c82e81 100644 --- a/src/java.base/share/classes/sun/security/util/Pem.java +++ b/src/java.base/share/classes/sun/security/util/Pem.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2023, 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 @@ -25,43 +25,14 @@ package sun.security.util; -import sun.security.x509.AlgorithmId; - -import java.io.*; +import java.io.IOException; import java.nio.charset.StandardCharsets; -import java.security.AccessController; -import java.security.NoSuchAlgorithmException; -import java.security.PrivilegedAction; -import java.security.Security; import java.util.Base64; -import java.util.HexFormat; -import java.util.Objects; -import java.util.regex.Matcher; -import java.util.regex.Pattern; /** * A utility class for PEM format encoding. */ public class Pem { - private static final char WS = 0x20; // Whitespace - - // Default algorithm from jdk.epkcs8.defaultAlgorithm in java.security - public static final String DEFAULT_ALGO; - - // Pattern matching for EKPI operations - private static final Pattern pbePattern; - - // Lazy initialized PBES2 OID value - private static ObjectIdentifier PBES2OID; - - static { - @SuppressWarnings("removal") - String d = AccessController.doPrivileged( - (PrivilegedAction) () -> - Security.getProperty("jdk.epkcs8.defaultAlgorithm")); - DEFAULT_ALGO = d; - pbePattern = Pattern.compile("^PBEWith.*And.*"); - } /** * Decodes a PEM-encoded block. @@ -69,207 +40,15 @@ public class Pem { * @param input the input string, according to RFC 1421, can only contain * characters in the base-64 alphabet and whitespaces. * @return the decoded bytes + * @throws java.io.IOException if input is invalid */ - public static byte[] decode(String input) { - byte[] src = input.replaceAll("\\s+", ""). - getBytes(StandardCharsets.ISO_8859_1); - return Base64.getDecoder().decode(src); - } - - /** - * Return the OID for a given PBE algorithm. PBES1 has an OID for each - * algorithm, while PBES2 has one OID for everything that complies with - * the formatting. Therefore, if the algorithm is not PBES1, it will - * return PBES2. Cipher will determine if this is a valid PBE algorithm. - * PBES2 specifies AES as the cipher algorithm, but any block cipher could - * be supported. - */ - public static ObjectIdentifier getPBEID(String algorithm) { - - // Verify pattern matches PBE Standard Name spec - if (!pbePattern.matcher(algorithm).matches()) { - throw new IllegalArgumentException("Invalid algorithm format."); - } - - // Return the PBES1 OID if it matches + public static byte[] decode(String input) throws IOException { + byte[] src = input.replaceAll("\\s+", "") + .getBytes(StandardCharsets.ISO_8859_1); try { - return AlgorithmId.get(algorithm).getOID(); - } catch (NoSuchAlgorithmException e) { - // fall-through - } - - // Lazy initialize - if (PBES2OID == null) { - try { - // Set to the hardcoded OID in KnownOID.java - PBES2OID = AlgorithmId.get("PBES2").getOID(); - } catch (NoSuchAlgorithmException e) { - // Should never fail. - throw new IllegalArgumentException(e); - } - } - return PBES2OID; - } - - /** - * Read the PEM text and return it in it's three components: header, - * base64, and footer. - * - * The method will leave the stream after reading the end of line of the - * footer or end of file - * @param is The pem data - * @param shortHeader if true, the hyphen length is 4 because the first - * hyphen is assumed to have been read. This is needed - * for the CertificateFactory X509 implementation. - * @return A new Pem object containing the three components - * @throws IOException on read errors - */ - public static PEMRecord readPEM(InputStream is, boolean shortHeader) - throws IOException { - Objects.requireNonNull(is); - - int hyphen = (shortHeader ? 1 : 0); - int eol = 0; - - // Find starting hyphens - do { - switch (is.read()) { - case '-' -> hyphen++; - case -1 -> { - return null; - } - default -> hyphen = 0; - } - } while (hyphen != 5); - - StringBuilder sb = new StringBuilder(64); - sb.append("-----"); - hyphen = 0; - int c; - - // Get header definition until first hyphen - do { - switch (c = is.read()) { - case '-' -> hyphen++; - case -1 -> throw new IllegalArgumentException( - "Input ended prematurely"); - case '\n', '\r' -> throw new IllegalArgumentException( - "Incomplete header"); - default -> sb.append((char) c); - } - } while (hyphen == 0); - - // Verify header ending with 5 hyphens. - do { - switch (is.read()) { - case '-' -> hyphen++; - default -> - throw new IllegalArgumentException("Incomplete header"); - } - } while (hyphen < 5); - - sb.append("-----"); - String header = sb.toString(); - if (header.length() < 16 || !header.startsWith("-----BEGIN ") || - !header.endsWith("-----")) { - throw new IllegalArgumentException("Illegal header: " + header); - } - - hyphen = 0; - sb = new StringBuilder(1024); - - // Determine the line break using the char after the last hyphen - switch (c = is.read()) { - case WS -> {} // skip whitespace - case '\r' -> { - c = is.read(); - if (c == '\n') { - eol = '\n'; - } else { - eol = '\r'; - sb.append((char) c); - } - } - case '\n' -> eol = '\n'; - default -> - throw new IllegalArgumentException("No EOL character found"); - } - - // Read data until we find the first footer hyphen. - do { - switch (c = is.read()) { - case -1 -> - throw new IllegalArgumentException("Incomplete header"); - case '-' -> hyphen++; - case WS, '\t', '\n', '\r' -> {} // skip whitespace, tab, etc - default -> sb.append((char) c); - } - } while (hyphen == 0); - - String data = sb.toString(); - - // Verify footer starts with 5 hyphens. - do { - switch (is.read()) { - case '-' -> hyphen++; - case -1 -> throw new IllegalArgumentException( - "Input ended prematurely"); - default -> throw new IllegalArgumentException( - "Incomplete footer"); - } - } while (hyphen < 5); - - hyphen = 0; - sb = new StringBuilder(64); - sb.append("-----"); - - // Look for Complete header by looking for the end of the hyphens - do { - switch (c = is.read()) { - case '-' -> hyphen++; - case -1 -> throw new IllegalArgumentException( - "Input ended prematurely"); - default -> sb.append((char) c); - } - } while (hyphen == 0); - - // Verify ending with 5 hyphens. - do { - switch (is.read()) { - case '-' -> hyphen++; - case -1 -> throw new IllegalArgumentException( - "Input ended prematurely"); - default -> throw new IllegalArgumentException( - "Incomplete footer"); - } - } while (hyphen < 5); - - while ((c = is.read()) != eol && c != -1 && c != '\r' && c != WS) { - throw new IllegalArgumentException("Invalid PEM format: " + - "No EOL char found in footer: 0x" + - HexFormat.of().toHexDigits((byte) c)); - } - - sb.append("-----"); - String footer = sb.toString(); - if (footer.length() < 14 || !footer.startsWith("-----END ") || - !footer.endsWith("-----")) { - throw new IllegalArgumentException("Illegal footer: " + footer); - } - - // Verify the object type in the header and the footer are the same. - String headerType = header.substring(11, header.length() - 5); - String footerType = footer.substring(9, footer.length() - 5); - if (!headerType.equals(footerType)) { - throw new IllegalArgumentException("Header and footer do not " + - "match: " + headerType + " " + footerType); + return Base64.getDecoder().decode(src); + } catch (IllegalArgumentException e) { + throw new IOException(e); } - - return new PEMRecord(header, data); } - - public static PEMRecord readPEM(InputStream is) throws IOException { - return readPEM(is, false); - } - } diff --git a/src/java.base/share/classes/sun/security/x509/X509Key.java b/src/java.base/share/classes/sun/security/x509/X509Key.java index 0483fc990e681..719748394e195 100644 --- a/src/java.base/share/classes/sun/security/x509/X509Key.java +++ b/src/java.base/share/classes/sun/security/x509/X509Key.java @@ -36,6 +36,7 @@ import java.security.NoSuchAlgorithmException; import java.security.spec.InvalidKeySpecException; import java.security.spec.X509EncodedKeySpec; +import java.util.Objects; import sun.security.util.HexDumpEncoder; import sun.security.util.*; @@ -82,8 +83,7 @@ public X509Key() { } * data is stored and transmitted losslessly, but no knowledge * about this particular algorithm is available. */ - @SuppressWarnings("this-escape") - public X509Key(AlgorithmId algid, BitArray key) { + private X509Key(AlgorithmId algid, BitArray key) { this.algid = algid; setKey(key); encode(); @@ -100,7 +100,7 @@ protected void setKey(BitArray key) { * Gets the key. The key may or may not be byte aligned. * @return a BitArray containing the key. */ - public BitArray getKey() { + protected BitArray getKey() { return (BitArray)bitStringKey.clone(); } @@ -154,7 +154,7 @@ public static PublicKey parse(DerValue in) throws IOException * @exception InvalidKeyException on invalid key encodings. */ protected void parseKeyBits() throws InvalidKeyException { - getEncodedInternal(); + encode(); } /* @@ -243,7 +243,7 @@ public String getAlgorithm() { /** * Returns the algorithm ID to be used with this key. */ - public AlgorithmId getAlgorithmId() { return algid; } + public AlgorithmId getAlgorithmId() { return algid; } /** * Encode SubjectPublicKeyInfo sequence on the DER output stream. @@ -260,7 +260,7 @@ public byte[] getEncoded() { return getEncodedInternal().clone(); } - private byte[] getEncodedInternal() { + public byte[] getEncodedInternal() { byte[] encoded = encodedKey; if (encoded == null) { DerOutputStream out = new DerOutputStream(); @@ -314,7 +314,7 @@ public String toString() * @param val a DER-encoded X.509 SubjectPublicKeyInfo value * @exception InvalidKeyException on parsing errors. */ - public void decode(DerValue val) throws InvalidKeyException { + void decode(DerValue val) throws InvalidKeyException { try { if (val.tag != DerValue.tag_Sequence) throw new InvalidKeyException("invalid key format"); @@ -338,24 +338,6 @@ public void decode(byte[] encodedKey) throws InvalidKeyException { } } - /** - * Parses X509 public key. With PKCS8v2 allowing public keys in private key - * encoding, this method allows PKCS8Key access, but keeps the code in this - * file. - */ - public static PublicKey parseKey(byte[] encoded) throws IOException { - PublicKey pubKey; - try { - X509EncodedKeySpec spec = new X509EncodedKeySpec(encoded); - pubKey = KeyFactory.getInstance(spec.getAlgorithm()) - .generatePublic(spec); - } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { - // Ignore and return raw key - throw new IOException("error with encoding"); - } - return pubKey; - } - /** * Serialization write ... X.509 keys serialize as * themselves, and they're parsed when they get read back. diff --git a/src/java.base/share/conf/security/java.security b/src/java.base/share/conf/security/java.security index f3eb572619e5a..9d07cb85a47ca 100644 --- a/src/java.base/share/conf/security/java.security +++ b/src/java.base/share/conf/security/java.security @@ -1565,11 +1565,3 @@ jdk.tls.alpnCharset=ISO_8859_1 # security property value defined here. # #jdk.security.krb5.name.case.sensitive=false - -# -# Default algorithm for PEMEncoder Encrypted PKCS#8 -# -# This property defines default Public-based Encryption algorithm for -# java.security.PEMEncoder is configured for encryption with `withEncryption()`. -# -jdk.epkcs8.defaultAlgorithm=PBEWithHmacSHA256AndAES_128 \ No newline at end of file diff --git a/test/jdk/java/security/KeyFactory/KeyFactoryGetKeySpecForInvalidSpec.java b/test/jdk/java/security/KeyFactory/KeyFactoryGetKeySpecForInvalidSpec.java index 3901229dbe54f..71b05b0c2252f 100644 --- a/test/jdk/java/security/KeyFactory/KeyFactoryGetKeySpecForInvalidSpec.java +++ b/test/jdk/java/security/KeyFactory/KeyFactoryGetKeySpecForInvalidSpec.java @@ -1,6 +1,5 @@ /* * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. - * Copyright (c) 2024, 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 @@ -125,7 +124,7 @@ public static void main(String[] args) throws Exception { } } - public static class FakeX509Spec extends EncodedKeySpec { + public static class FakeX509Spec extends X509EncodedKeySpec { public FakeX509Spec(byte[] encodedKey) { super(encodedKey); } @@ -133,10 +132,5 @@ public FakeX509Spec(byte[] encodedKey) { public FakeX509Spec(byte[] encodedKey, String algorithm) { super(encodedKey, algorithm); } - - @Override - public String getFormat() { - return null; - } } } diff --git a/test/jdk/java/security/PEM/PEMCerts.java b/test/jdk/java/security/PEM/PEMCerts.java deleted file mode 100644 index a9d9412edac41..0000000000000 --- a/test/jdk/java/security/PEM/PEMCerts.java +++ /dev/null @@ -1,358 +0,0 @@ -/* - * Copyright (c) 2024, 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. - */ - -import javax.crypto.EncryptedPrivateKeyInfo; -import java.security.DEREncodable; -import java.security.cert.X509Certificate; -import java.security.interfaces.*; -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Pattern; - -/** - * Library class for PEMEncoderTest and PEMDecoderTest - */ -class PEMCerts { - public static final Entry ecprivpem = new Entry("ecprivpem", - """ - -----BEGIN PRIVATE KEY----- - MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgkW3Jx561NlEgBnut - KwDdi3cNwu7YYD/QtJ+9+AEBdoqhRANCAASL+REY4vvAI9M3gonaml5K3lRgHq5w - +OO4oO0VNduC44gUN1nrk7/wdNSpL+xXNEX52Dsff+2RD/fop224ANvB - -----END PRIVATE KEY----- - """, ECPrivateKey.class); - - public static final Entry privpem = new Entry("privpem", - """ - -----BEGIN PRIVATE KEY----- - MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAOtjMnCzPy4jCeZb - OdOvmvU3jl7+cvPFgL5MfqDCM5a8yI0yImg/hzibJJHLk3emUVBSnekgHvCqyGLW - 3qGR2DuBEaMy0mkg8hfKcSpHLaYjDYaspO27d2qtb6d1qtsPoPjJFjWFYeW6K463 - OHG654K5/2FcJgQdlLVyp3zCiQU/AgMBAAECgYEAwNkDkTv5rlX8nWLuLJV5kh/T - H9a93SRZxw8qy5Bv7bZ7ZNrHP7uUkHbi7iPojKWRhwo43692SdzR0dCSk7LGgN9q - CYvndsYR6gifVGBi0WF+St4+NdtcQ3VlNdsojy2BdIx0oC+r7i3bn+zc968O/kI+ - EgdgrMcjjFqyx6tMHpECQQD8TYPKGHyN7Jdy28llCoUX/sL/yZ2vIi5mnDAFE5ae - KZQSkNAXG+8i9Qbs/Wdd5S3oZDqu+6DBn9gib80pYY05AkEA7tY59Oy8ka7nBlGP - g6Wo1usF2bKqk8vjko9ioZQay7f86aB10QFcAjCr+cCUm16Lc9DwzWl02nNggRZa - Jz8eNwJBAO+1zfLjFOPa14F/JHdlaVKE8EwKCFDuztsapd0M4Vtf8Zk6ERsDpU63 - Ml9T2zOwnM9g+whpdjDAZ59ATdJ1JrECQQDReJQ2SxeL0lGPCiOLu9RcQp7L81aF - 79G1bgp8WlAyEjlAkloiqEWRKiz7DDuKFR7Lwhognng9S+n87aS+PS57AkBh75t8 - 6onPAs4hkm+63dfzCojvEkALevO8J3OVX7YS5q9J1r75wDn60Ob0Zh+iiorpx8Ob - WqcWcoJqfdLEyBT+ - -----END PRIVATE KEY----- - """, RSAPrivateKey.class); - - public static final Entry privpembc = new Entry("privpembc", - """ - -----BEGIN PRIVATE KEY----- - MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAOtjMnCzPy4jCeZb - OdOvmvU3jl7+cvPFgL5MfqDCM5a8yI0yImg/hzibJJHLk3emUVBSnekgHvCqyGLW - 3qGR2DuBEaMy0mkg8hfKcSpHLaYjDYaspO27d2qtb6d1qtsPoPjJFjWFYeW6K463 - OHG654K5/2FcJgQdlLVyp3zCiQU/AgMBAAECgYEAwNkDkTv5rlX8nWLuLJV5kh/T - H9a93SRZxw8qy5Bv7bZ7ZNrHP7uUkHbi7iPojKWRhwo43692SdzR0dCSk7LGgN9q - CYvndsYR6gifVGBi0WF+St4+NdtcQ3VlNdsojy2BdIx0oC+r7i3bn+zc968O/kI+ - EgdgrMcjjFqyx6tMHpECQQD8TYPKGHyN7Jdy28llCoUX/sL/yZ2vIi5mnDAFE5ae - KZQSkNAXG+8i9Qbs/Wdd5S3oZDqu+6DBn9gib80pYY05AkEA7tY59Oy8ka7nBlGP - g6Wo1usF2bKqk8vjko9ioZQay7f86aB10QFcAjCr+cCUm16Lc9DwzWl02nNggRZa - Jz8eNwJBAO+1zfLjFOPa14F/JHdlaVKE8EwKCFDuztsapd0M4Vtf8Zk6ERsDpU63 - Ml9T2zOwnM9g+whpdjDAZ59ATdJ1JrECQQDReJQ2SxeL0lGPCiOLu9RcQp7L81aF - 79G1bgp8WlAyEjlAkloiqEWRKiz7DDuKFR7Lwhognng9S+n87aS+PS57AkBh75t8 - 6onPAs4hkm+63dfzCojvEkALevO8J3OVX7YS5q9J1r75wDn60Ob0Zh+iiorpx8Ob - WqcWcoJqfdLEyBT+ - -----END PRIVATE KEY----- - """, RSAPrivateKey.class); - - public static final Entry privec25519pem = new Entry("privpemed25519", - """ - -----BEGIN PRIVATE KEY----- - MC4CAQAwBQYDK2VwBCIEIFFZsmD+OKk67Cigc84/2fWtlKsvXWLSoMJ0MHh4jI4I - -----END PRIVATE KEY----- - """, EdECPrivateKey.class); - - public static final Entry pubrsapem = new Entry("pubrsapem", - """ - -----BEGIN PUBLIC KEY----- - MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDrYzJwsz8uIwnmWznTr5r1N45e - /nLzxYC+TH6gwjOWvMiNMiJoP4c4mySRy5N3plFQUp3pIB7wqshi1t6hkdg7gRGj - MtJpIPIXynEqRy2mIw2GrKTtu3dqrW+ndarbD6D4yRY1hWHluiuOtzhxuueCuf9h - XCYEHZS1cqd8wokFPwIDAQAB - -----END PUBLIC KEY----- - """, RSAPublicKey.class); - - public static final Entry pubrsapembc = new Entry("pubrsapembc", - """ - -----BEGIN PUBLIC KEY----- - MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDrYzJwsz8uIwnmWznTr5r1N45e - /nLzxYC+TH6gwjOWvMiNMiJoP4c4mySRy5N3plFQUp3pIB7wqshi1t6hkdg7gRGj - MtJpIPIXynEqRy2mIw2GrKTtu3dqrW+ndarbD6D4yRY1hWHluiuOtzhxuueCuf9h - XCYEHZS1cqd8wokFPwIDAQAB - -----END PUBLIC KEY----- - """, RSAPublicKey.class); - - public static final Entry pubecpem = new Entry("pubecpem", """ - -----BEGIN PUBLIC KEY----- - MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEi/kRGOL7wCPTN4KJ2ppeSt5UYB6u - cPjjuKDtFTXbguOIFDdZ65O/8HTUqS/sVzRF+dg7H3/tkQ/36KdtuADbwQ== - -----END PUBLIC KEY----- - """, ECPublicKey.class); - - // EC key with explicit parameters -- Not currently supported by SunEC - public static final String pubec_explicit = """ - -----BEGIN PUBLIC KEY----- - MIIBSzCCAQMGByqGSM49AgEwgfcCAQEwLAYHKoZIzj0BAQIhAP////8AAAABAAAA - AAAAAAAAAAAA////////////////MFsEIP////8AAAABAAAAAAAAAAAAAAAA//// - ///////////8BCBaxjXYqjqT57PrvVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMVAMSd - NgiG5wSTamZ44ROdJreBn36QBEEEaxfR8uEsQkf4vOblY6RA8ncDfYEt6zOg9KE5 - RdiYwpZP40Li/hp/m47n60p8D54WK84zV2sxXs7LtkBoN79R9QIhAP////8AAAAA - //////////+85vqtpxeehPO5ysL8YyVRAgEBA0IABIv5ERji+8Aj0zeCidqaXkre - VGAernD447ig7RU124LjiBQ3WeuTv/B01Kkv7Fc0RfnYOx9/7ZEP9+inbbgA28E= - -----END PUBLIC KEY----- - """; - - public static final Entry oasbcpem = new Entry("oasbcpem", - """ - -----BEGIN PRIVATE KEY----- - MIIDCAIBATANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAOtjMnCzPy4jCeZbOdOvmvU3jl7+ - cvPFgL5MfqDCM5a8yI0yImg/hzibJJHLk3emUVBSnekgHvCqyGLW3qGR2DuBEaMy0mkg8hfKcSpH - LaYjDYaspO27d2qtb6d1qtsPoPjJFjWFYeW6K463OHG654K5/2FcJgQdlLVyp3zCiQU/AgMBAAEC - gYEAwNkDkTv5rlX8nWLuLJV5kh/TH9a93SRZxw8qy5Bv7bZ7ZNrHP7uUkHbi7iPojKWRhwo43692 - SdzR0dCSk7LGgN9qCYvndsYR6gifVGBi0WF+St4+NdtcQ3VlNdsojy2BdIx0oC+r7i3bn+zc968O - /kI+EgdgrMcjjFqyx6tMHpECQQD8TYPKGHyN7Jdy28llCoUX/sL/yZ2vIi5mnDAFE5aeKZQSkNAX - G+8i9Qbs/Wdd5S3oZDqu+6DBn9gib80pYY05AkEA7tY59Oy8ka7nBlGPg6Wo1usF2bKqk8vjko9i - oZQay7f86aB10QFcAjCr+cCUm16Lc9DwzWl02nNggRZaJz8eNwJBAO+1zfLjFOPa14F/JHdlaVKE - 8EwKCFDuztsapd0M4Vtf8Zk6ERsDpU63Ml9T2zOwnM9g+whpdjDAZ59ATdJ1JrECQQDReJQ2SxeL - 0lGPCiOLu9RcQp7L81aF79G1bgp8WlAyEjlAkloiqEWRKiz7DDuKFR7Lwhognng9S+n87aS+PS57 - AkBh75t86onPAs4hkm+63dfzCojvEkALevO8J3OVX7YS5q9J1r75wDn60Ob0Zh+iiorpx8ObWqcW - coJqfdLEyBT+gYGNADCBiQKBgQDrYzJwsz8uIwnmWznTr5r1N45e/nLzxYC+TH6gwjOWvMiNMiJo - P4c4mySRy5N3plFQUp3pIB7wqshi1t6hkdg7gRGjMtJpIPIXynEqRy2mIw2GrKTtu3dqrW+ndarb - D6D4yRY1hWHluiuOtzhxuueCuf9hXCYEHZS1cqd8wokFPwIDAQAB - -----END PRIVATE KEY----- - """, DEREncodable.class); - - public static final Entry oasrfc8410 = new Entry("oasrfc8410", - """ - -----BEGIN PRIVATE KEY----- - MHICAQEwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC - oB8wHQYKKoZIhvcNAQkJFDEPDA1DdXJkbGUgQ2hhaXJzgSEAGb9ECWmEzf6FQbrB - Z9w7lshQhqowtrbLDFw4rXAxZuE= - -----END PRIVATE KEY----- - """, DEREncodable.class); - - public static final Entry rsaOpenSSL = new Entry("rsaOpenSSL", - """ - -----BEGIN RSA PRIVATE KEY----- - MIIEowIBAAKCAQEAqozTLan1qFcOCWnS63jXQn5lLyGOKDv3GM11n2zkGGrChayj - cSzB2KTlDmN9NgOyFdqGNWbSgdmXR5ToHGHYwaKubJoQIoPQcsipWDI156d3+X/8 - BxCGY8l5nYwvS4olOXc+2kEjeFF1eamnm9IQ5DHZfaFPl0ri4Yfm1YHBAbt/7HvF - 3MBjgBj1xSsSFLW4O6ws6guRVGDfKBVyyRNUhRTbSua/nEz0wAjxF2PWT+ZTHS6M - 0siYwVTuPI4/n4ItoYoahvGb9JskkXP+bc/QZJCTFYdyxF5tKqVMSdYaJTxop02p - Jo3oeafVKSlBrr0K731xgNBKqBud44aKT5R96QIDAQABAoIBAQCD9Q/T7gOvayPm - LqXOISJURV1emRTXloX5/8Y5QtQ8/CVjrg6Lm3ikefjsKBgR+cwJUpmyqcrIQyXk - cZchlqdSMt/IEW/YdKqMlStJnRfOE+ok9lx2ztdcT9+0AWn6hXmFu/i6f9nE1yoQ - py6SxnbhSJyhsnTVd1CR9Uep/InsHvYW/15WlVMD1VuCSIt9sefqXwavbAfBaqbn - mjwBB/ulsqKhHSuRq/QWqlj+jyGqhhYmTguC1Qwt0woDbThiHtK+suCTAlGBj/A+ - IZ1U9d+VsHBcWDKBkxmlKWcJAGR3xXiKKy9vfzC+DU7L99kgay80VZarDyXgiy78 - 9xMMzRMBAoGBANoxnZhu1bUFtLqTJ1HfDm6UB+1zVd2Mu4DXYdy/AHjoaCLp05OQ - 0ZeyhO/eXPT+eGpzCxkWD7465KO/QDfnp54p/NS73jaJVdWQHBhzJx1MymqURy3N - JQeW4+ojzwSmVXcrs7Og6EBa4L+PWLpMLW2kODniCY+vp9f5LS6m8UPJAoGBAMgZ - 4rBw7B9YFZZW/EE4eos4Q7KtA5tEP6wvCq04oxfiSytWXifYX0ToPp0CHhZlWOxk - v9a/BDGqM7AxAQJs7mmIvT5AT2V1w7oTbFPnnAo6pQtLcfaxdFFqr0h6t0sXSOKC - rQeZAqqFqwuOyP7vT0goGlBruHkwS21NKkzCyzkhAoGAc2JjhbWu+8Cdt0CUPX5o - ol9T5eTlFnkSuuqrTNIQzN+SGkxu341o2QDFvhdoLwLW6OwXhVeeUanROSqtKiMu - B70Kf/EtbMephXtk8CUNHTh7nmr1TSo8F8xakHoJQts3PQL2T9qal1W3nnWOpU4d - g+qg9TMsfTiV2OdjVlVgJskCgYBSnjV1qjojuue22hVvDFW0c7en5z2M9wHfItEi - sjbMnrdwnklj5Dd5qPZpNz2a+59ag0Kd9OJTazXKMoF7MeTCGB4ivMTLXHNCudBJ - WGCZ7JrGbhEQzTX8g7L5lwlk7KlANLoiX++03lm//OVKNR6j6ULsH33cM6+A4pJr - fSYRYQKBgCr9iMTmL0x+n6AmMNecR+MhDxi99Oy0s2EBAYqN9g/8yNgwM4KR0cjz - EcgIOtkvoTrJ9Cquvuj+O7/d2yNoH0SZQ4IYJKq47/Z4kKhwXzJnBCCCBKgkjfub - RTQSNnSEgTaBD29l7FrhNRHX9lIKFZ23caCTBS6o3q3+KgPbq7ao - -----END RSA PRIVATE KEY----- - """, RSAPrivateKey.class); - - private static final Entry encEdECKey = new Entry("encEdECKey", - """ - -----BEGIN ENCRYPTED PRIVATE KEY----- - MIGqMGYGCSqGSIb3DQEFDTBZMDgGCSqGSIb3DQEFDDArBBRyYnoNyrcqvubzch00 - jyuAb5YizgICEAACARAwDAYIKoZIhvcNAgkFADAdBglghkgBZQMEAQIEEM8BgEgO - vdMyi46+Dw7cOjwEQLtx5ME0NOOo7vlCGm3H/4j+Tf5UXrMb1UrkPjqc8OiLbC0n - IycFtI70ciPjgwDSjtCcPxR8fSxJPrm2yOJsRVo= - -----END ENCRYPTED PRIVATE KEY----- - """, EdECPrivateKey.class, "fish".toCharArray()); - - // This is not meant to be decrypted and to stay as an EKPI - private static final Entry encEdECKeyEKPI = new Entry("encEdECKeyEPKI", - encEdECKey.pem(), EncryptedPrivateKeyInfo.class, null); - - private static final Entry rsaCert = new Entry("rsaCert", - """ - -----BEGIN CERTIFICATE----- - MIIErDCCApQCCQD7ndjWbI/x0DANBgkqhkiG9w0BAQsFADAXMRUwEwYDVQQDDAxQ - RU0gVGVzdCBSU0EwIBcNMjQwMTA5MjMzNDIwWhgPMjA1MTA1MjYyMzM0MjBaMBcx - FTATBgNVBAMMDFBFTSBUZXN0IFJTQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC - AgoCggIBAKgO/Pciro8xn5iNjcVCR4IuXP+V1PNATtKAlMbWzwGVOupKgRcNeRbA - N9RlljxSgEChIWs0/DB9VsAw1wCIVeuIVxv0ZvhVAcuD8Yyl58eev1rptsSJhTkN - YJFxEPSP2kfWDxS21ltbg1bnY/c1SQbzWawDLJN16G+ICzQXo68UB5fCZV9Ugfgf - 9USPkCiC6aFt+RT7eQaN/JrjtCm+mFf4VbK7jYW7D8AfjviEY1HQCnPoTjHBxdy+ - o5s4aIOx1Wuu9wMoGuLXgY3do5/OSDCfByk7rc1drQB9GOKf2gkR8PL9TjK+R3Lq - wCA0a3jlCBiGPlH3oeZJrnp7jhAh/tVxbsd7yIdhQnasbiTfhew132AdPXoQE+ic - PFoh8MMtG1bdzt8EbvePC3GOjeyIP6f2Ixrh3B6wXzzYmJqBwON+X8TLQolcI1pa - Q7AUz5BScy3lO9nyJE/FJkX+Mr6n7WCdudCrQNP+0M845UvkgFyf4FcM7uUVugBm - AXy7sCqZgTeLdqHyTElMCoWzBa3MHKyiSCh8GUJH+I1yBY1gG95j3tITIOFvbZrk - vDiMwNtV9T6Ta2mb0+38GfKjbI6PF4DVrzB6xc7Q6/GwyhOb86YLOLlEHJfhuc+C - Pdy8hQrrulm2jiCO/skvHucABNJ2CENyWa7ljNJkcN6GNTziz4AhAgMBAAEwDQYJ - KoZIhvcNAQELBQADggIBAKFQE2AgYgc7/xzwveUAiZ55tfcds07UnazLCOdpz+JJ - W4MOt/1Qi9mUylqDEymfNZVLPd2dEjB4wJ57XBUjL+kXkH1SocuskxQPf05iz5zT - pEwg2fTmU73ilKMs5Q113nBnL9ZZtlRKCh1Oc5LvLW799uVXnU4UdSpWOBU9ePGY - +H1wUKf+e0/BkveQsZERYcamH9O9U/+h+bbhr3GpT1AVnuDRyF28OvRwARDCOVyy - ifh+xCR3WCnNcgfwCoH6cE1aXDKHchlAAZtvjc1lLud7/ECIg+15keVfTYk4HEbH - j/lprxyH7y99lMmRLQpnTve54RrZGGmg51UD7OmwPHLMGibfQkw6QgdNsggIYD6p - L91spgRRB+i4PTovocndOMR2RYgQEelGNqv8MsoUC7oRNxPCHxIEGuUPH1Vf3jnk - mTHbVzpjy57UtfcYp1uBFDf8WoWO1Mi6oXRw2YQA1YSMm1+3ftphxydcbRuBlS7O - 6Iiqk6XlFG9Dpd2jjAQQzJGtnC0QDgGz6/KGp1bGEhRnOWju07eLWvPbyaX5zeSh - 8gOYV33zkPhziWJt4uFMFIi7N2DLEk5UVZv1KTLZlfPl55DRs7j/Sb4vKHpB17AO - meVknxVvifDVY0TIz57t28Accsk6ClBCxNPluPU/8YLGAZJYsdDXjGcndQ13s5G7 - -----END CERTIFICATE----- - """, X509Certificate.class); - - private static final Entry ecCert = new Entry("ecCert", - """ - -----BEGIN CERTIFICATE----- - MIIBFzCBvgIJAOGVk/ky59ojMAoGCCqGSM49BAMCMBMxETAPBgNVBAMMCFBFTSB0 - ZXN0MCAXDTI0MDEwOTIzMzEwNloYDzIwNTEwNTI2MjMzMTA2WjATMREwDwYDVQQD - DAhQRU0gdGVzdDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABGYI0jD7JZzw4RYD - y9DCfaYNz0CHrpr9gJU5NXe6czvuNBdAOl/lJGQ1pqpEQSQaMDII68obvQyQQyFY - lU3G9QAwCgYIKoZIzj0EAwIDSAAwRQIgMwYld7aBzkcRt9mn27YOed5+n0xN1y8Q - VEcFjLI/tBYCIQDU3szDZ/PK2mUZwtgQxLqHdh+f1JY0UwQS6M8QUvoDHw== - -----END CERTIFICATE----- - """, X509Certificate.class); - - // EC cert with explicit parameters -- Not currently supported by SunEC - private static final String ecCertEX = """ - -----BEGIN CERTIFICATE----- - MIICrDCCAjMCCQDKAlI7uc1CVDAKBggqhkjOPQQDAjATMREwDwYDVQQDDAhQRU0g - dGVzdDAgFw0yNDAxMDkyMzIxNTlaGA8yMDUxMDUyNjIzMjE1OVowEzERMA8GA1UE - AwwIUEVNIHRlc3QwggHMMIIBZAYHKoZIzj0CATCCAVcCAQEwPAYHKoZIzj0BAQIx - AP/////////////////////////////////////////+/////wAAAAAAAAAA//// - /zB7BDD//////////////////////////////////////////v////8AAAAAAAAA - AP////wEMLMxL6fiPufkmI4Fa+P4LRkYHZxu/oFBEgMUCI9QE4daxlY5jYou0Z0q - hcjt0+wq7wMVAKM1kmqjGaJ6HQCJamdzpIJ6zaxzBGEEqofKIr6LBTeOscce8yCt - dG4dO2KLp5uYWfdB4IJUKjhVAvJdv1UpbDpUXjhydgq3NhfeSpYmLG9dnpi/kpLc - Kfj0Hb0omhR86doxE7XwuMAKYLHOHX6BnXpDHXyQ6g5fAjEA//////////////// - ////////////////x2NNgfQ3Ld9YGg2ySLCneuzsGWrMxSlzAgEBA2IABO+IbTh6 - WqyzmxdCeJ0uUQ2v2jKxRuCKRyPlYAnpBmmQypsRS+GBdbBa0Mu6MTnVJh5uvqXn - q7IuHVEiE3EFKw0DNW30nINuQg6lTv6PgN/4nYBqsl5FQgzk2SYN3bw+7jAKBggq - hkjOPQQDAgNnADBkAjATCnbbn3CgPRPi9Nym0hKpBAXc30D4eVB3mz8snK0oKU0+ - VP3F0EWcyM2QDSZCXIgCMHWknAhIGFTHxqypYUV8eAd3SY7ujZ6EPR0uG//csBWG - IqHcgr8slqi35ycQn5yMsQ== - -----END CERTIFICATE----- - """; - - public record Entry(String name, String pem, Class clazz, char[] password) { - - Entry(String name, String pem, Class clazz) { - this(name, pem, clazz, null); - - } - - public Entry newClass(String name, Class c) { - return new Entry(name, pem, c, password); - } - - public Entry newClass(Class c) { - return newClass(name, c); - } - - Entry makeCRLF(String name) { - return new Entry(name, - Pattern.compile(System.lineSeparator()).matcher(pem).replaceAll("\r\n"), - clazz, password()); - } - - Entry makeCR(String name) { - return new Entry(name, - Pattern.compile(System.lineSeparator()).matcher(pem).replaceAll("\r"), - clazz, password()); - } - - Entry makeNoCRLF(String name) { - return new Entry(name, - Pattern.compile(System.lineSeparator()).matcher(pem).replaceAll(""), - clazz, password()); - } - } - - static public Entry getEntry(String varname) { - return getEntry(passList, varname); - } - - static public Entry getEntry(List list, String varname) { - for (Entry entry : list) { - if (entry.name.compareToIgnoreCase(varname) == 0) { - return entry; - } - } - return null; - } - - static List passList = new ArrayList<>(); - static List entryList = new ArrayList<>(); - static List pubList = new ArrayList<>(); - static List privList = new ArrayList<>(); - static List oasList = new ArrayList<>(); - static List certList = new ArrayList<>(); - static List encryptedList = new ArrayList<>(); - static List failureEntryList = new ArrayList<>(); - - static { - pubList.add(pubrsapem); - pubList.add(pubrsapembc); - pubList.add(pubecpem.makeCR("pubecpem-r")); - pubList.add(pubecpem.makeCRLF("pubecpem-rn")); - privList.add(privpem); - privList.add(privpembc); - privList.add(ecprivpem); - privList.add(privec25519pem); - privList.add(encEdECKeyEKPI); // The non-EKPI version needs decryption - privList.add(rsaOpenSSL); - oasList.add(oasrfc8410); - oasList.add(oasbcpem); - - certList.add(rsaCert); - certList.add(ecCert); - - entryList.addAll(pubList); - entryList.addAll(privList); - entryList.addAll(oasList); - entryList.addAll(certList); - - encryptedList.add(encEdECKey); - - passList.addAll(entryList); - passList.addAll(encryptedList); - - failureEntryList.add(new Entry("emptyPEM", "", DEREncodable.class, null)); - failureEntryList.add(new Entry("nullPEM", null, DEREncodable.class, null)); - } -} \ No newline at end of file diff --git a/test/jdk/java/security/PEM/PEMDecoderTest.java b/test/jdk/java/security/PEM/PEMDecoderTest.java deleted file mode 100644 index 975bdc52da4c2..0000000000000 --- a/test/jdk/java/security/PEM/PEMDecoderTest.java +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright (c) 2024, 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 - * @summary Testing basic PEM API decodings - * @enablePreview - */ - -import javax.crypto.EncryptedPrivateKeyInfo; -import java.lang.Class; -import java.io.IOException; -import java.security.*; -import java.security.interfaces.*; -import java.security.spec.X509EncodedKeySpec; -import java.util.*; -import java.util.Arrays; - -public class PEMDecoderTest { - - static HexFormat hex = HexFormat.of(); - - PEMDecoderTest() { - } - - public static void main(String[] args) throws IOException { - System.out.println("Decoder test:"); - PEMCerts.entryList.forEach(PEMDecoderTest::test); - System.out.println("Decoder test returning DEREncodable class:"); - PEMCerts.entryList.forEach(entry -> test(entry, DEREncodable.class)); - System.out.println("Decoder test with encrypted PEM:"); - PEMCerts.encryptedList.forEach(PEMDecoderTest::testEncrypted); - System.out.println("Decoder test with OAS:"); - testTwoKeys(); - System.out.println("Decoder test RSA PEM setting RSAKey.class returned:"); - test(PEMCerts.getEntry("privpem"), RSAKey.class); - System.out.println("Decoder test failures:"); - PEMCerts.failureEntryList.forEach(PEMDecoderTest::testFailure); - System.out.println("Decoder test ECpriv PEM asking for ECPublicKey.class returned:"); - testFailure(PEMCerts.getEntry("ecprivpem"), ECPublicKey.class); - System.out.println("Decoder test RSApriv PEM setting P8EKS.class returned:"); - testClass(PEMCerts.getEntry("privpem"), RSAPrivateKey.class); - System.out.println("Decoder test RSApriv P1 PEM asking for RSAPublicKey.class returned:"); - testFailure(PEMCerts.getEntry(PEMCerts.privList, "rsaOpenSSL"), RSAPublicKey.class); - System.out.println("Decoder test RSApriv PEM asking X509EKS.class returned:"); - testClass(PEMCerts.getEntry("privpem"), X509EncodedKeySpec.class, false); - System.out.println("Decoder test RSAcert PEM asking X509EKS.class returned:"); - testClass(PEMCerts.getEntry("rsaCert"), X509EncodedKeySpec.class, false); - System.out.println("Decoder test OAS RFC PEM asking PrivateKey.class returned:"); - testClass(PEMCerts.getEntry("oasrfc8410"), PrivateKey.class, true); - testClass(PEMCerts.getEntry("oasrfc8410"), PublicKey.class, true); - System.out.println("Decoder test encEdECkey:"); - testFailure(PEMCerts.pubecpem.makeNoCRLF("pubecpem-no")); - System.out.println("Decoder test RSAcert with decryption Decoder:"); - PEMDecoder d = PEMDecoder.of().withDecryption("123".toCharArray()); - d.decode(PEMCerts.getEntry("rsaCert").pem()); - System.out.println("Decoder test ECpriv with decryption Decoder:"); - d.decode(PEMCerts.getEntry("ecprivpem").pem()); - - } - - static void testFailure(PEMCerts.Entry entry) { - testFailure(entry, entry.clazz()); - } - - static void testFailure(PEMCerts.Entry entry, Class c) { - try { - test(entry.pem(), c, PEMDecoder.of()); - throw new AssertionError("Failure with " + - entry.name() + ": Not supposed to succeed."); - } catch (NullPointerException e) { - System.out.println("PASS (" + entry.name() + "): " + e.getClass() + - ": " + e.getMessage()); - } catch (IOException | RuntimeException e) { - System.out.println("PASS (" + entry.name() + "): " + e.getClass() + - ": " + e.getMessage()); - } - } - - static DEREncodable testEncrypted(PEMCerts.Entry entry) { - PEMDecoder decoder = PEMDecoder.of(); - if (!Objects.equals(entry.clazz(), EncryptedPrivateKeyInfo.class)) { - decoder = decoder.withDecryption(entry.password()); - } - - try { - return test(entry.pem(), entry.clazz(), decoder); - } catch (Exception | AssertionError e) { - throw new RuntimeException("Error with PEM (" + entry.name() + - "): " + e.getMessage(), e); - } - } - - // Change the Entry to use the given class as the expected class returned - static DEREncodable test(PEMCerts.Entry entry, Class c) { - return test(entry.newClass(c)); - } - - // Run test with a given Entry - static DEREncodable test(PEMCerts.Entry entry) { - try { - DEREncodable r = test(entry.pem(), entry.clazz(), PEMDecoder.of()); - System.out.println("PASS (" + entry.name() + ")"); - return r; - } catch (Exception | AssertionError e) { - throw new RuntimeException("Error with PEM (" + entry.name() + - "): " + e.getMessage(), e); - } - } - - static List getInterfaceList(Class ccc) { - Class[] interfaces = ccc.getInterfaces(); - List list = new ArrayList<>(Arrays.asList(interfaces)); - var x = ccc.getSuperclass(); - if (x != null) { - list.add(x); - } - List results = new ArrayList<>(list); - if (list.size() > 0) { - for (Class cname : list) { - try { - if (cname != null && - cname.getName().startsWith("java.security.")) { - results.addAll(getInterfaceList(cname)); - } - } catch (Exception e) { - System.err.println("Exception with " + cname); - } - } - } - return results; - } - - /** - * Perform the decoding test with the given decoder, on the given pem, and - * expect the clazz to be returned. - */ - static DEREncodable test(String pem, Class clazz, PEMDecoder decoder) throws IOException { - var pk = decoder.decode(pem); - - // Check that clazz matches what pk returned. - if (pk.getClass().equals(clazz)) { - return pk; - } - - // Search interfaces and inheritance to find a match with clazz - List list = getInterfaceList(pk.getClass()); - for (Class cc : list) { - if (cc != null && cc.equals(clazz)) { - return pk; - } - } - - throw new RuntimeException("Entry did not contain expected: " + - clazz.getName()); - } - - // Run the same key twice through the same decoder and make sure the - // result is the same - static void testTwoKeys() throws IOException { - PublicKey p1, p2; - PEMDecoder pd = PEMDecoder.of(); - p1 = pd.decode(PEMCerts.pubrsapem.pem(), RSAPublicKey.class); - p2 = pd.decode(PEMCerts.pubrsapem.pem(), RSAPublicKey.class); - if (!Arrays.equals(p1.getEncoded(), p2.getEncoded())) { - System.err.println("These two should have matched:"); - System.err.println(hex.parseHex(new String(p1.getEncoded()))); - System.err.println(hex.parseHex(new String(p2.getEncoded()))); - throw new AssertionError("Two decoding of the same key failed to" + - " match: "); - } - } - - static void testClass(PEMCerts.Entry entry, Class clazz) throws IOException { - var pk = PEMDecoder.of().decode(entry.pem(), clazz); - } - - static void testClass(PEMCerts.Entry entry, Class clazz, boolean pass) throws RuntimeException { - try { - testClass(entry, clazz); - } catch (Exception e) { - if (pass) { - throw new RuntimeException(e); - } - } - } -} \ No newline at end of file diff --git a/test/jdk/java/security/PEM/PEMEncoderTest.java b/test/jdk/java/security/PEM/PEMEncoderTest.java deleted file mode 100644 index 156e37f908402..0000000000000 --- a/test/jdk/java/security/PEM/PEMEncoderTest.java +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright (c) 2024, 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 - * @summary Testing basic PEM API encoding - * @enablePreview - * @modules java.base/sun.security.util - */ - -import sun.security.util.Pem; - -import javax.crypto.EncryptedPrivateKeyInfo; -import javax.crypto.spec.PBEParameterSpec; - -import java.nio.charset.StandardCharsets; -import java.security.*; -import java.security.spec.InvalidParameterSpecException; -import java.util.*; -import java.util.regex.Pattern; - -public class PEMEncoderTest { - - static Map keymap; - final static Pattern CR = Pattern.compile("\r"); - final static Pattern LF = Pattern.compile("\n"); - final static Pattern LSDEFAULT = Pattern.compile(System.lineSeparator()); - - - public static void main(String[] args) throws Exception { - PEMEncoder encoder = PEMEncoder.of(); - - PEMCerts.entryList.remove(PEMCerts.getEntry("rsaOpenSSL")); - keymap = generateObjKeyMap(PEMCerts.entryList); - System.out.println("Same instance Encoder test:"); - keymap.keySet().stream().forEach(key -> test(key, encoder)); - System.out.println("New instance Encoder test:"); - keymap.keySet().stream().forEach(key -> test(key, PEMEncoder.of())); - System.out.println("Same instance Encoder testToString:"); - keymap.keySet().stream().forEach(key -> testToString(key, encoder)); - System.out.println("New instance Encoder testToString:"); - keymap.keySet().stream().forEach(key -> testToString(key, PEMEncoder.of())); - - keymap = generateObjKeyMap(PEMCerts.encryptedList); - System.out.println("Same instance Encoder match test:"); - keymap.keySet().stream().forEach(key -> testEncryptedMatch(key, encoder)); - System.out.println("Same instance Encoder new withEnc test:"); - keymap.keySet().stream().forEach(key -> testEncrypted(key, encoder)); - System.out.println("New instance Encoder and withEnc test:"); - keymap.keySet().stream().forEach(key -> testEncrypted(key, PEMEncoder.of())); - System.out.println("Same instance encrypted Encoder test:"); - PEMEncoder encEncoder = encoder.withEncryption("fish".toCharArray()); - keymap.keySet().stream().forEach(key -> testSameEncryptor(key, encEncoder)); - try { - encoder.withEncryption(null); - } catch (Exception e) { - if (!(e instanceof NullPointerException)) { - throw new Exception("Should have been a NullPointerException thrown"); - } - } - } - - static Map generateObjKeyMap(List list) { - Map keymap = new HashMap<>(); - PEMDecoder pemd = PEMDecoder.of(); - for (PEMCerts.Entry entry : list) { - try { - if (entry.password() != null) { - keymap.put(entry.name(), pemd.withDecryption( - entry.password()).decode(entry.pem())); - } else { - keymap.put(entry.name(), pemd.decode(entry.pem())); - } - } catch (Exception e) { - System.err.println("Verify PEMDecoderTest passes before debugging this test."); - throw new AssertionError("Failed to initialize map on" + - " entry \"" + entry.name() + "\"", e); - } - } - return keymap; - } - - static void test(String key, PEMEncoder encoder) { - byte[] result; - PEMCerts.Entry entry = PEMCerts.getEntry(key); - try { - result = encoder.encode(keymap.get(key)); - } catch (RuntimeException e) { - throw new AssertionError("Encoder use failure with " + - entry.name(), e); - } - - checkResults(entry, new String(result, StandardCharsets.UTF_8)); - System.out.println("PASS: " + entry.name()); - } - - static void testToString(String key, PEMEncoder encoder) { - String result; - PEMCerts.Entry entry = PEMCerts.getEntry(key); - try { - result = encoder.encodeToString(keymap.get(key)); - } catch (RuntimeException e) { - throw new AssertionError("Encoder use failure with " + - entry.name(), e); - } - - checkResults(entry, result); - System.out.println("PASS: " + entry.name()); - } - - /* - Test cannot verify PEM was the same as known PEM because we have no - public access to the AlgoritmID.params and PBES2Parameters. - */ - static void testEncrypted(String key, PEMEncoder encoder) { - PEMCerts.Entry entry = PEMCerts.getEntry(key); - try { - encoder.withEncryption( - (entry.password() != null ? entry.password() : - "fish".toCharArray())) - .encodeToString(keymap.get(key)); - } catch (RuntimeException e) { - throw new AssertionError("Encrypted encoder failed with " + - entry.name(), e); - } - - System.out.println("PASS: " + entry.name()); - } - - /* - Test cannot verify PEM was the same as known PEM because we have no - public access to the AlgoritmID.params and PBES2Parameters. - */ - static void testSameEncryptor(String key, PEMEncoder encoder) { - PEMCerts.Entry entry = PEMCerts.getEntry(key); - try { - encoder.encodeToString(keymap.get(key)); - } catch (RuntimeException e) { - throw new AssertionError("Encrypted encoder failured with " + - entry.name(), e); - } - - System.out.println("PASS: " + entry.name()); - } - - static void testEncryptedMatch(String key, PEMEncoder encoder) { - String result; - PEMCerts.Entry entry = PEMCerts.getEntry(key); - try { - PrivateKey pkey = (PrivateKey) keymap.get(key); - EncryptedPrivateKeyInfo ekpi = PEMDecoder.of().decode(entry.pem(), - EncryptedPrivateKeyInfo.class); - if (entry.password() != null) { - EncryptedPrivateKeyInfo.encryptKey(pkey, entry.password(), - Pem.DEFAULT_ALGO, ekpi.getAlgParameters(). - getParameterSpec(PBEParameterSpec.class), - null); - } - result = encoder.encodeToString(ekpi); - } catch (RuntimeException | InvalidParameterSpecException e) { - throw new AssertionError("Encrypted encoder failure with " + - entry.name(), e); - } - - checkResults(entry, result); - System.out.println("PASS: " + entry.name()); - } - - static void checkResults(PEMCerts.Entry entry, String result) { - String pem = new String(entry.pem()); - // The below matches the \r\n generated PEM with the PEM passed - // into the test. - pem = CR.matcher(pem).replaceAll(""); - pem = LF.matcher(pem).replaceAll(""); - result = LSDEFAULT.matcher(result).replaceAll(""); - try { - if (pem.compareTo(result) != 0) { - System.out.println("expected:\n" + pem); - System.out.println("generated:\n" + result); - indexDiff(pem, result); - } - } catch (AssertionError e) { - throw new AssertionError("Encoder PEM mismatch " + - entry.name(), e); - } - } - - static void indexDiff(String a, String b) { - String lenerr = ""; - int len = a.length(); - int lenb = b.length(); - if (len != lenb) { - lenerr = ": Length mismatch: " + len + " vs " + lenb; - len = Math.min(len, lenb); - } - for (int i = 0; i < len; i++) { - if (a.charAt(i) != b.charAt(i)) { - throw new AssertionError("Char mistmatch, index #" + i + - " (" + a.charAt(i) + " vs " + b.charAt(i) + ")" + lenerr); - } - } - } -} - diff --git a/test/jdk/sun/security/pkcs/pkcs8/PKCS8Test.java b/test/jdk/sun/security/pkcs/pkcs8/PKCS8Test.java index 014de3d474831..f4ac02c798263 100644 --- a/test/jdk/sun/security/pkcs/pkcs8/PKCS8Test.java +++ b/test/jdk/sun/security/pkcs/pkcs8/PKCS8Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2021, 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 @@ -30,18 +30,22 @@ * java.base/sun.security.util * java.base/sun.security.provider * java.base/sun.security.x509 - * @run main PKCS8Test + * @compile -XDignore.symbol.file PKCS8Test.java + * @run testng PKCS8Test */ +import java.io.IOException; import java.math.BigInteger; -import java.security.InvalidKeyException; import java.util.Arrays; import java.util.HexFormat; import jdk.test.lib.hexdump.ASN1Formatter; import jdk.test.lib.hexdump.HexPrinter; +import org.testng.Assert; +import org.testng.annotations.Test; import sun.security.pkcs.PKCS8Key; import sun.security.provider.DSAPrivateKey; +import sun.security.util.DerValue; public class PKCS8Test { @@ -58,7 +62,8 @@ public class PKCS8Test { "3009020102020103020104" + // p=2, q=3, g=4 "0403020101"); // PrivateKey OCTET int x = 1 - public static void main(String[] args) throws Exception { + @Test + public void test() throws IOException { byte[] encodedKey = new DSAPrivateKey( BigInteger.valueOf(1), @@ -66,45 +71,34 @@ public static void main(String[] args) throws Exception { BigInteger.valueOf(3), BigInteger.valueOf(4)).getEncoded(); - if (!Arrays.equals(encodedKey, EXPECTED)) { - throw new AssertionError( + Assert.assertTrue(Arrays.equals(encodedKey, EXPECTED), HexPrinter.simple() - .formatter(ASN1Formatter.formatter()) - .toString(encodedKey)); - } + .formatter(ASN1Formatter.formatter()) + .toString(encodedKey)); PKCS8Key decodedKey = (PKCS8Key)PKCS8Key.parseKey(encodedKey); - assert(ALGORITHM.equalsIgnoreCase(decodedKey.getAlgorithm())); - assert(FORMAT.equalsIgnoreCase(decodedKey.getFormat())); - assert(EXPECTED_ALG_ID_CHRS.equalsIgnoreCase(decodedKey.getAlgorithmId().toString())); + Assert.assertEquals(decodedKey.getAlgorithm(), ALGORITHM); + Assert.assertEquals(decodedKey.getFormat(), FORMAT); + Assert.assertEquals(decodedKey.getAlgorithmId().toString(), + EXPECTED_ALG_ID_CHRS); byte[] encodedOutput = decodedKey.getEncoded(); - if (!Arrays.equals(encodedOutput, EXPECTED)) { - - throw new AssertionError( + Assert.assertTrue(Arrays.equals(encodedOutput, EXPECTED), HexPrinter.simple() - .formatter(ASN1Formatter.formatter()) - .toString(encodedOutput)); - } + .formatter(ASN1Formatter.formatter()) + .toString(encodedOutput)); // Test additional fields enlarge(0, "8000"); // attributes - - // PKCSv2 testing done by PEMEncoder/PEMDecoder tests - - assertThrows(() -> enlarge(2)); - assertThrows(() -> enlarge(0, "8000", "8000")); // no dup - assertThrows(() -> enlarge(0, "810100")); // no public in v1 - assertThrows(() -> enlarge(1, "810100", "8000")); // bad order - assertThrows(() -> enlarge(1, "820100")); // bad tag - } - - private static void assertThrows(Runnable o) { - try { - o.run(); - throw new AssertionError("Test failed"); - } catch (Exception e) {} + enlarge(1, "810100"); // public key for v2 + enlarge(1, "8000", "810100"); // both + + Assert.assertThrows(() -> enlarge(2)); // bad ver + Assert.assertThrows(() -> enlarge(0, "8000", "8000")); // no dup + Assert.assertThrows(() -> enlarge(0, "810100")); // no public in v1 + Assert.assertThrows(() -> enlarge(1, "810100", "8000")); // bad order + Assert.assertThrows(() -> enlarge(1, "820100")); // bad tag } /** @@ -113,7 +107,7 @@ private static void assertThrows(Runnable o) { * @param newVersion new version * @param fields extra fields to add, in hex */ - static void enlarge(int newVersion, String... fields) { + static void enlarge(int newVersion, String... fields) throws IOException { byte[] original = EXPECTED.clone(); int length = original.length; for (String field : fields) { // append fields @@ -122,13 +116,9 @@ static void enlarge(int newVersion, String... fields) { System.arraycopy(add, 0, original, length, add.length); length += add.length; } - assert (length < 127); - original[1] = (byte) (length - 2); // the length field inside DER - original[4] = (byte) newVersion; // the version inside DER - try { - PKCS8Key.parseKey(original); - } catch (InvalidKeyException e) { - throw new RuntimeException(e); - } + Assert.assertTrue(length < 127); + original[1] = (byte)(length - 2); // the length field inside DER + original[4] = (byte)newVersion; // the version inside DER + PKCS8Key.parseKey(original); } }