Bug 2036462
Summary: | rh1991003 patch breaks sun.security.pkcs11.wrapper.PKCS11.getInstance() | ||
---|---|---|---|
Product: | [Fedora] Fedora | Reporter: | Francisco de la Peña <fran> |
Component: | java-1.8.0-openjdk | Assignee: | Andrew John Hughes <ahughes> |
Status: | CLOSED ERRATA | QA Contact: | Fedora Extras Quality Assurance <extras-qa> |
Severity: | unspecified | Docs Contact: | |
Priority: | unspecified | ||
Version: | 35 | CC: | ahughes, dbhole, fferrari, hristo, jerboaa, jvanek, mbalao, mmillson, msrb, mvala, sgehwolf |
Target Milestone: | --- | ||
Target Release: | --- | ||
Hardware: | Unspecified | ||
OS: | Unspecified | ||
Whiteboard: | |||
Fixed In Version: | java-1.8.0-openjdk-1.8.0.342.b07-1.fc35 | Doc Type: | If docs needed, set a value |
Doc Text: | Story Points: | --- | |
Clone Of: | Environment: | ||
Last Closed: | 2022-08-03 01:48:54 UTC | Type: | Bug |
Regression: | --- | Mount Type: | --- |
Documentation: | --- | CRM: | |
Verified Versions: | Category: | --- | |
oVirt Team: | --- | RHEL 7.3 requirements from Atomic Host: | |
Cloudforms Team: | --- | Target Upstream Version: | |
Embargoed: | |||
Attachments: |
Description
Francisco de la Peña
2022-01-02 00:11:07 UTC
A possible simple fix for this would be method overloading by creating a method with four arguments calling the one with five arguments with a null for the fifth argument. Comment on attachment 1848563 [details] Test class to reproduce the building/running issue >class Test { > public static void main(String[] args) throws Exception { > sun.security.pkcs11.wrapper.CK_C_INITIALIZE_ARGS initArgs = new sun.security.pkcs11.wrapper.CK_C_INITIALIZE_ARGS(); > initArgs.flags = 0; > sun.security.pkcs11.wrapper.PKCS11 pkcs11 = sun.security.pkcs11.wrapper.PKCS11.getInstance("/usr/lib64/opensc-pkcs11.so", "C_GetFunctionList", initArgs, false); > > long[] slotList = pkcs11.C_GetSlotList(true); > for (long slot : slotList) { > sun.security.pkcs11.wrapper.CK_TOKEN_INFO tokenInfo = pkcs11.C_GetTokenInfo(slot); > System.out.println("Slot info:\n" + tokenInfo.toString()); > } > } >} Created attachment 1848630 [details]
Fixed version with 4 args as intended
Created attachment 1848631 [details]
Fixed version with 4 args as intended (ignore the previous attachment, it was a .class file)
To clarify, the issue breaks programs even without enabling FIPS mode. RHEL 8/9 is affected, too. Confirmed the issue affects java-11-openjdk and java-latest-openjdk, too. Yes, looks like this should be easily fixable. I'll get a fix into the JDKs. As commented in an internal email: The sun.security.pkcs11.wrapper package is not public. This API is internal and not available for applications or libraries direct use. In JDK-8, that can be inferred from the namespace. In later releases, that is blocked by module encapsulation. The method "getInstance" is package-public because the class SunPKCS11 (which is in a different package, sun.security.pkcs11) needs access. While we can overload the method with a backward-compatible signature (as suggested in comment 1 [1]), relying on internal APIs is strongly discouraged. With that said, can we get a pointer to the code that uses this API? I wonder what's the reason behind, which JDK releases does the code support and if it's still done that way in its latest release. Martin.- -- [1] - https://bugzilla.redhat.com/show_bug.cgi?id=2036462#c1 Java public PKCS#11 API does not allow enumerating certificates without login. At most, you can get a lot list number, but not checking contents to determinate which slot should use. This is an issue when more than one slot is connected and increases the risk of using a PIN for the wrong token and getting locked. Unfortunately, we are aware of multiple implementations (mostly from Government from several countries) using sun wrapper because it allows this without JNI. There are multiple sites with smart card access and java web start for accessing or signing on websites using them and they likely can't fix their software. Since Java 16, it is still possible to access sun classes by passing the command line argument: --add-exports jdk.crypto.cryptoki/sun.security.pkcs11.wrapper=ALL-UNNAMED There are even cases using it this way to circumvent the restriction on newer Java versions for launching standalone JARs without passing args, e.g. for Web Start. It could even work on (currently unmaintained) icedtea-web as the latest F35 version didn't provide the upstream fix for supporting relevant fixes such multi-release JAR support or prefixed jnlp.*/javaws.* for -Dargs to match those supported by Oracle's javaws: class Test { public static void doMain() { /* "real" main content goes here */ } public static void main(String[] args) throws Throwable { if (System.getProperty("java.version").startsWith("1.")) doMain(); else { for(String s : args) { if(s.equals("run")) { doMain(); return; } } new ProcessBuilder().inheritIO().command("java", "--add-exports", "jdk.crypto.cryptoki/sun.security.pkcs11.wrapper=ALL-UNNAMED", "-jar", MethodHandles.lookup().lookupClass().getProtectionDomain().getCodeSource().getLocation().toURI().getPath(), "run").start().waitFor(); } } } Here's a complete example with Sun's wrapper features: import java.io.ByteArrayInputStream; import java.lang.invoke.MethodHandles; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.text.SimpleDateFormat; import javax.naming.ldap.LdapName; import javax.naming.ldap.Rdn; import sun.security.pkcs11.wrapper.CK_ATTRIBUTE; import sun.security.pkcs11.wrapper.CK_C_INITIALIZE_ARGS; import sun.security.pkcs11.wrapper.CK_INFO; import sun.security.pkcs11.wrapper.CK_SLOT_INFO; import sun.security.pkcs11.wrapper.CK_TOKEN_INFO; import sun.security.pkcs11.wrapper.PKCS11; import sun.security.pkcs11.wrapper.PKCS11Exception; import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_CLASS; import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_ID; import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_VALUE; import static sun.security.pkcs11.wrapper.PKCS11Constants.CKF_OS_LOCKING_OK; import static sun.security.pkcs11.wrapper.PKCS11Constants.CKF_SERIAL_SESSION; import static sun.security.pkcs11.wrapper.PKCS11Constants.CKF_TOKEN_PRESENT; import static sun.security.pkcs11.wrapper.PKCS11Constants.CKO_CERTIFICATE; import static sun.security.pkcs11.wrapper.PKCS11Constants.CKR_TOKEN_NOT_RECOGNIZED; class Test { public static void doMain() throws Throwable { String lib = "/usr/lib64/opensc-pkcs11.so"; String functionList = "C_GetFunctionList"; CK_C_INITIALIZE_ARGS pInitArgs = new CK_C_INITIALIZE_ARGS(); PKCS11 pkcs11; try { pInitArgs.flags = CKF_OS_LOCKING_OK; pkcs11 = PKCS11.getInstance(lib, functionList, pInitArgs, false); } catch (PKCS11Exception e) { pInitArgs.flags = 0; pkcs11 = PKCS11.getInstance(lib, functionList, pInitArgs, false); } CK_INFO info = pkcs11.C_GetInfo(); System.out.println("Interface: " + new String(info.libraryDescription).trim()); Boolean tokenPresent = true; for (long slotID : pkcs11.C_GetSlotList(tokenPresent)) { CK_SLOT_INFO slotInfo = pkcs11.C_GetSlotInfo(slotID); System.out.println("Slot " + slotID + ": " + new String(slotInfo.slotDescription).trim()); if ((slotInfo.flags & CKF_TOKEN_PRESENT) != 0) { // Not required if tokenPresent = true, condition could be removed if true, it's just for testing empty slot enumeration try { // TODO: check if slotID could be reused after switching card. Try CK_SESSION_INFO sessionInfo = pkcs11.C_GetSessionInfo(hSession); and catch PCKCS11Exception meaning invalid session instead. CK_TOKEN_INFO tokenInfo = pkcs11.C_GetTokenInfo(slotID); System.out.println("Token: " + new String(tokenInfo.label).trim() + " (" + new String(tokenInfo.serialNumber).trim() + ")"); CK_ATTRIBUTE[] pTemplate = { new CK_ATTRIBUTE(CKA_CLASS, CKO_CERTIFICATE) }; long ulMaxObjectCount = 32; long hSession = pkcs11.C_OpenSession(slotID, CKF_SERIAL_SESSION, null, null); // TODO verify slot session just after getting PIN but just before login pkcs11.C_FindObjectsInit(hSession, pTemplate); long[] phObject = pkcs11.C_FindObjects(hSession, ulMaxObjectCount); pkcs11.C_FindObjectsFinal(hSession); for (long object : phObject) { CK_ATTRIBUTE[] pTemplate2 = { new CK_ATTRIBUTE(CKA_VALUE), new CK_ATTRIBUTE(CKA_ID) }; // if you add more attributes, update the iterator jump pkcs11.C_GetAttributeValue(hSession, object, pTemplate2); for (int i = 0; i < pTemplate2.length; i = i + 2) { // iterator jump value to read just certificates at pTemplate[0], pTemplate[2]... X509Certificate certificate = (X509Certificate)CertificateFactory.getInstance("X.509").generateCertificate(new ByteArrayInputStream((byte[])pTemplate2[i].pValue)); boolean[] keyUsage = certificate.getKeyUsage(); if (certificate.getBasicConstraints() == -1 && keyUsage[0] && keyUsage[1]) { LdapName ldapName = new LdapName(certificate.getSubjectX500Principal().getName("RFC1779")); String firstName = "", lastName = "", identification = ""; for (Rdn rdn : ldapName.getRdns()) { if (rdn.getType().equals("OID.2.5.4.5")) identification = rdn.getValue().toString(); if (rdn.getType().equals("OID.2.5.4.4")) lastName = rdn.getValue().toString(); if (rdn.getType().equals("OID.2.5.4.42")) firstName = rdn.getValue().toString(); } String expires = new SimpleDateFormat("yyyy-MM-dd").format(certificate.getNotAfter()); System.out.println(firstName + " " + lastName + " (" + identification + ") " + certificate.getSerialNumber().toString(16) + " [Token serial number: " + new String(tokenInfo.serialNumber) + "] (Expires: " + expires+ ")"); Object keyIdentifier = pTemplate2[i + 1]/* .pValue */; // TODO use pValue to get the value for comparison when using it to match with private key! System.out.println("Public/Private key pair identifier: " + keyIdentifier); // After logging in with PIN, find the matching private key pValue. } // TODO Don't assume there's a single valid certificate per token } } pkcs11.C_CloseSession(hSession); } catch (PKCS11Exception e) { if (e.getErrorCode() == CKR_TOKEN_NOT_RECOGNIZED) { System.err.println("Slot reports token is present but not recognized by the cryptoki library"); } else throw e; } } else System.out.println("No token present in this slot"); // Not required if tokenPresent = true, condition could be removed } } public static void main(String[] args) throws Throwable { if (System.getProperty("java.version").startsWith("1.")) doMain(); else { for(String s : args) { if(s.equals("run")) { doMain(); return; } } // Launch JAR from JAR with own args for Java 16+ compatibility new ProcessBuilder().inheritIO().command("java", "--add-exports", "jdk.crypto.cryptoki/sun.security.pkcs11.wrapper=ALL-UNNAMED", "-jar", MethodHandles.lookup().lookupClass().getProtectionDomain().getCodeSource().getLocation().toURI().getPath(), "run").start().waitFor(); // TODO test with icedtea-web (2.0 alpha builds) with Java > 8 } } } One library is https://github.com/Keyfactor/ejbca-ce The most recent one has adapted to the added MethodHandle signature: https://github.com/Keyfactor/ejbca-ce/commit/de34790d08c76a6d1a05008493593c0e2a1cd98d Old code is unaware of this: https://github.com/Keyfactor/ejbca-ce/blob/745fe7364d5e81b82203a8a2f56bba17c066ef19/modules/cesecore-common/src/org/cesecore/keys/token/p11/Pkcs11Wrapper.java#L103 FEDORA-2022-4d338824b7 has been submitted as an update to Fedora 35. https://bodhi.fedoraproject.org/updates/FEDORA-2022-4d338824b7 FEDORA-2022-4d338824b7 has been pushed to the Fedora 35 testing repository. Soon you'll be able to install the update with the following command: `sudo dnf upgrade --enablerepo=updates-testing --refresh --advisory=FEDORA-2022-4d338824b7` You can provide feedback for this update here: https://bodhi.fedoraproject.org/updates/FEDORA-2022-4d338824b7 See also https://fedoraproject.org/wiki/QA:Updates_Testing for more information on how to test updates. FEDORA-2022-80afe2304a has been pushed to the Fedora 35 testing repository. Soon you'll be able to install the update with the following command: `sudo dnf upgrade --enablerepo=updates-testing --refresh --advisory=FEDORA-2022-80afe2304a` You can provide feedback for this update here: https://bodhi.fedoraproject.org/updates/FEDORA-2022-80afe2304a See also https://fedoraproject.org/wiki/QA:Updates_Testing for more information on how to test updates. FEDORA-2022-80afe2304a has been pushed to the Fedora 35 stable repository. If problem still persists, please make note of it in this bug report. The needinfo request[s] on this closed bug have been removed as they have been unresolved for 365 days |