Skip to content

Commit 757b384

Browse files
authored
Merge pull request #667 from slovensko-digital/refactor-pkcs11-invocation
Refactor pkcs11 invocation
2 parents eb9aa95 + 3b650d4 commit 757b384

6 files changed

Lines changed: 135 additions & 53 deletions

File tree

.idea/compiler.xml

Lines changed: 0 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.run/Main.run.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
<component name="ProjectRunConfigurationManager">
22
<configuration default="false" name="Main" type="Application" factoryName="Application" nameIsGenerated="true">
3-
<option name="ALTERNATIVE_JRE_PATH" value="$PROJECT_DIR$/target/jdkCache/LIBERICA_jdk24.0.1+11_linux_amd64-full" />
3+
<option name="ALTERNATIVE_JRE_PATH" value="liberica-24" />
44
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
55
<option name="MAIN_CLASS_NAME" value="digital.slovensko.autogram.Main" />
66
<module name="autogram" />
7+
<option name="PROGRAM_PARAMETERS" value="--url=&quot;autogram://listen?protocol=http&amp;port=37200&quot;" />
78
<option name="VM_PARAMETERS" value="--add-exports jdk.crypto.cryptoki/sun.security.pkcs11.wrapper=ALL-UNNAMED --add-opens java.base/java.security=ALL-UNNAMED --add-opens jdk.crypto.cryptoki/sun.security.pkcs11=ALL-UNNAMED --enable-native-access=javafx.graphics" />
89
<extension name="coverage">
910
<pattern>

.vscode/launch.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@
88
"mainClass": "digital.slovensko.autogram.Main",
99
"projectName": "autogram",
1010
"preLaunchTask": "java (build): Build Workspace",
11-
// "vmArgs": "-Dprism.maxvram=2G",
11+
"vmArgs": "--add-exports jdk.crypto.cryptoki/sun.security.pkcs11.wrapper=ALL-UNNAMED --add-opens java.base/java.security=ALL-UNNAMED --add-opens jdk.crypto.cryptoki/sun.security.pkcs11=ALL-UNNAMED --add-modules jdk.crypto.cryptoki --enable-native-access=javafx.graphics,javafx.web",
1212
},
1313
{
1414
"type": "java",
1515
"name": "Autogram URL",
1616
"request": "launch",
1717
"mainClass": "digital.slovensko.autogram.Main",
1818
"projectName": "autogram",
19-
"vmArgs": "--add-exports javafx.graphics/com.sun.javafx.tk=ALL-UNNAMED",
19+
"vmArgs": "--add-exports javafx.graphics/com.sun.javafx.tk=ALL-UNNAMED --add-exports jdk.crypto.cryptoki/sun.security.pkcs11.wrapper=ALL-UNNAMED --add-opens java.base/java.security=ALL-UNNAMED --add-opens jdk.crypto.cryptoki/sun.security.pkcs11=ALL-UNNAMED --add-modules jdk.crypto.cryptoki --enable-native-access=javafx.graphics,javafx.web",
2020
"args": "--url=\"autogram://listen?protocol=http&host=localhost&port=37200&origin=*&language=sk\"",
2121
"osx": {
2222
// this is because of some escaping problem with zsh
@@ -29,7 +29,7 @@
2929
"request": "launch",
3030
"mainClass": "digital.slovensko.autogram.Main",
3131
"projectName": "autogram",
32-
"vmArgs": "--add-exports javafx.graphics/com.sun.javafx.tk=ALL-UNNAMED",
32+
"vmArgs": "--add-exports jdk.crypto.cryptoki/sun.security.pkcs11.wrapper=ALL-UNNAMED --add-opens java.base/java.security=ALL-UNNAMED --add-opens jdk.crypto.cryptoki/sun.security.pkcs11=ALL-UNNAMED --add-modules jdk.crypto.cryptoki --enable-native-access=javafx.graphics,javafx.web",
3333
"args": "--url=\"autogram://listen?protocol=https&host=localhost&port=37200&origin=*&language=sk\"",
3434
"osx": {
3535
// this is because of some escaping problem with zsh
@@ -42,7 +42,7 @@
4242
"request": "launch",
4343
"mainClass": "digital.slovensko.autogram.Main",
4444
"projectName": "autogram",
45-
"vmArgs": "--add-exports javafx.graphics/com.sun.javafx.tk=ALL-UNNAMED",
45+
"vmArgs": "--add-exports jdk.crypto.cryptoki/sun.security.pkcs11.wrapper=ALL-UNNAMED --add-opens java.base/java.security=ALL-UNNAMED --add-opens jdk.crypto.cryptoki/sun.security.pkcs11=ALL-UNNAMED --add-modules jdk.crypto.cryptoki --enable-native-access=javafx.graphics,javafx.web",
4646
"args": "${input:commandLineArguments}",
4747
"osx": {
4848
// this is because of some escaping problem with zsh

pom.xml

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,28 @@
193193
</plugins>
194194
</pluginManagement>
195195
<plugins>
196+
<plugin>
197+
<groupId>org.apache.maven.plugins</groupId>
198+
<artifactId>maven-enforcer-plugin</artifactId>
199+
<version>3.5.0</version>
200+
<executions>
201+
<execution>
202+
<id>enforce-maven-jdk</id>
203+
<goals>
204+
<goal>enforce</goal>
205+
</goals>
206+
<configuration>
207+
<rules>
208+
<requireJavaVersion>
209+
<version>[24,)</version>
210+
<message>This build must be run with JDK 24 or newer. The jlink step uses JDK 24 modules and fails with InvalidModuleDescriptorException if Maven runs on an older JDK.</message>
211+
</requireJavaVersion>
212+
</rules>
213+
</configuration>
214+
</execution>
215+
</executions>
216+
</plugin>
217+
196218
<plugin>
197219
<groupId>org.apache.maven.plugins</groupId>
198220
<artifactId>maven-compiler-plugin</artifactId>
@@ -203,12 +225,6 @@
203225
<executable>${jlink.jdk.path}${file.separator}bin${file.separator}javac</executable>
204226
<source>${java.version}</source>
205227
<target>${java.version}</target>
206-
<compilerArgs>
207-
<arg>--add-exports</arg>
208-
<arg>jdk.crypto.cryptoki/sun.security.pkcs11.wrapper=ALL-UNNAMED</arg>
209-
<arg>--add-modules</arg>
210-
<arg>jdk.crypto.cryptoki</arg>
211-
</compilerArgs>
212228
</configuration>
213229
</plugin>
214230

src/main/java/digital/slovensko/autogram/drivers/NativePkcs11SignatureToken.java

Lines changed: 97 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,11 @@
1212
import eu.europa.esig.dss.token.DSSPrivateKeyEntry;
1313
import eu.europa.esig.dss.token.KSPrivateKeyEntry;
1414
import eu.europa.esig.dss.token.Pkcs11SignatureToken;
15-
import sun.security.pkcs11.wrapper.CK_ATTRIBUTE;
16-
import sun.security.pkcs11.wrapper.PKCS11;
17-
import sun.security.pkcs11.wrapper.PKCS11Constants;
18-
import sun.security.pkcs11.wrapper.PKCS11Exception;
1915

16+
import java.lang.reflect.Array;
2017
import java.lang.reflect.Field;
18+
import java.lang.reflect.InvocationTargetException;
19+
import java.lang.reflect.Method;
2120
import java.security.GeneralSecurityException;
2221
import java.security.PrivateKey;
2322
import java.security.Signature;
@@ -26,6 +25,10 @@
2625

2726
public class NativePkcs11SignatureToken extends Pkcs11SignatureToken {
2827
private static final long CKU_CONTEXT_SPECIFIC = 2L;
28+
private static final String PKCS11_EXCEPTION_CLASS_NAME = "sun.security.pkcs11.wrapper.PKCS11Exception";
29+
private static final String PKCS11_CONSTANTS_CLASS_NAME = "sun.security.pkcs11.wrapper.PKCS11Constants";
30+
private static final String CK_ATTRIBUTE_CLASS_NAME = "sun.security.pkcs11.wrapper.CK_ATTRIBUTE";
31+
2932
private final PasswordManager passwordManager;
3033
private final SignatureTokenSettings settings;
3134

@@ -60,44 +63,51 @@ private void runContextSpecificLoginIfNeeded(Signature signature, PrivateKey pk)
6063

6164
if (isAlwaysAuthenticate(p11, sessionId, pk) && (settings.getForceContextSpecificLoginEnabled() || !isProtectedAuthenticationPath(p11, getSlotListIndex()))) {
6265
var password = passwordManager.getContextSpecificPassword();
63-
if (password == null) throw new PasswordNotProvidedException(); // handle password not provided
64-
p11.C_Login(sessionId, CKU_CONTEXT_SPECIFIC, password);
66+
if (password == null) throw new PasswordNotProvidedException();
67+
invokeCLogin(p11, sessionId, CKU_CONTEXT_SPECIFIC, password);
6568
}
6669
} catch (AutogramException e) {
67-
throw e; // rethrow autogram errors
68-
} catch (PKCS11Exception e) {
69-
if (e.getMessage().equals("CKR_PIN_INCORRECT"))
70-
throw new PINIncorrectException();
71-
72-
throw new GeneralSecurityException(e);
70+
throw e;
71+
} catch (Exception e) {
72+
var cause = unwrapInvocationException(e);
73+
if (isPkcs11Exception(cause)) {
74+
if ("CKR_PIN_INCORRECT".equals(cause.getMessage())) {
75+
throw new PINIncorrectException();
76+
}
77+
throw new GeneralSecurityException(cause);
78+
}
79+
throw new GeneralSecurityException(cause);
7380
}
7481
}
7582

76-
private static boolean isAlwaysAuthenticate(PKCS11 p11, long sessionId, PrivateKey pk) throws PKCS11Exception {
83+
private static boolean isAlwaysAuthenticate(Object p11, long sessionId, PrivateKey pk) throws Exception {
7784
var keyID = getKeyID(pk);
78-
var attrs = new CK_ATTRIBUTE[]{new CK_ATTRIBUTE(PKCS11Constants.CKA_ALWAYS_AUTHENTICATE)};
85+
var attrs = newAttributeArray(getPkcs11Constant("CKA_ALWAYS_AUTHENTICATE"));
7986

80-
p11.C_GetAttributeValue(sessionId, keyID, attrs);
87+
invokeCGetAttributeValue(p11, sessionId, keyID, attrs);
8188

82-
var result = attrs[0].pValue;
89+
var attr = Array.get(attrs, 0);
90+
var result = getPublicField(attr, "pValue");
8391
if (result instanceof byte[]) {
8492
return ((byte[]) result)[0] == 1;
85-
} else {
86-
return false; // CKA_ALWAYS_AUTHENTICATE not found
8793
}
94+
return false;
8895
}
8996

90-
private static boolean isProtectedAuthenticationPath(PKCS11 p11, int slotIndex) throws PKCS11Exception {
91-
92-
var slotList = p11.C_GetSlotList(false);
93-
if (slotList.length <= slotIndex || slotList.length < 1)
97+
private static boolean isProtectedAuthenticationPath(Object p11, int slotIndex) throws Exception {
98+
var slotList = invokeCGetSlotList(p11, false);
99+
if (slotList.length <= slotIndex || slotList.length < 1) {
94100
return false;
101+
}
95102

96-
if (slotIndex < 0)
103+
if (slotIndex < 0) {
97104
slotIndex = 0;
105+
}
98106

99107
var slotId = slotList[slotIndex];
100-
return (p11.C_GetTokenInfo(slotId).flags & PKCS11Constants.CKF_PROTECTED_AUTHENTICATION_PATH) != 0;
108+
var tokenInfo = invokeCGetTokenInfo(p11, slotId);
109+
var flags = (long) getPublicField(tokenInfo, "flags");
110+
return (flags & getPkcs11Constant("CKF_PROTECTED_AUTHENTICATION_PATH")) != 0;
101111
}
102112

103113
private int getSlotListIndex() {
@@ -145,7 +155,7 @@ private static long getSessionId(Signature signature) {
145155
}
146156
}
147157

148-
private static PKCS11 getP11(Signature signature) {
158+
private static Object getP11(Signature signature) {
149159
try {
150160
Field sigSpiField = signature.getClass().getDeclaredField("sigSpi");
151161
sigSpiField.setAccessible(true);
@@ -157,14 +167,73 @@ private static PKCS11 getP11(Signature signature) {
157167

158168
var p11Field = token.getClass().getDeclaredField("p11");
159169
p11Field.setAccessible(true);
160-
var p11 = p11Field.get(token);
161-
162-
return (PKCS11) p11;
170+
return p11Field.get(token);
163171
} catch (NoSuchFieldException | IllegalAccessException e) {
164172
throw new RuntimeException(e);
165173
}
166174
}
167175

176+
private static Object newAttributeArray(long attributeType) throws Exception {
177+
var attrClass = Class.forName(CK_ATTRIBUTE_CLASS_NAME);
178+
var array = Array.newInstance(attrClass, 1);
179+
var attribute = attrClass.getConstructor(long.class).newInstance(attributeType);
180+
Array.set(array, 0, attribute);
181+
return array;
182+
}
183+
184+
private static long getPkcs11Constant(String fieldName) throws Exception {
185+
return Class.forName(PKCS11_CONSTANTS_CLASS_NAME).getField(fieldName).getLong(null);
186+
}
187+
188+
private static void invokeCLogin(Object p11, long sessionId, long userType, char[] pin) throws Exception {
189+
invokeMethod(p11, "C_Login", new Class<?>[]{long.class, long.class, char[].class}, sessionId, userType, pin);
190+
}
191+
192+
private static void invokeCGetAttributeValue(Object p11, long sessionId, long objectHandle, Object attrs) throws Exception {
193+
invokeMethod(p11, "C_GetAttributeValue", new Class<?>[]{long.class, long.class, attrs.getClass()}, sessionId, objectHandle, attrs);
194+
}
195+
196+
private static long[] invokeCGetSlotList(Object p11, boolean tokenPresent) throws Exception {
197+
return (long[]) invokeMethod(p11, "C_GetSlotList", new Class<?>[]{boolean.class}, tokenPresent);
198+
}
199+
200+
private static Object invokeCGetTokenInfo(Object p11, long slotId) throws Exception {
201+
return invokeMethod(p11, "C_GetTokenInfo", new Class<?>[]{long.class}, slotId);
202+
}
203+
204+
private static Object invokeMethod(Object target, String methodName, Class<?>[] parameterTypes, Object... args) throws Exception {
205+
Method method = target.getClass().getMethod(methodName, parameterTypes);
206+
try {
207+
return method.invoke(target, args);
208+
} catch (InvocationTargetException e) {
209+
var cause = unwrapInvocationException(e);
210+
if (cause instanceof Exception exception) {
211+
throw exception;
212+
}
213+
throw new RuntimeException(cause);
214+
}
215+
}
216+
217+
private static Object getPublicField(Object target, String fieldName) {
218+
try {
219+
Field field = target.getClass().getField(fieldName);
220+
return field.get(target);
221+
} catch (ReflectiveOperationException e) {
222+
throw new RuntimeException(e);
223+
}
224+
}
225+
226+
private static Throwable unwrapInvocationException(Throwable throwable) {
227+
if (throwable instanceof InvocationTargetException invocationTargetException && invocationTargetException.getCause() != null) {
228+
return invocationTargetException.getCause();
229+
}
230+
return throwable;
231+
}
232+
233+
private static boolean isPkcs11Exception(Throwable throwable) {
234+
return throwable != null && PKCS11_EXCEPTION_CLASS_NAME.equals(throwable.getClass().getName());
235+
}
236+
168237
// mostly copy & paste just to call overridden private sign method
169238
@Override
170239
public SignatureValue sign(ToBeSigned toBeSigned, SignatureAlgorithm signatureAlgorithm, DSSPrivateKeyEntry keyEntry) throws DSSException {

src/main/scripts/package.sh

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ unset IFS
3232

3333
jvmOptions="-Dfile.encoding=UTF-8 \
3434
-Dprism.maxvram=2G \
35-
--add-exports javafx.graphics/com.sun.javafx.tk=ALL-UNNAMED \
3635
--add-exports jdk.crypto.cryptoki/sun.security.pkcs11.wrapper=ALL-UNNAMED \
3736
--add-opens java.base/java.security=ALL-UNNAMED \
3837
--add-opens jdk.crypto.cryptoki/sun.security.pkcs11=ALL-UNNAMED \
@@ -179,18 +178,18 @@ if [[ "${platform}" == "mac" ]]; then
179178
fi
180179

181180
if [[ "${properties_mac_sign}" == "1" ]]; then
182-
export JPACKAGE_MAC_SIGN="1"
183181
if [[ -z "${APPLE_DEVELOPER_IDENTITY}" ]] || [[ -z "${APPLE_KEYCHAIN_PATH}" ]]; then
184-
echo "Missing APPLE_DEVELOPER_IDENTITY or APPLE_KEYCHAIN_PATH env variable"
185-
exit 1
186-
fi
182+
echo "Warning: APPLE_DEVELOPER_IDENTITY or APPLE_KEYCHAIN_PATH not set, skipping code signing"
183+
else
184+
export JPACKAGE_MAC_SIGN="1"
187185

188-
mac_signingKeyUserName=$(echo ${APPLE_DEVELOPER_IDENTITY} | sed -ne 's/Developer ID Application\:[[:space:]]\(.*\)[[:space:]]([0-9A-Z]*)/\1/p')
189-
arguments+=(
190-
"--mac-sign"
191-
"--mac-signing-keychain" "${APPLE_KEYCHAIN_PATH}"
192-
"--mac-signing-key-user-name" "${mac_signingKeyUserName}"
193-
)
186+
mac_signingKeyUserName=$(echo ${APPLE_DEVELOPER_IDENTITY} | sed -ne 's/Developer ID Application\:[[:space:]]\(.*\)[[:space:]]([0-9A-Z]*)/\1/p')
187+
arguments+=(
188+
"--mac-sign"
189+
"--mac-signing-keychain" "${APPLE_KEYCHAIN_PATH}"
190+
"--mac-signing-key-user-name" "${mac_signingKeyUserName}"
191+
)
192+
fi
194193
fi
195194

196195
# cwd je ./src/main/scripts/resources

0 commit comments

Comments
 (0)