This commit is contained in:
HuangQing 2024-10-04 17:10:46 +08:00
commit b1c4ebb87c
31 changed files with 1170 additions and 0 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

23
.gitignore vendored Normal file
View File

@ -0,0 +1,23 @@
# Compiled class file
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*

8
.idea/.gitignore generated vendored Normal file
View File

@ -0,0 +1,8 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

13
.idea/compiler.xml generated Normal file
View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<annotationProcessing>
<profile name="Maven default annotation processors profile" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<module name="Jrebel-License-Server" />
</profile>
</annotationProcessing>
</component>
</project>

7
.idea/encodings.xml generated Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" native2AsciiForPropertiesFiles="true" defaultCharsetForPropertiesFiles="UTF-8">
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
</component>
</project>

20
.idea/jarRepositories.xml generated Normal file
View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Central Repository" />
<option name="url" value="https://repo.maven.apache.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
</component>
</project>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

15
.idea/misc.xml generated Normal file
View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CodeInsightWorkspaceSettings">
<option name="optimizeImportsOnTheFly" value="true" />
</component>
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="liberica-17" project-jdk-type="JavaSDK" />
</project>

6
.idea/vcs.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

7
Dockerfile Normal file
View File

@ -0,0 +1,7 @@
FROM java:8-jre-alpine
ENV PORT 7001
ADD target/Jrebel-License-Server.jar /JrebelBrains.jar
CMD java -jar /JrebelBrains.jar -p $PORT

23
README.md Normal file
View File

@ -0,0 +1,23 @@
# 本地激活JRebel
## 介绍
本地激活JRebel
## 使用说明
1. 启动项目
2. 激活地址 http://127.0.0.1:7001/{guid}
> 注意:需要把 {guid} 换成生成的 guid
> 如 http://127.0.0.1:7001/d060463a-2720-45a3-977a-7d004349ec92
## Docker 方式启动
```shell
# 编译
mvn package
# 打镜像
docker build -t jrebel-active
# 启动容器
docker run -d --name jrebel-active --restart always -e PORT=7001 -p 7001:7001 jrebel-active
```

38
build.gradle Normal file
View File

@ -0,0 +1,38 @@
plugins {
id "com.github.johnrengelman.shadow" version "4.0.1"
}
apply plugin: 'java'
apply plugin: 'maven'
apply plugin: 'application'
group = 'com.vvvtimes.com'
version = '1.0-SNAPSHOT'
sourceCompatibility = 1.8
targetCompatibility = 1.8
tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
}
mainClassName = 'com.vvvtimes.server.MainServer'
jar {
manifest {
attributes 'Main-Class': mainClassName
}
}
repositories {
jcenter()
}
dependencies {
compile group: 'org.eclipse.jetty.aggregate', name: 'jetty-all', version: '8.1.22.v20160922'
compile group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: '1.60'
compile group: 'commons-codec', name: 'commons-codec', version: '1.11'
compile group: 'net.sf.json-lib', name: 'json-lib', version: '2.4', classifier: 'jdk15'
compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.1'
compileOnly group: 'javax.servlet', name: 'javax.servlet-api', version: '3.0.1'
}

View File

@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

172
gradlew vendored Normal file
View File

@ -0,0 +1,172 @@
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

84
gradlew.bat vendored Normal file
View File

@ -0,0 +1,84 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

110
pom.xml Normal file
View File

@ -0,0 +1,110 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.witty.com</groupId>
<artifactId>Jrebel-License-Server</artifactId>
<version>1.0.0</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<finalName>${artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<!-- 与Servlet版本对应 -->
<version>3.13.0</version>
<configuration>
<source>17</source>
<target>17</target>
<encoding>utf8</encoding>
</configuration>
</plugin>
<!-- 打包可运行jar包 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.6.0</version>
<!-- dependency-reduced-pom.xml -->
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.witty.server.MainServer</mainClass>
</transformer>
</transformers>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty.aggregate</groupId>
<artifactId>jetty-all</artifactId>
<version>9.4.56.v20240826</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.70</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.17.1</version>
</dependency>
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.53</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.17.0</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,51 @@
package com.witty.handler;
import org.eclipse.jetty.server.Request;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.UUID;
public class IndexHandler {
public static void indexHandler(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException {
response.setContentType("text/html; charset=utf-8");
response.setStatus(HttpServletResponse.SC_OK);
baseRequest.setHandled(true);
// 拼接服务器地址
String licenseUrl = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort();
StringBuffer html = new StringBuffer("<h3>使用说明Instructions for use</h3>");
html.append("<hr/>");
html.append("<h1>Hello,This is a Jrebel & JetBrains License Server!</h1>");
html.append("<p>License Server started at ").append(licenseUrl);
html.append("<p>JetBrains Activation address was: <span style='color:red'>").append(licenseUrl).append("/");
html.append("<p>JRebel 7.1 and earlier version Activation address was: <span style='color:red'>")
.append(licenseUrl).append("/{tokenname}")
.append("</span>, with any email.");
html.append("<p>JRebel 2018.1 and later version Activation address was: ")
.append(licenseUrl).append("/{guid}")
.append("(eg:<span style='color:red'>")
.append(licenseUrl).append("/").append(UUID.randomUUID())
.append("</span>), with any email.");
html.append("<hr/>");
html.append("<h1>Hello此地址是 Jrebel & JetBrains License Server!</h1>");
html.append("<p>JetBrains许可服务器激活地址 ").append(licenseUrl);
html.append("<p>JetBrains激活地址是: <span style='color:red'>").append(licenseUrl).append("/");
html.append("<p>JRebel 7.1 及旧版本激活地址: <span style='color:red'>")
.append(licenseUrl).append("/{tokenname}")
.append("</span>, 以及任意邮箱地址。");
html.append("<p>JRebel 2018.1+ 版本激活地址: ")
.append(licenseUrl).append("/{guid}")
.append("(例如:<span style='color:red'>")
.append(licenseUrl).append("/").append(UUID.randomUUID())
.append("</span>), 以及任意邮箱地址。");
response.getWriter().println(html);
}
}

View File

@ -0,0 +1,66 @@
package com.witty.handler;
import com.witty.util.RsaSign;
import org.eclipse.jetty.server.Request;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
public class JetBrainsHandler {
public static void releaseTicketHandler(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException {
response.setContentType("text/html; charset=utf-8");
response.setStatus(HttpServletResponse.SC_OK);
String salt = request.getParameter("salt");
baseRequest.setHandled(true);
if (salt == null) {
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
} else {
String xmlContent = "<ReleaseTicketResponse><message></message><responseCode>OK</responseCode><salt>" + salt + "</salt></ReleaseTicketResponse>";
String xmlSignature = RsaSign.Sign(xmlContent);
String body = "<!-- " + xmlSignature + " -->\n" + xmlContent;
response.getWriter().print(body);
}
}
public static void pingHandler(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException {
response.setContentType("text/html; charset=utf-8");
response.setStatus(HttpServletResponse.SC_OK);
String salt = request.getParameter("salt");
baseRequest.setHandled(true);
if (salt == null) {
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
} else {
String xmlContent = "<PingResponse><message></message><responseCode>OK</responseCode><salt>" + salt + "</salt></PingResponse>";
String xmlSignature = RsaSign.Sign(xmlContent);
String body = "<!-- " + xmlSignature + " -->\n" + xmlContent;
response.getWriter().print(body);
}
}
public static void obtainTicketHandler(String target, Request baseRequest, HttpServletRequest request,
HttpServletResponse response) throws IOException {
response.setContentType("text/html; charset=utf-8");
response.setStatus(HttpServletResponse.SC_OK);
SimpleDateFormat fm = new SimpleDateFormat("EEE,d MMM yyyy hh:mm:ss Z", Locale.ENGLISH);
String date = fm.format(new Date()) + " GMT";
//response.setHeader("Date", date);
//response.setHeader("Server", "fasthttp");
String salt = request.getParameter("salt");
String username = request.getParameter("userName");
String prolongationPeriod = "607875500";
baseRequest.setHandled(true);
if (salt == null || username == null) {
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
} else {
String xmlContent = "<ObtainTicketResponse><message></message><prolongationPeriod>" + prolongationPeriod + "</prolongationPeriod><responseCode>OK</responseCode><salt>" + salt + "</salt><ticketId>1</ticketId><ticketProperties>licensee=" + username + "\tlicenseType=0\t</ticketProperties></ObtainTicketResponse>";
String xmlSignature = RsaSign.Sign(xmlContent);
String body = "<!-- " + xmlSignature + " -->\n" + xmlContent;
response.getWriter().print(body);
}
}
}

View File

@ -0,0 +1,120 @@
package com.witty.handler;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.witty.util.JrebelUtil.JrebelSign;
import org.eclipse.jetty.server.Request;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class JrebelHandler {
public static void jrebelLeasesHandler(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException {
response.setContentType("application/json; charset=utf-8");
response.setStatus(HttpServletResponse.SC_OK);
String clientRandomness = request.getParameter("randomness");
String username = request.getParameter("username");
String guid = request.getParameter("guid");
System.out.println(request.getParameterMap());
boolean offline = Boolean.parseBoolean(request.getParameter("offline"));
String validFrom = "null";
String validUntil = "null";
if (offline) {
String clientTime = request.getParameter("clientTime");
String offlineDays = request.getParameter("offlineDays");
//long clientTimeUntil = Long.parseLong(clientTime) + Long.parseLong(offlineDays) * 24 * 60 * 60 * 1000;
long clientTimeUtil = Long.parseLong(clientTime) + 180L * 24 * 60 * 60 * 1000;
validFrom = clientTime;
validUntil = String.valueOf(clientTimeUtil);
}
baseRequest.setHandled(true);
String jsonStr = """
{
"serverVersion": "3.2.4",
"serverProtocolVersion": "1.1",
"serverGuid": "a1b4aea8-b031-4302-b602-670a990272cb",
"groupType": "managed",
"id": 1,
"licenseType": 1,
"evaluationLicense": false,
"signature": "OJE9wGg2xncSb+VgnYT+9HGCFaLOk28tneMFhCbpVMKoC/Iq4LuaDKPirBjG4o394/UjCDGgTBpIrzcXNPdVxVr8PnQzpy7ZSToGO8wv/KIWZT9/ba7bDbA8/RZ4B37YkCeXhjaixpmoyz/CIZMnei4q7oWR7DYUOlOcEWDQhiY=",
"serverRandomness": "H2ulzLlh7E0=",
"seatPoolType": "standalone",
"statusCode": "SUCCESS",
"offline": %s,
"validFrom": %s,
"validUntil": %s,
"company": "Administrator",
"orderId": "",
"zeroIds": [
\s
],
"licenseValidFrom": 1490544001000,
"licenseValidUntil": 1691839999000
}
""";
jsonStr = String.format(jsonStr, offline, validFrom, validUntil);
JSONObject jsonObject = JSON.parseObject(jsonStr);
if (clientRandomness == null || username == null || guid == null) {
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
} else {
JrebelSign jrebelSign = new JrebelSign();
jrebelSign.toLeaseCreateJson(clientRandomness, guid, offline, validFrom, validUntil);
String signature = jrebelSign.getSignature();
jsonObject.put("signature", signature);
jsonObject.put("company", username);
String body = jsonObject.toString();
response.getWriter().print(body);
}
}
public static void jrebelValidateHandler(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException {
response.setContentType("application/json; charset=utf-8");
response.setStatus(HttpServletResponse.SC_OK);
baseRequest.setHandled(true);
String jsonStr = """
{
"serverVersion": "3.2.4",
"serverProtocolVersion": "1.1",
"serverGuid": "a1b4aea8-b031-4302-b602-670a990272cb",
"groupType": "managed",
"statusCode": "SUCCESS",
"company": "Administrator",
"canGetLease": true,
"licenseType": 1,
"evaluationLicense": false,
"seatPoolType": "standalone"
}
""";
JSONObject jsonObject = JSON.parseObject(jsonStr);
String body = jsonObject.toString();
response.getWriter().print(body);
}
public static void jrebelLeases1Handler(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException {
response.setContentType("application/json; charset=utf-8");
response.setStatus(HttpServletResponse.SC_OK);
String username = request.getParameter("username");
baseRequest.setHandled(true);
String jsonStr = """
{
"serverVersion": "3.2.4",
"serverProtocolVersion": "1.1",
"serverGuid": "a1b4aea8-b031-4302-b602-670a990272cb",
"groupType": "managed",
"statusCode": "SUCCESS",
"msg": null,
"statusMessage": null
}
""";
JSONObject jsonObject = JSON.parseObject(jsonStr);
if (username != null) {
jsonObject.put("company", username);
}
String body = jsonObject.toString();
response.getWriter().print(body);
}
}

View File

@ -0,0 +1,19 @@
package com.witty.handler;
import org.eclipse.jetty.server.Request;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.UUID;
public class ToolHandler {
public static void guidHandler(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException {
response.setContentType("text/html; charset=utf-8");
response.setStatus(HttpServletResponse.SC_OK);
baseRequest.setHandled(true);
String body = UUID.randomUUID().toString();
response.getWriter().print(body);
}
}

View File

@ -0,0 +1,91 @@
package com.witty.server;
import com.witty.handler.IndexHandler;
import com.witty.handler.JetBrainsHandler;
import com.witty.handler.JrebelHandler;
import com.witty.handler.ToolHandler;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.AbstractHandler;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class MainServer extends AbstractHandler {
private static Map<String, String> parseArguments(String[] args) {
if (args.length % 2 != 0) {
throw new IllegalArgumentException("Error in argument's length ");
}
Map<String, String> params = new HashMap<>();
for (int i = 0, len = args.length; i < len; ) {
String argName = args[i++];
if (argName.charAt(0) == '-') {
if (argName.length() < 2) {
throw new IllegalArgumentException("Error at argument " + argName);
}
argName = argName.substring(1);
}
params.put(argName, args[i++]);
}
return params;
}
public static void main(String[] args) throws Exception {
Map<String, String> arguments = parseArguments(args);
String port = arguments.get("p");
if (port == null || !port.matches("\\d+")) {
port = "7001";
}
Server server = new Server(Integer.parseInt(port));
server.setHandler(new MainServer());
server.start();
String serverStr = "http://localhost";
if (!"80".equals(port)) {
serverStr = serverStr + ":" + port;
}
System.out.println("License Server started at " + serverStr);
System.out.println("JetBrains Activation address was: " + serverStr + "/");
System.out.println("JRebel 7.1 and earlier version Activation address was: " + serverStr + "/{tokenname}, with any email.");
System.out.println("JRebel 2018.1 and later version Activation address was: " + serverStr + "/{guid}(eg:http://localhost:" + port + "/" + UUID.randomUUID().toString() + "), with any email.");
server.join();
}
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
throws IOException {
System.out.println(target);
switch (target) {
case "/" -> IndexHandler.indexHandler(target, baseRequest, request, response);
case "/jrebel/leases", "/agent/leases" ->
JrebelHandler.jrebelLeasesHandler(target, baseRequest, request, response);
case "/jrebel/leases/1", "/agent/leases/1" ->
JrebelHandler.jrebelLeases1Handler(target, baseRequest, request, response);
case "/jrebel/validate-connection" ->
JrebelHandler.jrebelValidateHandler(target, baseRequest, request, response);
case "/rpc/ping.action" -> JetBrainsHandler.pingHandler(target, baseRequest, request, response);
case "/rpc/obtainTicket.action" ->
JetBrainsHandler.obtainTicketHandler(target, baseRequest, request, response);
case "/rpc/releaseTicket.action" ->
JetBrainsHandler.releaseTicketHandler(target, baseRequest, request, response);
case "/guid" -> ToolHandler.guidHandler(target, baseRequest, request, response);
default -> response.setStatus(HttpServletResponse.SC_FORBIDDEN);
}
}
}

View File

@ -0,0 +1,25 @@
package com.witty.util;
public class Base64 {
/**
* 编码
*
* @param bstr
* @return String
*/
public static String encode(byte[] bstr) {
return org.apache.commons.codec.binary.Base64.encodeBase64String(bstr);
}
/**
* 解码
*
* @param str
* @return string
*/
public static byte[] decode(String str) {
return org.apache.commons.codec.binary.Base64.decodeBase64(str);
}
}

View File

@ -0,0 +1,42 @@
package com.witty.util;
public class Hex {
public static String bytesToHexString(byte[] src){
StringBuilder stringBuilder = new StringBuilder("");
if (src == null || src.length <= 0) {
return null;
}
for (int i = 0; i < src.length; i++) {
int v = src[i] & 0xFF;
String hv = Integer.toHexString(v);
if (hv.length() < 2) {
stringBuilder.append(0);
}
stringBuilder.append(hv);
}
return stringBuilder.toString();
}
/**
* Convert hex string to byte[]
* @param hexString the hex string
* @return byte[]
*/
public static byte[] hexStringToBytes(String hexString) {
if (hexString == null || hexString.equals("")) {
return null;
}
hexString = hexString.toUpperCase();
int length = hexString.length() / 2;
char[] hexChars = hexString.toCharArray();
byte[] d = new byte[length];
for (int i = 0; i < length; i++) {
int pos = i * 2;
d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
}
return d;
}
private static byte charToByte(char c) {
return (byte) "0123456789ABCDEF".indexOf(c);
}
}

View File

@ -0,0 +1,34 @@
package com.witty.util.JrebelUtil;
import org.apache.commons.codec.binary.Base64;
import java.nio.charset.StandardCharsets;
import java.util.Random;
public class ByteUtil {
private static final Random a;
public static String a(final byte[] binaryData) {
if (binaryData == null) {
return null;
}
return new String(Base64.encodeBase64(binaryData), StandardCharsets.UTF_8);
}
public static byte[] a(final String s) {
if (s == null) {
return null;
}
return Base64.decodeBase64(s.getBytes(StandardCharsets.UTF_8));
}
public static byte[] a(final int n) {
final byte[] array = new byte[n];
ByteUtil.a.nextBytes(array);
return array;
}
static {
a = new Random();
}
}

View File

@ -0,0 +1,27 @@
package com.witty.util.JrebelUtil;
import org.apache.commons.lang3.StringUtils;
public class JrebelSign {
private String signature;
public void toLeaseCreateJson(String clientRandomness, String guid, boolean offline, String validFrom, String validUntil) {
//String serverRandomness = ByteUtil.a(ByteUtil.a(8));
String serverRandomness = "H2ulzLlh7E0="; //服务端随机数,如果要自己生成务必将其写到json的serverRandomness中
//String value = String.valueOf("false");
String s2= "";
if(offline){
s2 = StringUtils.join(new String[]{clientRandomness, serverRandomness, guid, String.valueOf(offline), validFrom, validUntil}, ';');
}else{
s2 = StringUtils.join(new String[]{clientRandomness, serverRandomness, guid, String.valueOf(offline)}, ';');
}
System.out.println(s2);
final byte[] a2 =LicenseServer2ToJRebelPrivateKey.a(s2.getBytes());
this.signature = ByteUtil.a(a2);
}
public String getSignature() {
return signature;
}
}

View File

@ -0,0 +1,41 @@
package com.witty.util.JrebelUtil;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
public class LicenseServer2ToJRebelPrivateKey {
private static final String b = "MIICXAIBAAKBgQDQ93CP6SjEneDizCF1P/MaBGf582voNNFcu8oMhgdTZ/N6qa6O7XJDr1FSCyaDdKSsPCdxPK7Y4Usq/fOPas2kCgYcRS/iebrtPEFZ/7TLfk39HLuTEjzo0/CNvjVsgWeh9BYznFaxFDLx7fLKqCQ6w1OKScnsdqwjpaXwXqiulwIDAQABAoGATOQvvBSMVsTNQkbgrNcqKdGjPNrwQtJkk13aO/95ZJxkgCc9vwPqPrOdFbZappZeHa5IyScOI2nLEfe+DnC7V80K2dBtaIQjOeZQt5HoTRG4EHQaWoDh27BWuJoip5WMrOd+1qfkOtZoRjNcHl86LIAh/+3vxYyebkug4UHNGPkCQQD+N4ZUkhKNQW7mpxX6eecitmOdN7Yt0YH9UmxPiW1LyCEbLwduMR2tfyGfrbZALiGzlKJize38shGC1qYSMvZFAkEA0m6psWWiTUWtaOKMxkTkcUdigalZ9xFSEl6jXFB94AD+dlPS3J5gNzTEmbPLc14VIWJFkO+UOrpl77w5uF2dKwJAaMpslhnsicvKMkv31FtBut5iK6GWeEafhdPfD94/bnidpP362yJl8Gmya4cI1GXvwH3pfj8S9hJVA5EFvgTB3QJBAJP1O1uAGp46X7Nfl5vQ1M7RYnHIoXkWtJ417Kb78YWPLVwFlD2LHhuy/okT4fk8LZ9LeZ5u1cp1RTdLIUqAiAECQC46OwOm87L35yaVfpUIjqg/1gsNwNsj8HvtXdF/9d30JIM3GwdytCvNRLqP35Ciogb9AO8ke8L6zY83nxPbClM=";
private static final byte[] c;
private static final BouncyCastleProvider d;
private static PrivateKey a() {
final PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(LicenseServer2ToJRebelPrivateKey.c);
try {
return KeyFactory.getInstance("RSA", LicenseServer2ToJRebelPrivateKey.d).generatePrivate(pkcs8EncodedKeySpec);
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
public static byte[] a(final byte[] array) {
try {
final Signature instance = Signature.getInstance("SHA1withRSA", LicenseServer2ToJRebelPrivateKey.d);
instance.initSign(a());
instance.update(array);
return instance.sign();
} catch (GeneralSecurityException ex) {
throw new RuntimeException("License Server installation error 0000000F2", ex);
}
}
static {
c = ByteUtil.a("MIICXAIBAAKBgQDQ93CP6SjEneDizCF1P/MaBGf582voNNFcu8oMhgdTZ/N6qa6O7XJDr1FSCyaDdKSsPCdxPK7Y4Usq/fOPas2kCgYcRS/iebrtPEFZ/7TLfk39HLuTEjzo0/CNvjVsgWeh9BYznFaxFDLx7fLKqCQ6w1OKScnsdqwjpaXwXqiulwIDAQABAoGATOQvvBSMVsTNQkbgrNcqKdGjPNrwQtJkk13aO/95ZJxkgCc9vwPqPrOdFbZappZeHa5IyScOI2nLEfe+DnC7V80K2dBtaIQjOeZQt5HoTRG4EHQaWoDh27BWuJoip5WMrOd+1qfkOtZoRjNcHl86LIAh/+3vxYyebkug4UHNGPkCQQD+N4ZUkhKNQW7mpxX6eecitmOdN7Yt0YH9UmxPiW1LyCEbLwduMR2tfyGfrbZALiGzlKJize38shGC1qYSMvZFAkEA0m6psWWiTUWtaOKMxkTkcUdigalZ9xFSEl6jXFB94AD+dlPS3J5gNzTEmbPLc14VIWJFkO+UOrpl77w5uF2dKwJAaMpslhnsicvKMkv31FtBut5iK6GWeEafhdPfD94/bnidpP362yJl8Gmya4cI1GXvwH3pfj8S9hJVA5EFvgTB3QJBAJP1O1uAGp46X7Nfl5vQ1M7RYnHIoXkWtJ417Kb78YWPLVwFlD2LHhuy/okT4fk8LZ9LeZ5u1cp1RTdLIUqAiAECQC46OwOm87L35yaVfpUIjqg/1gsNwNsj8HvtXdF/9d30JIM3GwdytCvNRLqP35Ciogb9AO8ke8L6zY83nxPbClM=");
d = new BouncyCastleProvider();
}
}

View File

@ -0,0 +1,98 @@
package com.witty.util;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.pkcs.RSAPrivateKeyStructure;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPrivateKeySpec;
public class RsaSign {
static String header = "<!-- 537606aed546c5ba42c0820ad7fd0d74ee7caf90c232a484d0464b3332c42a9189555aebdba3570fe6566842ba7b7bb88da360f202ae9536a2a12fcdf39600c7 --><ObtainTicketResponse><message></message><prolongationPeriod>607875500</prolongationPeriod><responseCode>OK</responseCode><salt>1508484258274</salt><ticketId>1</ticketId><ticketProperties>licensee=Administrator licenseType=0 </ticketProperties></ObtainTicketResponse>";
static String content = "<ObtainTicketResponse><message></message><prolongationPeriod>607875500</prolongationPeriod><responseCode>OK</responseCode><salt>1508484258274</salt><ticketId>1</ticketId><ticketProperties>licensee=Administrator licenseType=0 </ticketProperties></ObtainTicketResponse>";
String ASNKEY = "-----BEGIN RSA PRIVATE KEY-----\r\n"
+ "MIIBOgIBAAJBALecq3BwAI4YJZwhJ+snnDFj3lF3DMqNPorV6y5ZKXCiCMqj8OeOmxk4YZW9aaV9\r\n"
+ "ckl/zlAOI0mpB3pDT+Xlj2sCAwEAAQJAW6/aVD05qbsZHMvZuS2Aa5FpNNj0BDlf38hOtkhDzz/h\r\n"
+ "kYb+EBYLLvldhgsD0OvRNy8yhz7EjaUqLCB0juIN4QIhAOeCQp+NXxfBmfdG/S+XbRUAdv8iHBl+\r\n"
+ "F6O2wr5fA2jzAiEAywlDfGIl6acnakPrmJE0IL8qvuO3FtsHBrpkUuOnXakCIQCqdr+XvADI/UTh\r\n"
+ "TuQepuErFayJMBSAsNe3NFsw0cUxAQIgGA5n7ZPfdBi3BdM4VeJWb87WrLlkVxPqeDSbcGrCyMkC\r\n"
+ "IFSs5JyXvFTreWt7IQjDssrKDRIPmALdNjvfETwlNJyY\r\n"
+ "-----END RSA PRIVATE KEY-----";
String PCKS8KEY = "-----BEGIN PRIVATE KEY-----\r\n"
+ "MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAt5yrcHAAjhglnCEn\r\n"
+ "6yecMWPeUXcMyo0+itXrLlkpcKIIyqPw546bGThhlb1ppX1ySX/OUA4jSakHekNP\r\n"
+ "5eWPawIDAQABAkBbr9pUPTmpuxkcy9m5LYBrkWk02PQEOV/fyE62SEPPP+GRhv4Q\r\n"
+ "Fgsu+V2GCwPQ69E3LzKHPsSNpSosIHSO4g3hAiEA54JCn41fF8GZ90b9L5dtFQB2\r\n"
+ "/yIcGX4Xo7bCvl8DaPMCIQDLCUN8YiXppydqQ+uYkTQgvyq+47cW2wcGumRS46dd\r\n"
+ "qQIhAKp2v5e8AMj9ROFO5B6m4SsVrIkwFICw17c0WzDRxTEBAiAYDmftk990GLcF\r\n"
+ "0zhV4lZvztasuWRXE+p4NJtwasLIyQIgVKzknJe8VOt5a3shCMOyysoNEg+YAt02\r\n"
+ "O98RPCU0nJg=\r\n" + "-----END PRIVATE KEY-----";
static String key22 = "MIIBOgIBAAJBALecq3BwAI4YJZwhJ+snnDFj3lF3DMqNPorV6y5ZKXCiCMqj8OeOmxk4YZW9aaV9"
+ "ckl/zlAOI0mpB3pDT+Xlj2sCAwEAAQJAW6/aVD05qbsZHMvZuS2Aa5FpNNj0BDlf38hOtkhDzz/h"
+ "kYb+EBYLLvldhgsD0OvRNy8yhz7EjaUqLCB0juIN4QIhAOeCQp+NXxfBmfdG/S+XbRUAdv8iHBl+"
+ "F6O2wr5fA2jzAiEAywlDfGIl6acnakPrmJE0IL8qvuO3FtsHBrpkUuOnXakCIQCqdr+XvADI/UTh"
+ "TuQepuErFayJMBSAsNe3NFsw0cUxAQIgGA5n7ZPfdBi3BdM4VeJWb87WrLlkVxPqeDSbcGrCyMkC"
+ "IFSs5JyXvFTreWt7IQjDssrKDRIPmALdNjvfETwlNJyY";
static String key33 = "MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAt5yrcHAAjhglnCEn"
+ "6yecMWPeUXcMyo0+itXrLlkpcKIIyqPw546bGThhlb1ppX1ySX/OUA4jSakHekNP"
+ "5eWPawIDAQABAkBbr9pUPTmpuxkcy9m5LYBrkWk02PQEOV/fyE62SEPPP+GRhv4Q"
+ "Fgsu+V2GCwPQ69E3LzKHPsSNpSosIHSO4g3hAiEA54JCn41fF8GZ90b9L5dtFQB2"
+ "/yIcGX4Xo7bCvl8DaPMCIQDLCUN8YiXppydqQ+uYkTQgvyq+47cW2wcGumRS46dd"
+ "qQIhAKp2v5e8AMj9ROFO5B6m4SsVrIkwFICw17c0WzDRxTEBAiAYDmftk990GLcF"
+ "0zhV4lZvztasuWRXE+p4NJtwasLIyQIgVKzknJe8VOt5a3shCMOyysoNEg+YAt02"
+ "O98RPCU0nJg=";
public static String Sign(String content){
return RsaSign.Sign(content.getBytes(), key22);
}
public static String Sign2(String content){
return RsaSign.Sign2(content.getBytes(), key33);
}
//传入秘钥为ASN格式
//私钥签名程序privateKey是私钥base64编码字符串即私钥文件数据中中间的主体部分
public static String Sign(byte[] content, String privateKey) {
try {
byte[] keybyte = Base64.decode(privateKey.toString());
ASN1InputStream in = new ASN1InputStream(keybyte);
ASN1Primitive obj = in.readObject();
RSAPrivateKeyStructure pStruct = RSAPrivateKeyStructure.getInstance(obj);
RSAPrivateKeySpec spec = new RSAPrivateKeySpec(pStruct.getModulus(), pStruct.getPrivateExponent());
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey priKey = keyFactory.generatePrivate(spec);
java.security.Signature signature = java.security.Signature.getInstance("MD5WithRSA");
signature.initSign(priKey);
signature.update(content);
byte[] signed = signature.sign();
return Hex.bytesToHexString(signed);
}
catch (Exception e) {
e.printStackTrace();
}
return null;
}
//传入秘钥为PKCS#8私钥非加密格式
//私钥签名程序privateKey是私钥base64编码字符串即私钥文件数据中中间的主体部分
public static String Sign2(byte[] content, String privateKey) {
try {
PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(Base64.decode(privateKey));
KeyFactory keyf = KeyFactory.getInstance("RSA");
PrivateKey priKey = keyf.generatePrivate(priPKCS8);
java.security.Signature signature = java.security.Signature.getInstance("MD5WithRSA");
signature.initSign(priKey);
signature.update(content);
byte[] signed = signature.sign();
return Hex.bytesToHexString(signed);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}

BIN
target/.DS_Store vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,5 @@
#Generated by Maven
#Thu Jul 06 00:38:37 CST 2023
version=1.0.0
groupId=com.witty.com
artifactId=Jrebel-License-Server

View File

@ -0,0 +1,11 @@
/Users/huangqing/code/jrebel-license-server-for-java/src/main/java/com/witty/handler/ToolHandler.java
/Users/huangqing/code/jrebel-license-server-for-java/src/main/java/com/witty/handler/JrebelHandler.java
/Users/huangqing/code/jrebel-license-server-for-java/src/main/java/com/witty/util/Base64.java
/Users/huangqing/code/jrebel-license-server-for-java/src/main/java/com/witty/util/JrebelUtil/ByteUtil.java
/Users/huangqing/code/jrebel-license-server-for-java/src/main/java/com/witty/util/Hex.java
/Users/huangqing/code/jrebel-license-server-for-java/src/main/java/com/witty/util/RsaSign.java
/Users/huangqing/code/jrebel-license-server-for-java/src/main/java/com/witty/handler/IndexHandler.java
/Users/huangqing/code/jrebel-license-server-for-java/src/main/java/com/witty/util/JrebelUtil/JrebelSign.java
/Users/huangqing/code/jrebel-license-server-for-java/src/main/java/com/witty/server/MainServer.java
/Users/huangqing/code/jrebel-license-server-for-java/src/main/java/com/witty/handler/JetBrainsHandler.java
/Users/huangqing/code/jrebel-license-server-for-java/src/main/java/com/witty/util/JrebelUtil/LicenseServer2ToJRebelPrivateKey.java