获取DBeaver激活码和JaNetfilter规则的方法。
声明:下面的代码没有一行是我写的。只在 dbeaver-ue-24.0.0-macos-x86_64.dmg 中测试通过。已经有佬友用Python实现过了,帖子在DBeaver 使用ja-netfilter power插件激活。我只是一个Java CURD仔,Python代码看不太懂。又有佬友在帖子DBeaver Ultimate 激活方法分享提到了一个链接DBeaver Ultimate Edition License验证分析,拜读之,在24.0.0版本复现整个操作。在拜读和复现的过程中com.dbeaver.lm.api.LMMain类(com.dbeaver.lm.api_3.0.2.202404011634.jar 包)引起了我的注意,来看看它的main方法长什么样子
public static void main(String[] args) throws Exception {
System.out.println("LM 2.0");
if (args.length > 0 && args.equals("gen-keys")) {
System.out.println("Test key generation");
generateKeyPair();
} else if (args.length > 0 && args.equals("encrypt-license")) {
System.out.println("Encrypt license");
encryptLicense();
} else if (args.length > 0 && args.equals("decrypt-license")) {
System.out.println("Decrypt license");
decryptLicense();
} else if (args.length > 0 && args.equals("import-license")) {
System.out.println("Import license");
importLicense();
} else {
System.out.println("Test license generation");
generateLicense();
}
}
这样的方法命名简直了,我愿称DBeaver也是大善人,直接告诉你怎么生成KEY,怎么生成License,结合狗群主的Janetfilter这是开卷考试啊,我们只需要把里面的代码抠出来组合一下不就成了么。
生成自定义的密钥对
生成密钥对的方法来自 LMMain#generateKeyPair 方法,DBeaver 密钥对的 keySize 为 2048,并且密钥对使用 Base64 编码后被按每行最多 76 个字符显示
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.util.Base64;
public class KeyGenerator {
public static void main(String[] args) throws Exception {
KeyPair keyPair = generateKeyPair(2048);
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
System.out.println("--- PUBLIC KEY ---");
System.out.println(splitLines(Base64.getEncoder().encodeToString(publicKey.getEncoded()), 76));
System.out.println("--- PRIVATE KEY ---");
System.out.println(splitLines(Base64.getEncoder().encodeToString(privateKey.getEncoded()), 76));
}
public static KeyPair generateKeyPair(int keySize) throws NoSuchAlgorithmException {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
keyPairGenerator.initialize(keySize, secureRandom);
return keyPairGenerator.generateKeyPair();
}
public static String splitLines(String bigString, int lineLength) {
return bigString.replaceAll("(.{" + lineLength + "})", "$1\n");
}
}
将生成的公钥和私钥分别保存到 user-public-key.txt 和 user-private-key.txt 文件中。
提取 DBeaver 公钥
找到com.dbeaver.app.ultimate_24.0.0.202404011634.jar包下的dbeaver-ue-public.key文件, 内容为
--- PUBLIC KEY ---
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk7ciFU/aUCIgH5flBbGD0t7B3KOmfL0l
BMf2ENuLA0w/T8A1RvteUYk2EQo3UrZ7kMZ8rK93nmDjituN7jlv/bsxGyAox87BbKYSs9oH5f9P
hYHAiTE0PxoMODnl4NgR+Bpc+Ath8wDLHMC+BzYkOy4JQo8EX/ff58TT9UYP8eoDeGdSxQmW3FJC
i82UiC5zIk75dx20Al9ql0fdxnzo31q/2MbnNCAfSchsqrKtzBtheex4JvvqZjxn98wk5Te1QgZz
Caz4ay9dkLVjSt79QYm5hKb8Jt3O5SxSUsrjmYVeG+k2bQlidw8dENwLZmvJkIJi8kb94yEwY/dq
lENDkQIDAQAB
将文件内容保存到 dbeaver-ue-public-key.txt 文件中。
生成 DBeaver 激活码
为了生成激活码我们需要几个辅助类,它们都来自 com.dbeaver.lm.api_3.0.2.202404011634.jar 包。
第一个是 LicenseType,它定义了各种 License 类型枚举
public enum LicenseType {
STANDARD('S', "Yearly subscription", true, true),
YEAR_UPDATE('Y', "Perpetual", false, false),
YEAR_CORPORATE('C', "Corporate", false, false),
ULTIMATE('U', "Ultimate", false, false),
LIMITED('L', "Limited", true, true),
PARTNER('P', "Technical partner", false, false),
TRIAL('T', "Trial", true, true),
ACADEMIC('A', "Academic", true, true),
TEAM('M', "Yearly subscription (Team)", true, true),
CUSTOM('X', "Custom", false, false);
private final char id;
private final String displayName;
private boolean isExtendable;
private boolean needsEndTime;
private LicenseType(char id, String displayName, boolean isExtendable, boolean needsEndTime) {
this.id = id;
this.displayName = displayName;
this.isExtendable = isExtendable;
this.needsEndTime = needsEndTime;
}
public byte getId() {
return (byte) this.id;
}
public String getDisplayName() {
return this.displayName;
}
public boolean isExtendable() {
return this.isExtendable;
}
public boolean needsEndTime() {
return this.needsEndTime;
}
}
第二个是 LicenseFormat,它定义了各种 License 格式枚举
public enum LicenseFormat {
STANDARD((byte) 0, 218, "Initial basic license format"),
EXTENDED((byte) 1, 238, "Extended format with owner email and corporate license info"),
ADVANCED((byte) 2, 490, "Advanced format for role-based licenses");
private final byte id;
private final int encryptedLength;
private final String description;
private LicenseFormat(byte id, int encryptedLength, String description) {
this.id = id;
this.encryptedLength = encryptedLength;
this.description = description;
}
public byte getId() {
return this.id;
}
public String getDescription() {
return this.description;
}
public int getEncryptedLength() {
return this.encryptedLength;
}
}
第三个是 License,它定义了 License 包含哪些内容
import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Date;
public class License {
public static final long FLAG_NONE = 0L;
public static final long FLAG_ROLE_BASED = 1L;
public static final long FLAG_CANCELED = 2L;
public static final long FLAG_RESELLER = 4L;
public static final long FLAG_SUBSCRIPTION = 8L;
public static final long FLAG_LIMITED = 16L;
public static final long FLAG_LIMITED_VERSION = 32L;
public static final long FLAG_SERVER_LICENSE = 64L;
public static final long FLAG_UNLIMITED_USERS = 256L;
public static final long FLAG_UNLIMITED_TIME = 512L;
public static final long FLAG_UNLIMITED_SERVERS = 1024L;
public static final long FLAG_MULTI_INSTANCE = 2048L;
private final String licenseId;
private final LicenseType licenseType;
private final Date licenseIssueTime;
private final Date licenseStartTime;
private final Date licenseEndTime;
private long flags;
private final String productId;
private final String productVersion;
private final String ownerId;
private final String ownerCompany;
private final String ownerName;
private String ownerEmail;
private byte yearsNumber;
private byte reserved1;
private short usersNumber;
private LicenseFormat licenseFormat;
public License(String licenseId, LicenseType licenseType, Date licenseIssueTime, Date licenseStartTime, Date licenseEndTime, long flags, String productId, String productVersion, String ownerId, String ownerCompany, String ownerName, String ownerEmail) {
this.licenseFormat = (flags & 1L) != 0L ? LicenseFormat.ADVANCED : LicenseFormat.EXTENDED;
this.licenseId = licenseId;
this.licenseType = licenseType;
this.licenseIssueTime = licenseIssueTime;
this.licenseStartTime = licenseStartTime;
this.licenseEndTime = licenseEndTime;
this.flags = flags;
this.productId = productId;
this.productVersion = productVersion;
this.ownerId = ownerId;
this.ownerCompany = ownerCompany;
this.ownerName = ownerName;
this.ownerEmail = ownerEmail;
this.yearsNumber = 1;
this.reserved1 = 0;
this.usersNumber = 1;
}
public byte[] getData() {
ByteArrayOutputStream output = new ByteArrayOutputStream(this.licenseFormat.getEncryptedLength());
output.write(this.licenseFormat.getId());
writeStringToBuffer(output, this.licenseId, 16);
output.write(this.licenseType.getId());
writeDateToBuffer(output, this.licenseIssueTime);
writeDateToBuffer(output, this.licenseStartTime);
writeDateToBuffer(output, this.licenseEndTime);
writeLongToBuffer(output, this.flags);
writeStringToBuffer(output, this.productId, 16);
writeStringToBuffer(output, this.productVersion, 8);
writeStringToBuffer(output, this.ownerId, 16);
writeStringToBuffer(output, this.ownerCompany, 64);
if (this.licenseFormat == LicenseFormat.STANDARD) {
writeStringToBuffer(output, this.ownerName, 64);
} else {
writeStringToBuffer(output, this.ownerName, 32);
writeStringToBuffer(output, this.ownerEmail, 48);
output.write(this.yearsNumber);
output.write(this.reserved1);
writeShortToBuffer(output, this.usersNumber);
}
return output.toByteArray();
}
public void writeStringToBuffer(ByteArrayOutputStream output, String value, int length) {
output.writeBytes(getStringData(value, length));
}
public void writeDateToBuffer(ByteArrayOutputStream output, Date date) {
long value = date == null ? 0L : date.getTime();
writeLongToBuffer(output, value);
}
public byte[] getStringData(String value, int length) {
byte[] bytes = value == null ? new byte : value.getBytes(StandardCharsets.UTF_8);
byte[] data = Arrays.copyOf(bytes, length);
Arrays.fill(data, Math.min(bytes.length, length), length, (byte) 32);
return data;
}
public void writeLongToBuffer(ByteArrayOutputStream output, long value) {
ByteBuffer buffer = ByteBuffer.wrap(new byte);
buffer.putLong(value);
output.writeBytes(buffer.array());
}
public void writeShortToBuffer(ByteArrayOutputStream output, short value) {
ByteBuffer buffer = ByteBuffer.wrap(new byte);
buffer.putShort(value);
output.writeBytes(buffer.array());
}
}
生成激活码的代码主要逻辑来自 LMMain#encryptLicense 方法,部分方法来自 LMUtils 和 LMEncryption 类
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Arrays;
import java.util.Base64;
import java.util.Date;
public class LicenseGenerator {
public static void main(String[] args) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException {
PrivateKey privateKey = readPrivateKey("user-private-key.txt");
License license = new License("JL-0FB16-000A2GC", LicenseType.ULTIMATE, new Date(), new Date(), null, License.FLAG_UNLIMITED_SERVERS, "dbeaver-ue", "24.0", "10000", "Linux DO", "zhuma", "");
byte[] licenseData = license.getData();
byte[] licenseEncrypted = encrypt(licenseData, privateKey);
String licenseBase64 = splitLines(Base64.getEncoder().encodeToString(licenseEncrypted), 76);
System.out.println("--- LICENSE ---");
System.out.println(licenseBase64);
}
public static PrivateKey readPrivateKey(String filename) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
try (FileInputStream fis = new FileInputStream(filename)) {
byte[] bytes = readEncryptedString(fis);
return generatePrivateKey(bytes);
}
}
public static byte[] readEncryptedString(InputStream stream) throws IOException {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream))) {
StringBuilder result = new StringBuilder(4000);
while (true) {
String line = reader.readLine();
if (line == null || line.isEmpty()) {
return Base64.getDecoder().decode(result.toString());
}
if (!line.startsWith("-") && !line.startsWith("#")) {
result.append(line);
}
}
}
}
public static PrivateKey generatePrivateKey(byte[] privateKeyBytes) throws NoSuchAlgorithmException, InvalidKeySpecException {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
return keyFactory.generatePrivate(privateKeySpec);
}
public static byte[] encrypt(byte[] data, Key key) throws NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, IOException, InvalidKeyException {
return cipherAsymmetric(data, key, 1);
}
public static byte[] cipherAsymmetric(byte[] data, Key key, int mode) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int chunkSize = mode == 2 ? 256 : 245;
int chunkCount = data.length / chunkSize;
if (data.length % chunkSize > 0) {
++chunkCount;
}
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
for (int i = 0; idata.length) {
length = data.length - chunkSize * i;
}
byte[] segment = Arrays.copyOfRange(data, offset, offset + length);
byte[] segmentEncrypted = cipher.doFinal(segment);
buffer.write(segmentEncrypted);
}
return buffer.toByteArray();
}
public static String splitLines(String bigString, int lineLength) {
return bigString.replaceAll("(.{" + lineLength + "})", "$1\n");
}
}
生成 Power 规则
生成 Power 规则的代码来自 LMMain、LMUtils 和 LMEncryption 类
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
public class RuleGenerator {
public static void main(String[] args) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
RSAPublicKey userPublicKey = (RSAPublicKey) readPublicKey("user-public-key.txt");
RSAPublicKey dbeaverPublicKey = (RSAPublicKey) readPublicKey("dbeaver-ue-public-key.txt");
String rule = "EQUAL,65537," + dbeaverPublicKey.getModulus() + "->65537," + userPublicKey.getModulus();
System.out.println("--- POWER RULE ---");
System.out.println(rule);
}
private static PublicKey readPublicKey(String filename) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
try (FileInputStream fis = new FileInputStream(filename)) {
byte[] bytes = readEncryptedString(fis);
return generatePublicKey(bytes);
}
}
public static byte[] readEncryptedString(InputStream stream) throws IOException {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream))) {
StringBuilder result = new StringBuilder(4000);
while (true) {
String line = reader.readLine();
if (line == null || line.isEmpty()) {
return Base64.getDecoder().decode(result.toString());
}
if (!line.startsWith("-") && !line.startsWith("#")) {
result.append(line);
}
}
}
}
public static PublicKey generatePublicKey(byte[] publicKeyBytes) throws NoSuchAlgorithmException, InvalidKeySpecException {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(publicKeyBytes);
return keyFactory.generatePublic(publicKeySpec);
}
}
激活 DBeaver
替换 JRE
为什么参考热佬DBeaver Ultimate 22.1 旗舰版激活方法,这里改为
-vm
/Library/Java/JavaVirtualMachines/jdk-17.0.2.jdk/Contents/Home/bin/java
配置 javaagent
在 /Applications/DBeaverUltimate.app/Contents/Eclipse/dbeaver.ini 文件末尾增加如下内容
--add-opens=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED
--add-opens=java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED
-javaagent:/Users/nucintosh/janetfilter/jetbra/ja-netfilter.jar=jetbrains
配置 power.conf
在 power.conf 文件的 一节中增加 RuleGenerator 生成的规则,比如
EQUAL,65537,18647337145970099840985713466364918234362046859276071603267794715593136936160492846272521898537004122925301027678489984501477994730537725810211014632147698623617755644615810363782781403177803009057237675193508609212761456687315887698721555027495273857704703161501556278932648428860647172378607605329884151219327938607350280135400521619196335682733371571813479415591793504469016713220060778679994845066654727814845227443267965199130452466305398985400201871520156163836666885179554470299593371783155887571488588786209640709589687137547072170350760576933982430617266249638449664790360846298962977961246175829506878817169->65537,26568176594452198874357955233081865355601482575454463708433306578459030820569735111660768507557691552912716777149966830516251509872885609614711295305581089270555091503264286693743745315059054820199160842222042189849689922692298273645936389982899317988537289718057741782081574951312275856190235441646123049577974991407616013276510427782805925379988038959945798382561302135542700860204474353714516664853954768210723310513114678397057324482615339456351078381638951155512135128405408033884232379809701908270283694138677289134377234340940696655420259553525375619972769383153649791856349266450908175059345382153220830798463
配置 dns.conf
EQUAL,dbeaver.com
输入激活码
输入LicenseGenerator生成的激活码
Dm3qISOS+h8eu4kliBh+K6WXzIxIx03rKg6F/NxE7WqCw9HVN0/uxpGFvi8+EI2/ZGu/eDOQbirU
Y8dk2NTCekvzKldoJgPPSKFsvUUFCUp6iVtgtTeD4ddvJS6HQzXM9R+P8dR0f4v38yUGuTsU8PCv
RXfoQTXtSxkdYF4v/8bEELPf4qmnYgs7HWbcvq2YYVK2dokYxaV4F0X5oM5Tv1BsHlPqI8Dh8OHm
Nj8FkYZ1zErqZ+2zFh3b+m6XE9IHOxf3X+5HEF+0esq1J1NtyRGuoA2dIPkIoOX1zXaZkKgxFKrc
L5xn/kcqnqPiBJMg7y7AIYV9+DgE76Vv+m1PYQ==
打完收工!!!真的一行代码都不是我写的,DBeaver真的都帮你写好了,它就差出个教程教你怎么激活了,你就说DBeaver是不是大善人吧!!!
最后,和Python比起来Java真是啰里八嗦!!!
"
页:
[1]