Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
6782021: It is not possible to read local computer certificates with …
…the SunMSCAPI provider

Reviewed-by: weijun
  • Loading branch information
macarte authored and wangweij committed May 17, 2022
1 parent d65fba4 commit 5e5500c
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 32 deletions.
14 changes: 13 additions & 1 deletion src/java.base/share/classes/sun/security/tools/KeyStoreUtil.java
Expand Up @@ -91,7 +91,11 @@ public static boolean signedBy(X509Certificate end, X509Certificate ca) {
public static boolean isWindowsKeyStore(String storetype) {
return storetype != null
&& (storetype.equalsIgnoreCase("Windows-MY")
|| storetype.equalsIgnoreCase("Windows-ROOT"));
|| storetype.equalsIgnoreCase("Windows-ROOT")
|| storetype.equalsIgnoreCase("Windows-MY-CURRENTUSER")
|| storetype.equalsIgnoreCase("Windows-ROOT-CURRENTUSER")
|| storetype.equalsIgnoreCase("Windows-MY-LOCALMACHINE")
|| storetype.equalsIgnoreCase("Windows-ROOT-LOCALMACHINE"));
}

/**
Expand All @@ -102,6 +106,14 @@ public static String niceStoreTypeName(String storetype) {
return "Windows-MY";
} else if(storetype.equalsIgnoreCase("Windows-ROOT")) {
return "Windows-ROOT";
} else if(storetype.equalsIgnoreCase("Windows-MY-CURRENTUSER")) {
return "Windows-MY-CURRENTUSER";
} else if(storetype.equalsIgnoreCase("Windows-ROOT-CURRENTUSER")) {
return "Windows-ROOT-CURRENTUSER";
} else if(storetype.equalsIgnoreCase("Windows-MY-LOCALMACHINE")) {
return "Windows-MY-LOCALMACHINE";
} else if(storetype.equalsIgnoreCase("Windows-ROOT-LOCALMACHINE")) {
return "Windows-ROOT-LOCALMACHINE";
} else {
return storetype.toUpperCase(Locale.ENGLISH);
}
Expand Down
Expand Up @@ -54,15 +54,30 @@
*/
abstract class CKeyStore extends KeyStoreSpi {

private static final int LOCATION_CURRENTUSER = 0;
private static final int LOCATION_LOCALMACHINE = 1;

public static final class MY extends CKeyStore {
public MY() {
super("MY");
super("MY", LOCATION_CURRENTUSER);
}
}

public static final class ROOT extends CKeyStore {
public ROOT() {
super("ROOT");
super("ROOT", LOCATION_CURRENTUSER);
}
}

public static final class MYLocalMachine extends CKeyStore {
public MYLocalMachine() {
super("MY", LOCATION_LOCALMACHINE);
}
}

public static final class ROOTLocalMachine extends CKeyStore {
public ROOTLocalMachine() {
super("ROOT", LOCATION_LOCALMACHINE);
}
}

Expand Down Expand Up @@ -220,7 +235,12 @@ public void delete() throws KeyStoreException {
*/
private final String storeName;

CKeyStore(String storeName) {
/*
* The keystore location.
*/
private final int storeLocation;

CKeyStore(String storeName, int storeLocation) {
// Get the compatibility mode
@SuppressWarnings("removal")
String prop = AccessController.doPrivileged(
Expand All @@ -233,6 +253,7 @@ public void delete() throws KeyStoreException {
}

this.storeName = storeName;
this.storeLocation = storeLocation;
}

/**
Expand All @@ -259,7 +280,7 @@ public void delete() throws KeyStoreException {
* @exception UnrecoverableKeyException if the key cannot be recovered.
*/
public java.security.Key engineGetKey(String alias, char[] password)
throws NoSuchAlgorithmException, UnrecoverableKeyException {
throws NoSuchAlgorithmException, UnrecoverableKeyException {
if (alias == null) {
return null;
}
Expand Down Expand Up @@ -705,7 +726,7 @@ public void engineLoad(InputStream stream, char[] password)
try {

// Load keys and/or certificate chains
loadKeysOrCertificateChains(getName());
loadKeysOrCertificateChains(getName(), getLocation());

} catch (KeyStoreException e) {
throw new IOException(e);
Expand Down Expand Up @@ -801,7 +822,7 @@ private void generateKeyAndCertificateChain(boolean isRSA, String alias,
* @param certCollection Collection of certificates.
*/
private void generateCertificate(byte[] data,
Collection<Certificate> certCollection) {
Collection<Certificate> certCollection) {
try {
ByteArrayInputStream bis = new ByteArrayInputStream(data);

Expand Down Expand Up @@ -829,12 +850,20 @@ private String getName() {
}

/**
* Load keys and/or certificates from keystore into Collection.
* Returns the location of the keystore.
*/
private int getLocation() {
return storeLocation;
}

/**
* Loads keys and/or certificates from keystore into Collection.
*
* @param name Name of keystore.
* @param location Location of keystore.
*/
private native void loadKeysOrCertificateChains(String name)
throws KeyStoreException;
private native void loadKeysOrCertificateChains(String name,
int location) throws KeyStoreException;

/**
* Stores a DER-encoded certificate into the certificate store
Expand All @@ -844,8 +873,8 @@ private native void loadKeysOrCertificateChains(String name)
* @param encoding DER-encoded certificate.
*/
private native void storeCertificate(String name, String alias,
byte[] encoding, int encodingLength, long hCryptProvider,
long hCryptKey) throws CertificateException, KeyStoreException;
byte[] encoding, int encodingLength, long hCryptProvider,
long hCryptKey) throws CertificateException, KeyStoreException;

/**
* Removes the certificate from the certificate store
Expand All @@ -855,7 +884,7 @@ private native void storeCertificate(String name, String alias,
* @param encoding DER-encoded certificate.
*/
private native void removeCertificate(String name, String alias,
byte[] encoding, int encodingLength)
byte[] encoding, int encodingLength)
throws CertificateException, KeyStoreException;

/**
Expand All @@ -864,7 +893,7 @@ private native void removeCertificate(String name, String alias,
* @param keyContainerName The name of the key container.
*/
private native void destroyKeyContainer(String keyContainerName)
throws KeyStoreException;
throws KeyStoreException;

/**
* Removes a CNG key.
Expand All @@ -877,16 +906,16 @@ private native void destroyKeyContainer(String keyContainerName)
* Generates a private-key BLOB from a key's components.
*/
private native byte[] generateRSAPrivateKeyBlob(
int keyBitLength,
byte[] modulus,
byte[] publicExponent,
byte[] privateExponent,
byte[] primeP,
byte[] primeQ,
byte[] exponentP,
byte[] exponentQ,
byte[] crtCoefficient) throws InvalidKeyException;
int keyBitLength,
byte[] modulus,
byte[] publicExponent,
byte[] privateExponent,
byte[] primeP,
byte[] primeQ,
byte[] exponentP,
byte[] exponentQ,
byte[] crtCoefficient) throws InvalidKeyException;

private native CPrivateKey storePrivateKey(String alg, byte[] keyBlob,
String keyContainerName, int keySize) throws KeyStoreException;
String keyContainerName, int keySize) throws KeyStoreException;
}
Expand Up @@ -91,10 +91,14 @@ public Object newInstance(Object ctrParamObj)
return new PRNG();
}
} else if (type.equals("KeyStore")) {
if (algo.equals("Windows-MY")) {
if (algo.equals("Windows-MY") || algo.equals("Windows-MY-CURRENTUSER")) {
return new CKeyStore.MY();
} else if (algo.equals("Windows-ROOT")) {
} else if (algo.equals("Windows-ROOT") || algo.equals("Windows-ROOT-CURRENTUSER")) {
return new CKeyStore.ROOT();
} else if (algo.equals("Windows-MY-LOCALMACHINE")) {
return new CKeyStore.MYLocalMachine();
} else if (algo.equals("Windows-ROOT-LOCALMACHINE")) {
return new CKeyStore.ROOTLocalMachine();
}
} else if (type.equals("Signature")) {
if (algo.equals("NONEwithRSA")) {
Expand Down Expand Up @@ -165,8 +169,16 @@ public Void run() {
*/
putService(new ProviderService(p, "KeyStore",
"Windows-MY", "sun.security.mscapi.CKeyStore$MY"));
putService(new ProviderService(p, "KeyStore",
"Windows-MY-CURRENTUSER", "sun.security.mscapi.CKeyStore$MY"));
putService(new ProviderService(p, "KeyStore",
"Windows-ROOT", "sun.security.mscapi.CKeyStore$ROOT"));
putService(new ProviderService(p, "KeyStore",
"Windows-ROOT-CURRENTUSER", "sun.security.mscapi.CKeyStore$ROOT"));
putService(new ProviderService(p, "KeyStore",
"Windows-MY-LOCALMACHINE", "sun.security.mscapi.CKeyStore$MYLocalMachine"));
putService(new ProviderService(p, "KeyStore",
"Windows-ROOT-LOCALMACHINE", "sun.security.mscapi.CKeyStore$ROOTLocalMachine"));

/*
* Signature engines
Expand Down
26 changes: 20 additions & 6 deletions src/jdk.crypto.mscapi/windows/native/libsunmscapi/security.cpp
Expand Up @@ -59,6 +59,9 @@
#define SIGNATURE_EXCEPTION "java/security/SignatureException"
#define OUT_OF_MEMORY_ERROR "java/lang/OutOfMemoryError"

#define KEYSTORE_LOCATION_CURRENTUSER 0
#define KEYSTORE_LOCATION_LOCALMACHINE 1

#define SS_CHECK(Status) \
if (Status != ERROR_SUCCESS) { \
ThrowException(env, SIGNATURE_EXCEPTION, Status); \
Expand Down Expand Up @@ -386,10 +389,10 @@ JNIEXPORT jbyteArray JNICALL Java_sun_security_mscapi_PRNG_generateSeed
/*
* Class: sun_security_mscapi_CKeyStore
* Method: loadKeysOrCertificateChains
* Signature: (Ljava/lang/String;)V
* Signature: (Ljava/lang/String;I)V
*/
JNIEXPORT void JNICALL Java_sun_security_mscapi_CKeyStore_loadKeysOrCertificateChains
(JNIEnv *env, jobject obj, jstring jCertStoreName)
(JNIEnv *env, jobject obj, jstring jCertStoreName, jint jCertStoreLocation)
{
/**
* Certificate in cert store has enhanced key usage extension
Expand All @@ -407,16 +410,27 @@ JNIEXPORT void JNICALL Java_sun_security_mscapi_CKeyStore_loadKeysOrCertificateC
char* pszNameString = NULL; // certificate's friendly name
DWORD cchNameString = 0;


__try
{
// Open a system certificate store.
if ((pszCertStoreName = env->GetStringUTFChars(jCertStoreName, NULL))
== NULL) {
__leave;
}
if ((hCertStore = ::CertOpenSystemStore(NULL, pszCertStoreName))
== NULL) {

if (jCertStoreLocation == KEYSTORE_LOCATION_CURRENTUSER) {
hCertStore = ::CertOpenSystemStore(NULL, pszCertStoreName);
}
else if (jCertStoreLocation == KEYSTORE_LOCATION_LOCALMACHINE) {
hCertStore = ::CertOpenStore(CERT_STORE_PROV_SYSTEM_A, 0, NULL,
CERT_SYSTEM_STORE_LOCAL_MACHINE, pszCertStoreName);
}
else {
PP("jCertStoreLocation is not a valid value");
__leave;
}

if (hCertStore == NULL) {

ThrowException(env, KEYSTORE_EXCEPTION, GetLastError());
__leave;
Expand Down Expand Up @@ -469,7 +483,7 @@ JNIEXPORT void JNICALL Java_sun_security_mscapi_CKeyStore_loadKeysOrCertificateC
PP("--------------------------");
// Check if private key available - client authentication certificate
// must have private key available.
HCRYPTPROV hCryptProv = NULL;
HCRYPTPROV_OR_NCRYPT_KEY_HANDLE hCryptProv = NULL;
DWORD dwKeySpec = 0;
HCRYPTKEY hUserKey = NULL;
BOOL bCallerFreeProv = FALSE;
Expand Down
87 changes: 87 additions & 0 deletions test/jdk/sun/security/mscapi/AllTypes.java
@@ -0,0 +1,87 @@
/*
* Copyright (c) 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
* 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.
*/

import jdk.test.lib.Asserts;
import jdk.test.lib.SecurityTools;

import java.io.IOException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.util.Collections;
import java.util.List;
import java.util.Locale;

/*
* @test
* @bug 6782021
* @requires os.family == "windows"
* @library /test/lib
* @summary More keystore types
*/
public class AllTypes {

public static void main(String[] args) throws Exception {
var nm = test("windows-my");
var nr = test("windows-root");
var nmu = test("windows-my-currentuser");
var nru = test("windows-root-currentuser");
var hasAdminPrivileges = detectIfRunningWithAdminPrivileges();
var nmm = adminTest("windows-my-localmachine", hasAdminPrivileges);
var nrm = adminTest("windows-root-localmachine", hasAdminPrivileges);
Asserts.assertEQ(nm, nmu);
Asserts.assertEQ(nr, nru);
}

private static boolean detectIfRunningWithAdminPrivileges() {
try {
Process p = Runtime.getRuntime().exec("reg query \"HKU\\S-1-5-19\"");
p.waitFor();
return (p.exitValue() == 0);
}
catch (Exception ex) {
System.out.println("Warning: unable to detect admin privileges, assuming none");
return false;
}
}

private static List<String> adminTest(String type, boolean hasAdminPrivileges) throws Exception {
if (hasAdminPrivileges) {
return test(type);
}
System.out.println("Ignoring: " + type + " as it requires admin privileges");
return null;
}

private static List<String> test(String type) throws Exception {
var stdType = "Windows-" + type.substring(8).toUpperCase(Locale.ROOT);
SecurityTools.keytool("-storetype " + type + " -list")
.shouldHaveExitValue(0)
.shouldContain("Keystore provider: SunMSCAPI")
.shouldContain("Keystore type: " + stdType);
KeyStore ks = KeyStore.getInstance(type);
ks.load(null, null);
var content = Collections.list(ks.aliases());
Collections.sort(content);
return content;
}
}

1 comment on commit 5e5500c

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.