add key serialization

This commit is contained in:
Sen 2025-05-30 01:43:37 +02:00
parent be0ab15153
commit b14e539464
Signed by: sen
GPG key ID: 3AC50A6F47D1B722
5 changed files with 117 additions and 13 deletions

View file

@ -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)};
}
}

View file

@ -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;
}
}