add key serialization
This commit is contained in:
parent
be0ab15153
commit
b14e539464
5 changed files with 117 additions and 13 deletions
|
@ -2,8 +2,6 @@ package client.gui;
|
|||
|
||||
import java.security.KeyPair;
|
||||
import java.security.PublicKey;
|
||||
import java.util.Base64;
|
||||
|
||||
import client.gui.GuiConnect.ServerInfo;
|
||||
import client.gui.element.ActButton;
|
||||
import client.gui.element.ButtonCallback;
|
||||
|
@ -97,7 +95,7 @@ public class GuiServer extends Gui implements FieldCallback {
|
|||
this.keyButton = this.add(new ActButton(0, 120, 391, 24, new ButtonCallback() {
|
||||
public void use(ActButton elem, PressType action) {
|
||||
if(GuiServer.this.keypair == null) {
|
||||
GuiServer.this.keypair = EncryptUtil.generateKeyPair();
|
||||
GuiServer.this.keypair = EncryptUtil.createKeypair();
|
||||
GuiServer.this.keyDigest = EncryptUtil.getXorSha512Hash(GuiServer.this.keypair.getPublic().getEncoded());
|
||||
GuiServer.this.keyLabel.setText("Anmelde-Pubkey: RSA-2048 " + GuiServer.this.keyDigest);
|
||||
GuiServer.this.keyButton.setText("Schlüsselpaar entfernen");
|
||||
|
@ -152,7 +150,7 @@ public class GuiServer extends Gui implements FieldCallback {
|
|||
this.copyKeyButton = this.add(new ActButton(395, 120, 85, 24, new ButtonCallback() {
|
||||
public void use(ActButton elem, PressType action) {
|
||||
if(GuiServer.this.keypair != null)
|
||||
Window.setClipboard(Base64.getEncoder().encodeToString(GuiServer.this.keypair.getPublic().getEncoded()));
|
||||
Window.setClipboard(EncryptUtil.getArmoredPubkey(GuiServer.this.keypair.getPublic(), GuiServer.this.userBox.getText()));
|
||||
}
|
||||
}, "Kopieren"));
|
||||
this.copyKeyButton.enabled = this.keypair != null;
|
||||
|
@ -176,7 +174,7 @@ public class GuiServer extends Gui implements FieldCallback {
|
|||
this.copyIdButton = this.add(new ActButton(395, 300, 85, 24, new ButtonCallback() {
|
||||
public void use(ActButton elem, PressType action) {
|
||||
if(GuiServer.this.serverKey != null)
|
||||
Window.setClipboard(Base64.getEncoder().encodeToString(GuiServer.this.serverKey.getEncoded()));
|
||||
Window.setClipboard(EncryptUtil.getArmoredPubkey(GuiServer.this.serverKey, GuiServer.this.nameBox.getText()));
|
||||
}
|
||||
}, "Kopieren"));
|
||||
this.copyIdButton.enabled = this.serverKey != null;
|
||||
|
|
|
@ -67,7 +67,7 @@ public class ClientLoginHandler extends NetHandler implements IClientLoginHandle
|
|||
return;
|
||||
}
|
||||
this.connection.setConnectionState(PacketRegistry.LOGIN);
|
||||
final SecretKey secret = EncryptUtil.createNewSharedKey();
|
||||
final SecretKey secret = EncryptUtil.createSharedKey();
|
||||
final PublicKey pubkey = packet.getKey();
|
||||
final byte[] token = packet.getToken();
|
||||
if(this.server.getServerKey() == null) {
|
||||
|
|
|
@ -15,6 +15,9 @@ import java.security.spec.EncodedKeySpec;
|
|||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
|
@ -27,7 +30,7 @@ import javax.crypto.spec.SecretKeySpec;
|
|||
import common.log.Log;
|
||||
|
||||
public class EncryptUtil {
|
||||
public static SecretKey createNewSharedKey() {
|
||||
public static SecretKey createSharedKey() {
|
||||
try {
|
||||
KeyGenerator keygen = KeyGenerator.getInstance("AES");
|
||||
keygen.init(128);
|
||||
|
@ -38,7 +41,7 @@ public class EncryptUtil {
|
|||
}
|
||||
}
|
||||
|
||||
public static KeyPair generateKeyPair() {
|
||||
public static KeyPair createKeypair() {
|
||||
try {
|
||||
KeyPairGenerator pairgen = KeyPairGenerator.getInstance("RSA");
|
||||
pairgen.initialize(2048);
|
||||
|
@ -79,14 +82,14 @@ public class EncryptUtil {
|
|||
}
|
||||
|
||||
public static byte[] encryptData(Key key, byte[] data) {
|
||||
return cipherOperation(Cipher.ENCRYPT_MODE, key, data);
|
||||
return cipher(Cipher.ENCRYPT_MODE, key, data);
|
||||
}
|
||||
|
||||
public static byte[] decryptData(Key key, byte[] data) {
|
||||
return cipherOperation(Cipher.DECRYPT_MODE, key, data);
|
||||
return cipher(Cipher.DECRYPT_MODE, key, data);
|
||||
}
|
||||
|
||||
private static byte[] cipherOperation(int mode, Key key, byte[] data) {
|
||||
private static byte[] cipher(int mode, Key key, byte[] data) {
|
||||
try {
|
||||
return createCipher(mode, key.getAlgorithm(), key).doFinal(data);
|
||||
}
|
||||
|
@ -137,4 +140,67 @@ public class EncryptUtil {
|
|||
}
|
||||
return Util.getHexString(xor);
|
||||
}
|
||||
|
||||
public static String getArmoredPubkey(PublicKey pubkey, String cn) {
|
||||
StringBuilder sb = new StringBuilder("tcr-rsa-2048 ");
|
||||
sb.append(Base64.getEncoder().encodeToString(pubkey.getEncoded()));
|
||||
sb.append(' ').append(Base64.getEncoder().encodeToString(crc24(pubkey.getEncoded())));
|
||||
return cn == null || cn.isEmpty() ? sb.toString() : sb.append(' ').append(Util.sanitizeCommonName(cn)).toString();
|
||||
}
|
||||
|
||||
public static Tuple<PublicKey, String> parseArmoredPubkey(String armor) throws IllegalArgumentException {
|
||||
String[] tok = armor.trim().split(" ");
|
||||
if(tok.length != 3 && tok.length != 4)
|
||||
throw new IllegalArgumentException("Key muss aus 3 oder 4 Segmenten bestehen");
|
||||
if(!tok[0].equals("tcr-rsa-2048"))
|
||||
throw new IllegalArgumentException("Algorithmus '" + tok[0] + "' ist nicht unterstützt, es wird derzeit nur tcr-rsa-2048 verwendet");
|
||||
byte[] key;
|
||||
try {
|
||||
key = Base64.getDecoder().decode(tok[1]);
|
||||
}
|
||||
catch(IllegalArgumentException e) {
|
||||
throw new IllegalArgumentException("Schlüssel ist nicht im Base64-Format", e);
|
||||
}
|
||||
byte[] hash;
|
||||
try {
|
||||
hash = Base64.getDecoder().decode(tok[2]);
|
||||
}
|
||||
catch(IllegalArgumentException e) {
|
||||
throw new IllegalArgumentException("Prüfwert ist nicht im Base64-Format", e);
|
||||
}
|
||||
if(hash.length != 3)
|
||||
throw new IllegalArgumentException("Prüfwert hat die falsche Länge, erwarte 3 Bytes, habe " + hash.length + " Bytes");
|
||||
byte[] keyHash = crc24(key);
|
||||
if(!Arrays.equals(hash, keyHash))
|
||||
throw new IllegalArgumentException("Prüfwert ist falsch, erwarte " + Util.getHexString(hash) + ", habe " + Util.getHexString(keyHash));
|
||||
PublicKey pubkey;
|
||||
try {
|
||||
EncodedKeySpec spec = new X509EncodedKeySpec(key);
|
||||
KeyFactory factory = KeyFactory.getInstance("RSA");
|
||||
pubkey = factory.generatePublic(spec);
|
||||
}
|
||||
catch(NoSuchAlgorithmException | InvalidKeySpecException e) {
|
||||
throw new IllegalArgumentException("Öffentlicher Schlüssel konnte nicht dekodiert werden", e);
|
||||
}
|
||||
if(tok.length == 4 && !Util.isValidCommonName(tok[3]))
|
||||
throw new IllegalArgumentException("Name muss aus 'a-z' '0-9' und '-' bestehen, kann keine aufeinander folgenden Bindestriche haben und darf nicht damit beginnen oder darin enden");
|
||||
return new Tuple<PublicKey, String>(pubkey, tok.length == 4 ? tok[3] : null);
|
||||
}
|
||||
|
||||
private static final long CRC24_INIT = 0xB704CEL;
|
||||
private static final long CRC24_POLY = 0x1864CFBL;
|
||||
|
||||
private static byte[] crc24(byte[] data) {
|
||||
long crc = CRC24_INIT;
|
||||
for(byte bt : data) {
|
||||
crc ^= (long)bt << 16;
|
||||
for(int z = 0; z < 8; z++) {
|
||||
crc <<= 1;
|
||||
if((crc & 0x1000000) != 0)
|
||||
crc ^= CRC24_POLY;
|
||||
}
|
||||
}
|
||||
int value = (int)(crc & 0xFFFFFFL);
|
||||
return new byte[] {(byte)((value >> 16) & 0xff), (byte)((value >> 8) & 0xff), (byte)(value & 0xff)};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -431,4 +431,44 @@ int utf_len(const char *str) {
|
|||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public static String breakString(String str, int width) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for(int z = 0; z < str.length() / width; z++) {
|
||||
sb.append(str.substring(z * width, (z + 1) * width)).append('\n');
|
||||
}
|
||||
return sb.append(str.substring(str.length() - (str.length() % width), str.length())).toString();
|
||||
}
|
||||
|
||||
public static String sanitizeCommonName(String str) {
|
||||
str = str.trim();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
boolean hyphen = true;
|
||||
for(int z = 0; z < str.length(); z++) {
|
||||
char ch = Character.toLowerCase(str.charAt(z));
|
||||
if((ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9')) {
|
||||
sb.append(ch);
|
||||
hyphen = false;
|
||||
}
|
||||
else if(ch == '-' || ch == ' ') {
|
||||
if(!hyphen)
|
||||
sb.append('-');
|
||||
hyphen = true;
|
||||
}
|
||||
}
|
||||
return sb.length() > 0 && sb.charAt(sb.length() - 1) == '-' ? sb.substring(0, sb.length() - 1) : sb.toString();
|
||||
}
|
||||
|
||||
public static boolean isValidCommonName(String str) {
|
||||
boolean hyphen = true;
|
||||
for(int z = 0; z < str.length(); z++) {
|
||||
char ch = Character.toLowerCase(str.charAt(z));
|
||||
if((ch < 'a' || ch > 'z') && (ch < '0' || ch > '9') && ch != '-')
|
||||
return false;
|
||||
if(ch == '-' && hyphen)
|
||||
return false;
|
||||
hyphen = ch == '-';
|
||||
}
|
||||
return !hyphen;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -464,7 +464,7 @@ public final class Server implements IThreadListener {
|
|||
long wtime = this.loadServerConfig();
|
||||
if(this.keyPair == null) {
|
||||
Log.SYSTEM.info("Generiere neues Schlüsselpaar");
|
||||
this.keyPair = EncryptUtil.generateKeyPair();
|
||||
this.keyPair = EncryptUtil.createKeypair();
|
||||
}
|
||||
// if(dtime == -1L) // {
|
||||
// dtime = World.START_TIME;
|
||||
|
@ -499,7 +499,7 @@ public final class Server implements IThreadListener {
|
|||
}
|
||||
else {
|
||||
Log.SYSTEM.info("Generiere temporäres Schlüsselpaar");
|
||||
this.keyPair = EncryptUtil.generateKeyPair();
|
||||
this.keyPair = EncryptUtil.createKeypair();
|
||||
Config.clear();
|
||||
UniverseRegistry.clear();
|
||||
Config.set("daylightCycle", "false", false);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue