mirror of
				https://github.com/NotoChen/Jetbrains-Help.git
				synced 2025-11-01 01:38:31 +08:00 
			
		
		
		
	append license server
This commit is contained in:
		
							parent
							
								
									882dbfc126
								
							
						
					
					
						commit
						f4a7d653c5
					
				|  | @ -1,9 +1,11 @@ | ||||||
| package com.jetbrains.help; | package com.jetbrains.help; | ||||||
| 
 | 
 | ||||||
| import cn.hutool.core.collection.CollUtil; | import cn.hutool.core.collection.CollUtil; | ||||||
|  | import cn.hutool.core.io.IoUtil; | ||||||
| import cn.hutool.core.net.Ipv4Util; | import cn.hutool.core.net.Ipv4Util; | ||||||
| import cn.hutool.core.text.CharSequenceUtil; | import cn.hutool.core.text.CharSequenceUtil; | ||||||
| import cn.hutool.core.util.StrUtil; | import cn.hutool.core.util.StrUtil; | ||||||
|  | import cn.hutool.crypto.SecureUtil; | ||||||
| import cn.hutool.extra.spring.SpringUtil; | import cn.hutool.extra.spring.SpringUtil; | ||||||
| import com.jetbrains.help.context.*; | import com.jetbrains.help.context.*; | ||||||
| import lombok.SneakyThrows; | import lombok.SneakyThrows; | ||||||
|  | @ -17,6 +19,7 @@ import org.springframework.scheduling.annotation.EnableScheduling; | ||||||
| import org.springframework.scheduling.annotation.Scheduled; | import org.springframework.scheduling.annotation.Scheduled; | ||||||
| 
 | 
 | ||||||
| import java.net.InetAddress; | import java.net.InetAddress; | ||||||
|  | import java.security.cert.Certificate; | ||||||
| import java.util.Collection; | import java.util.Collection; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| 
 | 
 | ||||||
|  | @ -37,7 +40,6 @@ public class JetbrainsHelpApplication { | ||||||
|         PluginsContextHolder.init(); |         PluginsContextHolder.init(); | ||||||
|         CertificateContextHolder.init(); |         CertificateContextHolder.init(); | ||||||
|         AgentContextHolder.init(); |         AgentContextHolder.init(); | ||||||
| 
 |  | ||||||
|         InetAddress localHost = InetAddress.getLocalHost(); |         InetAddress localHost = InetAddress.getLocalHost(); | ||||||
|         String address = CharSequenceUtil.format("http://{}:{}", localHost.getHostAddress(), SpringUtil.getProperty("server.port")); |         String address = CharSequenceUtil.format("http://{}:{}", localHost.getHostAddress(), SpringUtil.getProperty("server.port")); | ||||||
|         String runSuccessWarn = "\n====================================================================================\n" + |         String runSuccessWarn = "\n====================================================================================\n" + | ||||||
|  |  | ||||||
|  | @ -35,7 +35,7 @@ public class AgentContextHolder { | ||||||
|     public static void init() { |     public static void init() { | ||||||
|         log.info("Agent context init loading..."); |         log.info("Agent context init loading..."); | ||||||
|         jaNetfilterZipFile = FileTools.getFileOrCreat(JA_NETFILTER_FILE_PATH + ".zip"); |         jaNetfilterZipFile = FileTools.getFileOrCreat(JA_NETFILTER_FILE_PATH + ".zip"); | ||||||
|         if (!FileTools.fileExists(JA_NETFILTER_FILE_PATH)) { |         if (FileTools.fileNotExists(JA_NETFILTER_FILE_PATH)) { | ||||||
|             unzipJaNetfilter(); |             unzipJaNetfilter(); | ||||||
|             if (!powerConfHasInit()) { |             if (!powerConfHasInit()) { | ||||||
|                 log.info("Agent config init loading..."); |                 log.info("Agent config init loading..."); | ||||||
|  | @ -75,14 +75,27 @@ public class AgentContextHolder { | ||||||
| 
 | 
 | ||||||
|     @SneakyThrows |     @SneakyThrows | ||||||
|     private static String generatePowerConfigRule() { |     private static String generatePowerConfigRule() { | ||||||
|         X509Certificate crt = (X509Certificate) KeyUtil.readX509Certificate(IoUtil.toStream(CertificateContextHolder.crtFile())); |         StringBuilder result = new StringBuilder(); | ||||||
|         RSAPublicKey publicKey = (RSAPublicKey) PemUtil.readPemPublicKey(IoUtil.toStream(CertificateContextHolder.publicKeyFile())); |         X509Certificate crt = (X509Certificate) KeyUtil.readX509Certificate(IoUtil.toStream(CertificateContextHolder.getCrtFile())); | ||||||
|         RSAPublicKey rootPublicKey = (RSAPublicKey) PemUtil.readPemPublicKey(IoUtil.toStream(CertificateContextHolder.rootKeyFile())); |         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 x = new BigInteger(1, crt.getSignature()); | ||||||
|         BigInteger y = BigInteger.valueOf(65537L); |         BigInteger y = rootPublicKey.getPublicExponent(); | ||||||
|         BigInteger z = rootPublicKey.getModulus(); |         BigInteger z = rootPublicKey.getModulus(); | ||||||
|         BigInteger r = x.modPow(publicKey.getPublicExponent(), publicKey.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) { |     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.DateField; | ||||||
| import cn.hutool.core.date.DateUtil; | import cn.hutool.core.date.DateUtil; | ||||||
| import cn.hutool.core.io.FileUtil; | import cn.hutool.core.io.FileUtil; | ||||||
|  | import cn.hutool.core.io.IoUtil; | ||||||
| import cn.hutool.crypto.PemUtil; | import cn.hutool.crypto.PemUtil; | ||||||
| import cn.hutool.crypto.SecureUtil; | import cn.hutool.crypto.SecureUtil; | ||||||
| import com.jetbrains.help.util.FileTools; | import com.jetbrains.help.util.FileTools; | ||||||
| import lombok.AccessLevel; | import lombok.AccessLevel; | ||||||
|  | import lombok.Getter; | ||||||
| import lombok.NoArgsConstructor; | import lombok.NoArgsConstructor; | ||||||
| import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||||
| import org.bouncycastle.asn1.x500.X500Name; | import org.bouncycastle.asn1.x500.X500Name; | ||||||
|  | @ -27,29 +29,47 @@ import java.security.cert.Certificate; | ||||||
| import java.security.cert.CertificateEncodingException; | import java.security.cert.CertificateEncodingException; | ||||||
| import java.security.cert.CertificateException; | 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 | @Slf4j | ||||||
| @NoArgsConstructor(access = AccessLevel.PRIVATE) | @NoArgsConstructor(access = AccessLevel.PRIVATE) | ||||||
| public class CertificateContextHolder { | public class CertificateContextHolder { | ||||||
| 
 | 
 | ||||||
|     private static final String ROOT_KEY_FILE_NAME = "external/certificate/root.key"; |     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 PRIVATE_KEY_FILE_NAME = "external/certificate/private.key"; | ||||||
|     private static final String PUBLIC_KEY_FILE_NAME = "external/certificate/public.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 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; |     private static File rootKeyFile; | ||||||
| 
 | 
 | ||||||
|  |     @Getter | ||||||
|  |     private static File rootLicenseKeyFile; | ||||||
|  | 
 | ||||||
|  |     @Getter | ||||||
|     private static File privateKeyFile; |     private static File privateKeyFile; | ||||||
| 
 | 
 | ||||||
|  |     @Getter | ||||||
|     private static File publicKeyFile; |     private static File publicKeyFile; | ||||||
| 
 | 
 | ||||||
|  |     @Getter | ||||||
|     private static File crtFile; |     private static File crtFile; | ||||||
| 
 | 
 | ||||||
|  |     @Getter | ||||||
|  |     private static File licenseCrtFile; | ||||||
|  | 
 | ||||||
|     public static void init() { |     public static void init() { | ||||||
|         log.info("certificate context init loading..."); |         log.info("certificate context init loading..."); | ||||||
|         rootKeyFile = FileTools.getFileOrCreat(ROOT_KEY_FILE_NAME); |         rootKeyFile = FileTools.getFileOrCreat(ROOT_KEY_FILE_NAME); | ||||||
|         if (!FileTools.fileExists(PRIVATE_KEY_FILE_NAME) |         rootLicenseKeyFile = FileTools.getFileOrCreat(ROOT_LICENSE_KEY_FILE_NAME); | ||||||
|                 || !FileTools.fileExists(PUBLIC_KEY_FILE_NAME) |         if (FileTools.fileNotExists(PRIVATE_KEY_FILE_NAME) | ||||||
|                 || !FileTools.fileExists(CET_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..."); |             log.info("certificate context generate loading..."); | ||||||
|             generateCertificate(); |             generateCertificate(); | ||||||
|             log.info("certificate context generate success!"); |             log.info("certificate context generate success!"); | ||||||
|  | @ -57,27 +77,11 @@ public class CertificateContextHolder { | ||||||
|             privateKeyFile = FileTools.getFileOrCreat(PRIVATE_KEY_FILE_NAME); |             privateKeyFile = FileTools.getFileOrCreat(PRIVATE_KEY_FILE_NAME); | ||||||
|             publicKeyFile = FileTools.getFileOrCreat(PUBLIC_KEY_FILE_NAME); |             publicKeyFile = FileTools.getFileOrCreat(PUBLIC_KEY_FILE_NAME); | ||||||
|             crtFile = FileTools.getFileOrCreat(CET_FILE_NAME); |             crtFile = FileTools.getFileOrCreat(CET_FILE_NAME); | ||||||
|  |             licenseCrtFile = FileTools.getFileOrCreat(LICENSE_CET_FILE_NAME); | ||||||
|         } |         } | ||||||
|         log.info("certificate context init success !"); |         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() { |     public static void generateCertificate() { | ||||||
|         KeyPair keyPair = SecureUtil.generateKeyPair("RSA", 4096); |         KeyPair keyPair = SecureUtil.generateKeyPair("RSA", 4096); | ||||||
|         PrivateKey privateKey = keyPair.getPrivate(); |         PrivateKey privateKey = keyPair.getPrivate(); | ||||||
|  | @ -86,16 +90,8 @@ public class CertificateContextHolder { | ||||||
|         PemUtil.writePemObject("PRIVATE KEY", privateKey.getEncoded(), FileUtil.getWriter(privateKeyFile, StandardCharsets.UTF_8, false)); |         PemUtil.writePemObject("PRIVATE KEY", privateKey.getEncoded(), FileUtil.getWriter(privateKeyFile, StandardCharsets.UTF_8, false)); | ||||||
|         publicKeyFile = FileTools.getFileOrCreat(PUBLIC_KEY_FILE_NAME); |         publicKeyFile = FileTools.getFileOrCreat(PUBLIC_KEY_FILE_NAME); | ||||||
|         PemUtil.writePemObject("PUBLIC KEY", publicKey.getEncoded(), FileUtil.getWriter(publicKeyFile, StandardCharsets.UTF_8, false)); |         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 { |         try { | ||||||
|             ContentSigner signer = new JcaContentSignerBuilder("SHA256withRSA").build(privateKey); |             Certificate certificate = generateCertificate(keyPair,"JetProfile CA","Jetbrains-Help"); | ||||||
|             Certificate certificate = new JcaX509CertificateConverter().setProvider("BC").getCertificate(certificateBuilder.build(signer)); |  | ||||||
|             crtFile = FileTools.getFileOrCreat(CET_FILE_NAME); |             crtFile = FileTools.getFileOrCreat(CET_FILE_NAME); | ||||||
|             PemUtil.writePemObject("CERTIFICATE", certificate.getEncoded(), FileUtil.getWriter(crtFile, StandardCharsets.UTF_8, false)); |             PemUtil.writePemObject("CERTIFICATE", certificate.getEncoded(), FileUtil.getWriter(crtFile, StandardCharsets.UTF_8, false)); | ||||||
|         } catch (OperatorCreationException e) { |         } catch (OperatorCreationException e) { | ||||||
|  | @ -105,7 +101,33 @@ public class CertificateContextHolder { | ||||||
|         } catch (CertificateException e) { |         } catch (CertificateException e) { | ||||||
|             throw new IllegalArgumentException("The certificate read exception", 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; | package com.jetbrains.help.context; | ||||||
| 
 | 
 | ||||||
| import cn.hutool.core.codec.Base64; | import cn.hutool.core.codec.Base64; | ||||||
| import cn.hutool.core.collection.CollUtil; |  | ||||||
| import cn.hutool.core.io.IoUtil; | import cn.hutool.core.io.IoUtil; | ||||||
| import cn.hutool.core.text.CharSequenceUtil; | import cn.hutool.core.text.CharSequenceUtil; | ||||||
| import cn.hutool.core.util.IdUtil; | import cn.hutool.core.util.IdUtil; | ||||||
|  | import cn.hutool.core.util.XmlUtil; | ||||||
| import cn.hutool.crypto.PemUtil; | import cn.hutool.crypto.PemUtil; | ||||||
| import cn.hutool.crypto.SecureUtil; | import cn.hutool.crypto.SecureUtil; | ||||||
| import cn.hutool.crypto.SignUtil; | import cn.hutool.crypto.SignUtil; | ||||||
| import cn.hutool.crypto.asymmetric.Sign; | import cn.hutool.crypto.asymmetric.Sign; | ||||||
| import cn.hutool.json.JSONUtil; | import cn.hutool.json.JSONUtil; | ||||||
| import lombok.AccessLevel; | import lombok.AccessLevel; | ||||||
|  | import lombok.Builder; | ||||||
| import lombok.Data; | import lombok.Data; | ||||||
| import lombok.NoArgsConstructor; | import lombok.NoArgsConstructor; | ||||||
| import lombok.extern.slf4j.Slf4j; | 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.PrivateKey; | ||||||
| import java.security.PublicKey; | import java.security.PublicKey; | ||||||
| import java.security.cert.Certificate; | import java.security.cert.Certificate; | ||||||
|  | @ -22,7 +26,7 @@ import java.security.cert.CertificateEncodingException; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| import java.util.Set; | import java.util.Set; | ||||||
| 
 | 
 | ||||||
| import static cn.hutool.crypto.asymmetric.SignAlgorithm.SHA1withRSA; | import static cn.hutool.crypto.asymmetric.SignAlgorithm.*; | ||||||
| 
 | 
 | ||||||
| @Slf4j | @Slf4j | ||||||
| @NoArgsConstructor(access = AccessLevel.PRIVATE) | @NoArgsConstructor(access = AccessLevel.PRIVATE) | ||||||
|  | @ -43,9 +47,9 @@ public class LicenseContextHolder { | ||||||
|                 .setProducts(products); |                 .setProducts(products); | ||||||
|         String licensePartJson = JSONUtil.toJsonStr(licensePart); |         String licensePartJson = JSONUtil.toJsonStr(licensePart); | ||||||
|         String licensePartBase64 = Base64.encode(licensePartJson); |         String licensePartBase64 = Base64.encode(licensePartJson); | ||||||
|         PrivateKey privateKey = PemUtil.readPemPrivateKey(IoUtil.toStream(CertificateContextHolder.privateKeyFile())); |         PrivateKey privateKey = PemUtil.readPemPrivateKey(IoUtil.toStream(CertificateContextHolder.getPrivateKeyFile())); | ||||||
|         PublicKey publicKey = PemUtil.readPemPublicKey(IoUtil.toStream(CertificateContextHolder.publicKeyFile())); |         PublicKey publicKey = PemUtil.readPemPublicKey(IoUtil.toStream(CertificateContextHolder.getPublicKeyFile())); | ||||||
|         Certificate certificate = SecureUtil.readX509Certificate(IoUtil.toStream(CertificateContextHolder.crtFile())); |         Certificate certificate = SecureUtil.readX509Certificate(IoUtil.toStream(CertificateContextHolder.getCrtFile())); | ||||||
|         Sign sign = SignUtil.sign(SHA1withRSA, privateKey.getEncoded(), publicKey.getEncoded()); |         Sign sign = SignUtil.sign(SHA1withRSA, privateKey.getEncoded(), publicKey.getEncoded()); | ||||||
|         String signatureBase64 = Base64.encode(sign.sign(licensePartJson)); |         String signatureBase64 = Base64.encode(sign.sign(licensePartJson)); | ||||||
|         String certBase64; |         String certBase64; | ||||||
|  | @ -74,4 +78,147 @@ public class LicenseContextHolder { | ||||||
|         private String paidUpTo; |         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)); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -3,12 +3,10 @@ package com.jetbrains.help.util; | ||||||
| import cn.hutool.core.io.FileUtil; | import cn.hutool.core.io.FileUtil; | ||||||
| import cn.hutool.core.text.CharSequenceUtil; | import cn.hutool.core.text.CharSequenceUtil; | ||||||
| import cn.hutool.core.util.ObjectUtil; | import cn.hutool.core.util.ObjectUtil; | ||||||
| import cn.hutool.core.util.StrUtil; |  | ||||||
| import org.springframework.boot.system.ApplicationHome; | import org.springframework.boot.system.ApplicationHome; | ||||||
| import org.springframework.core.io.ClassPathResource; | import org.springframework.core.io.ClassPathResource; | ||||||
| 
 | 
 | ||||||
| import java.io.File; | import java.io.File; | ||||||
| import java.io.IOException; |  | ||||||
| 
 | 
 | ||||||
| public interface FileTools { | public interface FileTools { | ||||||
| 
 | 
 | ||||||
|  | @ -19,6 +17,10 @@ public interface FileTools { | ||||||
|         return getFile(path).exists(); |         return getFile(path).exists(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     static boolean fileNotExists(String path) { | ||||||
|  |         return !fileExists(path); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     static File getFile(String path) { |     static File getFile(String path) { | ||||||
|         File homeDir = application.getDir(); |         File homeDir = application.getDir(); | ||||||
|         File source = application.getSource(); |         File source = application.getSource(); | ||||||
|  |  | ||||||
|  | @ -6,6 +6,6 @@ spring: | ||||||
| server: | server: | ||||||
|   port: 10768 |   port: 10768 | ||||||
| help: | help: | ||||||
|   default-license-name: 光云 |   default-license-name: cikaros.top | ||||||
|   default-assignee-name: 藏柏 |   default-assignee-name: Cikaros | ||||||
|   default-expiry-date: 2111-11-11 |   default-expiry-date: 2099-12-31 | ||||||
							
								
								
									
										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----- | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Cikaros
						Cikaros