package mara.mybox.controller; import java.io.File; import java.nio.file.Files; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.security.KeyStore; import java.security.cert.Certificate; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.fxml.FXML; import javafx.scene.control.Button; import javafx.scene.control.CheckBox; import javafx.scene.control.SelectionMode; import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; import javafx.scene.control.TextArea; import javafx.scene.control.TextField; import javafx.scene.control.cell.PropertyValueFactory; import javafx.stage.Modality; import mara.mybox.data.CertificateEntry; import mara.mybox.data.VisitHistory; import mara.mybox.fxml.FxmlStage; import mara.mybox.fxml.TableDateCell; import mara.mybox.tools.DateTools; import mara.mybox.tools.FileTools; import mara.mybox.tools.NetworkTools; import mara.mybox.tools.SystemTools; import mara.mybox.value.AppVariables; import static mara.mybox.value.AppVariables.logger; import static mara.mybox.value.AppVariables.message; import mara.mybox.value.CommonFxValues; import mara.mybox.value.CommonValues; /** * @Author Mara * @CreateDate 2019-11-29 * @License Apache License Version 2.0 */ public class SecurityCertificatesController extends BaseController { protected ObservableList<CertificateEntry> tableData; protected File lastBackup; @FXML protected TextField passwordInput; @FXML protected TableView<CertificateEntry> tableView; @FXML protected TableColumn<CertificateEntry, String> aliasColumn, timeColumn; @FXML protected TextArea certArea; @FXML protected Button plusButton, htmlButton; @FXML protected CheckBox backupCheck; public SecurityCertificatesController() { baseTitle = AppVariables.message("SecurityCertificates"); SourceFileType = VisitHistory.FileType.Certificate; SourcePathType = VisitHistory.FileType.Certificate; TargetPathType = VisitHistory.FileType.Html; TargetFileType = VisitHistory.FileType.Html; sourcePathKey = "CertificateFilePath"; targetPathKey = "HtmlFilePath"; sourceExtensionFilter = CommonFxValues.KeyStoreExtensionFilter; targetExtensionFilter = CommonFxValues.HtmlExtensionFilter; } @Override public void initializeNext() { try { tableData = FXCollections.observableArrayList(); aliasColumn.setCellValueFactory(new PropertyValueFactory<>("alias")); timeColumn.setCellValueFactory(new PropertyValueFactory<>("createTime")); timeColumn.setCellFactory(new TableDateCell()); tableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); tableView.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() { @Override public void changed(ObservableValue ov, Object t, Object t1) { checkSelected(); } }); checkSelected(); tableView.setItems(tableData); sourceFileInput.setText(SystemTools.keystore()); passwordInput.setText(SystemTools.keystorePassword()); htmlButton.setDisable(true); plusButton.setDisable(true); } catch (Exception e) { logger.error(e.toString()); } } protected void checkSelected() { if (isSettingValues) { return; } CertificateEntry selected = tableView.getSelectionModel().getSelectedItem(); if (selected == null) { certArea.setText(""); deleteButton.setDisable(true); } else { certArea.setText(selected.getCertificates()); deleteButton.setDisable(false); } } @FXML public void readAction() { loadAll(null); } @FXML public void loadAll(String selectAlias) { lastBackup = null; tableView.getItems().clear(); certArea.setText(""); htmlButton.setDisable(true); plusButton.setDisable(true); if (sourceFileInput.getText().isEmpty() || passwordInput.getText().isEmpty()) { return; } try { synchronized (this) { if (task != null) { return; } task = new SingletonTask<Void>() { private String texts; private List<CertificateEntry> entires; private CertificateEntry selectCert; @Override protected boolean handle() { try { texts = error = null; entires = new ArrayList(); selectCert = null; // https://docs.oracle.com/javase/10/docs/api/java/security/KeyStore.html try { char[] passphrase = passwordInput.getText().toCharArray(); File keyStoreFile = new File(sourceFileInput.getText()); KeyStore keyStore = KeyStore.getInstance(keyStoreFile, passphrase); Enumeration<String> storeAliases = keyStore.aliases(); while (storeAliases.hasMoreElements()) { String alias = storeAliases.nextElement(); if (!keyStore.isCertificateEntry(alias)) { continue; } try { Certificate[] chain = keyStore.getCertificateChain(alias); if (chain == null) { Certificate cert = keyStore.getCertificate(alias); if (cert != null) { chain = new Certificate[1]; chain[0] = cert; } } CertificateEntry entry = CertificateEntry.create() .setAlias(alias) .setCreateTime(keyStore.getCreationDate(alias).getTime()) .setCertificateChain(chain); entires.add(entry); if (selectAlias != null && alias.equals(selectAlias)) { selectCert = entry; } } catch (Exception e) { error = e.toString(); } } if (selectCert == null) { StringBuilder s = new StringBuilder(); s.append("## ").append(message("Type")).append(": ").append(keyStore.getType()).append(" "). append(message("Size")).append(": ").append(keyStore.size()). append("\n\n"); for (CertificateEntry entry : entires) { s.append("#### ").append(message("Alias")).append(": ").append(entry.getAlias()).append("\n"); s.append("----------------------------\n"); if (entry.getCertificateChain() != null) { for (Certificate cert : entry.getCertificateChain()) { s.append(cert).append("\n\n"); } } } texts = s.toString(); } } catch (Exception e) { error = e.toString(); } } catch (Exception e) { error = e.toString(); } return true; } @Override protected void whenSucceeded() { if (!entires.isEmpty()) { isSettingValues = true; tableView.getItems().addAll(entires); // https://stackoverflow.com/questions/36240142/sort-tableview-by-certain-column-javafx?r=SearchResults tableView.getSortOrder().add(timeColumn); timeColumn.setSortType(TableColumn.SortType.DESCENDING); tableView.sort(); isSettingValues = false; if (selectCert != null) { tableView.scrollTo(selectCert); tableView.getSelectionModel().select(selectCert); } else { certArea.setText(texts); } htmlButton.setDisable(false); plusButton.setDisable(false); bottomLabel.setText(message("Total") + ": " + tableData.size()); } else { popError(error); } } }; openHandlingStage(task, Modality.WINDOW_MODAL); Thread thread = new Thread(task); thread.setDaemon(true); thread.start(); } } catch (Exception e) { logger.error(e.toString()); } } @FXML public void htmlAction() { if (sourceFileInput.getText().isEmpty() || passwordInput.getText().isEmpty()) { return; } try { synchronized (this) { if (task != null) { return; } task = new SingletonTask<Void>() { private String result; @Override protected boolean handle() { try { result = error = null; try { File keyStoreFile = new File(sourceFileInput.getText()); char[] passphrase = passwordInput.getText().toCharArray(); KeyStore keyStore = KeyStore.getInstance(keyStoreFile, passphrase); StringBuilder s = new StringBuilder(); s.append("<h1 class=\"center\">").append(keyStoreFile.getAbsolutePath()).append("</h1>\n"); s.append("<h2 class=\"center\">"). append(message("Type")).append(": ").append(keyStore.getType()).append(" "). append(message("Size")).append(": ").append(keyStore.size()). append("</h2>\n"); s.append("<hr>\n"); Enumeration<String> aliases = keyStore.aliases(); while (aliases.hasMoreElements()) { String alias = aliases.nextElement(); s.append("<h3 class=\"center\">"). append(message("Alias")).append(": ").append(alias). append("</h3>\n"); Certificate[] chain = keyStore.getCertificateChain(alias); if (chain != null) { for (Certificate cert : chain) { s.append("<pre>").append(cert).append("</pre>\n\n"); } } else { Certificate cert = keyStore.getCertificate(alias); if (cert != null) { s.append("<pre>").append(cert).append("</pre>\n"); } } } result = s.toString(); } catch (Exception e) { error = e.toString(); } } catch (Exception e) { error = e.toString(); } return true; } @Override protected void whenSucceeded() { if (result != null) { FxmlStage.openHtmlViewer(null, result); } else { popError(error); } } }; openHandlingStage(task, Modality.WINDOW_MODAL); Thread thread = new Thread(task); thread.setDaemon(true); thread.start(); } } catch (Exception e) { logger.error(e.toString()); } } @FXML public void plusAction() { try { SecurityCertificatesAddController controller = (SecurityCertificatesAddController) openStage(CommonValues.SecurityCertificateAddFxml); controller.setCertController(this); } catch (Exception e) { logger.error(e.toString()); } } public boolean backupKeyStore() { if (!backupCheck.isSelected()) { return true; } sourceFile = new File(sourceFileInput.getText()); if (!sourceFile.exists() || !sourceFile.isFile()) { popError(message("NotExist")); return false; } try { File newKsFile = new File(sourceFile.getParentFile().getAbsolutePath() + File.separator + FileTools.appendName(sourceFile.getName(), DateTools.nowString4())); Files.copy(Paths.get(sourceFile.getAbsolutePath()), Paths.get(newKsFile.getAbsolutePath()), StandardCopyOption.COPY_ATTRIBUTES); lastBackup = newKsFile; bottomLabel.setText(message("Total") + ": " + tableData.size() + " " + message("KeyStoreBacked") + ": " + lastBackup ); return true; } catch (Exception e) { popError(e.toString()); return false; } } @FXML @Override public void deleteAction() { List<CertificateEntry> selected = tableView.getSelectionModel().getSelectedItems(); if (selected == null || selected.isEmpty() || !backupKeyStore()) { return; } List<String> aliases = new ArrayList(); for (CertificateEntry cert : selected) { aliases.add(cert.getAlias()); } try { synchronized (this) { if (task != null) { return; } task = new SingletonTask<Void>() { @Override protected boolean handle() { error = null; try { error = NetworkTools.uninstallCertificate( sourceFileInput.getText(), passwordInput.getText(), aliases); } catch (Exception e) { error = e.toString(); } return true; } @Override protected void whenSucceeded() { if (error == null) { readAction(); popSuccessful(); } else { popError(error); } } }; openHandlingStage(task, Modality.WINDOW_MODAL); Thread thread = new Thread(task); thread.setDaemon(true); thread.start(); } } catch (Exception e) { logger.error(e.toString()); } } /* get/set */ public TextField getPasswordInput() { return passwordInput; } public void setPasswordInput(TextField passwordInput) { this.passwordInput = passwordInput; } }