JDK-8313367 : SunMSCAPI cannot read Local Computer certs w/o Windows elevation
  • Type: Bug
  • Component: security-libs
  • Sub-Component: java.security
  • Affected Version: 17,20
  • Priority: P3
  • Status: Open
  • Resolution: Unresolved
  • OS: windows
  • CPU: x86_64
  • Submitted: 2023-07-26
  • Updated: 2024-04-11
Related Reports
Relates :  
Description
ADDITIONAL SYSTEM INFORMATION :
OS:  Windows 11 version 22H2, or Windows Server 2022

OpenJDK: 17.0.8, or 20.0.2


A DESCRIPTION OF THE PROBLEM :
The enhancement developed to allow keystore access provider SunMSCAPI to access the Windows Local Computer keystore, JDK-6782021, works as expected only if processes are run as elevated.

https://bugs.openjdk.org/browse/JDK-6782021

But with non-elevated access, the SunMSCAPI provider fails to access a read only private key from the Local Computer certificate store.

StackOverflow user "Andreas Gusenbauer" posted a question on this issue in January, 2023:

https://stackoverflow.com/questions/75255985/java-keystore-type-windows-my-root-localmachine-requires-administrator-permissio





STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
The StackOverflow post includes an answer from user "Snaps-A-Lot", which includes instructions on how to reproduce the issue complete with images and references.   But for this bug report, here is a top-down written instruction:

To reproduce the issue:

1)  Login to Windows using an account with membership in the local Administrators group.

2)  Create a self signed code signing certificate using the Powershell:
    $cert = New-SelfSignedCertificate -DNSName "JDK-6782021 Read Only Test" -CertStoreLocation Cert:\LocalMachine\My -Type CodeSigningCert -FriendlyName "JDK-6782021"

3)  Open the Control Panel, search for "certificates", click Manage Computer Certificates, expand Personal Certificates, right click the certificate issued to "JDK-6782021 Read Only Test".  From the menu, click "All Tasks / Manage Private Keys...".  In the pop-up dialog, add your user name, grant it "Allow" Read access (and nothing else), and click Okay to save your changes.

4)  Install a recent version of Visual Studio, such as Visual Studio 2019, if it is not already installed.  Locate singtool.exe on the system, and add its folder to the Path.

5)  Install OpenJDK 17.0.8, if it is not already installed. Add its "bin" folder to the Path.

6)  Open a non-elevated (Not "Administrator"" command prompt.

7)  Create a temporary folder, and make it current:
    md C:\temp\JDK-6782021_test
    cd C:\temp\JDK-6782021_test

8)  Copy any EXE file to the temporary folder, and rename it to MyApp.exe.

9)  Copy any JAR file to the temporary folder, and rename it to MyPkg.jar.

10) From the non-elevated command prompt, enter the following commands:

    signtool sign /sm /i "JDK-6782021 Read Only Test"  MyApp.exe
    jarsigner -keystore NONE -storetype Windows-MY-LocalMachine -providerClass sun.security.mscapi.SunMSCAPI MyPkg.jar JDK-6782021


EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
C:\temp\JDK-6782021_test> signtool sign /sm /i "JDK-6782021 Read Only Test"  MyApp.exe
Done Adding Additional Store
Successfully signed: MyApp.exe

C:\temp\JDK-6782021_test> jarsigner -keystore NONE -storetype Windows-MY-LocalMachine -providerClass sun.security.mscapi.SunMSCAPI MyPkg.jar JDK-6782021
jar signed.
ACTUAL -
Microsoft's signtool.exe successfully complete a code signature based on a read only ky from the Windows Local Computer keystore:, while the OpenJDK jarsigner.exe utility fails to do likewise:

C:\temp\JDK-6782021_test> signtool sign /sm /i "JDK-6782021 Read Only Test"  MyApp.exe
Done Adding Additional Store
Successfully signed: MyApp.exe

C:\temp\JDK-6782021_test> jarsigner -keystore NONE -storetype Windows-MY-LocalMachine -providerClass sun.security.mscapi.SunMSCAPI MyPkg.jar JDK-6782021
jarsigner error: java.lang.RuntimeException: keystore load: java.security.KeyStoreException: error 5, Access is denied.

CUSTOMER SUBMITTED WORKAROUND :
There is no true workaround for a high security environment, since the functionality added for JDK-6782021 cannot be used in a secure environment.    Instead, users must resort to pre-JDK-6782021 techniques.

Running the test from an elevated prompt will yield the expected success, but that would violate basic security policy due to the requirement for the user to be a member of the Local Administrators group.

To proceed with signing code, a better approach is to revert to using the "Windows My-CurrentUser" key store (or simply "Windows My") keystore, as was done prior to the JDK-6782021 enhancement.  Private key access control is not available, although export can still be disabled.  The certificate would also have to be deployed once for each user, but at least they can run without elevated access.

FREQUENCY : always



Comments
it's hard to add a regression test because in our testing environment all clients have the privilege and can always access the entries.
11-04-2024

[~mullan] Sean, I took a look at the PR and have added comments there. I would argue that this is an enhancement (to provide readonly access) and not a bug
04-03-2024

Taking a look at the fix
19-12-2023

A pull request was submitted for review. URL: https://git.openjdk.org/jdk/pull/16687 Date: 2023-11-16 12:06:26 +0000
20-11-2023

Additional information from the submitter: I followed up with creating the Java test case, in case you want to attach it to the report. Here is the Java code that will trigger the same system error 5 “Access is denied” encountered by the OpenJDK’s jarsigner tool: // JDK8313367test.java - Simple test case to demonstrate OpenJDK defect JDK-8313367 // References: // * https://bugs.java.com/bugdatabase/view_bug?bug_id=JDK-8313367 // * https://stackoverflow.com/questions/75255985/java-keystore-type-windows-my-root-localmachine-requires-administrator-permissio import java.io.*; import java.security.KeyStore; import java.security.Security; import java.util.Enumeration; import sun.security.mscapi.SunMSCAPI; public class JDK8313367test { public static void main(String[] args) { try { Security.addProvider(new SunMSCAPI()); KeyStore keyStore = KeyStore.getInstance("Windows-MY-LOCALMACHINE"); // When running as non-elevated, the SunMSCAPI provider, enhanced with JDK-6782021, incorrectly // triggers system error 5 "Access is denied" when attempting to load the keystore when invoking the following method: keyStore.load(null, null); Enumeration<String> aliases = keyStore.aliases(); // Print Friendly Names, a.k.a. aliases, of each certificate in the keystore for (int i = 0 ; aliases.hasMoreElements() ; i++) { System.out.println( aliases.nextElement() ); } } catch (Exception e) { throw new RuntimeException(e); } } } Here is the command line to run the test using JDK 17.0.8 (or 20.0.2) java --add-modules=jdk.crypto.mscapi --add-exports=jdk.crypto.mscapi/sun.security.mscapi=ALL-UNNAMED JDK8313367test.java When run as elevated, the test program will simply list all the aliases in the Windows Local Computer’s Personal Certificates keystore. The issue is reproduced when running it again as non-elevated (as an “ordinary” user), as required in a secure environment. It will produce the same stacktrace output reported in the StackOverflow posting, although there are a few more due to the way it is invoked above: Exception in thread "main" java.lang.RuntimeException: java.io.IOException: java.security.KeyStoreException: error 5, Access is denied. at JDK8313367test.main(JDK8313367test.java:26) Caused by: java.io.IOException: java.security.KeyStoreException: error 5, Access is denied. at jdk.crypto.mscapi/sun.security.mscapi.CKeyStore.engineLoad(CKeyStore.java:732) at jdk.crypto.mscapi/sun.security.mscapi.CKeyStore$MYLocalMachine.engineLoad(CKeyStore.java:72) at java.base/java.security.KeyStore.load(KeyStore.java:1500) at JDK8313367test.main(JDK8313367test.java:19) at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) at java.base/java.lang.reflect.Method.invoke(Method.java:578) at jdk.compiler/com.sun.tools.javac.launcher.Main.execute(Main.java:435) at jdk.compiler/com.sun.tools.javac.launcher.Main.run(Main.java:205) at jdk.compiler/com.sun.tools.javac.launcher.Main.main(Main.java:132) Caused by: java.security.KeyStoreException: error 5, Access is denied. at jdk.crypto.mscapi/sun.security.mscapi.CKeyStore.loadKeysOrCertificateChains(Native Method) at jdk.crypto.mscapi/sun.security.mscapi.CKeyStore.engineLoad(CKeyStore.java:729) ... 8 more This provides definitive evidence that the issue is not in the OpenJDK’s jarsigner tool, as previously asserted.
02-08-2023

[~macarte] Mat, I am assigning this to you since you were the RE for JDK-6782021. Can you please evaluate this issue? Thanks.
31-07-2023

Additional information from the submitter: The OpenJDK’s jassigner.exe is used to reproduce this issue. It is demonstrated in the steps to reproduce in contract with Microsoft’s signtool. Jarsigner is not the issue, though, it is the SunMSCAPI provider. If the bug report needs source of jarsigner, I assumed that the assigned developer would have that readily available. Otherwise, do you think the bug report requires something other than the jarsigner to prove the issue? FMI, is the source of the SunMSCAPI provider included with the OpenJDK source? I can do some additional work on the bug report if needed, including figuring that out. I am more of a builder role player, but I do some debugging now and then. But I wanted to get this issue submitted ASAP, given it is a showstopper in a high security environment.
29-07-2023

Requested a complete reproducer from the submitter.
28-07-2023