├── .gitignore ├── Dockerfile ├── README.md ├── docker-compose.yml ├── pom.xml └── src └── main ├── java └── com │ └── github │ └── jrebel │ ├── JrebelApplication.java │ └── core │ ├── controller │ ├── IndexController.java │ ├── JetBrainsHandlerController.java │ └── JrebelController.java │ ├── service │ ├── JetBrainsService.java │ └── JrebelService.java │ └── util │ ├── Base64.java │ ├── Hex.java │ ├── IPUtil.java │ ├── JsonUtil.java │ ├── RandomUtil.java │ ├── RsaSign.java │ └── jrebel │ ├── ByteUtil.java │ ├── JrebelSign.java │ └── LicenseServer2ToJRebelPrivateKey.java └── resources ├── application.yml └── templates └── index.html /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | *.log 22 | *.gz 23 | 24 | ### NetBeans ### 25 | /nbproject/private/ 26 | /nbbuild/ 27 | /dist/ 28 | /nbdist/ 29 | /.nb-gradle/ 30 | build/ 31 | !**/src/main/**/build/ 32 | !**/src/test/**/build/ 33 | 34 | ### VS Code ### 35 | .vscode/ 36 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | #编译环境 2 | FROM lianshufeng/maven:jdk17 as build 3 | MAINTAINER lianshufeng <251708339@qq.com> 4 | 5 | ARG GIT_URL="https://github.com/lianshufeng/Jrebel.git" 6 | ARG GIT_NAME="Jrebel" 7 | ARG FILE_NAME="jrebel-0.0.1-SNAPSHOT.jar" 8 | ARG JAR_FILE="/opt/jar" 9 | 10 | 11 | #下载源码 12 | RUN set -xe \ 13 | && yum install -y git \ 14 | && source /etc/profile \ 15 | && cd /tmp/ \ 16 | && git clone $GIT_URL \ 17 | && cd /tmp/$GIT_NAME \ 18 | && mvn package \ 19 | && mkdir -p $JAR_FILE \ 20 | && cp /tmp/$GIT_NAME/target/$FILE_NAME $JAR_FILE/$FILE_NAME \ 21 | 22 | #刷新环境变量 23 | && source /etc/profile 24 | 25 | 26 | #运行环境 27 | FROM lianshufeng/springboot:jdk17 28 | ARG FILE_NAME="jrebel-0.0.1-SNAPSHOT.jar" 29 | ARG JAR_FILE="/opt/jar" 30 | COPY --from=build $JAR_FILE/$FILE_NAME $JAR_FILE/$FILE_NAME 31 | WORKDIR $JAR_FILE 32 | 33 | 34 | #默认的启动命令 35 | ENV ENTRYPOINT="nohup java -Dfile.encoding=UTF-8 -Xmx300m -Xms100m -Duser.timezone=GMT+8 -jar $JAR_FILE/$FILE_NAME" 36 | 37 | #创建启动脚本 38 | RUN set -xe \ 39 | #引导程序 40 | && echo "#!/bin/bash" > /opt/bootstrap.sh \ 41 | && echo "source /etc/profile" >> /opt/bootstrap.sh \ 42 | && echo "export LANG=en_US.UTF-8" >> /opt/bootstrap.sh \ 43 | && echo "echo \${ENTRYPOINT}|awk '{run=\$0;system(run)}'" >> /opt/bootstrap.sh 44 | 45 | 46 | 47 | 48 | #启动项 49 | ENTRYPOINT sh /opt/bootstrap.sh 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Jrebel License Server for Java 2 | 3 | A license server for Jrebel & JetBrains products, it also support JRebel for Android and XRebel. 4 | 5 | ### latest 6 | ````shell 7 | jrebel 2023.4.0 + 8 | ```` -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | springboot: 5 | build: 6 | context: ./ 7 | dockerfile: Dockerfile 8 | image: lianshufeng/jrebel 9 | environment: 10 | licenseUrl: 42.193.18.168:8088 11 | ports: 12 | - "8080:8080" 13 | working_dir: /opt/jar 14 | container_name: jrebel 15 | restart: always 16 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 3.3.1 9 | 10 | 11 | 12 | 13 | com.github 14 | jrebel 15 | 0.0.1-SNAPSHOT 16 | jrebel 17 | Demo project for Spring Boot 18 | 19 | 20 | UTF-8 21 | UTF-8 22 | 17 23 | 24 | 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-web 29 | 30 | 31 | 32 | org.projectlombok 33 | lombok 34 | true 35 | 36 | 37 | 38 | org.springframework.boot 39 | spring-boot-starter-test 40 | test 41 | 42 | 43 | org.junit.vintage 44 | junit-vintage-engine 45 | 46 | 47 | 48 | 49 | 50 | org.apache.commons 51 | commons-lang3 52 | 3.12.0 53 | 54 | 55 | 56 | commons-codec 57 | commons-codec 58 | 1.15 59 | 60 | 61 | 62 | 63 | org.bouncycastle 64 | bcprov-jdk15on 65 | 1.69 66 | 67 | 68 | 69 | 70 | 71 | org.springframework.boot 72 | spring-boot-starter-thymeleaf 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | nexus 82 | nexus 83 | http://maven.aliyun.com/nexus/content/groups/public 84 | 85 | true 86 | 87 | 88 | true 89 | always 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | org.springframework.boot 98 | spring-boot-maven-plugin 99 | 100 | 101 | 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /src/main/java/com/github/jrebel/JrebelApplication.java: -------------------------------------------------------------------------------- 1 | package com.github.jrebel; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.context.annotation.ComponentScan; 6 | 7 | 8 | @SpringBootApplication 9 | @ComponentScan("com.github.jrebel.core") 10 | public class JrebelApplication { 11 | 12 | public static void main(String[] args) { 13 | SpringApplication.run(JrebelApplication.class, args); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/github/jrebel/core/controller/IndexController.java: -------------------------------------------------------------------------------- 1 | package com.github.jrebel.core.controller; 2 | 3 | import com.github.jrebel.core.util.RandomUtil; 4 | import jakarta.servlet.http.HttpServletRequest; 5 | import jakarta.servlet.http.HttpServletResponse; 6 | import lombok.SneakyThrows; 7 | import org.springframework.web.bind.annotation.RequestMapping; 8 | import org.springframework.web.bind.annotation.RestController; 9 | import org.springframework.web.servlet.ModelAndView; 10 | 11 | import java.util.HashMap; 12 | import java.util.Optional; 13 | import java.util.Random; 14 | import java.util.UUID; 15 | 16 | @RestController 17 | public class IndexController { 18 | 19 | @RequestMapping({"/", ""}) 20 | @SneakyThrows 21 | public void index(HttpServletRequest request, HttpServletResponse response) { 22 | // String licenseUrl = System.getenv().getOrDefault("licenseUrl", request.getServerName() + (request.getServerPort() == 80 ? "" : ":" + request.getServerPort())); 23 | // String protocol = System.getenv().getOrDefault("protocol", "http://"); 24 | // 25 | // 26 | // return new ModelAndView("index", new HashMap() {{ 27 | // put("licenseUrl", licenseUrl); 28 | // put("protocol", protocol); 29 | // put("uuid", UUID.randomUUID().toString()); 30 | // put("mail", "%s@qq.com".formatted(RandomUtil.next(10000, 999999999))); 31 | // }}); 32 | response.sendRedirect("//www.jpy.wang/page/jrebel.html"); 33 | } 34 | 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/github/jrebel/core/controller/JetBrainsHandlerController.java: -------------------------------------------------------------------------------- 1 | package com.github.jrebel.core.controller; 2 | 3 | import jakarta.servlet.http.HttpServletRequest; 4 | import jakarta.servlet.http.HttpServletResponse; 5 | import lombok.SneakyThrows; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RestController; 10 | import com.github.jrebel.core.service.JetBrainsService; 11 | import com.github.jrebel.core.util.IPUtil; 12 | 13 | import java.util.UUID; 14 | 15 | @Slf4j 16 | @RestController 17 | public class JetBrainsHandlerController { 18 | 19 | @Autowired 20 | private JetBrainsService jrebelLeasesHandler; 21 | 22 | 23 | @RequestMapping("/rpc/ping.action") 24 | public void pingHandler(HttpServletRequest request, HttpServletResponse response) { 25 | log.info("ip : {} ", IPUtil.getRemoteIp(request) + " -> " + request.getRequestURI()); 26 | jrebelLeasesHandler.pingHandler(request, response); 27 | } 28 | 29 | @RequestMapping("/rpc/obtainTicket.action") 30 | public void obtainTicketHandler(HttpServletRequest request, HttpServletResponse response) { 31 | log.info("ip : {} ", IPUtil.getRemoteIp(request) + " -> " + request.getRequestURI()); 32 | jrebelLeasesHandler.obtainTicketHandler(request, response); 33 | } 34 | 35 | @RequestMapping("/rpc/releaseTicket.action") 36 | public void releaseTicketHandler(HttpServletRequest request, HttpServletResponse response) { 37 | log.info("ip : {} ", IPUtil.getRemoteIp(request) + " -> " + request.getRequestURI()); 38 | jrebelLeasesHandler.releaseTicketHandler(request, response); 39 | } 40 | 41 | @SneakyThrows 42 | @RequestMapping("/guid") 43 | public void guid(HttpServletRequest request, HttpServletResponse response) { 44 | log.info("ip : {} ", IPUtil.getRemoteIp(request) + " -> " + request.getRequestURI()); 45 | response.setContentType("text/html; charset=utf-8"); 46 | response.setStatus(HttpServletResponse.SC_OK); 47 | String body = UUID.randomUUID().toString(); 48 | response.getWriter().print(body); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/github/jrebel/core/controller/JrebelController.java: -------------------------------------------------------------------------------- 1 | package com.github.jrebel.core.controller; 2 | 3 | import com.github.jrebel.core.service.JrebelService; 4 | import jakarta.servlet.http.HttpServletRequest; 5 | import jakarta.servlet.http.HttpServletResponse; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RestController; 10 | import com.github.jrebel.core.util.IPUtil; 11 | 12 | 13 | @Slf4j 14 | @RestController 15 | public class JrebelController { 16 | 17 | @Autowired 18 | private JrebelService jrebelService; 19 | 20 | 21 | @RequestMapping({"/jrebel/leases"}) 22 | public void jrebelLeases(HttpServletRequest request, HttpServletResponse response) { 23 | log.info("ip : {} ", IPUtil.getRemoteIp(request) + " -> " + request.getRequestURI()); 24 | jrebelService.jrebelLeasesHandler(request, response); 25 | } 26 | 27 | @RequestMapping("/jrebel/leases/1") 28 | public void jrebelLeases1(HttpServletRequest request, HttpServletResponse response) { 29 | log.info("ip : {} ", IPUtil.getRemoteIp(request) + " -> " + request.getRequestURI()); 30 | jrebelService.jrebelLeases1Handler(request, response); 31 | } 32 | 33 | @RequestMapping("/agent/leases") 34 | public void agentLeases(HttpServletRequest request, HttpServletResponse response) { 35 | log.info("ip : {} ", IPUtil.getRemoteIp(request) + " -> " + request.getRequestURI()); 36 | jrebelService.jrebelLeasesHandler(request, response); 37 | } 38 | 39 | @RequestMapping("/agent/leases/1") 40 | public void agentLeases1(HttpServletRequest request, HttpServletResponse response) { 41 | log.info("ip : {} ", IPUtil.getRemoteIp(request) + " -> " + request.getRequestURI()); 42 | jrebelService.jrebelLeases1Handler(request, response); 43 | } 44 | 45 | @RequestMapping("/jrebel/validate-connection") 46 | public void jrebelValidateHandler(HttpServletRequest request, HttpServletResponse response) { 47 | log.info("ip : {} ", IPUtil.getRemoteIp(request) + " -> " + request.getRequestURI()); 48 | jrebelService.jrebelValidateHandler(request, response); 49 | } 50 | 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/github/jrebel/core/service/JetBrainsService.java: -------------------------------------------------------------------------------- 1 | package com.github.jrebel.core.service; 2 | 3 | import jakarta.servlet.http.HttpServletRequest; 4 | import jakarta.servlet.http.HttpServletResponse; 5 | import lombok.SneakyThrows; 6 | import org.springframework.stereotype.Service; 7 | import com.github.jrebel.core.util.RsaSign; 8 | 9 | import java.text.SimpleDateFormat; 10 | import java.util.Date; 11 | import java.util.Locale; 12 | 13 | @Service 14 | public class JetBrainsService { 15 | @SneakyThrows 16 | public void releaseTicketHandler(HttpServletRequest request, HttpServletResponse response) { 17 | response.setContentType("text/html; charset=utf-8"); 18 | response.setStatus(HttpServletResponse.SC_OK); 19 | String salt = request.getParameter("salt"); 20 | if (salt == null) { 21 | response.setStatus(HttpServletResponse.SC_FORBIDDEN); 22 | } else { 23 | 24 | String xmlContent = """ 25 | 26 | 27 | OK 28 | %s 29 | 30 | """.formatted(salt); 31 | String xmlSignature = RsaSign.Sign(xmlContent); 32 | String body = "\n" + xmlContent; 33 | response.getWriter().print(body); 34 | } 35 | } 36 | 37 | 38 | @SneakyThrows 39 | public void pingHandler(HttpServletRequest request, HttpServletResponse response) { 40 | response.setContentType("text/html; charset=utf-8"); 41 | response.setStatus(HttpServletResponse.SC_OK); 42 | String salt = request.getParameter("salt"); 43 | if (salt == null) { 44 | response.setStatus(HttpServletResponse.SC_FORBIDDEN); 45 | } else { 46 | String xmlContent = """ 47 | 48 | 49 | OK 50 | %s 51 | 52 | """.formatted(salt); 53 | String xmlSignature = RsaSign.Sign(xmlContent); 54 | String body = "\n" + xmlContent; 55 | response.getWriter().print(body); 56 | } 57 | 58 | } 59 | 60 | @SneakyThrows 61 | public void obtainTicketHandler(HttpServletRequest request, HttpServletResponse response) { 62 | response.setContentType("text/html; charset=utf-8"); 63 | response.setStatus(HttpServletResponse.SC_OK); 64 | SimpleDateFormat fm = new SimpleDateFormat("EEE,d MMM yyyy hh:mm:ss Z", Locale.ENGLISH); 65 | String date = fm.format(new Date()) + " GMT"; 66 | //response.setHeader("Date", date); 67 | //response.setHeader("Server", "fasthttp"); 68 | String salt = request.getParameter("salt"); 69 | String username = request.getParameter("userName"); 70 | String prolongationPeriod = "607875500"; 71 | if (salt == null || username == null) { 72 | response.setStatus(HttpServletResponse.SC_FORBIDDEN); 73 | } else { 74 | 75 | String xmlContent = """ 76 | 77 | 78 | %s 79 | OK 80 | %s 81 | 1 82 | licensee=%s licenseType=0 83 | 84 | """.formatted(prolongationPeriod, salt, username); 85 | String xmlSignature = RsaSign.Sign(xmlContent); 86 | String body = "\n" + xmlContent; 87 | response.getWriter().print(body); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/com/github/jrebel/core/service/JrebelService.java: -------------------------------------------------------------------------------- 1 | package com.github.jrebel.core.service; 2 | 3 | import com.github.jrebel.core.util.jrebel.JrebelSign; 4 | import com.github.jrebel.core.util.JsonUtil; 5 | import jakarta.servlet.http.HttpServletRequest; 6 | import jakarta.servlet.http.HttpServletResponse; 7 | import lombok.SneakyThrows; 8 | import org.springframework.stereotype.Service; 9 | 10 | import java.util.Map; 11 | 12 | @Service 13 | public class JrebelService { 14 | 15 | 16 | @SneakyThrows 17 | public void jrebelLeasesHandler(HttpServletRequest request, HttpServletResponse response) { 18 | response.setContentType("application/json; charset=utf-8"); 19 | response.setStatus(HttpServletResponse.SC_OK); 20 | String clientRandomness = request.getParameter("randomness"); 21 | String username = request.getParameter("username"); 22 | String guid = request.getParameter("guid"); 23 | // boolean offline = Boolean.parseBoolean(request.getParameter("offline")); 24 | final boolean offline = true; 25 | String validFrom = "null"; 26 | String validUntil = "null"; 27 | if (offline) { 28 | String clientTime = request.getParameter("clientTime"); 29 | String offlineDays = request.getParameter("offlineDays"); 30 | //long clinetTimeUntil = Long.parseLong(clientTime) + Long.parseLong(offlineDays) * 24 * 60 * 60 * 1000; 31 | long clinetTimeUntil = Long.parseLong(clientTime) + 30L * 24 * 60 * 60 * 1000; 32 | validFrom = clientTime; 33 | validUntil = String.valueOf(clinetTimeUntil); 34 | } 35 | String jsonStr = """ 36 | { 37 | "serverVersion": "3.2.4", 38 | "serverProtocolVersion": "1.1", 39 | "serverGuid": "a1b4aea8-b031-4302-b602-670a990272cb", 40 | "groupType": "managed", 41 | "id": 1, 42 | "licenseType": 1, 43 | "evaluationLicense": false, 44 | "signature": "OJE9wGg2xncSb+VgnYT+9HGCFaLOk28tneMFhCbpVMKoC/Iq4LuaDKPirBjG4o394/UjCDGgTBpIrzcXNPdVxVr8PnQzpy7ZSToGO8wv/KIWZT9/ba7bDbA8/RZ4B37YkCeXhjaixpmoyz/CIZMnei4q7oWR7DYUOlOcEWDQhiY=", 45 | "serverRandomness": "H2ulzLlh7E0=", 46 | "seatPoolType": "standalone", 47 | "statusCode": "SUCCESS", 48 | "offline": %s, 49 | "validFrom": %s, 50 | "validUntil": %s, 51 | "company": "Administrator", 52 | "orderId": "", 53 | "zeroIds": [ 54 | \s 55 | ], 56 | "licenseValidFrom": 1490544001000, 57 | "licenseValidUntil": %s 58 | } 59 | """.formatted(offline, validFrom, validUntil, System.currentTimeMillis() ); 60 | 61 | if (clientRandomness == null || username == null || guid == null) { 62 | response.setStatus(HttpServletResponse.SC_FORBIDDEN); 63 | } else { 64 | Map map = JsonUtil.toObject(jsonStr, Map.class); 65 | JrebelSign jrebelSign = new JrebelSign(); 66 | jrebelSign.toLeaseCreateJson(clientRandomness, guid, offline, validFrom, validUntil); 67 | String signature = jrebelSign.getSignature(); 68 | map.put("signature", signature); 69 | map.put("company", username); 70 | String body = JsonUtil.toJson(map); 71 | response.getWriter().print(body); 72 | } 73 | } 74 | 75 | 76 | @SneakyThrows 77 | public void jrebelValidateHandler(HttpServletRequest request, HttpServletResponse response) { 78 | response.setContentType("application/json; charset=utf-8"); 79 | response.setStatus(HttpServletResponse.SC_OK); 80 | String jsonStr = """ 81 | { 82 | "serverVersion": "3.2.4", 83 | "serverProtocolVersion": "1.1", 84 | "serverGuid": "a1b4aea8-b031-4302-b602-670a990272cb", 85 | "groupType": "managed", 86 | "statusCode": "SUCCESS", 87 | "company": "Administrator", 88 | "canGetLease": true, 89 | "licenseType": 1, 90 | "evaluationLicense": false, 91 | "seatPoolType": "standalone" 92 | } 93 | """; 94 | response.getWriter().print(JsonUtil.toJson(JsonUtil.toObject(jsonStr, Object.class))); 95 | } 96 | 97 | @SneakyThrows 98 | public void jrebelLeases1Handler(HttpServletRequest request, HttpServletResponse response) { 99 | response.setContentType("application/json; charset=utf-8"); 100 | response.setStatus(HttpServletResponse.SC_OK); 101 | String username = request.getParameter("username"); 102 | String jsonStr = """ 103 | { 104 | "serverVersion": "3.2.4", 105 | "serverProtocolVersion": "1.1", 106 | "serverGuid": "a1b4aea8-b031-4302-b602-670a990272cb", 107 | "groupType": "managed", 108 | "statusCode": "SUCCESS", 109 | "msg": null, 110 | "statusMessage": null 111 | } 112 | """; 113 | Map map = JsonUtil.toObject(jsonStr, Map.class); 114 | if (username != null) { 115 | map.put("company", username); 116 | } 117 | String body = JsonUtil.toJson(map); 118 | response.getWriter().print(body); 119 | } 120 | 121 | 122 | } 123 | -------------------------------------------------------------------------------- /src/main/java/com/github/jrebel/core/util/Base64.java: -------------------------------------------------------------------------------- 1 | package com.github.jrebel.core.util; 2 | 3 | 4 | public class Base64 { 5 | /** 6 | * 编码 7 | * 8 | * @param bstr 9 | * @return String 10 | */ 11 | public static String encode(byte[] bstr) { 12 | return org.apache.commons.codec.binary.Base64.encodeBase64String(bstr); 13 | } 14 | 15 | /** 16 | * 解码 17 | * 18 | * @param str 19 | * @return string 20 | */ 21 | public static byte[] decode(String str) { 22 | return org.apache.commons.codec.binary.Base64.decodeBase64(str); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/github/jrebel/core/util/Hex.java: -------------------------------------------------------------------------------- 1 | package com.github.jrebel.core.util; 2 | 3 | public class Hex { 4 | public static String bytesToHexString(byte[] src){ 5 | StringBuilder stringBuilder = new StringBuilder(""); 6 | if (src == null || src.length <= 0) { 7 | return null; 8 | } 9 | for (int i = 0; i < src.length; i++) { 10 | int v = src[i] & 0xFF; 11 | String hv = Integer.toHexString(v); 12 | if (hv.length() < 2) { 13 | stringBuilder.append(0); 14 | } 15 | stringBuilder.append(hv); 16 | } 17 | return stringBuilder.toString(); 18 | } 19 | /** 20 | * Convert hex string to byte[] 21 | * @param hexString the hex string 22 | * @return byte[] 23 | */ 24 | public static byte[] hexStringToBytes(String hexString) { 25 | if (hexString == null || hexString.equals("")) { 26 | return null; 27 | } 28 | hexString = hexString.toUpperCase(); 29 | int length = hexString.length() / 2; 30 | char[] hexChars = hexString.toCharArray(); 31 | byte[] d = new byte[length]; 32 | for (int i = 0; i < length; i++) { 33 | int pos = i * 2; 34 | d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1])); 35 | } 36 | return d; 37 | } 38 | private static byte charToByte(char c) { 39 | return (byte) "0123456789ABCDEF".indexOf(c); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/github/jrebel/core/util/IPUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.jrebel.core.util; 2 | 3 | import jakarta.servlet.http.HttpServletRequest; 4 | import org.springframework.util.StringUtils; 5 | 6 | 7 | /** 8 | * 取ip的工具 9 | */ 10 | public class IPUtil { 11 | 12 | 13 | /** 14 | * 可能出现用户真实ip的头 15 | */ 16 | private final static String[] headNames = new String[]{ 17 | "X-FORWARDED-FOR", 18 | "Proxy-Client-IP", 19 | "WL-Proxy-Client-IP", 20 | "HTTP_CLIENT_IP", 21 | "HTTP_X_FORWARDED_FOR" 22 | }; 23 | 24 | 25 | /** 26 | * 获取远程ip 27 | * 28 | * @param request 29 | * @return 30 | */ 31 | public static String getRemoteIp(HttpServletRequest request) { 32 | for (String name : headNames) { 33 | String ip = request.getHeader(name); 34 | if (!StringUtils.isEmpty(ip)) { 35 | int at = ip.indexOf(","); 36 | if (at > -1) { 37 | ip = ip.substring(0, at); 38 | } 39 | return ip; 40 | } 41 | } 42 | return request.getRemoteAddr(); 43 | } 44 | 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/github/jrebel/core/util/JsonUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.jrebel.core.util; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.DeserializationFeature; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import com.fasterxml.jackson.databind.ObjectWriter; 7 | import com.fasterxml.jackson.databind.SerializationFeature; 8 | import org.springframework.util.StreamUtils; 9 | 10 | import java.io.InputStream; 11 | 12 | /** 13 | * json工具 14 | * 15 | * @作者 练书锋 16 | * @联系 oneday@vip.qq.com 17 | * @时间 2014年5月17日 18 | */ 19 | public class JsonUtil { 20 | 21 | private static ObjectMapper objectMapper = new ObjectMapper().disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); 22 | 23 | static { 24 | objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); 25 | } 26 | 27 | /** 28 | * 转换到json字符串 29 | * 30 | * @param object 31 | * @return 32 | * @throws Exception 33 | */ 34 | public static String toJson(Object object, boolean format) { 35 | try { 36 | ObjectWriter objectWriter; 37 | if (format) { 38 | return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(object); 39 | } else { 40 | return objectMapper.writeValueAsString(object); 41 | } 42 | } catch (JsonProcessingException e) { 43 | e.printStackTrace(); 44 | } 45 | return null; 46 | } 47 | 48 | 49 | /** 50 | * 转换到json字符串 51 | * 52 | * @param object 53 | * @return 54 | */ 55 | public static String toJson(Object object) { 56 | return toJson(object, false); 57 | } 58 | 59 | 60 | /** 61 | * 转换为对象 62 | * 63 | * @param json 64 | * @param cls 65 | * @return 66 | * @throws Exception 67 | */ 68 | public static T toObject(String json, Class cls) throws Exception { 69 | return objectMapper.readValue(json, cls); 70 | } 71 | 72 | /** 73 | * 载入文件到对象 74 | * 75 | * @param configName 76 | * @param cls 77 | * @return 78 | * @throws Exception 79 | */ 80 | public static T loadToObject(String configName, Class cls) throws Exception { 81 | T t = null; 82 | InputStream inputStream = JsonUtil.class.getClassLoader().getResourceAsStream(configName); 83 | byte[] bin = StreamUtils.copyToByteArray(inputStream); 84 | String json = new String(bin, "UTF-8"); 85 | t = toObject(json, cls); 86 | inputStream.close(); 87 | return t; 88 | } 89 | 90 | 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/com/github/jrebel/core/util/RandomUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.jrebel.core.util; 2 | 3 | import java.util.Random; 4 | 5 | public class RandomUtil { 6 | 7 | public static int next(int min, int max) { 8 | Random random = new Random(); 9 | return random.nextInt(max - min + 1) + min; 10 | } 11 | 12 | 13 | public static long next(long min, long max) { 14 | Random random = new Random(); 15 | return random.nextLong(max - min + 1) + min; 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/github/jrebel/core/util/RsaSign.java: -------------------------------------------------------------------------------- 1 | package com.github.jrebel.core.util; 2 | 3 | import org.bouncycastle.asn1.ASN1InputStream; 4 | import org.bouncycastle.asn1.ASN1Primitive; 5 | import org.bouncycastle.asn1.pkcs.RSAPrivateKeyStructure; 6 | 7 | import java.security.KeyFactory; 8 | import java.security.PrivateKey; 9 | import java.security.spec.PKCS8EncodedKeySpec; 10 | import java.security.spec.RSAPrivateKeySpec; 11 | 12 | 13 | public class RsaSign { 14 | static String header = "607875500OK15084842582741licensee=Administrator licenseType=0 "; 15 | static String content = "607875500OK15084842582741licensee=Administrator licenseType=0 "; 16 | String ASNKEY = "-----BEGIN RSA PRIVATE KEY-----\r\n" 17 | + "MIIBOgIBAAJBALecq3BwAI4YJZwhJ+snnDFj3lF3DMqNPorV6y5ZKXCiCMqj8OeOmxk4YZW9aaV9\r\n" 18 | + "ckl/zlAOI0mpB3pDT+Xlj2sCAwEAAQJAW6/aVD05qbsZHMvZuS2Aa5FpNNj0BDlf38hOtkhDzz/h\r\n" 19 | + "kYb+EBYLLvldhgsD0OvRNy8yhz7EjaUqLCB0juIN4QIhAOeCQp+NXxfBmfdG/S+XbRUAdv8iHBl+\r\n" 20 | + "F6O2wr5fA2jzAiEAywlDfGIl6acnakPrmJE0IL8qvuO3FtsHBrpkUuOnXakCIQCqdr+XvADI/UTh\r\n" 21 | + "TuQepuErFayJMBSAsNe3NFsw0cUxAQIgGA5n7ZPfdBi3BdM4VeJWb87WrLlkVxPqeDSbcGrCyMkC\r\n" 22 | + "IFSs5JyXvFTreWt7IQjDssrKDRIPmALdNjvfETwlNJyY\r\n" 23 | + "-----END RSA PRIVATE KEY-----"; 24 | String PCKS8KEY = "-----BEGIN PRIVATE KEY-----\r\n" 25 | + "MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAt5yrcHAAjhglnCEn\r\n" 26 | + "6yecMWPeUXcMyo0+itXrLlkpcKIIyqPw546bGThhlb1ppX1ySX/OUA4jSakHekNP\r\n" 27 | + "5eWPawIDAQABAkBbr9pUPTmpuxkcy9m5LYBrkWk02PQEOV/fyE62SEPPP+GRhv4Q\r\n" 28 | + "Fgsu+V2GCwPQ69E3LzKHPsSNpSosIHSO4g3hAiEA54JCn41fF8GZ90b9L5dtFQB2\r\n" 29 | + "/yIcGX4Xo7bCvl8DaPMCIQDLCUN8YiXppydqQ+uYkTQgvyq+47cW2wcGumRS46dd\r\n" 30 | + "qQIhAKp2v5e8AMj9ROFO5B6m4SsVrIkwFICw17c0WzDRxTEBAiAYDmftk990GLcF\r\n" 31 | + "0zhV4lZvztasuWRXE+p4NJtwasLIyQIgVKzknJe8VOt5a3shCMOyysoNEg+YAt02\r\n" 32 | + "O98RPCU0nJg=\r\n" + "-----END PRIVATE KEY-----"; 33 | 34 | static String key22 = "MIIBOgIBAAJBALecq3BwAI4YJZwhJ+snnDFj3lF3DMqNPorV6y5ZKXCiCMqj8OeOmxk4YZW9aaV9" 35 | + "ckl/zlAOI0mpB3pDT+Xlj2sCAwEAAQJAW6/aVD05qbsZHMvZuS2Aa5FpNNj0BDlf38hOtkhDzz/h" 36 | + "kYb+EBYLLvldhgsD0OvRNy8yhz7EjaUqLCB0juIN4QIhAOeCQp+NXxfBmfdG/S+XbRUAdv8iHBl+" 37 | + "F6O2wr5fA2jzAiEAywlDfGIl6acnakPrmJE0IL8qvuO3FtsHBrpkUuOnXakCIQCqdr+XvADI/UTh" 38 | + "TuQepuErFayJMBSAsNe3NFsw0cUxAQIgGA5n7ZPfdBi3BdM4VeJWb87WrLlkVxPqeDSbcGrCyMkC" 39 | + "IFSs5JyXvFTreWt7IQjDssrKDRIPmALdNjvfETwlNJyY"; 40 | 41 | static String key33 = "MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAt5yrcHAAjhglnCEn" 42 | + "6yecMWPeUXcMyo0+itXrLlkpcKIIyqPw546bGThhlb1ppX1ySX/OUA4jSakHekNP" 43 | + "5eWPawIDAQABAkBbr9pUPTmpuxkcy9m5LYBrkWk02PQEOV/fyE62SEPPP+GRhv4Q" 44 | + "Fgsu+V2GCwPQ69E3LzKHPsSNpSosIHSO4g3hAiEA54JCn41fF8GZ90b9L5dtFQB2" 45 | + "/yIcGX4Xo7bCvl8DaPMCIQDLCUN8YiXppydqQ+uYkTQgvyq+47cW2wcGumRS46dd" 46 | + "qQIhAKp2v5e8AMj9ROFO5B6m4SsVrIkwFICw17c0WzDRxTEBAiAYDmftk990GLcF" 47 | + "0zhV4lZvztasuWRXE+p4NJtwasLIyQIgVKzknJe8VOt5a3shCMOyysoNEg+YAt02" 48 | + "O98RPCU0nJg="; 49 | 50 | public static String Sign(String content){ 51 | return RsaSign.Sign(content.getBytes(), key22); 52 | } 53 | 54 | public static String Sign2(String content){ 55 | return RsaSign.Sign2(content.getBytes(), key33); 56 | } 57 | 58 | //传入秘钥为ASN格式 59 | //私钥签名程序,privateKey是私钥base64编码字符串,即私钥文件数据中,中间的主体部分 60 | public static String Sign(byte[] content, String privateKey) { 61 | try { 62 | byte[] keybyte = Base64.decode(privateKey.toString()); 63 | ASN1InputStream in = new ASN1InputStream(keybyte); 64 | ASN1Primitive obj = in.readObject(); 65 | RSAPrivateKeyStructure pStruct = RSAPrivateKeyStructure.getInstance(obj); 66 | RSAPrivateKeySpec spec = new RSAPrivateKeySpec(pStruct.getModulus(), pStruct.getPrivateExponent()); 67 | KeyFactory keyFactory = KeyFactory.getInstance("RSA"); 68 | PrivateKey priKey = keyFactory.generatePrivate(spec); 69 | java.security.Signature signature = java.security.Signature.getInstance("MD5WithRSA"); 70 | signature.initSign(priKey); 71 | signature.update(content); 72 | byte[] signed = signature.sign(); 73 | return Hex.bytesToHexString(signed); 74 | } 75 | catch (Exception e) { 76 | e.printStackTrace(); 77 | } 78 | return null; 79 | } 80 | 81 | //传入秘钥为PKCS#8私钥非加密格式 82 | //私钥签名程序,privateKey是私钥base64编码字符串,即私钥文件数据中,中间的主体部分 83 | public static String Sign2(byte[] content, String privateKey) { 84 | try { 85 | PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(Base64.decode(privateKey)); 86 | KeyFactory keyf = KeyFactory.getInstance("RSA"); 87 | PrivateKey priKey = keyf.generatePrivate(priPKCS8); 88 | java.security.Signature signature = java.security.Signature.getInstance("MD5WithRSA"); 89 | signature.initSign(priKey); 90 | signature.update(content); 91 | byte[] signed = signature.sign(); 92 | return Hex.bytesToHexString(signed); 93 | } catch (Exception e) { 94 | e.printStackTrace(); 95 | } 96 | return null; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/com/github/jrebel/core/util/jrebel/ByteUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.jrebel.core.util.jrebel; 2 | 3 | 4 | import java.nio.charset.Charset; 5 | import java.util.Base64; 6 | import java.util.Random; 7 | 8 | public class ByteUtil { 9 | private static final Random a; 10 | 11 | public static String a(final byte[] binaryData) { 12 | if (binaryData == null) { 13 | return null; 14 | } 15 | return new String(Base64.getEncoder().encode(binaryData), Charset.forName("UTF-8")); 16 | } 17 | 18 | public static byte[] a(final String s) { 19 | if (s == null) { 20 | return null; 21 | } 22 | return Base64.getDecoder().decode(s.getBytes(Charset.forName("UTF-8"))); 23 | } 24 | 25 | public static byte[] a(final int n) { 26 | final byte[] array = new byte[n]; 27 | ByteUtil.a.nextBytes(array); 28 | return array; 29 | } 30 | 31 | static { 32 | a = new Random(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/github/jrebel/core/util/jrebel/JrebelSign.java: -------------------------------------------------------------------------------- 1 | package com.github.jrebel.core.util.jrebel; 2 | 3 | 4 | import org.apache.commons.lang3.StringUtils; 5 | 6 | public class JrebelSign { 7 | private String signature; 8 | 9 | public void toLeaseCreateJson(String clientRandomness, String guid, boolean offline, String validFrom, String validUntil) { 10 | String serverRandomness = "H2ulzLlh7E0="; //服务端随机数,如果要自己生成,务必将其写到json的serverRandomness中 11 | String installationGuidString = guid; 12 | String s2 = ""; 13 | if (offline) { 14 | s2 = StringUtils.join((Object[]) new String[]{clientRandomness, serverRandomness, installationGuidString, String.valueOf(offline), validFrom, validUntil}, ';'); 15 | } else { 16 | s2 = StringUtils.join((Object[]) new String[]{clientRandomness, serverRandomness, installationGuidString, String.valueOf(offline)}, ';'); 17 | } 18 | final byte[] a2 = LicenseServer2ToJRebelPrivateKey.a(s2.getBytes()); 19 | this.signature = ByteUtil.a(a2); 20 | } 21 | 22 | public String getSignature() { 23 | return signature; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/github/jrebel/core/util/jrebel/LicenseServer2ToJRebelPrivateKey.java: -------------------------------------------------------------------------------- 1 | package com.github.jrebel.core.util.jrebel; 2 | 3 | import org.bouncycastle.jce.provider.BouncyCastleProvider; 4 | 5 | import java.security.GeneralSecurityException; 6 | import java.security.KeyFactory; 7 | import java.security.PrivateKey; 8 | import java.security.Signature; 9 | import java.security.spec.PKCS8EncodedKeySpec; 10 | 11 | public class LicenseServer2ToJRebelPrivateKey { 12 | private static final String b = "MIICXAIBAAKBgQDQ93CP6SjEneDizCF1P/MaBGf582voNNFcu8oMhgdTZ/N6qa6O7XJDr1FSCyaDdKSsPCdxPK7Y4Usq/fOPas2kCgYcRS/iebrtPEFZ/7TLfk39HLuTEjzo0/CNvjVsgWeh9BYznFaxFDLx7fLKqCQ6w1OKScnsdqwjpaXwXqiulwIDAQABAoGATOQvvBSMVsTNQkbgrNcqKdGjPNrwQtJkk13aO/95ZJxkgCc9vwPqPrOdFbZappZeHa5IyScOI2nLEfe+DnC7V80K2dBtaIQjOeZQt5HoTRG4EHQaWoDh27BWuJoip5WMrOd+1qfkOtZoRjNcHl86LIAh/+3vxYyebkug4UHNGPkCQQD+N4ZUkhKNQW7mpxX6eecitmOdN7Yt0YH9UmxPiW1LyCEbLwduMR2tfyGfrbZALiGzlKJize38shGC1qYSMvZFAkEA0m6psWWiTUWtaOKMxkTkcUdigalZ9xFSEl6jXFB94AD+dlPS3J5gNzTEmbPLc14VIWJFkO+UOrpl77w5uF2dKwJAaMpslhnsicvKMkv31FtBut5iK6GWeEafhdPfD94/bnidpP362yJl8Gmya4cI1GXvwH3pfj8S9hJVA5EFvgTB3QJBAJP1O1uAGp46X7Nfl5vQ1M7RYnHIoXkWtJ417Kb78YWPLVwFlD2LHhuy/okT4fk8LZ9LeZ5u1cp1RTdLIUqAiAECQC46OwOm87L35yaVfpUIjqg/1gsNwNsj8HvtXdF/9d30JIM3GwdytCvNRLqP35Ciogb9AO8ke8L6zY83nxPbClM="; 13 | private static final byte[] c; 14 | private static final BouncyCastleProvider d; 15 | 16 | private static PrivateKey a() { 17 | final PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(LicenseServer2ToJRebelPrivateKey.c); 18 | try { 19 | return KeyFactory.getInstance("RSA", LicenseServer2ToJRebelPrivateKey.d).generatePrivate(pkcs8EncodedKeySpec); 20 | } catch (Exception ex) { 21 | ex.printStackTrace(); 22 | return null; 23 | } 24 | } 25 | 26 | public static byte[] a(final byte[] array) { 27 | try { 28 | final Signature instance = Signature.getInstance("SHA1withRSA", LicenseServer2ToJRebelPrivateKey.d); 29 | instance.initSign(a()); 30 | instance.update(array); 31 | return instance.sign(); 32 | } catch (GeneralSecurityException ex) { 33 | throw new RuntimeException("License Server installation error 0000000F2", ex); 34 | } 35 | } 36 | 37 | static { 38 | c = ByteUtil.a("MIICXAIBAAKBgQDQ93CP6SjEneDizCF1P/MaBGf582voNNFcu8oMhgdTZ/N6qa6O7XJDr1FSCyaDdKSsPCdxPK7Y4Usq/fOPas2kCgYcRS/iebrtPEFZ/7TLfk39HLuTEjzo0/CNvjVsgWeh9BYznFaxFDLx7fLKqCQ6w1OKScnsdqwjpaXwXqiulwIDAQABAoGATOQvvBSMVsTNQkbgrNcqKdGjPNrwQtJkk13aO/95ZJxkgCc9vwPqPrOdFbZappZeHa5IyScOI2nLEfe+DnC7V80K2dBtaIQjOeZQt5HoTRG4EHQaWoDh27BWuJoip5WMrOd+1qfkOtZoRjNcHl86LIAh/+3vxYyebkug4UHNGPkCQQD+N4ZUkhKNQW7mpxX6eecitmOdN7Yt0YH9UmxPiW1LyCEbLwduMR2tfyGfrbZALiGzlKJize38shGC1qYSMvZFAkEA0m6psWWiTUWtaOKMxkTkcUdigalZ9xFSEl6jXFB94AD+dlPS3J5gNzTEmbPLc14VIWJFkO+UOrpl77w5uF2dKwJAaMpslhnsicvKMkv31FtBut5iK6GWeEafhdPfD94/bnidpP362yJl8Gmya4cI1GXvwH3pfj8S9hJVA5EFvgTB3QJBAJP1O1uAGp46X7Nfl5vQ1M7RYnHIoXkWtJ417Kb78YWPLVwFlD2LHhuy/okT4fk8LZ9LeZ5u1cp1RTdLIUqAiAECQC46OwOm87L35yaVfpUIjqg/1gsNwNsj8HvtXdF/9d30JIM3GwdytCvNRLqP35Ciogb9AO8ke8L6zY83nxPbClM="); 39 | d = new BouncyCastleProvider(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8080 3 | 4 | 5 | spring: 6 | thymeleaf: 7 | suffix: .html 8 | mode: HTML 9 | prefix: classpath:/templates/ 10 | cache: false 11 | encoding: UTF-8 12 | 13 | 14 | #调试或者输出日志 15 | logging: 16 | file: 17 | name: logs/jrebel.log 18 | 19 | #debug: true -------------------------------------------------------------------------------- /src/main/resources/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Jrebel 激活服务 6 | 7 | 8 | 9 | 10 | 11 |

使用说明(Instructions for use)

12 |
13 | 14 |

Jrebel License Server! (兼容 2023.4.0 +)

15 |

16 | JRebel 激活地址: 17 |

18 |

19 | JRebel 激活邮箱: 20 |

21 | 22 | 23 |

JRebel 无限试用,请拷贝到命令提示符中执行

24 |

25 | curl https://register.jpy.wang/ReRegister/src/main/java/jrebel/JrebelMain.java -o tmp.java && java tmp.java && del 26 | tmp.java 27 |

28 | 29 | 30 | 31 | 32 | 33 | 34 | --------------------------------------------------------------------------------