
cn.nukkit.network.encryption.EncryptionUtils Maven / Gradle / Ivy
package cn.nukkit.network.encryption;
import cn.nukkit.api.PowerNukkitXOnly;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.JWSObject;
import com.nimbusds.jose.crypto.ECDSASigner;
import com.nimbusds.jose.crypto.ECDSAVerifier;
import com.nimbusds.jose.jwk.Curve;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
import javax.crypto.Cipher;
import javax.crypto.KeyAgreement;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.net.URI;
import java.security.*;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.Base64;
@PowerNukkitXOnly
public final class EncryptionUtils {
private static final ECPublicKey MOJANG_PUBLIC_KEY;
private static final ECPublicKey OLD_MOJANG_PUBLIC_KEY;
private static final SecureRandom SECURE_RANDOM = new SecureRandom();
private static final String MOJANG_PUBLIC_KEY_BASE64 =
"MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAECRXueJeTDqNRRgJi/vlRufByu/2G0i2Ebt6YMar5QX/R0DIIyrJMcUpruK4QveTfJSTp3Shlq4Gk34cD/4GUWwkv0DVuzeuB+tXija7HBxii03NHDbPAD0AKnLr2wdAp";
private static final String OLD_MOJANG_PUBLIC_KEY_BASE64 =
"MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8ELkixyLcwlZryUQcu1TvPOmI2B7vX83ndnWRUaXm74wFfa5f/lwQNTfrLVHa2PmenpGI6JhIMUJaWZrjmMj90NoKNFSNBuKdm8rYiXsfaz3K36x/1U26HpG0ZxK/V1V";
private static final KeyPairGenerator KEY_PAIR_GEN;
static {
// DO NOT REMOVE THIS
// Since Java 8u231, secp384r1 is deprecated and will throw an exception.
String namedGroups = System.getProperty("jdk.tls.namedGroups");
System.setProperty("jdk.tls.namedGroups", namedGroups == null || namedGroups.isEmpty() ? "secp384r1" : ", secp384r1");
try {
KEY_PAIR_GEN = KeyPairGenerator.getInstance("EC");
KEY_PAIR_GEN.initialize(new ECGenParameterSpec("secp384r1"));
MOJANG_PUBLIC_KEY = generateKey(MOJANG_PUBLIC_KEY_BASE64);
OLD_MOJANG_PUBLIC_KEY = generateKey(OLD_MOJANG_PUBLIC_KEY_BASE64);
} catch (Exception e) {
throw new AssertionError("Unable to initialize required encryption", e);
}
}
private EncryptionUtils() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}
public static ECPublicKey generateKey(String b64) throws NoSuchAlgorithmException, InvalidKeySpecException {
return (ECPublicKey)KeyFactory.getInstance("EC").generatePublic(new X509EncodedKeySpec(Base64.getDecoder().decode(b64)));
}
public static KeyPair createKeyPair() {
return KEY_PAIR_GEN.generateKeyPair();
}
public static void signJwt(JWSObject jws, ECPrivateKey key) throws JOSEException {
jws.sign(new ECDSASigner(key, Curve.P_384));
}
public static boolean verifyJwt(JWSObject jws, ECPublicKey key) throws JOSEException {
return jws.verify(new ECDSAVerifier(key));
}
public static SecretKey getSecretKey(PrivateKey localPrivateKey, PublicKey remotePublicKey, byte[] token) throws InvalidKeyException {
byte[] sharedSecret = getEcdhSecret(localPrivateKey, remotePublicKey);
MessageDigest digest;
try {
digest = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
throw new AssertionError(e);
}
digest.update(token);
digest.update(sharedSecret);
byte[] secretKeyBytes = digest.digest();
return new SecretKeySpec(secretKeyBytes, "AES");
}
private static byte[] getEcdhSecret(PrivateKey localPrivateKey, PublicKey remotePublicKey) throws InvalidKeyException {
KeyAgreement agreement;
try {
agreement = KeyAgreement.getInstance("ECDH");
} catch (NoSuchAlgorithmException e) {
throw new AssertionError(e);
}
agreement.init(localPrivateKey);
agreement.doPhase(remotePublicKey, true);
return agreement.generateSecret();
}
public static JWSObject createHandshakeJwt(KeyPair serverKeyPair, byte[] token) throws JOSEException {
URI x5u = URI.create(Base64.getEncoder().encodeToString(serverKeyPair.getPublic().getEncoded()));
JWTClaimsSet claimsSet = new JWTClaimsSet.Builder().claim("salt", Base64.getEncoder().encodeToString(token)).build();
SignedJWT jwt = new SignedJWT(new JWSHeader.Builder(JWSAlgorithm.ES384).x509CertURL(x5u).build(), claimsSet);
signJwt(jwt, (ECPrivateKey) serverKeyPair.getPrivate());
return jwt;
}
public static byte[] generateRandomToken() {
byte[] token = new byte[16];
SECURE_RANDOM.nextBytes(token);
return token;
}
public static ECPublicKey getMojangPublicKey() {
return MOJANG_PUBLIC_KEY;
}
public static ECPublicKey getOldMojangPublicKey() {
return OLD_MOJANG_PUBLIC_KEY;
}
public static Cipher createCipher(boolean gcm, boolean encrypt, SecretKey key) {
try {
byte[] iv;
String transformation;
if (gcm) {
iv = new byte[16];
System.arraycopy(key.getEncoded(), 0, iv, 0, 12);
iv[15] = 2;
transformation = "AES/CTR/NoPadding";
} else {
iv = Arrays.copyOf(key.getEncoded(), 16);
transformation = "AES/CFB8/NoPadding";
}
Cipher cipher = Cipher.getInstance(transformation);
cipher.init(encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
return cipher;
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException e) {
throw new AssertionError("Unable to initialize required encryption", e);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy