Skip to content

Commit

Permalink
Merge pull request #320 from solver-it-sro/add-configurable-keystore
Browse files Browse the repository at this point in the history
Add configurable keystore
  • Loading branch information
celuchmarek authored Nov 13, 2023
2 parents 17b1136 + 36d73ce commit 338089b
Show file tree
Hide file tree
Showing 13 changed files with 185 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ public class AppStarter {
addOption("f", "force", false, "Overwrite existing file(s).").
addOption(null, "pdfa", false, "Check PDF/A compliance before signing.").
addOption(null, "parents", false, "Create all parent directories for target if needed.").
addOption("d", "driver", true, "PCKS driver name for signing. Supported values: eid, secure_store, monet, gemalto.").
addOption("d", "driver", true, "PCKS driver name for signing. Supported values: eid, secure_store, monet, gemalto, keystore.").
addOption(null, "keystore", true, "Absolute path to a keystore file that can be used for signing.").
addOption(null, "slot-id", true, "Slot ID for PKCS11 driver. If not specified, first available slot is used.").
addOption(null, "pdf-level", true, "PDF signature level. Supported values: PAdES_BASELINE_B (default), XAdES_BASELINE_B, CAdES_BASELINE_B.").
addOption(null, "en319132", false, "Sign according to EN 319 132 or EN 319 122.");
Expand Down
8 changes: 0 additions & 8 deletions src/main/java/digital/slovensko/autogram/core/Autogram.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,10 @@ public class Autogram {
private final boolean shouldDisplayVisualizationError;
private final Integer slotId;

public Autogram(UI ui, boolean shouldDisplayVisualizationError) {
this(ui, shouldDisplayVisualizationError, new DefaultDriverDetector(), -1);
}

public Autogram(UI ui, boolean shouldDisplayVisualizationError , DriverDetector driverDetector) {
this(ui, shouldDisplayVisualizationError, driverDetector, -1);
}

public Autogram(UI ui, boolean shouldDisplayVisualizationError , Integer slotId) {
this(ui, shouldDisplayVisualizationError, new DefaultDriverDetector(), slotId);
}

public Autogram(UI ui, boolean shouldDisplayVisualizationError , DriverDetector driverDetector, Integer slotId) {
this.ui = ui;
this.driverDetector = driverDetector;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public CliParameters(CommandLine cmd) throws SourceDoesNotExistException, TokenD
SlotIdIsNotANumberException, PDFSignatureLevelIsNotValidException {
source = getValidSource(cmd.getOptionValue("s"));
target = cmd.getOptionValue("t");
driver = getValidTokenDriver(cmd.getOptionValue("d"));
driver = getValidTokenDriver(cmd.getOptionValue("d"), cmd.getOptionValue("keystore", ""));
slotId = getValidSlotId(cmd.getOptionValue("slot-id"));
force = cmd.hasOption("f");
checkPDFACompliance = cmd.hasOption("pdfa");
Expand Down Expand Up @@ -91,11 +91,11 @@ private static File getValidSource(String sourcePath) throws SourceDoesNotExistE
return sourcePath == null ? null : new File(sourcePath);
}

private static TokenDriver getValidTokenDriver(String driverName) throws TokenDriverDoesNotExistException {
private static TokenDriver getValidTokenDriver(String driverName, String customKeystorePath) throws TokenDriverDoesNotExistException {
if (driverName == null)
return null;

Optional<TokenDriver> tokenDriver = new DefaultDriverDetector()
Optional<TokenDriver> tokenDriver = new DefaultDriverDetector(customKeystorePath, true)
.getAvailableDrivers()
.stream()
.filter(d -> d.getShortname().equals(driverName))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package digital.slovensko.autogram.core;

import digital.slovensko.autogram.drivers.FakeTokenDriver;
import digital.slovensko.autogram.drivers.PKCS11TokenDriver;
import digital.slovensko.autogram.drivers.TokenDriver;
import digital.slovensko.autogram.drivers.FakeTokenDriver;
import digital.slovensko.autogram.drivers.PKCS12KeystoreTokenDriver;
import digital.slovensko.autogram.util.OperatingSystem;

import java.nio.file.Path;
Expand All @@ -15,32 +16,50 @@ public static class TokenDriverShortnames {
public static final String MONET = "monet";
public static final String GEMALTO = "gemalto";
public static final String FAKE = "fake";
public static final String KEYSTORE = "keystore";
}

private final String customKeystorePath;
private final boolean customKeystorePasswordPrompt;

public DefaultDriverDetector(String customKeystorePath, boolean customKeystorePasswordPrompt) {
this.customKeystorePath = customKeystorePath;
this.customKeystorePasswordPrompt = customKeystorePasswordPrompt;
}

public static final List<TokenDriver> LINUX_DRIVERS = List.of(
new PKCS11TokenDriver("Občiansky preukaz (eID klient)", Path.of("/usr/lib/eID_klient/libpkcs11_x64.so"), false, TokenDriverShortnames.EID),
new PKCS11TokenDriver("Občiansky preukaz (starý eID klient)", Path.of("/usr/lib/eac_mw_klient/libpkcs11_x64.so"), false, TokenDriverShortnames.EID),
new PKCS11TokenDriver("I.CA SecureStore", Path.of("/usr/lib/pkcs11/libICASecureStorePkcs11.so"), true, TokenDriverShortnames.SECURE_STORE),
new PKCS11TokenDriver("MONET+ ProID+Q", Path.of("/usr/lib/x86_64-linux-gnu/libproidqcm11.so"), true, TokenDriverShortnames.MONET),
new PKCS11TokenDriver("Gemalto IDPrime 940", Path.of("/usr/lib/libIDPrimePKCS11.so"), true, TokenDriverShortnames.GEMALTO),
new FakeTokenDriver("Fake token driver", Path.of("fakeTokenDriver"), false, TokenDriverShortnames.FAKE)
);

public static final List<TokenDriver> WINDOWS_DRIVERS = List.of(
new PKCS11TokenDriver("Občiansky preukaz (eID klient)", Path.of("C:\\Program Files (x86)\\eID_klient\\pkcs11_x64.dll"), false, TokenDriverShortnames.EID),
new PKCS11TokenDriver("I.CA SecureStore", Path.of("C:\\Windows\\System32\\SecureStorePkcs11.dll"), true, TokenDriverShortnames.SECURE_STORE),
new PKCS11TokenDriver("MONET+ ProID+Q", Path.of( "C:\\Windows\\system32\\proidqcm11.dll"), true, TokenDriverShortnames.MONET),
new PKCS11TokenDriver("Gemalto IDPrime 940", Path.of("C:\\Windows\\System32\\eTPKCS11.dll"), true, TokenDriverShortnames.GEMALTO),
new FakeTokenDriver("Fake token driver", Path.of("fakeTokenDriver"), false, TokenDriverShortnames.FAKE)
);

public static final List<TokenDriver> MAC_DRIVERS = List.of(
new PKCS11TokenDriver("Občiansky preukaz (eID klient)", Path.of("/Applications/eID_klient.app/Contents/Frameworks/libPkcs11.dylib"), false, TokenDriverShortnames.EID),
new PKCS11TokenDriver("I.CA SecureStore", Path.of("/usr/local/lib/pkcs11/libICASecureStorePkcs11.dylib"), true, TokenDriverShortnames.SECURE_STORE),
new PKCS11TokenDriver("MONET+ ProID+Q", Path.of("/usr/local/lib/ProIDPlus/libproidqcm11.dylib"), true, TokenDriverShortnames.MONET),
new PKCS11TokenDriver("Gemalto IDPrime 940", Path.of("/usr/local/lib/libIDPrimePKCS11.dylib"), true, TokenDriverShortnames.GEMALTO),
new FakeTokenDriver("Fake token driver", Path.of("fakeTokenDriver"), false, TokenDriverShortnames.FAKE)
);
private final List<TokenDriver> getLinuxDrivers(){
return List.of(
new PKCS11TokenDriver("Občiansky preukaz (eID klient)", Path.of("/usr/lib/eID_klient/libpkcs11_x64.so"), false, TokenDriverShortnames.EID),
new PKCS11TokenDriver("Občiansky preukaz (starý eID klient)", Path.of("/usr/lib/eac_mw_klient/libpkcs11_x64.so"), false, TokenDriverShortnames.EID),
new PKCS11TokenDriver("I.CA SecureStore", Path.of("/usr/lib/pkcs11/libICASecureStorePkcs11.so"), true, TokenDriverShortnames.SECURE_STORE),
new PKCS11TokenDriver("MONET+ ProID+Q", Path.of("/usr/lib/x86_64-linux-gnu/libproidqcm11.so"), true, TokenDriverShortnames.MONET),
new PKCS11TokenDriver("Gemalto IDPrime 940", Path.of("/usr/lib/libIDPrimePKCS11.so"), true, TokenDriverShortnames.GEMALTO),
new PKCS12KeystoreTokenDriver("Zo súboru", Path.of(customKeystorePath), customKeystorePasswordPrompt, TokenDriverShortnames.KEYSTORE),
new FakeTokenDriver("Fake token driver", Path.of("fakeTokenDriver"), false, TokenDriverShortnames.FAKE)
);
}

private final List<TokenDriver> getWindowsDrivers() {
return List.of(
new PKCS11TokenDriver("Občiansky preukaz (eID klient)", Path.of("C:\\Program Files (x86)\\eID_klient\\pkcs11_x64.dll"), false, TokenDriverShortnames.EID),
new PKCS11TokenDriver("I.CA SecureStore", Path.of("C:\\Windows\\System32\\SecureStorePkcs11.dll"), true, TokenDriverShortnames.SECURE_STORE),
new PKCS11TokenDriver("MONET+ ProID+Q", Path.of( "C:\\Windows\\system32\\proidqcm11.dll"), true, TokenDriverShortnames.MONET),
new PKCS11TokenDriver("Gemalto IDPrime 940", Path.of("C:\\Windows\\System32\\eTPKCS11.dll"), true, TokenDriverShortnames.GEMALTO),
new PKCS12KeystoreTokenDriver("Zo súboru", Path.of(customKeystorePath), customKeystorePasswordPrompt, TokenDriverShortnames.KEYSTORE),
new FakeTokenDriver("Fake token driver", Path.of("fakeTokenDriver"), false, TokenDriverShortnames.FAKE)
);
}

private final List<TokenDriver> getMacDrivers() {
return List.of(
new PKCS11TokenDriver("Občiansky preukaz (eID klient)", Path.of("/Applications/eID_klient.app/Contents/Frameworks/libPkcs11.dylib"), false, TokenDriverShortnames.EID),
new PKCS11TokenDriver("I.CA SecureStore", Path.of("/usr/local/lib/pkcs11/libICASecureStorePkcs11.dylib"), true, TokenDriverShortnames.SECURE_STORE),
new PKCS11TokenDriver("MONET+ ProID+Q", Path.of("/usr/local/lib/ProIDPlus/libproidqcm11.dylib"), true, TokenDriverShortnames.MONET),
new PKCS11TokenDriver("Gemalto IDPrime 940", Path.of("/usr/local/lib/libIDPrimePKCS11.dylib"), true, TokenDriverShortnames.GEMALTO),
new PKCS12KeystoreTokenDriver("Zo súboru", Path.of(customKeystorePath), customKeystorePasswordPrompt, TokenDriverShortnames.KEYSTORE),
new FakeTokenDriver("Fake token driver", Path.of("fakeTokenDriver"), false, TokenDriverShortnames.FAKE)
);
}

public List<TokenDriver> getAvailableDrivers() {
return getAllDrivers().stream().filter(TokenDriver::isInstalled).toList();
Expand All @@ -49,13 +68,13 @@ public List<TokenDriver> getAvailableDrivers() {
private List<TokenDriver> getAllDrivers() {
switch (OperatingSystem.current()) {
case WINDOWS -> {
return WINDOWS_DRIVERS;
return getWindowsDrivers();
}
case LINUX -> {
return LINUX_DRIVERS;
return getLinuxDrivers();
}
case MAC -> {
return MAC_DRIVERS;
return getMacDrivers();
}
default -> throw new IllegalStateException("Unexpected value: " + OperatingSystem.current());
}
Expand Down
33 changes: 31 additions & 2 deletions src/main/java/digital/slovensko/autogram/core/UserSettings.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,14 @@ public class UserSettings {
private boolean serverEnabled;
private boolean expiredCertsEnabled;
private List<String> trustedList;
private String customKeystorePath;
private boolean customKeystorePasswordPrompt;

private UserSettings(SignatureLevel signatureLevel, String driver, boolean en319132,
boolean signIndividually, boolean correctDocumentDisplay,
boolean signaturesValidity, boolean pdfaCompliance,
boolean serverEnabled, boolean expiredCertsEnabled, List<String> trustedList) {
boolean serverEnabled, boolean expiredCertsEnabled, List<String> trustedList,
String customKeystorePath, boolean customKeystorePassword) {
this.signatureLevel = signatureLevel;
this.driver = driver;
this.en319132 = en319132;
Expand All @@ -35,6 +38,8 @@ private UserSettings(SignatureLevel signatureLevel, String driver, boolean en319
this.serverEnabled = serverEnabled;
this.expiredCertsEnabled = expiredCertsEnabled;
this.trustedList = trustedList;
this.customKeystorePath = customKeystorePath;
this.customKeystorePasswordPrompt = customKeystorePassword;
}

public static UserSettings load() {
Expand All @@ -50,6 +55,8 @@ public static UserSettings load() {
var serverEnabled = prefs.getBoolean("SERVER_ENABLED", true);
var expiredCertsEnabled = prefs.getBoolean("EXPIRED_CERTS_ENABLED", false);
var trustedList = prefs.get("TRUSTED_LIST", "SK,CZ,AT,PL,HU");
var customKeystorePath = prefs.get("CUSTOM_KEYSTORE_PATH", "");
var customKeystorePasswordPrompt = prefs.getBoolean("CUSTOM_KEYSTORE_PASSWORD_PROMPT", false);

var signatureLevelStringConverter = new SignatureLevelStringConverter();
var signatureLevel = Arrays
Expand All @@ -69,7 +76,9 @@ public static UserSettings load() {
pdfaCompliance,
serverEnabled,
expiredCertsEnabled,
trustedList == null ? new ArrayList<>() : new ArrayList<>(List.of(trustedList.split(","))));
trustedList == null ? new ArrayList<>() : new ArrayList<>(List.of(trustedList.split(","))),
customKeystorePath,
customKeystorePasswordPrompt);
}

public SignatureLevel getSignatureLevel() {
Expand Down Expand Up @@ -171,6 +180,24 @@ public void removeFromTrustedList(String country) {
save();
}

public String getCustomKeystorePath() {
return customKeystorePath;
}

public void setCustomKeystorePath(String value) {
customKeystorePath = value;
save();
}

public boolean getCustomKeystorePasswordPrompt() {
return customKeystorePasswordPrompt;
}

public void setCustomKeystorePasswordPrompt(boolean value) {
customKeystorePasswordPrompt = value;
save();
}

private void save() {
var prefs = Preferences.userNodeForPackage(UserSettings.class);

Expand All @@ -184,5 +211,7 @@ private void save() {
prefs.putBoolean("SERVER_ENABLED", serverEnabled);
prefs.putBoolean("EXPIRED_CERTS_ENABLED", expiredCertsEnabled);
prefs.put("TRUSTED_LIST", trustedList.stream().collect(Collectors.joining(",")));
prefs.put("CUSTOM_KEYSTORE_PATH", customKeystorePath);
prefs.putBoolean("CUSTOM_KEYSTORE_PASSWORD_PROMPT", customKeystorePasswordPrompt);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package digital.slovensko.autogram.drivers;

import java.io.IOException;
import java.nio.file.Path;
import java.security.KeyStore;

import eu.europa.esig.dss.token.AbstractKeyStoreTokenConnection;
import eu.europa.esig.dss.token.Pkcs12SignatureToken;

public class PKCS12KeystoreTokenDriver extends TokenDriver {
public PKCS12KeystoreTokenDriver(String name, Path path, boolean needsPassword, String shortname) {
super(name, path, needsPassword, shortname);
}


@Override
public AbstractKeyStoreTokenConnection createTokenWithPassword(Integer slotId, char[] password) {
try {
return new Pkcs12SignatureToken(getPath().toString(), new KeyStore.PasswordProtection(password));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
7 changes: 5 additions & 2 deletions src/main/java/digital/slovensko/autogram/ui/cli/CliApp.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import digital.slovensko.autogram.core.Autogram;
import digital.slovensko.autogram.core.CliParameters;
import digital.slovensko.autogram.core.DefaultDriverDetector;
import digital.slovensko.autogram.core.SigningJob;
import digital.slovensko.autogram.core.errors.SourceNotDefindedException;
import digital.slovensko.autogram.core.TargetPath;
Expand All @@ -21,8 +22,10 @@ public static void start(CommandLine cmd) {

try {
var params = new CliParameters(cmd);
var autogram = params.getDriver() == null ? new Autogram(ui, false, params.getSlotId())
: new Autogram(ui, false, () -> Collections.singletonList(params.getDriver()), params.getSlotId());
var autogram = new Autogram(ui, false, params.getDriver() != null ?
() -> Collections.singletonList(params.getDriver())
: new DefaultDriverDetector("", false),
params.getSlotId());

if (params.getSource() == null)
throw new SourceNotDefindedException();
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/digital/slovensko/autogram/ui/gui/GUI.java
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ public void pickTokenDriverAndThen(List<TokenDriver> drivers, Consumer<TokenDriv
@Override
public void requestPasswordAndThen(TokenDriver driver, Consumer<char[]> callback) {
if (!driver.needsPassword()) {
callback.accept(null);
callback.accept("".toCharArray());
return;
}

Expand Down
4 changes: 3 additions & 1 deletion src/main/java/digital/slovensko/autogram/ui/gui/GUIApp.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.util.concurrent.ScheduledExecutorService;

import digital.slovensko.autogram.core.Autogram;
import digital.slovensko.autogram.core.DefaultDriverDetector;
import digital.slovensko.autogram.core.LaunchParameters;
import digital.slovensko.autogram.core.UserSettings;
import digital.slovensko.autogram.server.AutogramServer;
Expand All @@ -21,7 +22,8 @@ public class GUIApp extends Application {
public void start(Stage windowStage) throws Exception {
var userSettings = UserSettings.load();
var ui = new GUI(getHostServices(), userSettings);
var autogram = new Autogram(ui, userSettings.isCorrectDocumentDisplay());
var autogram = new Autogram(ui, userSettings.isCorrectDocumentDisplay(), new DefaultDriverDetector(
userSettings.getCustomKeystorePath(), userSettings.getCustomKeystorePasswordPrompt()));

Platform.setImplicitExit(false);
autogram.checkForUpdate();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ public class SettingsDialogController {
@FXML
private HBox localServerEnabledRadios;
@FXML
private TextField customKeystorePathTextField;
@FXML
private HBox customKeystoreRadios;
@FXML
private Button saveButton;
@FXML
private Button closeButton;
Expand All @@ -59,6 +63,7 @@ public void initialize() {
initializeExpiredCertsEnabledCheckBox();
initializeLocalServerEnabledCheckBox();
initializeTrustedCountriesList();
initializeCustomKeystoreSettings();
}

private void initializeSignatureLevelChoiceBox() {
Expand All @@ -75,9 +80,10 @@ private void initializeSignatureLevelChoiceBox() {
}

private void initializeDriverChoiceBox() {
driverChoiceBox.setConverter(new TokenDriverStringConverter());
var driverDetector = new DefaultDriverDetector(userSettings.getCustomKeystorePath(), userSettings.getCustomKeystorePasswordPrompt());
driverChoiceBox.setConverter(new TokenDriverStringConverter(driverDetector));
driverChoiceBox.getItems().add(new FakeTokenDriver("Žiadne", null, false, "none"));
driverChoiceBox.getItems().addAll(new DefaultDriverDetector().getAvailableDrivers());
driverChoiceBox.getItems().addAll(driverDetector.getAvailableDrivers());
var defaultDriver = driverChoiceBox.getItems().stream()
.filter(d -> d != null && d.getName().equals(userSettings.getDriver())).findFirst();
driverChoiceBox.setValue(defaultDriver.orElse(null));
Expand Down Expand Up @@ -195,6 +201,16 @@ private HBox createCountryElement(Country country, boolean isCountryInTrustedLis
return new HBox(countryBox, new VBox(checkBox));
}

private void initializeCustomKeystoreSettings() {
initializeBooleanRadios(customKeystoreRadios, t -> userSettings.setCustomKeystorePasswordPrompt(t),
userSettings.getCustomKeystorePasswordPrompt());

customKeystorePathTextField.setText(userSettings.getCustomKeystorePath());
customKeystorePathTextField.setOnKeyTyped((e) -> {
userSettings.setCustomKeystorePath(customKeystorePathTextField.getText());
});
}

public void onCancelButtonAction() {
var stage = (Stage) closeButton.getScene().getWindow();
stage.close();
Expand Down
Loading

0 comments on commit 338089b

Please sign in to comment.