学习制作 JetBrains KeyGen
2025-10-05 21:09:40

之前都是用别人的 KeyGen,但是好奇想了解一下 JetBrains 系列软件的注册原理,所以查阅了一些文章:
IDEA 2019.1版本的破解文件JetbrainsCrack.jar分析
注册码证书验证过程
ja-netfilter 代理框架
ja-netfilter破解某IDE原理分析及生成激活码
ja-netfilter power插件原理
Idea破解原理

激活码验证流程

激活码由4部分组成,用-字符分隔

1
2
3
4
5
6
7
key = "1OOG7EX2FA-xxx-xxx-xxx"

licenseParts = key.split('-')
licenseId = licenseParts[0] # ID,明文,无关紧要,和授权信息中的id保持一致就行
licensePartBase64 = licenseParts[1] # 授权信息,就是 JSON 内容
signatureBase64 = licenseParts[2] # 授权信息的签名
certBase64 = licenseParts[3] # 证书

软件验证激活码流程:

  1. 软件内会先用内置 CA 证书对激活码中的证书验证。
  2. 用激活码中的证书、签名对licensePart部分验签,目的是防止篡改licensePart


ja-netfilter 这个项目正好可以解决第一步中对证书的验签。
而第二步我们可以自行创建一个证书对授权信息签名,这样激活码中的签名部分就是正确的。

注册机

抄的大佬们的,优化了一下。
注册分两步:

  1. 生成自己的证书。
  2. 自己定义授权信息,然后用自己的证书签名。


创建证书的脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import datetime

from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.x509.oid import NameOID

one_day = datetime.timedelta(days=1)
ten_day = datetime.timedelta(days=3650)
today = datetime.datetime.today()
yesterday = today - one_day
tomorrow = today + ten_day

private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=4096,
backend=default_backend()
)
public_key = private_key.public_key()
builder = x509.CertificateBuilder()

builder = builder.subject_name(x509.Name([
x509.NameAttribute(NameOID.COMMON_NAME, 'jack'),
]))
builder = builder.issuer_name(x509.Name([
x509.NameAttribute(NameOID.COMMON_NAME, 'JetProfile CA'),
]))
builder = builder.not_valid_before(yesterday)
builder = builder.not_valid_after(tomorrow)
builder = builder.serial_number(x509.random_serial_number())
builder = builder.public_key(public_key)

certificate = builder.sign(
private_key=private_key, algorithm=hashes.SHA256(),
backend=default_backend()
)
private_bytes = private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.TraditionalOpenSSL,
encryption_algorithm=serialization.NoEncryption())
public_bytes = certificate.public_bytes(
encoding=serialization.Encoding.PEM)
with open("ca.key", "wb") as fout:
fout.write(private_bytes)
with open("ca.cer", "wb") as fout:
fout.write(public_bytes)

注册机:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import base64
import json

from Crypto.Hash import SHA1, SHA256
from Crypto.PublicKey import RSA
from Crypto.Signature import pkcs1_15
from Crypto.Util.asn1 import DerSequence, DerObjectId, DerNull, DerOctetString
from cryptography import x509
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import serialization

# JetBrains CA
# 来自 https://github.com/JetBrains/marketplace-makemecoffee-plugin/blob/master/src/main/java/com/company/license/CheckLicense.java
ROOT_CERTIFICATES = ("MIIFOzCCAyOgAwIBAgIJANJssYOyg3nhMA0GCSqGSIb3DQEBCwUAMBgxFjAUBgNV\n" +
"BAMMDUpldFByb2ZpbGUgQ0EwHhcNMTUxMDAyMTEwMDU2WhcNNDUxMDI0MTEwMDU2\n" +
"WjAYMRYwFAYDVQQDDA1KZXRQcm9maWxlIENBMIICIjANBgkqhkiG9w0BAQEFAAOC\n" +
"Ag8AMIICCgKCAgEA0tQuEA8784NabB1+T2XBhpB+2P1qjewHiSajAV8dfIeWJOYG\n" +
"y+ShXiuedj8rL8VCdU+yH7Ux/6IvTcT3nwM/E/3rjJIgLnbZNerFm15Eez+XpWBl\n" +
"m5fDBJhEGhPc89Y31GpTzW0vCLmhJ44XwvYPntWxYISUrqeR3zoUQrCEp1C6mXNX\n" +
"EpqIGIVbJ6JVa/YI+pwbfuP51o0ZtF2rzvgfPzKtkpYQ7m7KgA8g8ktRXyNrz8bo\n" +
"iwg7RRPeqs4uL/RK8d2KLpgLqcAB9WDpcEQzPWegbDrFO1F3z4UVNH6hrMfOLGVA\n" +
"xoiQhNFhZj6RumBXlPS0rmCOCkUkWrDr3l6Z3spUVgoeea+QdX682j6t7JnakaOw\n" +
"jzwY777SrZoi9mFFpLVhfb4haq4IWyKSHR3/0BlWXgcgI6w6LXm+V+ZgLVDON52F\n" +
"LcxnfftaBJz2yclEwBohq38rYEpb+28+JBvHJYqcZRaldHYLjjmb8XXvf2MyFeXr\n" +
"SopYkdzCvzmiEJAewrEbPUaTllogUQmnv7Rv9sZ9jfdJ/cEn8e7GSGjHIbnjV2ZM\n" +
"Q9vTpWjvsT/cqatbxzdBo/iEg5i9yohOC9aBfpIHPXFw+fEj7VLvktxZY6qThYXR\n" +
"Rus1WErPgxDzVpNp+4gXovAYOxsZak5oTV74ynv1aQ93HSndGkKUE/qA/JECAwEA\n" +
"AaOBhzCBhDAdBgNVHQ4EFgQUo562SGdCEjZBvW3gubSgUouX8bMwSAYDVR0jBEEw\n" +
"P4AUo562SGdCEjZBvW3gubSgUouX8bOhHKQaMBgxFjAUBgNVBAMMDUpldFByb2Zp\n" +
"bGUgQ0GCCQDSbLGDsoN54TAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkq\n" +
"hkiG9w0BAQsFAAOCAgEAjrPAZ4xC7sNiSSqh69s3KJD3Ti4etaxcrSnD7r9rJYpK\n" +
"BMviCKZRKFbLv+iaF5JK5QWuWdlgA37ol7mLeoF7aIA9b60Ag2OpgRICRG79QY7o\n" +
"uLviF/yRMqm6yno7NYkGLd61e5Huu+BfT459MWG9RVkG/DY0sGfkyTHJS5xrjBV6\n" +
"hjLG0lf3orwqOlqSNRmhvn9sMzwAP3ILLM5VJC5jNF1zAk0jrqKz64vuA8PLJZlL\n" +
"S9TZJIYwdesCGfnN2AETvzf3qxLcGTF038zKOHUMnjZuFW1ba/12fDK5GJ4i5y+n\n" +
"fDWVZVUDYOPUixEZ1cwzmf9Tx3hR8tRjMWQmHixcNC8XEkVfztID5XeHtDeQ+uPk\n" +
"X+jTDXbRb+77BP6n41briXhm57AwUI3TqqJFvoiFyx5JvVWG3ZqlVaeU/U9e0gxn\n" +
"8qyR+ZA3BGbtUSDDs8LDnE67URzK+L+q0F2BC758lSPNB2qsJeQ63bYyzf0du3wB\n" +
"/gb2+xJijAvscU3KgNpkxfGklvJD/oDUIqZQAnNcHe7QEf8iG2WqaMJIyXZlW3me\n" +
"0rn+cgvxHPt6N4EBh5GgNZR4l0eaFEV+fxVsydOQYo1RIyFMXtafFBqQl6DDxujl\n" +
"FeU3FZ+Bcp12t7dlM4E0/sS1XdL47CfGVj4Bp+/VbF862HmkAbd7shs7sDQkHbU=\n")

def create_power_file(active_code):
subcert = x509.load_der_x509_certificate(base64.b64decode(active_code.split('-')[3]))
s = int.from_bytes(subcert.signature, byteorder='big', signed=False)

digest_cert = SHA256.new(subcert.tbs_certificate_bytes)
digest_info = create_digest_info(digest_cert)

# 使用标准的 PKCS#1 v1.5 填充构造
key_size_bytes = subcert.public_key().key_size // 8 # 转换为字节数
padded_data = create_pkcs1v15_padding(digest_info, key_size_bytes)
r = int.from_bytes(padded_data, byteorder='big', signed=False)

# 写入 power.conf 文件
with open('power.conf', 'w', encoding='utf-8') as f:
jb_ca = x509.load_der_x509_certificate(base64.b64decode(ROOT_CERTIFICATES))
jb_ca_public_key = jb_ca.public_key()
jb_ca_public_key_n = jb_ca_public_key.public_numbers().n
jb_ca_public_key_e = jb_ca_public_key.public_numbers().e

f.write("[Result]\n")
f.write(f"EQUAL,{s},{jb_ca_public_key_e},{jb_ca_public_key_n}->{r}")


def create_digest_info(hash_obj):
# 构造 AlgorithmIdentifier
algorithm_id = DerSequence([
DerObjectId(hash_obj.oid), # 哈希算法的 OID
DerNull() # NULL 参数
])

# 构造完整的 DigestInfo
digest_info = DerSequence([
algorithm_id,
DerOctetString(hash_obj.digest())
])

return digest_info.encode()


def create_pkcs1v15_padding(digest_info, key_size_bytes):
# 计算所需的填充长度
padding_length = key_size_bytes - len(digest_info) - 3

# 验证填充长度(PKCS#1 要求至少8字节填充)
if padding_length < 8:
raise ValueError(f"Key size ({key_size_bytes * 8} bits) too small for hash digest")

# 构造标准的 PKCS#1 v1.5 填充格式
return b'\x00\x01' + b'\xff' * padding_length + b'\x00' + digest_info

# 从 ca.cer 文件中读取证书 (PEM 格式)
with open('ca.cer', 'rb') as cert_file:
cert = x509.load_pem_x509_certificate(cert_file.read())
cert_der = cert.public_bytes(encoding=serialization.Encoding.DER)
my_cert_content = base64.b64encode(cert_der).decode('utf-8')

# 从 license.json 文件中读取许可证数据
with open('license.json', 'r', encoding='utf-8') as f:
license_config = json.load(f)

licenseId = license_config['licenseId']
licensePart = json.dumps(license_config)

with open('ca.key') as prifile:
# 使用私钥对HASH值进行签名
private_key = RSA.import_key(prifile.read())
digest = SHA1.new(licensePart.encode('utf-8'))
signature = pkcs1_15.new(private_key).sign(digest)

sig_results = base64.b64encode(signature)
licensePartBase64 = base64.b64encode(licensePart.encode('utf-8'))

# 验签
cert.public_key().verify(
base64.b64decode(sig_results),
base64.b64decode(licensePartBase64),
padding=padding.PKCS1v15(),
algorithm=hashes.SHA1(),
)

# 将激活码写入 key.txt 文件
active_code = licenseId + "-" + licensePartBase64.decode('utf-8') + "-" + sig_results.decode('utf-8') + "-" + my_cert_content
with open('key.txt', 'w', encoding='utf-8') as f:
f.write(active_code)

# 生成 power.conf 文件
create_power_file(active_code)

使用方法很简单,在目录下创建名为license.json的文件,内容大致如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"licenseId": "ZCB571FZHV",
"licenseeName": "jack",
"assigneeName": "",
"assigneeEmail": "",
"licenseRestriction": "",
"checkConcurrentUse": false,
"products": [
{
"code": "CL",
"fallbackDate": "2099-12-31",
"paidUpTo": "2099-12-31",
"extended": true
}
],
"metadata": "0120220701PSAN000005",
"hash": "TRIAL:-594988122",
"gracePeriodDays": 7,
"autoProlongated": false,
"isAutoProlongated": false
}

根据自己的需求追加产品授权信息,然后运行脚本会得到power.confkey.txt文件。