Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8225181: KeyStore should have a getAttributes method #6026

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
28 changes: 28 additions & 0 deletions src/java.base/share/classes/java/security/KeyStore.java
Expand Up @@ -1020,6 +1020,34 @@ public final String getType()
return this.type;
}

/**
* Retrieves the attributes associated with the given alias.
*
* @param alias the alias name
* @return an unmodifiable {@code Set} of attributes. This set is
* empty if the {@code KeyStoreSpi} implementation has not overridden
* {@link KeyStoreSpi#engineGetAttributes(String)}, or the given
* alias does not exist, or there are no attributes associated
* with the alias. This set may also be empty for
* {@code PrivateKeyEntry} or {@code SecretKeyEntry}
* entries that contain protected attributes and are only available
* through the {@link Entry#getAttributes} method after the entry
* is extracted.
*
* @throws KeyStoreException if the keystore has not been initialized
* (loaded).
* @throws NullPointerException if {@code alias} is {@code null}
*
wangweij marked this conversation as resolved.
Show resolved Hide resolved
* @since 18
*/
public final Set<Entry.Attribute> getAttributes(String alias)
throws KeyStoreException {
if (!initialized) {
throw new KeyStoreException("Uninitialized keystore");
}
return keyStoreSpi.engineGetAttributes(Objects.requireNonNull(alias));
}

/**
* Returns the key associated with the given alias, using the given
* password to recover it. The key must have been associated with
Expand Down
29 changes: 28 additions & 1 deletion src/java.base/share/classes/java/security/KeyStoreSpi.java
@@ -1,5 +1,5 @@
/*
* Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1998, 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
Expand Down Expand Up @@ -447,6 +447,33 @@ void engineLoad(InputStream stream, KeyStore.LoadStoreParameter param)
return;
}

/**
* Retrieves the attributes associated with the given alias.
*
wangweij marked this conversation as resolved.
Show resolved Hide resolved
* @implSpec
* The default implementation returns an empty {@code Set}.
* {@code KeyStoreSpi} implementations that support attributes
* should override this method.
*
* @param alias the alias name
* @return an unmodifiable {@code Set} of attributes. This set is
* empty if the given alias does not exist or there are no
* attributes associated with the alias. This set may also be
* empty for {@code PrivateKeyEntry} or {@code SecretKeyEntry}
* entries that contain protected attributes. These protected
* attributes should be populated into the result returned by
* {@link #engineGetEntry} and can be retrieved by calling
* the {@link Entry#getAttributes} method.
*
* @throws KeyStoreException if the keystore has not been initialized
* (loaded).
*
* @since 18
*/
public Set<Entry.Attribute> engineGetAttributes(String alias) {
return Collections.emptySet();
wangweij marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Gets a {@code KeyStore.Entry} for the specified alias
* with the specified protection parameter.
Expand Down
Expand Up @@ -1307,6 +1307,15 @@ public synchronized void engineStore(OutputStream stream, char[] password)
stream.flush();
}

@Override
public Set<KeyStore.Entry.Attribute> engineGetAttributes(String alias) {
if (!engineContainsAlias(alias)) {
return super.engineGetAttributes(alias);
}
Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
return getAttributes(entry);
}

/**
* Gets a <code>KeyStore.Entry</code> for the specified alias
* with the specified protection parameter.
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 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
Expand Down Expand Up @@ -236,6 +236,28 @@ public Date engineGetCreationDate(String alias) {
return date;
}

@Override
public Set<KeyStore.Entry.Attribute> engineGetAttributes(String alias) {

AbstractMap.SimpleEntry<String, Collection<KeyStore>> pair =
getKeystoresForReading(alias);
Set<KeyStore.Entry.Attribute> result = Collections.emptySet();

try {
String entryAlias = pair.getKey();
for (KeyStore keystore : pair.getValue()) {
result = keystore.getAttributes(entryAlias);
if (result != null) {
break;
}
}
} catch (KeyStoreException e) {
throw new IllegalStateException(e);
}

return result;
}

/**
* Assigns the given private key to the given alias, protecting
* it with the given password as defined in PKCS8.
Expand Down
Expand Up @@ -129,6 +129,11 @@ public void engineDeleteEntry(String alias) throws KeyStoreException {
keystore.engineDeleteEntry(alias);
}

@Override
public Set<KeyStore.Entry.Attribute> engineGetAttributes(String alias) {
return keystore.engineGetAttributes(alias);
}

@Override
public Enumeration<String> engineAliases() {
return keystore.engineAliases();
Expand Down
76 changes: 76 additions & 0 deletions test/jdk/sun/security/pkcs12/GetAttributes.java
@@ -0,0 +1,76 @@
/*
* Copyright (c) 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

/*
* @test
* @bug 8225181
* @summary KeyStore should have a getAttributes method
* @library /test/lib
* @modules java.base/sun.security.tools.keytool
* java.base/sun.security.x509
*/

import jdk.test.lib.Asserts;
import sun.security.tools.keytool.CertAndKeyGen;
import sun.security.x509.X500Name;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.security.KeyStore;
import java.security.cert.Certificate;

public class GetAttributes {

static char[] pass = "changeit".toCharArray();

public static void main(String[] args) throws Exception {

// Create a keystore with one private key entry and one cert entry
CertAndKeyGen cag = new CertAndKeyGen("EC", "SHA256withECDSA");
KeyStore ks = KeyStore.getInstance("pkcs12");
ks.load(null, null);
cag.generate("secp256r1");
ks.setKeyEntry("a", cag.getPrivateKey(), pass, new Certificate[] {
cag.getSelfCertificate(new X500Name("CN=a"), 1000)} );
cag.generate("secp256r1");
ks.setCertificateEntry("b",
cag.getSelfCertificate(new X500Name("CN=b"), 1000));

// Test
check(ks);

// Test newly loaded
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ks.store(bos, pass);
KeyStore ks2 = KeyStore.getInstance("pkcs12");
ks2.load(new ByteArrayInputStream(bos.toByteArray()), pass);
check(ks2);
}

static void check(KeyStore ks) throws Exception {
var entry = ks.getEntry("a", new KeyStore.PasswordProtection(pass));
Asserts.assertEQ(ks.getAttributes("a"), entry.getAttributes());
entry = ks.getEntry("b", null);
Asserts.assertEQ(ks.getAttributes("b"), entry.getAttributes());
}
}