mirror of
				https://github.com/NotoChen/Jetbrains-Help.git
				synced 2025-11-01 01:38:31 +08:00 
			
		
		
		
	Merge 4b4f81f01b into 882dbfc126
				
					
				
			This commit is contained in:
		
						commit
						db04ba66e3
					
				|  | @ -54,7 +54,7 @@ | |||
| ### 功能列表 | ||||
| 
 | ||||
| | 功能                         | DID | | ||||
| |:-------------------------|:---:| | ||||
| |:---------------------------|:---:| | ||||
| | Jetbrains全产品支持             |  ✅  | | ||||
| | Jetbrains全插件支持             |  ✅  | | ||||
| | 插件库全自动订阅官网更新               |  ✅  | | ||||
|  | @ -66,6 +66,7 @@ | |||
| | 插件默认按名称排序                  |  ✅  | | ||||
| | 支持local/jar/dockerfile运行   |  ✅  | | ||||
| | 单码全家桶激活支持                  |  ✅  | | ||||
| | Jetbrains License Server支持 |  ✅  | | ||||
| | ……                         | ☑️  | | ||||
| 
 | ||||
| ## 运行教程 | ||||
|  | @ -139,7 +140,7 @@ | |||
| - **点击** `编辑自定义虚拟机选型` | ||||
| - **键入** 如下配置 | ||||
| ``` | ||||
| -javaagent:you-path/ja-netfilter.jar | ||||
| -javaagent:you-path/ja-netfilter.jar=jetbrains | ||||
| --add-opens=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED | ||||
| --add-opens=java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED | ||||
| ``` | ||||
|  | @ -157,7 +158,7 @@ | |||
| - **点击** `编辑JVM选项` | ||||
| - **键入** 如下配置 | ||||
| ``` | ||||
| -javaagent:you-path/ja-netfilter.jar | ||||
| -javaagent:you-path/ja-netfilter.jar=jetbrains | ||||
| --add-opens=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED | ||||
| --add-opens=java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED | ||||
| ``` | ||||
|  |  | |||
|  | @ -1,9 +1,11 @@ | |||
| package com.jetbrains.help; | ||||
| 
 | ||||
| import cn.hutool.core.collection.CollUtil; | ||||
| import cn.hutool.core.io.IoUtil; | ||||
| import cn.hutool.core.net.Ipv4Util; | ||||
| import cn.hutool.core.text.CharSequenceUtil; | ||||
| import cn.hutool.core.util.StrUtil; | ||||
| import cn.hutool.crypto.SecureUtil; | ||||
| import cn.hutool.extra.spring.SpringUtil; | ||||
| import com.jetbrains.help.context.*; | ||||
| import lombok.SneakyThrows; | ||||
|  | @ -17,6 +19,7 @@ import org.springframework.scheduling.annotation.EnableScheduling; | |||
| import org.springframework.scheduling.annotation.Scheduled; | ||||
| 
 | ||||
| import java.net.InetAddress; | ||||
| import java.security.cert.Certificate; | ||||
| import java.util.Collection; | ||||
| import java.util.List; | ||||
| 
 | ||||
|  | @ -37,7 +40,6 @@ public class JetbrainsHelpApplication { | |||
|         PluginsContextHolder.init(); | ||||
|         CertificateContextHolder.init(); | ||||
|         AgentContextHolder.init(); | ||||
| 
 | ||||
|         InetAddress localHost = InetAddress.getLocalHost(); | ||||
|         String address = CharSequenceUtil.format("http://{}:{}", localHost.getHostAddress(), SpringUtil.getProperty("server.port")); | ||||
|         String runSuccessWarn = "\n====================================================================================\n" + | ||||
|  |  | |||
|  | @ -26,7 +26,7 @@ public class AgentContextHolder { | |||
| 
 | ||||
|     private static final String JA_NETFILTER_FILE_PATH = "external/agent/ja-netfilter"; | ||||
| 
 | ||||
|     private static final String POWER_CONF_FILE_NAME = JA_NETFILTER_FILE_PATH + "/config/power.conf"; | ||||
|     private static final String POWER_CONF_FILE_NAME = JA_NETFILTER_FILE_PATH + "/config-jetbrains/power.conf"; | ||||
| 
 | ||||
|     private static File jaNetfilterFile; | ||||
| 
 | ||||
|  | @ -35,7 +35,7 @@ public class AgentContextHolder { | |||
|     public static void init() { | ||||
|         log.info("Agent context init loading..."); | ||||
|         jaNetfilterZipFile = FileTools.getFileOrCreat(JA_NETFILTER_FILE_PATH + ".zip"); | ||||
|         if (!FileTools.fileExists(JA_NETFILTER_FILE_PATH)) { | ||||
|         if (FileTools.fileNotExists(JA_NETFILTER_FILE_PATH)) { | ||||
|             unzipJaNetfilter(); | ||||
|             if (!powerConfHasInit()) { | ||||
|                 log.info("Agent config init loading..."); | ||||
|  | @ -75,14 +75,27 @@ public class AgentContextHolder { | |||
| 
 | ||||
|     @SneakyThrows | ||||
|     private static String generatePowerConfigRule() { | ||||
|         X509Certificate crt = (X509Certificate) KeyUtil.readX509Certificate(IoUtil.toStream(CertificateContextHolder.crtFile())); | ||||
|         RSAPublicKey publicKey = (RSAPublicKey) PemUtil.readPemPublicKey(IoUtil.toStream(CertificateContextHolder.publicKeyFile())); | ||||
|         RSAPublicKey rootPublicKey = (RSAPublicKey) PemUtil.readPemPublicKey(IoUtil.toStream(CertificateContextHolder.rootKeyFile())); | ||||
|         StringBuilder result = new StringBuilder(); | ||||
|         X509Certificate crt = (X509Certificate) KeyUtil.readX509Certificate(IoUtil.toStream(CertificateContextHolder.getCrtFile())); | ||||
|         X509Certificate licenseCrt = (X509Certificate) KeyUtil.readX509Certificate(IoUtil.toStream(CertificateContextHolder.getLicenseCrtFile())); | ||||
|         RSAPublicKey publicKey = (RSAPublicKey) PemUtil.readPemPublicKey(IoUtil.toStream(CertificateContextHolder.getPublicKeyFile())); | ||||
|         RSAPublicKey rootPublicKey = (RSAPublicKey) PemUtil.readPemPublicKey(IoUtil.toStream(CertificateContextHolder.getRootKeyFile())); | ||||
|         RSAPublicKey rootLicensePublicKey = (RSAPublicKey) PemUtil.readPemPublicKey(IoUtil.toStream(CertificateContextHolder.getRootLicenseKeyFile())); | ||||
|         BigInteger x = new BigInteger(1, crt.getSignature()); | ||||
|         BigInteger y = BigInteger.valueOf(65537L); | ||||
|         BigInteger y = rootPublicKey.getPublicExponent(); | ||||
|         BigInteger z = rootPublicKey.getModulus(); | ||||
|         BigInteger r = x.modPow(publicKey.getPublicExponent(), publicKey.getModulus()); | ||||
|         return CharSequenceUtil.format("EQUAL,{},{},{}->{}", x, y, z, r); | ||||
|         result.append("; Activation Code").append("\n") | ||||
|                 .append(CharSequenceUtil.format("EQUAL,{},{},{}->{}", x, y, z, r)) | ||||
|                 .append("\n"); | ||||
|         x = new BigInteger(1, licenseCrt.getSignature()); | ||||
|         z = rootLicensePublicKey.getModulus(); | ||||
|         y = rootLicensePublicKey.getPublicExponent(); | ||||
|         r = x.modPow(publicKey.getPublicExponent(), publicKey.getModulus()); | ||||
|         result.append("; License Server").append("\n") | ||||
|                 .append(CharSequenceUtil.format("EQUAL,{},{},{}->{}", x, y, z, r)) | ||||
|                 .append("\n"); | ||||
|         return result.toString(); | ||||
|     } | ||||
| 
 | ||||
|     private static String generatePowerConfigStr(String ruleValue) { | ||||
|  |  | |||
|  | @ -3,10 +3,12 @@ package com.jetbrains.help.context; | |||
| import cn.hutool.core.date.DateField; | ||||
| import cn.hutool.core.date.DateUtil; | ||||
| import cn.hutool.core.io.FileUtil; | ||||
| import cn.hutool.core.io.IoUtil; | ||||
| import cn.hutool.crypto.PemUtil; | ||||
| import cn.hutool.crypto.SecureUtil; | ||||
| import com.jetbrains.help.util.FileTools; | ||||
| import lombok.AccessLevel; | ||||
| import lombok.Getter; | ||||
| import lombok.NoArgsConstructor; | ||||
| import lombok.extern.slf4j.Slf4j; | ||||
| import org.bouncycastle.asn1.x500.X500Name; | ||||
|  | @ -27,29 +29,47 @@ import java.security.cert.Certificate; | |||
| import java.security.cert.CertificateEncodingException; | ||||
| import java.security.cert.CertificateException; | ||||
| 
 | ||||
| /** | ||||
|  * <a href="https://github.com/JetBrains/marketplace-makemecoffee-plugin/blob/master/src/main/java/com/company/license/CheckLicense.java">源证书来源</a> | ||||
|  */ | ||||
| @Slf4j | ||||
| @NoArgsConstructor(access = AccessLevel.PRIVATE) | ||||
| public class CertificateContextHolder { | ||||
| 
 | ||||
|     private static final String ROOT_KEY_FILE_NAME = "external/certificate/root.key"; | ||||
|     private static final String ROOT_LICENSE_KEY_FILE_NAME = "external/certificate/license-root.key"; | ||||
|     private static final String PRIVATE_KEY_FILE_NAME = "external/certificate/private.key"; | ||||
|     private static final String PUBLIC_KEY_FILE_NAME = "external/certificate/public.key"; | ||||
|     private static final String CET_FILE_NAME = "external/certificate/ca.crt"; | ||||
|     private static final String LICENSE_CET_FILE_NAME = "external/certificate/license-ca.crt"; | ||||
|     public static final String SERVER_UID = "cikaros.top"; | ||||
| 
 | ||||
|     @Getter | ||||
|     private static File rootKeyFile; | ||||
| 
 | ||||
|     @Getter | ||||
|     private static File rootLicenseKeyFile; | ||||
| 
 | ||||
|     @Getter | ||||
|     private static File privateKeyFile; | ||||
| 
 | ||||
|     @Getter | ||||
|     private static File publicKeyFile; | ||||
| 
 | ||||
|     @Getter | ||||
|     private static File crtFile; | ||||
| 
 | ||||
|     @Getter | ||||
|     private static File licenseCrtFile; | ||||
| 
 | ||||
|     public static void init() { | ||||
|         log.info("certificate context init loading..."); | ||||
|         rootKeyFile = FileTools.getFileOrCreat(ROOT_KEY_FILE_NAME); | ||||
|         if (!FileTools.fileExists(PRIVATE_KEY_FILE_NAME) | ||||
|                 || !FileTools.fileExists(PUBLIC_KEY_FILE_NAME) | ||||
|                 || !FileTools.fileExists(CET_FILE_NAME)) { | ||||
|         rootLicenseKeyFile = FileTools.getFileOrCreat(ROOT_LICENSE_KEY_FILE_NAME); | ||||
|         if (FileTools.fileNotExists(PRIVATE_KEY_FILE_NAME) | ||||
|                 || FileTools.fileNotExists(PUBLIC_KEY_FILE_NAME) | ||||
|                 || FileTools.fileNotExists(CET_FILE_NAME) | ||||
|                 || FileTools.fileNotExists(LICENSE_CET_FILE_NAME)) { | ||||
|             log.info("certificate context generate loading..."); | ||||
|             generateCertificate(); | ||||
|             log.info("certificate context generate success!"); | ||||
|  | @ -57,27 +77,11 @@ public class CertificateContextHolder { | |||
|             privateKeyFile = FileTools.getFileOrCreat(PRIVATE_KEY_FILE_NAME); | ||||
|             publicKeyFile = FileTools.getFileOrCreat(PUBLIC_KEY_FILE_NAME); | ||||
|             crtFile = FileTools.getFileOrCreat(CET_FILE_NAME); | ||||
|             licenseCrtFile = FileTools.getFileOrCreat(LICENSE_CET_FILE_NAME); | ||||
|         } | ||||
|         log.info("certificate context init success !"); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     public static File rootKeyFile() { | ||||
|         return CertificateContextHolder.rootKeyFile; | ||||
|     } | ||||
| 
 | ||||
|     public static File privateKeyFile() { | ||||
|         return CertificateContextHolder.privateKeyFile; | ||||
|     } | ||||
| 
 | ||||
|     public static File publicKeyFile() { | ||||
|         return CertificateContextHolder.publicKeyFile; | ||||
|     } | ||||
| 
 | ||||
|     public static File crtFile() { | ||||
|         return CertificateContextHolder.crtFile; | ||||
|     } | ||||
| 
 | ||||
|     public static void generateCertificate() { | ||||
|         KeyPair keyPair = SecureUtil.generateKeyPair("RSA", 4096); | ||||
|         PrivateKey privateKey = keyPair.getPrivate(); | ||||
|  | @ -86,16 +90,8 @@ public class CertificateContextHolder { | |||
|         PemUtil.writePemObject("PRIVATE KEY", privateKey.getEncoded(), FileUtil.getWriter(privateKeyFile, StandardCharsets.UTF_8, false)); | ||||
|         publicKeyFile = FileTools.getFileOrCreat(PUBLIC_KEY_FILE_NAME); | ||||
|         PemUtil.writePemObject("PUBLIC KEY", publicKey.getEncoded(), FileUtil.getWriter(publicKeyFile, StandardCharsets.UTF_8, false)); | ||||
|         JcaX509v3CertificateBuilder certificateBuilder = new JcaX509v3CertificateBuilder( | ||||
|                 new X500Name("CN=JetProfile CA"), | ||||
|                 BigInteger.valueOf(System.currentTimeMillis()), | ||||
|                 DateUtil.yesterday(), | ||||
|                 DateUtil.date().offset(DateField.YEAR, 100), | ||||
|                 new X500Name("CN=Jetbrains-Help"), | ||||
|                 SubjectPublicKeyInfo.getInstance(publicKey.getEncoded())); | ||||
|         try { | ||||
|             ContentSigner signer = new JcaContentSignerBuilder("SHA256withRSA").build(privateKey); | ||||
|             Certificate certificate = new JcaX509CertificateConverter().setProvider("BC").getCertificate(certificateBuilder.build(signer)); | ||||
|             Certificate certificate = generateCertificate(keyPair,"JetProfile CA","Jetbrains-Help"); | ||||
|             crtFile = FileTools.getFileOrCreat(CET_FILE_NAME); | ||||
|             PemUtil.writePemObject("CERTIFICATE", certificate.getEncoded(), FileUtil.getWriter(crtFile, StandardCharsets.UTF_8, false)); | ||||
|         } catch (OperatorCreationException e) { | ||||
|  | @ -105,7 +101,33 @@ public class CertificateContextHolder { | |||
|         } catch (CertificateException e) { | ||||
|             throw new IllegalArgumentException("The certificate read exception", e); | ||||
|         } | ||||
|         //TODO 创建License Servers CA | ||||
|         try { | ||||
|             Certificate certificate = generateCertificate(keyPair,"License Servers CA",String.format("%s.lsrv.jetbrains.com", SERVER_UID)); | ||||
|             licenseCrtFile = FileTools.getFileOrCreat(LICENSE_CET_FILE_NAME); | ||||
|             PemUtil.writePemObject("CERTIFICATE", certificate.getEncoded(), FileUtil.getWriter(licenseCrtFile, StandardCharsets.UTF_8, false)); | ||||
|         } catch (OperatorCreationException e) { | ||||
|             throw new IllegalArgumentException("Certificate operator creation exception", e); | ||||
|         } catch (CertificateEncodingException e) { | ||||
|             throw new IllegalArgumentException("The certificate encoding exception", e); | ||||
|         } catch (CertificateException e) { | ||||
|             throw new IllegalArgumentException("The certificate read exception", e); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private static Certificate generateCertificate(KeyPair keyPair, String issuer, String subject) throws OperatorCreationException, CertificateException { | ||||
|         PrivateKey privateKey = keyPair.getPrivate(); | ||||
|         PublicKey publicKey = keyPair.getPublic(); | ||||
|         JcaX509v3CertificateBuilder certificateBuilder = new JcaX509v3CertificateBuilder( | ||||
|                 new X500Name(String.format("CN=%s", issuer)), | ||||
|                 BigInteger.valueOf(System.currentTimeMillis()), | ||||
|                 DateUtil.yesterday(), | ||||
|                 DateUtil.date().offset(DateField.YEAR, 100), | ||||
|                 new X500Name(String.format("CN=%s", subject)), | ||||
|                 SubjectPublicKeyInfo.getInstance(publicKey.getEncoded())); | ||||
|         ContentSigner signer = new JcaContentSignerBuilder("SHA256withRSA").build(privateKey); | ||||
|         return new JcaX509CertificateConverter().setProvider("BC").getCertificate(certificateBuilder.build(signer)); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -1,20 +1,24 @@ | |||
| package com.jetbrains.help.context; | ||||
| 
 | ||||
| import cn.hutool.core.codec.Base64; | ||||
| import cn.hutool.core.collection.CollUtil; | ||||
| import cn.hutool.core.io.IoUtil; | ||||
| import cn.hutool.core.text.CharSequenceUtil; | ||||
| import cn.hutool.core.util.IdUtil; | ||||
| import cn.hutool.core.util.XmlUtil; | ||||
| import cn.hutool.crypto.PemUtil; | ||||
| import cn.hutool.crypto.SecureUtil; | ||||
| import cn.hutool.crypto.SignUtil; | ||||
| import cn.hutool.crypto.asymmetric.Sign; | ||||
| import cn.hutool.json.JSONUtil; | ||||
| import lombok.AccessLevel; | ||||
| import lombok.Builder; | ||||
| import lombok.Data; | ||||
| import lombok.NoArgsConstructor; | ||||
| import lombok.extern.slf4j.Slf4j; | ||||
| import org.w3c.dom.Document; | ||||
| import org.w3c.dom.Node; | ||||
| 
 | ||||
| import java.math.BigInteger; | ||||
| import java.security.PrivateKey; | ||||
| import java.security.PublicKey; | ||||
| import java.security.cert.Certificate; | ||||
|  | @ -22,7 +26,7 @@ import java.security.cert.CertificateEncodingException; | |||
| import java.util.List; | ||||
| import java.util.Set; | ||||
| 
 | ||||
| import static cn.hutool.crypto.asymmetric.SignAlgorithm.SHA1withRSA; | ||||
| import static cn.hutool.crypto.asymmetric.SignAlgorithm.*; | ||||
| 
 | ||||
| @Slf4j | ||||
| @NoArgsConstructor(access = AccessLevel.PRIVATE) | ||||
|  | @ -43,9 +47,9 @@ public class LicenseContextHolder { | |||
|                 .setProducts(products); | ||||
|         String licensePartJson = JSONUtil.toJsonStr(licensePart); | ||||
|         String licensePartBase64 = Base64.encode(licensePartJson); | ||||
|         PrivateKey privateKey = PemUtil.readPemPrivateKey(IoUtil.toStream(CertificateContextHolder.privateKeyFile())); | ||||
|         PublicKey publicKey = PemUtil.readPemPublicKey(IoUtil.toStream(CertificateContextHolder.publicKeyFile())); | ||||
|         Certificate certificate = SecureUtil.readX509Certificate(IoUtil.toStream(CertificateContextHolder.crtFile())); | ||||
|         PrivateKey privateKey = PemUtil.readPemPrivateKey(IoUtil.toStream(CertificateContextHolder.getPrivateKeyFile())); | ||||
|         PublicKey publicKey = PemUtil.readPemPublicKey(IoUtil.toStream(CertificateContextHolder.getPublicKeyFile())); | ||||
|         Certificate certificate = SecureUtil.readX509Certificate(IoUtil.toStream(CertificateContextHolder.getCrtFile())); | ||||
|         Sign sign = SignUtil.sign(SHA1withRSA, privateKey.getEncoded(), publicKey.getEncoded()); | ||||
|         String signatureBase64 = Base64.encode(sign.sign(licensePartJson)); | ||||
|         String certBase64; | ||||
|  | @ -74,4 +78,147 @@ public class LicenseContextHolder { | |||
|         private String paidUpTo; | ||||
|     } | ||||
| 
 | ||||
|     public static String obtainTicket(ObtainTicketRequest request) { | ||||
|         PrivateKey privateKey = PemUtil.readPemPrivateKey(IoUtil.toStream(CertificateContextHolder.getPrivateKeyFile())); | ||||
|         PublicKey publicKey = PemUtil.readPemPublicKey(IoUtil.toStream(CertificateContextHolder.getPublicKeyFile())); | ||||
|         Certificate cert = SecureUtil.readX509Certificate(IoUtil.toStream(CertificateContextHolder.getCrtFile())); | ||||
|         Certificate licenseCert = SecureUtil.readX509Certificate(IoUtil.toStream(CertificateContextHolder.getLicenseCrtFile())); | ||||
|         ObtainTicketResponse response = obtainTicket(privateKey, publicKey, cert, licenseCert, request); | ||||
|         Document document = XmlUtil.beanToXml(response); | ||||
|         String result = XmlUtil.toStr(document, "UTF-8", true, true); | ||||
|         //!important 必须去除XML中所有的换行和空格,即紧凑模式 | ||||
|         result = result.replaceAll("[\n ]", ""); | ||||
|         Sign sign = SignUtil.sign(SHA1withRSA, privateKey.getEncoded(), publicKey.getEncoded()); | ||||
|         String sign64 = Base64.encode(sign.sign(result)); | ||||
|         String licenseCertBase64; | ||||
|         try { | ||||
|             licenseCertBase64 = Base64.encode(licenseCert.getEncoded()); | ||||
|         } catch (CertificateEncodingException e) { | ||||
|             throw new IllegalArgumentException("Certificate extraction failed", e); | ||||
|         } | ||||
|         //<!--  SHA1withRSA-{下面 xml 主体内容的签名 }-{license Server CA}  --> | ||||
|         String signRes = String.format("<!-- SHA1withRSA-%s-%s -->", sign64, licenseCertBase64); | ||||
|         return signRes + "\n" + result; | ||||
|     } | ||||
| 
 | ||||
|     public static ObtainTicketResponse obtainTicket(PrivateKey privateKey, PublicKey publicKey, Certificate cert, Certificate licenseCert, ObtainTicketRequest request) { | ||||
|         String certBase64; | ||||
|         String licenseCertBase64; | ||||
|         try { | ||||
|             certBase64 = Base64.encode(cert.getEncoded()); | ||||
|             licenseCertBase64 = Base64.encode(licenseCert.getEncoded()); | ||||
|         } catch (CertificateEncodingException e) { | ||||
|             throw new IllegalArgumentException("Certificate extraction failed", e); | ||||
|         } | ||||
| 
 | ||||
|         long time = System.currentTimeMillis(); | ||||
|         long leaseTime = time + (1000L * 60 * 60 * 24 * 365); | ||||
|         //{失效时间戳}:{serverUid} | ||||
|         String serverLease = String.format("%d:%s", leaseTime, CertificateContextHolder.SERVER_UID); | ||||
| 
 | ||||
|         Sign sign = SignUtil.sign(SHA1withRSA, privateKey.getEncoded(), publicKey.getEncoded()); | ||||
|         String signatureBase64 = Base64.encode(sign.sign(String.format("%d:%s", time, request.getMachineId()))); | ||||
|         //{时间戳}:{客户端提交的machineId}:SHA1withRSA:{对({时间戳}:{客户端提交的machineId})的签名}:{license Server CA} | ||||
|         String confirmationStamp = String.format("%d:%s:SHA1withRSA:%s:%s", | ||||
|                 time, request.getMachineId(), signatureBase64, licenseCertBase64); | ||||
| 
 | ||||
|         sign = SignUtil.sign(SHA512withRSA, privateKey.getEncoded(), publicKey.getEncoded()); | ||||
|         byte[] bytes = sign.sign(serverLease); | ||||
|         signatureBase64 = Base64.encode(bytes); | ||||
|         //SHA512withRSA-{对「serverLease」的值进行签名}-{JetProfile CA} | ||||
|         String leaseSignature = String.format("SHA512withRSA-%s-%s", signatureBase64, certBase64); | ||||
| //        String leaseSignature = new BigInteger(1,bytes).toString(); | ||||
|         return ObtainTicketResponse.builder() | ||||
|                 .action(Action.NONE) | ||||
|                 .responseCode(ResponseCode.OK) | ||||
|                 .message(ResponseCode.OK.name()) | ||||
|                 .ticketId("20") | ||||
|                 .ticketProperties(String.format("licensee=%s\tlicenseType=4\tmetadata=0120230914PSAX000005", request.getUserName())) | ||||
|                 .validationPeriod(71829000L) | ||||
|                 .validationDeadlinePeriod(-1) | ||||
|                 .prolongationPeriod(63462000L)//有效期 | ||||
|                 .salt(request.getSalt()) | ||||
|                 .serverUid(CertificateContextHolder.SERVER_UID) | ||||
|                 .serverLease(serverLease) | ||||
|                 .confirmationStamp(confirmationStamp) | ||||
|                 .leaseSignature(leaseSignature) | ||||
|                 .build(); | ||||
|     } | ||||
| 
 | ||||
|     public enum Action { | ||||
|         NONE, | ||||
|     } | ||||
| 
 | ||||
|     public enum ResponseCode { | ||||
|         OK | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 获取认证票据 | ||||
|      * | ||||
|      * @author Cikaros | ||||
|      * @date 2023/11/1 | ||||
|      */ | ||||
|     @Data | ||||
|     @Builder | ||||
|     public static class ObtainTicketResponse { | ||||
|         @Builder.Default | ||||
|         private Action action = Action.NONE; | ||||
|         private String confirmationStamp; | ||||
|         private String leaseSignature; | ||||
|         @Builder.Default | ||||
|         private String message = ""; | ||||
|         @Builder.Default | ||||
|         private Long prolongationPeriod = 1800000L; | ||||
|         @Builder.Default | ||||
|         private ResponseCode responseCode = ResponseCode.OK; | ||||
|         private String salt; | ||||
|         private String serverLease; | ||||
|         private String serverUid; | ||||
|         @Builder.Default | ||||
|         private String ticketId = "1"; | ||||
|         private String ticketProperties; | ||||
|         @Builder.Default | ||||
|         private Integer validationDeadlinePeriod = -1; | ||||
|         @Builder.Default | ||||
|         private Long validationPeriod = 1800000L; | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @author Cikaros | ||||
|      * @date 2023/10/20 | ||||
|      */ | ||||
|     @Data | ||||
|     public static class ObtainTicketRequest { | ||||
| 
 | ||||
|         private String ideProductCode; | ||||
| 
 | ||||
|         private Boolean empty; | ||||
| 
 | ||||
|         private String buildDate; | ||||
| 
 | ||||
|         private String buildNumber; | ||||
| 
 | ||||
|         private Integer clientVersion; | ||||
| 
 | ||||
|         private String hostName; | ||||
| 
 | ||||
|         private String machineId; | ||||
| 
 | ||||
|         private String productCode; | ||||
| 
 | ||||
|         private String productFamilyId; | ||||
| 
 | ||||
|         private String salt; | ||||
| 
 | ||||
|         private Boolean secure; | ||||
| 
 | ||||
|         private String userName; | ||||
| 
 | ||||
|         private String version; | ||||
| 
 | ||||
|         private Long versionNumber; | ||||
| 
 | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -0,0 +1,22 @@ | |||
| package com.jetbrains.help.controller; | ||||
| 
 | ||||
| import com.jetbrains.help.context.LicenseContextHolder; | ||||
| import org.springframework.http.MediaType; | ||||
| import org.springframework.http.ResponseEntity; | ||||
| import org.springframework.stereotype.Controller; | ||||
| import org.springframework.web.bind.annotation.GetMapping; | ||||
| import org.springframework.web.bind.annotation.RequestMapping; | ||||
| import org.springframework.web.bind.annotation.ResponseBody; | ||||
| 
 | ||||
| @Controller | ||||
| @RequestMapping("/rpc") | ||||
| public class ObtainTicketApiController { | ||||
| 
 | ||||
|     @GetMapping(value = "/obtainTicket.action", produces = {"application/xml;charset=utf-8"}) | ||||
|     @ResponseBody | ||||
|     public ResponseEntity<String> obtainTicket(LicenseContextHolder.ObtainTicketRequest request) { | ||||
|         return ResponseEntity.ok() | ||||
|                 .contentType(MediaType.APPLICATION_XML) | ||||
|                 .body(LicenseContextHolder.obtainTicket(request)); | ||||
|     } | ||||
| } | ||||
|  | @ -4,15 +4,19 @@ import cn.hutool.core.io.FileUtil; | |||
| import cn.hutool.core.io.IoUtil; | ||||
| import cn.hutool.core.text.CharSequenceUtil; | ||||
| import cn.hutool.core.util.StrUtil; | ||||
| import cn.hutool.crypto.digest.DigestUtil; | ||||
| import com.jetbrains.help.JetbrainsHelpApplication; | ||||
| import com.jetbrains.help.context.AgentContextHolder; | ||||
| import com.jetbrains.help.context.PluginsContextHolder; | ||||
| import com.jetbrains.help.context.ProductsContextHolder; | ||||
| import com.jetbrains.help.properties.JetbrainsHelpProperties; | ||||
| import jakarta.servlet.http.HttpServletRequest; | ||||
| import lombok.AllArgsConstructor; | ||||
| import lombok.RequiredArgsConstructor; | ||||
| import org.springframework.cglib.core.Local; | ||||
| import org.springframework.core.io.InputStreamResource; | ||||
| import org.springframework.core.io.Resource; | ||||
| import org.springframework.http.HttpRequest; | ||||
| import org.springframework.http.MediaType; | ||||
| import org.springframework.http.ResponseEntity; | ||||
| import org.springframework.stereotype.Controller; | ||||
|  | @ -20,11 +24,13 @@ import org.springframework.ui.Model; | |||
| import org.springframework.web.bind.annotation.GetMapping; | ||||
| import org.springframework.web.bind.annotation.RequestParam; | ||||
| import org.springframework.web.bind.annotation.ResponseBody; | ||||
| import org.thymeleaf.util.DateUtils; | ||||
| 
 | ||||
| import java.io.File; | ||||
| import java.util.List; | ||||
| import java.util.*; | ||||
| 
 | ||||
| import static org.springframework.http.HttpHeaders.CONTENT_DISPOSITION; | ||||
| import static org.springframework.http.MediaType.APPLICATION_JSON; | ||||
| import static org.springframework.http.MediaType.APPLICATION_OCTET_STREAM; | ||||
| 
 | ||||
| @Controller | ||||
|  | @ -34,15 +40,37 @@ public class IndexController { | |||
|     private final JetbrainsHelpProperties jetbrainsHelpProperties; | ||||
| 
 | ||||
|     @GetMapping | ||||
|     public String index(Model model) { | ||||
|     public String index(HttpServletRequest request,Model model) { | ||||
|         String basePath = (request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath()); | ||||
|         List<ProductsContextHolder.ProductCache> productCacheList = ProductsContextHolder.productCacheList(); | ||||
|         List<PluginsContextHolder.PluginCache> pluginCacheList = PluginsContextHolder.pluginCacheList(); | ||||
|         model.addAttribute("products", productCacheList); | ||||
|         model.addAttribute("plugins", pluginCacheList); | ||||
|         model.addAttribute("defaults", jetbrainsHelpProperties); | ||||
|         model.addAttribute("basePath", basePath); | ||||
|         return "index"; | ||||
|     } | ||||
| 
 | ||||
|     @GetMapping("/scoop/ja-netfilter") | ||||
|     @ResponseBody | ||||
|     public ResponseEntity<Map<String,Object>> scoopInstall(HttpServletRequest request) { | ||||
|         String basePath = (request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath()); | ||||
|         Map<String, Object> json = new HashMap<>(); | ||||
|         String version = DateUtils.format(new Date(),"yyyyMMdd", Locale.getDefault()); | ||||
|         json.put("version", version); | ||||
|         json.put("description", "JetBrains' dragon slayer"); | ||||
|         json.put("homepage", "https://cikaros.top"); | ||||
|         json.put("license", "MIT"); | ||||
|         json.put("url", String.format("%s/ja-netfilter#dl.zip", basePath)); | ||||
|         json.put("extract_to", Arrays.asList("", "config-jetbrains", "plugins-jetbrains", "scripts", "vmoptions")); | ||||
|         json.put("hash", DigestUtil.sha256Hex(AgentContextHolder.jaNetfilterZipFile())); | ||||
|         json.put("post_install", "cscript $dir/scripts/install-current-user.vbs"); | ||||
|         json.put("pre_uninstall", "cscript $dir/scripts/uninstall-current-user.vbs"); | ||||
|         return ResponseEntity.ok() | ||||
|                 .contentType(APPLICATION_JSON) | ||||
|                 .body(json); | ||||
|     } | ||||
| 
 | ||||
|     @GetMapping("search") | ||||
|     public String index(@RequestParam(required = false) String search, Model model) { | ||||
|         List<ProductsContextHolder.ProductCache> productCacheList = ProductsContextHolder.productCacheList(); | ||||
|  | @ -70,4 +98,5 @@ public class IndexController { | |||
|                 .contentType(APPLICATION_OCTET_STREAM) | ||||
|                 .body(new InputStreamResource(FileUtil.getInputStream(jaNetfilterZipFile))); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -3,12 +3,10 @@ package com.jetbrains.help.util; | |||
| import cn.hutool.core.io.FileUtil; | ||||
| import cn.hutool.core.text.CharSequenceUtil; | ||||
| import cn.hutool.core.util.ObjectUtil; | ||||
| import cn.hutool.core.util.StrUtil; | ||||
| import org.springframework.boot.system.ApplicationHome; | ||||
| import org.springframework.core.io.ClassPathResource; | ||||
| 
 | ||||
| import java.io.File; | ||||
| import java.io.IOException; | ||||
| 
 | ||||
| public interface FileTools { | ||||
| 
 | ||||
|  | @ -19,6 +17,10 @@ public interface FileTools { | |||
|         return getFile(path).exists(); | ||||
|     } | ||||
| 
 | ||||
|     static boolean fileNotExists(String path) { | ||||
|         return !fileExists(path); | ||||
|     } | ||||
| 
 | ||||
|     static File getFile(String path) { | ||||
|         File homeDir = application.getDir(); | ||||
|         File source = application.getSource(); | ||||
|  |  | |||
|  | @ -6,6 +6,6 @@ spring: | |||
| server: | ||||
|   port: 10768 | ||||
| help: | ||||
|   default-license-name: 光云 | ||||
|   default-assignee-name: 藏柏 | ||||
|   default-expiry-date: 2111-11-11 | ||||
|   default-license-name: cikaros.top | ||||
|   default-assignee-name: Cikaros | ||||
|   default-expiry-date: 2099-12-31 | ||||
							
								
								
									
										
											BIN
										
									
								
								src/main/resources/external/agent/ja-netfilter.zip
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/main/resources/external/agent/ja-netfilter.zip
									
									
									
									
										vendored
									
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										31
									
								
								src/main/resources/external/certificate/license-root.key
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/main/resources/external/certificate/license-root.key
									
									
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,31 @@ | |||
| -----BEGIN CERTIFICATE----- | ||||
| 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 | ||||
| 7tlDFnvaRnQukqSG+VqNWg== | ||||
| -----END CERTIFICATE----- | ||||
|  | @ -13,7 +13,8 @@ | |||
|     <p> | ||||
|         🇨🇳 下载 <a href="ja-netfilter" title="Download jetbra first">ja-netfilter.zip</a> , 然后配置 | ||||
|         你的JetBrains IDE(anything)'s <a onclick="showVmoptins()">IDE.vmoptions</a> 配置文件!<br> | ||||
|         🇨🇳 当然你也可以 <a onclick="showLicenseForm()">重新定制激活授权</a> 用以自定义你的激活信息!</br> | ||||
|         🇨🇳 当然你也可以 <a onclick="showLicenseForm()">重新定制激活授权</a> 用以自定义你的激活信息!<br> | ||||
|         🇨🇳 也可使用Scoop进行安装:<code th:text="'scoop install ' + ${basePath}+'/scoop/ja-netfilter'"></code><br> | ||||
|         <strong>🇨🇳 请注意,此页面仅由 <span | ||||
|                 th:text="${defaults.getDefaultLicenseName()} + '/' + ${defaults.getDefaultAssigneeName()}"></span> | ||||
|             个人所有!</strong> | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Cikaros
						Cikaros