From 264fdc5b4ed5f4e35168048533196e670c3dda6c Mon Sep 17 00:00:00 2001 From: Mikhail Yankelevich Date: Tue, 10 Feb 2026 12:18:03 +0000 Subject: [PATCH] 8374808: Add new methods to KeyStore and KeyStoreSpi that return the creation date as an Instant instead of Date Reviewed-by: weijun --- .../classes/apple/security/KeychainStore.java | 47 +++++++++---- .../com/sun/crypto/provider/JceKeyStore.java | 67 ++++++++++++------- .../share/classes/java/security/KeyStore.java | 32 ++++++++- .../classes/java/security/KeyStoreSpi.java | 26 ++++++- .../sun/security/pkcs12/PKCS12KeyStore.java | 58 +++++++++++----- .../sun/security/provider/DomainKeyStore.java | 27 ++++++-- .../sun/security/provider/JavaKeyStore.java | 48 +++++++++---- .../security/KeyStore/TestKeyStoreBasic.java | 31 ++++++++- .../security/provider/KeyStore/DKSTest.java | 44 ++++++++---- 9 files changed, 291 insertions(+), 89 deletions(-) diff --git a/src/java.base/macosx/classes/apple/security/KeychainStore.java b/src/java.base/macosx/classes/apple/security/KeychainStore.java index 6f70fccbb24..63dfc39afe1 100644 --- a/src/java.base/macosx/classes/apple/security/KeychainStore.java +++ b/src/java.base/macosx/classes/apple/security/KeychainStore.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,6 +30,8 @@ import java.security.*; import java.security.cert.*; import java.security.cert.Certificate; import java.security.spec.*; +import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.*; import javax.crypto.*; @@ -114,7 +116,7 @@ abstract sealed class KeychainStore extends KeyStoreSpi { // SecCertificateRef. When we delete the key we have to delete all of the corresponding // native objects. static class KeyEntry { - Date date; // the creation date of this entry + Instant date; // the creation instant of this entry byte[] protectedPrivKey; char[] password; long keyRef; // SecKeyRef for this key @@ -124,7 +126,7 @@ abstract sealed class KeychainStore extends KeyStoreSpi { // Trusted certificates static class TrustedCertEntry { - Date date; // the creation date of this entry + Instant date; // the creation instant of this entry Certificate cert; long certRef; // SecCertificateRef for this key @@ -405,13 +407,32 @@ abstract sealed class KeychainStore extends KeyStoreSpi { * not exist */ public Date engineGetCreationDate(String alias) { - Object entry = entries.get(alias.toLowerCase(Locale.ROOT)); + final Instant instant = this.engineGetCreationInstant(alias); + if (instant == null) { + return null; + } + return Date.from(instant); + } + + /** + * Returns the instant that the entry identified by the given alias was + * created. + * + * @param alias the alias name + * + * @return the instant that the entry identified by the given alias + * was created, or {@code null} if the given alias does not exist + * + * @since 27 + */ + public Instant engineGetCreationInstant(String alias) { + final Object entry = entries.get(alias.toLowerCase(Locale.ROOT)); if (entry != null) { - if (entry instanceof TrustedCertEntry) { - return new Date(((TrustedCertEntry)entry).date.getTime()); + if (entry instanceof TrustedCertEntry trustedCertEntry) { + return trustedCertEntry.date; } else { - return new Date(((KeyEntry)entry).date.getTime()); + return ((KeyEntry)entry).date; } } else { return null; @@ -447,7 +468,7 @@ abstract sealed class KeychainStore extends KeyStoreSpi { synchronized(entries) { try { KeyEntry entry = new KeyEntry(); - entry.date = new Date(); + entry.date = Instant.now().truncatedTo(ChronoUnit.MILLIS); if (key instanceof PrivateKey) { if ((key.getFormat().equals("PKCS#8")) || @@ -525,7 +546,7 @@ abstract sealed class KeychainStore extends KeyStoreSpi { + "EncryptedPrivateKeyInfo"); } - entry.date = new Date(); + entry.date = Instant.now().truncatedTo(ChronoUnit.MILLIS); if ((chain != null) && (chain.length != 0)) { @@ -927,9 +948,9 @@ abstract sealed class KeychainStore extends KeyStoreSpi { // Make a creation date. if (creationDate != 0) - tce.date = new Date(creationDate); + tce.date = Instant.ofEpochMilli(creationDate); else - tce.date = new Date(); + tce.date = Instant.now().truncatedTo(ChronoUnit.MILLIS); entries.put(alias.toLowerCase(Locale.ROOT), tce); } catch (Exception e) { @@ -952,9 +973,9 @@ abstract sealed class KeychainStore extends KeyStoreSpi { // Make a creation date. if (creationDate != 0) - ke.date = new Date(creationDate); + ke.date = Instant.ofEpochMilli(creationDate); else - ke.date = new Date(); + ke.date = Instant.now().truncatedTo(ChronoUnit.MILLIS); // Next, create X.509 Certificate objects from the raw data. This is complicated // because a certificate's public key may be too long for Java's default encryption strength. diff --git a/src/java.base/share/classes/com/sun/crypto/provider/JceKeyStore.java b/src/java.base/share/classes/com/sun/crypto/provider/JceKeyStore.java index ad98653b9c2..1e45a4e6a20 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/JceKeyStore.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/JceKeyStore.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,8 @@ import sun.security.util.Debug; import sun.security.util.IOUtils; import java.io.*; +import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.*; import java.security.DigestInputStream; import java.security.DigestOutputStream; @@ -70,14 +72,14 @@ public final class JceKeyStore extends KeyStoreSpi { // Private key and supporting certificate chain private static final class PrivateKeyEntry { - Date date; // the creation date of this entry + Instant date; // the creation date of this entry byte[] protectedKey; Certificate[] chain; } // Secret key private static final class SecretKeyEntry { - Date date; // the creation date of this entry + Instant date; // the creation date of this entry SealedObject sealedKey; // Maximum possible length of sealedKey. Used to detect malicious @@ -89,7 +91,7 @@ public final class JceKeyStore extends KeyStoreSpi { // Trusted certificate private static final class TrustedCertEntry { - Date date; // the creation date of this entry + Instant date; // the creation date of this entry Certificate cert; } @@ -213,23 +215,38 @@ public final class JceKeyStore extends KeyStoreSpi { * not exist */ public Date engineGetCreationDate(String alias) { - Date date = null; + final Instant instant = this.engineGetCreationInstant(alias); + if (instant == null) { + return null; + } + return Date.from(instant); + } - Object entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); + /** + * Returns the instant that the entry identified by the given alias was + * created. + * + * @param alias the alias name + * + * @return the instant that the entry identified by the given alias + * was created, or {@code null} if the given alias does not exist + * + * @since 27 + */ + public Instant engineGetCreationInstant(String alias) { + final Object entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); if (entry != null) { - // We have to create a new instance of java.util.Date because - // dates are not immutable if (entry instanceof TrustedCertEntry) { - date = new Date(((TrustedCertEntry)entry).date.getTime()); + return ((TrustedCertEntry)entry).date; } else if (entry instanceof PrivateKeyEntry) { - date = new Date(((PrivateKeyEntry)entry).date.getTime()); + return ((PrivateKeyEntry)entry).date; } else { - date = new Date(((SecretKeyEntry)entry).date.getTime()); + return ((SecretKeyEntry)entry).date; } + } else { + return null; } - - return date; } /** @@ -264,7 +281,7 @@ public final class JceKeyStore extends KeyStoreSpi { if (key instanceof PrivateKey) { PrivateKeyEntry entry = new PrivateKeyEntry(); - entry.date = new Date(); + entry.date = Instant.now().truncatedTo(ChronoUnit.MILLIS); // protect the private key entry.protectedKey = keyProtector.protect((PrivateKey)key); @@ -282,7 +299,7 @@ public final class JceKeyStore extends KeyStoreSpi { } else { SecretKeyEntry entry = new SecretKeyEntry(); - entry.date = new Date(); + entry.date = Instant.now().truncatedTo(ChronoUnit.MILLIS); // seal and store the key entry.sealedKey = keyProtector.seal(key); @@ -325,7 +342,7 @@ public final class JceKeyStore extends KeyStoreSpi { // We assume it's a private key, because there is no standard // (ASN.1) encoding format for wrapped secret keys PrivateKeyEntry entry = new PrivateKeyEntry(); - entry.date = new Date(); + entry.date = Instant.now().truncatedTo(ChronoUnit.MILLIS); entry.protectedKey = key.clone(); if ((chain != null) && @@ -370,7 +387,7 @@ public final class JceKeyStore extends KeyStoreSpi { TrustedCertEntry trustedCertEntry = new TrustedCertEntry(); trustedCertEntry.cert = cert; - trustedCertEntry.date = new Date(); + trustedCertEntry.date = Instant.now().truncatedTo(ChronoUnit.MILLIS); entries.put(alias.toLowerCase(Locale.ENGLISH), trustedCertEntry); } } @@ -588,7 +605,7 @@ public final class JceKeyStore extends KeyStoreSpi { dos.writeUTF(alias); // write the (entry creation) date - dos.writeLong(pentry.date.getTime()); + dos.writeLong(pentry.date.toEpochMilli()); // write the protected private key dos.writeInt(pentry.protectedKey.length); @@ -618,7 +635,9 @@ public final class JceKeyStore extends KeyStoreSpi { dos.writeUTF(alias); // write the (entry creation) date - dos.writeLong(((TrustedCertEntry)entry).date.getTime()); + dos.writeLong( + ((TrustedCertEntry)entry).date.toEpochMilli() + ); // write the trusted certificate encoded = ((TrustedCertEntry)entry).cert.getEncoded(); @@ -635,7 +654,9 @@ public final class JceKeyStore extends KeyStoreSpi { dos.writeUTF(alias); // write the (entry creation) date - dos.writeLong(((SecretKeyEntry)entry).date.getTime()); + dos.writeLong( + ((SecretKeyEntry)entry).date.toEpochMilli() + ); // write the sealed key oos = new ObjectOutputStream(dos); @@ -753,7 +774,7 @@ public final class JceKeyStore extends KeyStoreSpi { alias = dis.readUTF(); // read the (entry creation) date - entry.date = new Date(dis.readLong()); + entry.date = Instant.ofEpochMilli(dis.readLong()); // read the private key entry.protectedKey = IOUtils.readExactlyNBytes(dis, dis.readInt()); @@ -798,7 +819,7 @@ public final class JceKeyStore extends KeyStoreSpi { alias = dis.readUTF(); // read the (entry creation) date - entry.date = new Date(dis.readLong()); + entry.date = Instant.ofEpochMilli(dis.readLong()); // read the trusted certificate if (xVersion == 2) { @@ -832,7 +853,7 @@ public final class JceKeyStore extends KeyStoreSpi { alias = dis.readUTF(); // read the (entry creation) date - entry.date = new Date(dis.readLong()); + entry.date = Instant.ofEpochMilli(dis.readLong()); // read the sealed key try { diff --git a/src/java.base/share/classes/java/security/KeyStore.java b/src/java.base/share/classes/java/security/KeyStore.java index 8f3d4ba29fd..83414082cab 100644 --- a/src/java.base/share/classes/java/security/KeyStore.java +++ b/src/java.base/share/classes/java/security/KeyStore.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,6 +30,7 @@ import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.security.cert.CertificateException; import java.security.spec.AlgorithmParameterSpec; +import java.time.Instant; import java.util.*; import javax.crypto.SecretKey; @@ -1182,6 +1183,9 @@ public class KeyStore { /** * Returns the creation date of the entry identified by the given alias. + *

+ * It is recommended to use the {@link #getCreationInstant(String)} + * method instead. * * @param alias the alias name * @@ -1200,6 +1204,32 @@ public class KeyStore { return keyStoreSpi.engineGetCreationDate(alias); } + + /** + * Returns the instant that the entry identified by the given alias was + * created. + * + * @param alias the alias name + * + * @return the instant that the entry identified by the given alias + * was created, or {@code null} if the given alias does not exist + * + * @throws KeyStoreException if the keystore has not been initialized + * (loaded) + * + * @since 27 + */ + public final Instant getCreationInstant(String alias) + throws KeyStoreException + { + if (!initialized) { + throw new KeyStoreException("Uninitialized keystore"); + } + return keyStoreSpi.engineGetCreationInstant(alias); + } + + + /** * Assigns the given key to the given alias, protecting it with the given * password. diff --git a/src/java.base/share/classes/java/security/KeyStoreSpi.java b/src/java.base/share/classes/java/security/KeyStoreSpi.java index 0b0aa8adf19..5c60f44e4e9 100644 --- a/src/java.base/share/classes/java/security/KeyStoreSpi.java +++ b/src/java.base/share/classes/java/security/KeyStoreSpi.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ package java.security; import java.io.*; +import java.time.Instant; import java.util.*; import java.security.KeyStore.*; @@ -127,6 +128,29 @@ public abstract class KeyStoreSpi { */ public abstract Date engineGetCreationDate(String alias); + /** + * Returns the instant that the entry identified by the given alias was + * created. + * + * @apiNote Subclasses should override this method to directly return an + * instant. + * + * @implSpec + * The default implementation calls {@code engineGetCreationDate(alias)} + * and returns the output as an {@code Instant} value. + * + * @param alias the alias name + * + * @return the instant that the entry identified by the given alias + * was created, or {@code null} if the given alias does not exist + * + * @since 27 + */ + public Instant engineGetCreationInstant(String alias) { + final Date date = engineGetCreationDate(alias); + return date == null ? null : date.toInstant(); + } + /** * Assigns the given key to the given alias, protecting it with the given * password. diff --git a/src/java.base/share/classes/sun/security/pkcs12/PKCS12KeyStore.java b/src/java.base/share/classes/sun/security/pkcs12/PKCS12KeyStore.java index f85ba0f9c4b..33e8406a7f6 100644 --- a/src/java.base/share/classes/sun/security/pkcs12/PKCS12KeyStore.java +++ b/src/java.base/share/classes/sun/security/pkcs12/PKCS12KeyStore.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,6 +35,8 @@ import java.security.spec.AlgorithmParameterSpec; import java.security.spec.InvalidParameterSpecException; import java.security.spec.KeySpec; import java.security.spec.PKCS8EncodedKeySpec; +import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.*; import javax.crypto.Cipher; import javax.crypto.SecretKey; @@ -179,7 +181,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi { // A keystore entry and associated attributes private static class Entry { - Date date; // the creation date of this entry + Instant date; // the creation date of this entry String alias; byte[] keyId; Set attributes; @@ -212,7 +214,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi { CertEntry(X509Certificate cert, byte[] keyId, String alias, ObjectIdentifier[] trustedKeyUsage, Set attributes) { - this.date = new Date(); + this.date = Instant.now().truncatedTo(ChronoUnit.MILLIS); this.cert = cert; this.keyId = keyId; this.alias = alias; @@ -541,9 +543,28 @@ public final class PKCS12KeyStore extends KeyStoreSpi { * not exist */ public Date engineGetCreationDate(String alias) { - Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); + final Instant instant = this.engineGetCreationInstant(alias); + if (instant == null) { + return null; + } + return Date.from(instant); + } + + /** + * Returns the instant that the entry identified by the given alias was + * created. + * + * @param alias the alias name + * + * @return the instant that the entry identified by the given alias + * was created, or {@code null} if the given alias does not exist + * + * @since 27 + */ + public Instant engineGetCreationInstant(String alias) { + final Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); if (entry != null) { - return new Date(entry.date.getTime()); + return entry.date; } else { return null; } @@ -606,7 +627,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi { checkX509Certs(chain); PrivateKeyEntry keyEntry = new PrivateKeyEntry(); - keyEntry.date = new Date(); + keyEntry.date = Instant.now().truncatedTo(ChronoUnit.MILLIS); if ((key.getFormat().equals("PKCS#8")) || (key.getFormat().equals("PKCS8"))) { @@ -651,7 +672,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi { } else if (key instanceof SecretKey) { SecretKeyEntry keyEntry = new SecretKeyEntry(); - keyEntry.date = new Date(); + keyEntry.date = Instant.now().truncatedTo(ChronoUnit.MILLIS); // Encode secret key in a PKCS#8 DerOutputStream secretKeyInfo = new DerOutputStream(); @@ -690,7 +711,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi { entry.attributes.addAll(attributes); } // set the keyId to current date - entry.keyId = ("Time " + (entry.date).getTime()).getBytes(UTF_8); + entry.keyId = ("Time " + entry.date.toEpochMilli()).getBytes(UTF_8); // set the alias entry.alias = alias.toLowerCase(Locale.ENGLISH); // add the entry @@ -745,7 +766,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi { } PrivateKeyEntry entry = new PrivateKeyEntry(); - entry.date = new Date(); + entry.date = Instant.now().truncatedTo(ChronoUnit.MILLIS); if (debug != null) { debug.println("Setting a protected private key at alias '" + @@ -753,7 +774,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi { } // set the keyId to current date - entry.keyId = ("Time " + (entry.date).getTime()).getBytes(UTF_8); + entry.keyId = ("Time " + entry.date.toEpochMilli()).getBytes(UTF_8); // set the alias entry.alias = alias.toLowerCase(Locale.ENGLISH); @@ -2411,21 +2432,22 @@ public final class PKCS12KeyStore extends KeyStoreSpi { } } entry.keyId = keyId; - // restore date if it exists + // restore instant if it exists String keyIdStr = new String(keyId, UTF_8); - Date date = null; + Instant instant = null; if (keyIdStr.startsWith("Time ")) { try { - date = new Date( - Long.parseLong(keyIdStr.substring(5))); + instant = Instant.ofEpochMilli( + Long.parseLong(keyIdStr.substring(5)) + ); } catch (Exception e) { - // date has been initialized to null + // instant has been initialized to null } } - if (date == null) { - date = new Date(); + if (instant == null) { + instant = Instant.now().truncatedTo(ChronoUnit.MILLIS); } - entry.date = date; + entry.date = instant; if (bagItem instanceof PrivateKeyEntry) { keyList.add(entry); diff --git a/src/java.base/share/classes/sun/security/provider/DomainKeyStore.java b/src/java.base/share/classes/sun/security/provider/DomainKeyStore.java index 342df6c5eeb..163454b55fc 100644 --- a/src/java.base/share/classes/sun/security/provider/DomainKeyStore.java +++ b/src/java.base/share/classes/sun/security/provider/DomainKeyStore.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,6 +30,7 @@ import java.net.*; import java.security.*; import java.security.cert.Certificate; import java.security.cert.CertificateException; +import java.time.Instant; import java.util.*; import static java.nio.charset.StandardCharsets.UTF_8; @@ -214,16 +215,32 @@ abstract class DomainKeyStore extends KeyStoreSpi { * not exist */ public Date engineGetCreationDate(String alias) { + final Instant instant = this.engineGetCreationInstant(alias); + return (instant == null) ? null : Date.from(instant); + } + + /** + * Returns the instant that the entry identified by the given alias was + * created. + * + * @param alias the alias name + * + * @return the instant that the entry identified by the given alias + * was created, or {@code null} if the given alias does not exist + * + * @since 27 + */ + public Instant engineGetCreationInstant(String alias) { AbstractMap.SimpleEntry> pair = getKeystoresForReading(alias); - Date date = null; + Instant instant = null; try { String entryAlias = pair.getKey(); for (KeyStore keystore : pair.getValue()) { - date = keystore.getCreationDate(entryAlias); - if (date != null) { + instant = keystore.getCreationInstant(entryAlias); + if (instant != null) { break; } } @@ -231,7 +248,7 @@ abstract class DomainKeyStore extends KeyStoreSpi { throw new IllegalStateException(e); } - return date; + return instant; } @Override diff --git a/src/java.base/share/classes/sun/security/provider/JavaKeyStore.java b/src/java.base/share/classes/sun/security/provider/JavaKeyStore.java index 73ca0c6bf16..f014c2ecbc0 100644 --- a/src/java.base/share/classes/sun/security/provider/JavaKeyStore.java +++ b/src/java.base/share/classes/sun/security/provider/JavaKeyStore.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,6 +30,8 @@ import java.security.*; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; import java.security.cert.CertificateException; +import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.*; import static java.nio.charset.StandardCharsets.UTF_8; @@ -100,14 +102,14 @@ public abstract sealed class JavaKeyStore extends KeyStoreSpi { // Private keys and their supporting certificate chains private static class KeyEntry { - Date date; // the creation date of this entry + Instant date; // the creation date of this entry byte[] protectedPrivKey; Certificate[] chain; } // Trusted certificates private static class TrustedCertEntry { - Date date; // the creation date of this entry + Instant date; // the creation date of this entry Certificate cert; } @@ -236,13 +238,31 @@ public abstract sealed class JavaKeyStore extends KeyStoreSpi { * not exist */ public Date engineGetCreationDate(String alias) { - Object entry = entries.get(convertAlias(alias)); + final Instant instant = this.engineGetCreationInstant(alias); + return instant == null ? null : Date.from(instant); + } + + + + /** + * Returns the instant that the entry identified by the given alias was + * created. + * + * @param alias the alias name + * + * @return the instant that the entry identified by the given alias + * was created, or {@code null} if the given alias does not exist + * + * @since 27 + */ + public Instant engineGetCreationInstant(String alias) { + final Object entry = entries.get(convertAlias(alias)); if (entry != null) { if (entry instanceof TrustedCertEntry) { - return new Date(((TrustedCertEntry)entry).date.getTime()); + return ((TrustedCertEntry)entry).date; } else { - return new Date(((KeyEntry)entry).date.getTime()); + return((KeyEntry)entry).date; } } else { return null; @@ -287,7 +307,7 @@ public abstract sealed class JavaKeyStore extends KeyStoreSpi { try { synchronized(entries) { KeyEntry entry = new KeyEntry(); - entry.date = new Date(); + entry.date = Instant.now().truncatedTo(ChronoUnit.MILLIS); // Protect the encoding of the key passwordBytes = convertToBytes(password); @@ -350,7 +370,7 @@ public abstract sealed class JavaKeyStore extends KeyStoreSpi { } KeyEntry entry = new KeyEntry(); - entry.date = new Date(); + entry.date = Instant.now().truncatedTo(ChronoUnit.MILLIS); entry.protectedPrivKey = key.clone(); if ((chain != null) && @@ -391,7 +411,7 @@ public abstract sealed class JavaKeyStore extends KeyStoreSpi { TrustedCertEntry trustedCertEntry = new TrustedCertEntry(); trustedCertEntry.cert = cert; - trustedCertEntry.date = new Date(); + trustedCertEntry.date = Instant.now().truncatedTo(ChronoUnit.MILLIS); entries.put(convertAlias(alias), trustedCertEntry); } } @@ -579,7 +599,7 @@ public abstract sealed class JavaKeyStore extends KeyStoreSpi { dos.writeUTF(alias); // Write the (entry creation) date - dos.writeLong(((KeyEntry)entry).date.getTime()); + dos.writeLong(((KeyEntry)entry).date.toEpochMilli()); // Write the protected private key dos.writeInt(((KeyEntry)entry).protectedPrivKey.length); @@ -608,7 +628,9 @@ public abstract sealed class JavaKeyStore extends KeyStoreSpi { dos.writeUTF(alias); // Write the (entry creation) date - dos.writeLong(((TrustedCertEntry)entry).date.getTime()); + dos.writeLong( + ((TrustedCertEntry)entry).date.toEpochMilli() + ); // Write the trusted certificate encoded = ((TrustedCertEntry)entry).cert.getEncoded(); @@ -707,7 +729,7 @@ public abstract sealed class JavaKeyStore extends KeyStoreSpi { alias = dis.readUTF(); // Read the (entry creation) date - entry.date = new Date(dis.readLong()); + entry.date = Instant.ofEpochMilli(dis.readLong()); // Read the private key entry.protectedPrivKey = @@ -756,7 +778,7 @@ public abstract sealed class JavaKeyStore extends KeyStoreSpi { alias = dis.readUTF(); // Read the (entry creation) date - entry.date = new Date(dis.readLong()); + entry.date = Instant.ofEpochMilli(dis.readLong()); // Read the trusted certificate if (xVersion == 2) { diff --git a/test/jdk/java/security/KeyStore/TestKeyStoreBasic.java b/test/jdk/java/security/KeyStore/TestKeyStoreBasic.java index 8218c9cb48a..e39793cf1b7 100644 --- a/test/jdk/java/security/KeyStore/TestKeyStoreBasic.java +++ b/test/jdk/java/security/KeyStore/TestKeyStoreBasic.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,13 +24,18 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.security.*; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.PEMDecoder; +import java.security.PrivateKey; +import java.security.UnrecoverableKeyException; import java.security.cert.Certificate; import java.security.cert.X509Certificate; +import java.time.Instant; /* * @test - * @bug 8048621 8133090 8167371 8236671 + * @bug 8048621 8133090 8167371 8236671 8374808 * @enablePreview * @summary Test basic operations with keystores (jks, jceks, pkcs12) * @author Yu-Ching Valerie PENG @@ -177,6 +182,8 @@ public class TestKeyStoreBasic { // compare the creation date of the 2 key stores for all aliases compareCreationDate(ks, ks2, numEntries); + compareCreationInstant(ks, ks2, numEntries); + // remove the last entry from the 2nd key store numEntries--; ks2.deleteEntry(ALIAS_HEAD + numEntries); @@ -213,6 +220,7 @@ public class TestKeyStoreBasic { // compare the creation date of the 2 key stores for all aliases compareCreationDate(ks, ks2, numEntries); + compareCreationInstant(ks, ks2, numEntries); // check setEntry/getEntry with a password protection algorithm if ("PKCS12".equalsIgnoreCase(ks.getType())) { @@ -284,6 +292,23 @@ public class TestKeyStoreBasic { } } + // compare the creation instants - true if all the same + private void compareCreationInstant(KeyStore o1, KeyStore o2, int range) + throws KeyStoreException { + String alias; + for (int k = 0; k < range; k++) { + alias = ALIAS_HEAD + k; + final Instant instant1 = o1.getCreationInstant(alias); + final Instant instant2 = o2.getCreationInstant(alias); + if (!(instant1.equals(instant2))) { + throw new RuntimeException("ERROR: entry creation time (" + k + + ") differs Instants {" + + instant1 + " - " + + instant2 + "}"); + } + } + } + // checks if an exception was caused by specified exception class private static boolean causedBy(Exception e, Class klass) { Throwable cause = e; diff --git a/test/jdk/sun/security/provider/KeyStore/DKSTest.java b/test/jdk/sun/security/provider/KeyStore/DKSTest.java index 9c35a1c1dd9..ee655f5a2a6 100644 --- a/test/jdk/sun/security/provider/KeyStore/DKSTest.java +++ b/test/jdk/sun/security/provider/KeyStore/DKSTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,19 +23,30 @@ /* * @test - * @bug 8007755 + * @bug 8007755 8374808 * @library /test/lib * @summary Support the logical grouping of keystores */ -import java.io.*; -import java.net.*; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.net.URI; import java.nio.file.Paths; -import java.security.*; +import java.security.DomainLoadStoreParameter; import java.security.KeyStore; -import java.security.cert.*; +import java.security.UnrecoverableKeyException; import java.security.cert.Certificate; -import java.util.*; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import jdk.test.lib.process.ProcessTools; import jdk.test.lib.process.OutputAnalyzer; @@ -205,17 +216,26 @@ public class DKSTest { new KeyStore.TrustedCertificateEntry(cert), null); } - private static void checkEntries(KeyStore keystore, int expected) + private static void checkEntries(KeyStore keystore, int expectedCount) throws Exception { - int i = 0; + int currCount = 0; for (String alias : Collections.list(keystore.aliases())) { System.out.print("."); - i++; + currCount++; + + // check creation date and instant + if(!keystore.getCreationDate(alias).equals( + Date.from(keystore.getCreationInstant(alias))) + ){ + throw new RuntimeException( + "Creation Date is not the same as Instant timestamp"); + } } System.out.println(); - if (expected != i) { + // Check if current count is expected + if (expectedCount != currCount) { throw new Exception("Error: unexpected entry count in keystore: " + - "loaded=" + i + ", expected=" + expected); + "loaded=" + currCount + ", expected=" + expectedCount); } }