mirror of
https://github.com/Hizenberg469/secure_mailer.git
synced 2026-04-20 01:02:23 +03:00
Eml hash error resolved
This commit is contained in:
40
src/main/java/com/secure_mailer/backend/HashGenerator.java
Normal file
40
src/main/java/com/secure_mailer/backend/HashGenerator.java
Normal file
@@ -0,0 +1,40 @@
|
||||
package com.secure_mailer.backend;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
public class HashGenerator {
|
||||
|
||||
public static String generateHash(String input) {
|
||||
try {
|
||||
// Create an instance of the SHA-256 algorithm
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||
|
||||
// Generate the hash
|
||||
byte[] encodedHash = digest.digest(input.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
// Convert the byte array to a hexadecimal string
|
||||
return bytesToHex(encodedHash);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException("SHA-256 algorithm not found!", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Helper method to convert byte array to a hexadecimal string
|
||||
private static String bytesToHex(byte[] hash) {
|
||||
StringBuilder hexString = new StringBuilder();
|
||||
for (byte b : hash) {
|
||||
String hex = Integer.toHexString(0xff & b);
|
||||
if (hex.length() == 1) hexString.append('0');
|
||||
hexString.append(hex);
|
||||
}
|
||||
return hexString.toString();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
String input = "exampleInputData";
|
||||
String hash = generateHash(input);
|
||||
System.out.println("Hash: " + hash);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.secure_mailer.backend;
|
||||
|
||||
import javafx.fxml.FXML;
|
||||
//import javafx.scene.control.Label;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.input.Clipboard;
|
||||
import javafx.scene.input.ClipboardContent;
|
||||
|
||||
public class SecretKeyController {
|
||||
|
||||
// @FXML
|
||||
// private Label keyLabel;
|
||||
|
||||
@FXML
|
||||
private TextField keyTextField;
|
||||
|
||||
// // Method to set the secret key text in the label
|
||||
// public void setSecretKey(String secretKey) {
|
||||
// keyLabel.setText("Secret Key: " + secretKey);
|
||||
// }
|
||||
|
||||
// Method to set the secret key text in the TextField
|
||||
public void setSecretKey(String secretKey) {
|
||||
keyTextField.setText(secretKey);
|
||||
}
|
||||
|
||||
// Copy the secret key to the clipboard
|
||||
@FXML
|
||||
private void handleCopy() {
|
||||
Clipboard clipboard = Clipboard.getSystemClipboard();
|
||||
ClipboardContent content = new ClipboardContent();
|
||||
content.putString(keyTextField.getText());
|
||||
clipboard.setContent(content);
|
||||
}
|
||||
|
||||
// Close the window when the "Close" button is clicked
|
||||
@FXML
|
||||
private void handleClose() {
|
||||
Stage stage = (Stage) keyTextField.getScene().getWindow();
|
||||
stage.close();
|
||||
}
|
||||
}
|
||||
@@ -68,6 +68,9 @@ public class ValidatorClient {
|
||||
this.toEmailId = toEmailId;
|
||||
this.emlHash = emlHash;
|
||||
|
||||
System.out.println("Size of eml hash : "+this.emlHash.length());
|
||||
System.out.println("eml hash : "+this.emlHash);
|
||||
|
||||
PrintStream ps = null;
|
||||
|
||||
try {
|
||||
@@ -79,6 +82,10 @@ public class ValidatorClient {
|
||||
this.timeStamp = Long.toString(timeStep);
|
||||
this.authCode = generateTOTP(secretKey, timeStep);
|
||||
|
||||
// String hash = "";
|
||||
if( this.emlHash.length() > 100 )
|
||||
this.emlHash = (this.emlHash).substring(0, 100);
|
||||
|
||||
String[] data = new String[] {
|
||||
this.fromEmailId,
|
||||
this.toEmailId,
|
||||
@@ -133,6 +140,11 @@ public class ValidatorClient {
|
||||
ps=new PrintStream(skt.getOutputStream());
|
||||
br=new BufferedReader(new InputStreamReader(skt.getInputStream()));
|
||||
|
||||
if( this.emlHash.length() > 100 )
|
||||
this.emlHash = (this.emlHash).substring(0, 100);
|
||||
|
||||
System.out.println("Eml Hash : "+this.emlHash);
|
||||
|
||||
String[] data = new String[] {
|
||||
this.emlHash
|
||||
};
|
||||
|
||||
@@ -14,6 +14,7 @@ import javax.mail.Transport;
|
||||
import javax.mail.internet.MimeBodyPart;
|
||||
import javax.mail.internet.MimeMessage;
|
||||
import javax.mail.internet.MimeMultipart;
|
||||
import javax.mail.internet.InternetAddress;
|
||||
import com.secure_mailer.backend.EmailAccount;
|
||||
import javafx.concurrent.Service;
|
||||
import javafx.concurrent.Task;
|
||||
@@ -56,8 +57,9 @@ public class EmailSendingService extends Service<Void> {
|
||||
protected Void call() throws Exception {
|
||||
try {
|
||||
|
||||
System.out.println("From email id : "+emailAccount.getAddress());
|
||||
MimeMessage mimeMessage = new MimeMessage(emailAccount.getSession());
|
||||
mimeMessage.setFrom(emailAccount.getAddress());
|
||||
mimeMessage.setFrom(new InternetAddress(emailAccount.getAddress()+"@peachy.in.net"));
|
||||
mimeMessage.addRecipients(Message.RecipientType.TO, recipient);
|
||||
mimeMessage.setSubject(subject);
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import com.secure_mailer.backend.service.MessageRendererService;
|
||||
import com.secure_mailer.backend.BaseController;
|
||||
import com.secure_mailer.backend.DatabaseSetup;
|
||||
import com.secure_mailer.backend.EmailManager;
|
||||
import com.secure_mailer.backend.HashGenerator;
|
||||
import com.secure_mailer.backend.SecretCodeDAO;
|
||||
import com.secure_mailer.backend.ValidatorClient;
|
||||
import com.secure_mailer.backend.EmailAccount;
|
||||
@@ -35,10 +36,10 @@ import javafx.scene.web.HTMLEditor;
|
||||
import javafx.scene.web.WebView;
|
||||
import javafx.util.Callback;
|
||||
|
||||
import javax.activation.DataHandler;
|
||||
import javax.activation.DataSource;
|
||||
import javax.activation.FileDataSource;
|
||||
import javax.crypto.Cipher;
|
||||
//import javax.activation.DataHandler;
|
||||
//import javax.activation.DataSource;
|
||||
//import javax.activation.FileDataSource;
|
||||
//import javax.crypto.Cipher;
|
||||
import javax.crypto.KeyGenerator;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
@@ -52,9 +53,10 @@ import javafx.animation.PauseTransition;
|
||||
//import javafx.util.Duration;
|
||||
//import javafx.application.Platform;
|
||||
|
||||
import javax.mail.internet.MimeBodyPart;
|
||||
import javax.mail.internet.MimeMessage;
|
||||
import javax.mail.internet.MimeMultipart;
|
||||
//import javax.mail.internet.InternetAddress;
|
||||
//import javax.mail.internet.MimeBodyPart;
|
||||
//import javax.mail.internet.MimeMessage;
|
||||
//import javax.mail.internet.MimeMultipart;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
@@ -74,6 +76,12 @@ import javafx.scene.control.Alert;
|
||||
import javafx.scene.control.Alert.AlertType;
|
||||
import javafx.util.Duration;
|
||||
|
||||
import com.secure_mailer.backend.SecretKeyController;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.stage.Modality;
|
||||
|
||||
//import com.secure_mailer.backend.HashGenerator;
|
||||
|
||||
//import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
|
||||
@@ -261,10 +269,29 @@ public class MainWindowController extends BaseController implements Initializabl
|
||||
if( emailMessage.getFromEmailID().equals(this.fromEmailID + "@peachy.in.net") == false) {
|
||||
String fromEmailId = emailMessage.getSender();
|
||||
String toEmailId = emailManager.getEmailAccount().getAddress();
|
||||
String emlHash = generateEmlHash(emailMessage.getMessage(), emailMessage.getSecretKey());
|
||||
String sKey = "";
|
||||
|
||||
ValidatorClient receiver = new ValidatorClient("peachy.in.net",2001);
|
||||
try {
|
||||
sKey = SecretCodeDAO.getSecretCode(fromEmailId);
|
||||
} catch (SQLException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
|
||||
String emlHash = generateEmlHash(emailMessage.getMessage(),StringToSecretKey.stringToSecretKey(sKey));
|
||||
// String emlHash = Integer.toString(emailMessage.getMessage().hashCode());
|
||||
// try {
|
||||
// emlHash = HashGenerator.generateHash(emailMessage.getMessage().getContent().toString());
|
||||
// } catch (IOException | MessagingException e) {
|
||||
// // TODO Auto-generated catch block
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
|
||||
ValidatorClient receiver = new ValidatorClient("peachy.in.net",9000);
|
||||
isAuthenticated = receiver.bringCode(fromEmailId, toEmailId, emlHash);
|
||||
|
||||
receiver.closeValidatorClient();
|
||||
}
|
||||
else {
|
||||
isAuthenticated = true;
|
||||
@@ -349,6 +376,7 @@ public class MainWindowController extends BaseController implements Initializabl
|
||||
String toId = composeTo.getText();
|
||||
String secretKey = "";
|
||||
|
||||
|
||||
try {
|
||||
secretKey = SecretCodeDAO.getSecretCode(toId);
|
||||
} catch (SQLException e) {
|
||||
@@ -378,6 +406,7 @@ public class MainWindowController extends BaseController implements Initializabl
|
||||
attachments,
|
||||
secretKey);
|
||||
|
||||
|
||||
EmailSendingService emailSenderService = new EmailSendingService(
|
||||
emailManager.getEmailAccount(),
|
||||
composeTitle.getText(),
|
||||
@@ -401,6 +430,8 @@ public class MainWindowController extends BaseController implements Initializabl
|
||||
e1.printStackTrace();
|
||||
}
|
||||
|
||||
|
||||
sender.closeValidatorClient();
|
||||
});
|
||||
|
||||
|
||||
@@ -423,6 +454,55 @@ public class MainWindowController extends BaseController implements Initializabl
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
void generateKey() {
|
||||
KeyGenerator keyGen;
|
||||
String sKey = "";
|
||||
try {
|
||||
keyGen = KeyGenerator.getInstance("AES");
|
||||
|
||||
keyGen.init(128); // You can also use 192 or 256-bit key size
|
||||
SecretKey secretKey = keyGen.generateKey();
|
||||
sKey = SecretKeyToString.secretKeyToString(secretKey);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
try {
|
||||
// Load the FXML file
|
||||
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/SecretKeyWindow.fxml"));
|
||||
Parent root = loader.load();
|
||||
|
||||
// Get the controller and set the secret key
|
||||
SecretKeyController controller = loader.getController();
|
||||
controller.setSecretKey(sKey);
|
||||
|
||||
// Create a new stage for the popup window
|
||||
Stage stage = new Stage();
|
||||
stage.initModality(Modality.APPLICATION_MODAL); // Block other windows until this is closed
|
||||
stage.setTitle("Secret Key");
|
||||
stage.setScene(new Scene(root));
|
||||
stage.setResizable(false); // Optional: Make window non-resizable
|
||||
stage.showAndWait();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// private void showAlertAndWait(String title, String content) {
|
||||
// Alert alert = new Alert(AlertType.INFORMATION);
|
||||
// alert.setTitle(title);
|
||||
// alert.setHeaderText(null);
|
||||
// alert.setContentText(content);
|
||||
// alert.showAndWait();
|
||||
//
|
||||
//// // Create a PauseTransition for 10 seconds
|
||||
//// PauseTransition delay = new PauseTransition(Duration.seconds(10));
|
||||
//// delay.setOnFinished(event -> alert.close()); // Close alert after 10 seconds
|
||||
//// delay.play(); // Start the timer
|
||||
// }
|
||||
|
||||
@FXML
|
||||
private void openCRUDWindow() {
|
||||
try {
|
||||
@@ -478,8 +558,21 @@ public class MainWindowController extends BaseController implements Initializabl
|
||||
private String generateEmlHash(Message message, SecretKey key) {
|
||||
try {
|
||||
|
||||
String msg = MessageToString(message);
|
||||
String encryptMessage = encryptMessage(msg, key);
|
||||
String msg = "";
|
||||
String contentType = message.getContentType();
|
||||
if (isMultipartType(contentType)) {
|
||||
Multipart multipart = (Multipart) message.getContent();
|
||||
msg = loadMultipart(multipart);
|
||||
}
|
||||
else if(isSimpleType(contentType)){
|
||||
|
||||
msg = message.getContent().toString();
|
||||
|
||||
}
|
||||
|
||||
System.out.println("Message received : "+message.toString());
|
||||
// String encryptMessage = encryptMessage(msg, key);
|
||||
String encryptMessage = HashGenerator.generateHash(msg);
|
||||
|
||||
return encryptMessage;
|
||||
} catch (Exception e) {
|
||||
@@ -496,31 +589,36 @@ public class MainWindowController extends BaseController implements Initializabl
|
||||
List<File> attachments,
|
||||
String secretKey){
|
||||
|
||||
String message;
|
||||
// Message message = null;
|
||||
try {
|
||||
|
||||
MimeMessage mimeMessage = new MimeMessage(emailAccount.getSession());
|
||||
mimeMessage.setFrom(emailAccount.getAddress());
|
||||
mimeMessage.addRecipients(Message.RecipientType.TO, recipient);
|
||||
mimeMessage.setSubject(subject);
|
||||
// MimeMessage mimeMessage = new MimeMessage(emailAccount.getSession());
|
||||
// mimeMessage.setFrom(new InternetAddress(emailAccount.getAddress()+"@peachy.in.net"));
|
||||
// mimeMessage.addRecipients(Message.RecipientType.TO, recipient);
|
||||
// mimeMessage.setSubject(subject);
|
||||
//
|
||||
// Multipart multipart = new MimeMultipart();
|
||||
// BodyPart messageBodyPart = new MimeBodyPart();
|
||||
// messageBodyPart.setContent(content, "text/html");
|
||||
// multipart.addBodyPart(messageBodyPart);
|
||||
// mimeMessage.setContent(multipart);
|
||||
//
|
||||
// if(attachments.size()>0) {
|
||||
// for (File file: attachments) {
|
||||
// MimeBodyPart mimeBodyPart = new MimeBodyPart();
|
||||
// DataSource source = new FileDataSource(file.getAbsolutePath());
|
||||
// mimeBodyPart.setDataHandler(new DataHandler(source));
|
||||
// mimeBodyPart.setFileName(file.getName());
|
||||
// multipart.addBodyPart(mimeBodyPart);
|
||||
// }
|
||||
// }
|
||||
// message = (MimeMessage)mimeMessage;
|
||||
|
||||
// String encryptMessage = encryptMessage(message.toString(), StringToSecretKey.stringToSecretKey(secretKey));
|
||||
String encryptMessage = HashGenerator.generateHash(content);
|
||||
// String encryptMessage = Integer.toString(message.hashCode());
|
||||
|
||||
Multipart multipart = new MimeMultipart();
|
||||
BodyPart messageBodyPart = new MimeBodyPart();
|
||||
messageBodyPart.setContent(content, "text/html");
|
||||
multipart.addBodyPart(messageBodyPart);
|
||||
mimeMessage.setContent(multipart);
|
||||
|
||||
if(attachments.size()>0) {
|
||||
for (File file: attachments) {
|
||||
MimeBodyPart mimeBodyPart = new MimeBodyPart();
|
||||
DataSource source = new FileDataSource(file.getAbsolutePath());
|
||||
mimeBodyPart.setDataHandler(new DataHandler(source));
|
||||
mimeBodyPart.setFileName(file.getName());
|
||||
multipart.addBodyPart(mimeBodyPart);
|
||||
}
|
||||
}
|
||||
message = MessageToString(mimeMessage);
|
||||
String encryptMessage = encryptMessage(message,StringToSecretKey.stringToSecretKey(secretKey));
|
||||
return encryptMessage;
|
||||
} catch (Exception e) {
|
||||
// TODO Auto-generated catch block
|
||||
@@ -558,29 +656,29 @@ public class MainWindowController extends BaseController implements Initializabl
|
||||
//
|
||||
// }
|
||||
|
||||
private String encryptMessage(String message, SecretKey secretKey) throws Exception{
|
||||
// private String encryptMessage(String message, SecretKey secretKey) throws Exception{
|
||||
// Generate AES key
|
||||
// KeyGenerator keyGen = KeyGenerator.getInstance("AES");
|
||||
// keyGen.init(128); // You can also use 192 or 256-bit key size
|
||||
// SecretKey secretKey = keyGen.generateKey();
|
||||
|
||||
// Initialize Cipher for encryption
|
||||
Cipher cipher = Cipher.getInstance("AES");
|
||||
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
|
||||
|
||||
byte[] encryptedBytes = cipher.doFinal(message.getBytes());
|
||||
|
||||
// Convert encrypted bytes to a readable format
|
||||
String encryptedText = Base64.getEncoder().encodeToString(encryptedBytes);
|
||||
|
||||
return encryptedText;
|
||||
// Cipher cipher = Cipher.getInstance("AES");
|
||||
// cipher.init(Cipher.ENCRYPT_MODE, secretKey);
|
||||
//
|
||||
// byte[] encryptedBytes = cipher.doFinal(message.getBytes());
|
||||
//
|
||||
// // Convert encrypted bytes to a readable format
|
||||
// String encryptedText = Base64.getEncoder().encodeToString(encryptedBytes);
|
||||
//
|
||||
// return encryptedText;
|
||||
// Initialize Cipher for decryption
|
||||
// cipher.init(Cipher.DECRYPT_MODE, secretKey);
|
||||
// byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedText));
|
||||
// String decryptedText = new String(decryptedBytes);
|
||||
// System.out.println("Decrypted Text: " + decryptedText);
|
||||
|
||||
}
|
||||
// }
|
||||
|
||||
class StringToSecretKey {
|
||||
public static SecretKey stringToSecretKey(String encodedKey) {
|
||||
@@ -596,4 +694,44 @@ public class MainWindowController extends BaseController implements Initializabl
|
||||
return Base64.getEncoder().encodeToString(encodedKey);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private boolean isMultipartType(String contentType) {
|
||||
if (contentType.contains("multipart"))return true;
|
||||
else return false;
|
||||
}
|
||||
|
||||
private boolean isTextPlain(String contentType) {
|
||||
return contentType.contains("TEXT/PLAIN");
|
||||
}
|
||||
|
||||
private boolean isSimpleType(String contentType) {
|
||||
if(contentType.contains("TEXT/HTML") ||
|
||||
contentType.contains("mixed") ||
|
||||
contentType.contains("text")) {
|
||||
return true;
|
||||
} else return false;
|
||||
}
|
||||
|
||||
private String loadMultipart(Multipart multipart) throws MessagingException, IOException {
|
||||
|
||||
String msg = "";
|
||||
for (int i = 0; i < multipart.getCount() ; i++) {
|
||||
BodyPart bodyPart = multipart.getBodyPart(i);
|
||||
String contentType = bodyPart.getContentType();
|
||||
if (isSimpleType(contentType)) {
|
||||
|
||||
msg = bodyPart.getContent().toString();
|
||||
|
||||
} else if (isMultipartType(contentType)){
|
||||
Multipart multipart2 = (Multipart) bodyPart.getContent();
|
||||
msg = loadMultipart(multipart2);
|
||||
} else if (!isTextPlain(contentType)) {
|
||||
// MimeBodyPart mbp = (MimeBodyPart) bodyPart;
|
||||
// if (!emailMessage.isAttachmentLoaded())emailMessage.addAttachment(mbp);
|
||||
}
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
14
src/main/resources/fxml/SecretKeyWindow.fxml
Normal file
14
src/main/resources/fxml/SecretKeyWindow.fxml
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<?import javafx.scene.control.TextField?>
|
||||
|
||||
<VBox xmlns:fx="http://javafx.com/fxml" fx:controller="com.secure_mailer.backend.SecretKeyController" alignment="CENTER" spacing="10">
|
||||
<Label fx:id="keyLabel" text="Secret Key:" />
|
||||
|
||||
<TextField fx:id="keyTextField" editable="false" />
|
||||
<Button text="Copy" onAction="#handleCopy" />
|
||||
<Button text="Close" onAction="#handleClose" />
|
||||
</VBox>
|
||||
@@ -53,7 +53,7 @@
|
||||
<String fx:value="font-grey" />
|
||||
</styleClass>
|
||||
</Label>
|
||||
<Button fx:id="decrypt" layoutX="230.0" layoutY="10.0" mnemonicParsing="false" prefHeight="26.0" prefWidth="78.0" text="Decrypt" onAction="#decryptEmail"/>
|
||||
<Button fx:id="decrypt" layoutX="230.0" layoutY="10.0" mnemonicParsing="false" onAction="#decryptEmail" prefHeight="26.0" prefWidth="78.0" text="Decrypt" />
|
||||
<Button id="key-trash" layoutX="-183.0" layoutY="-591.0" mnemonicParsing="false" onAction="#trashKeyAction" prefHeight="25.0" prefWidth="25.0" stylesheets="@../css/themeDefault.css" AnchorPane.rightAnchor="-603.0" AnchorPane.topAnchor="-591.0" />
|
||||
<Button id="key-star" layoutX="-209.0" layoutY="-591.0" mnemonicParsing="false" onAction="#starKeyAction" prefHeight="25.0" prefWidth="25.0" stylesheets="@../css/themeDefault.css" AnchorPane.rightAnchor="-577.0" AnchorPane.topAnchor="-591.0" />
|
||||
<Line endX="336.5999755859375" layoutX="126.0" layoutY="79.0" startX="-99.4000244140625" strokeLineCap="ROUND" strokeWidth="2.0" styleClass="line" AnchorPane.leftAnchor="26.0" AnchorPane.rightAnchor="44.0" AnchorPane.topAnchor="78.0" />
|
||||
@@ -138,5 +138,6 @@
|
||||
</children>
|
||||
</Pane>
|
||||
<Button layoutX="62.0" layoutY="512.0" mnemonicParsing="false" onAction="#openCRUDWindow" opacity="0.72" text="Enter Key" textFill="#373d04" />
|
||||
<Button fx:id="generateKey" layoutX="51.0" layoutY="551.0" mnemonicParsing="false" onAction="#generateKey" opacity="0.69" text="Generate Key" />
|
||||
</children>
|
||||
</AnchorPane>
|
||||
|
||||
Reference in New Issue
Block a user