├── .gitignore ├── jetbra-agent ├── src │ └── main │ │ ├── resources │ │ └── jarLocation.txt │ │ └── java │ │ └── win │ │ └── novice │ │ └── li │ │ ├── HttpClientAdvice.java │ │ ├── PKIXBuilderParametersAdvice.java │ │ ├── SystemAdvice.java │ │ ├── AgentMain.java │ │ └── TrustAnchorHolder.java └── pom.xml ├── readme.md ├── jetbra-server ├── src │ └── main │ │ ├── java │ │ └── win │ │ │ └── novice │ │ │ └── li │ │ │ ├── JetbraServerApplication.java │ │ │ ├── model │ │ │ ├── Product.java │ │ │ └── License.java │ │ │ └── controller │ │ │ └── LicenseController.java │ │ └── resources │ │ ├── jetbra.crt │ │ └── jetbra.key └── pom.xml ├── vmoptions ├── clion.vmoptions ├── goland.vmoptions ├── idea.vmoptions ├── rider.vmoptions ├── studio.vmoptions ├── webide.vmoptions ├── appcode.vmoptions ├── dataspell.vmoptions ├── gateway.vmoptions ├── phpstorm.vmoptions ├── pycharm.vmoptions ├── rubymine.vmoptions ├── webstorm.vmoptions ├── datagrip.vmoptions ├── devecostudio.vmoptions ├── jetbrains_client.vmoptions └── jetbrainsclient.vmoptions ├── script ├── uninstall-current-user.vbs ├── uninstall-all-users.vbs ├── uninstall.sh ├── install-current-user.vbs ├── install-all-users.vbs └── install.sh ├── pom.xml ├── LICENSE ├── trust-crt ├── janetfilter.crt └── jetbra.crt ├── jetbra-dist ├── pom.xml └── package.xml ├── crt_and_key_gen.py └── jetbra.js /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .idea/ 3 | target/ -------------------------------------------------------------------------------- /jetbra-agent/src/main/resources/jarLocation.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Jetbra 2 | 适用于jetbrains家族产品的一款工具,参照[热老的项目](https://jetbra.in/s)自己写了点代码 ,使用方式与之一致 3 | 在替换janetfilter前需要执行janetfilter的卸载脚本 4 | 5 | 6 | 7 | 新增[油猴脚本](https://greasyfork.org/zh-CN/scripts/480799-jetbra), 使用这个脚本可以生成插件的激活码,需配合该工具使用 8 | -------------------------------------------------------------------------------- /jetbra-server/src/main/java/win/novice/li/JetbraServerApplication.java: -------------------------------------------------------------------------------- 1 | package win.novice.li; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class JetbraServerApplication { 8 | public static void main(String[] args) { 9 | SpringApplication.run(JetbraServerApplication.class,args); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /jetbra-agent/src/main/java/win/novice/li/HttpClientAdvice.java: -------------------------------------------------------------------------------- 1 | package win.novice.li; 2 | 3 | import net.bytebuddy.asm.Advice; 4 | 5 | import java.net.SocketTimeoutException; 6 | 7 | public class HttpClientAdvice { 8 | @Advice.OnMethodExit 9 | public static void intercept(@Advice.This Object x) throws Exception { 10 | if (x.toString().contains("validateKey.action")){ 11 | throw new SocketTimeoutException(); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /jetbra-server/src/main/java/win/novice/li/model/Product.java: -------------------------------------------------------------------------------- 1 | 2 | package win.novice.li.model; 3 | 4 | 5 | import jakarta.validation.constraints.NotBlank; 6 | import jakarta.validation.constraints.NotNull; 7 | import lombok.Data; 8 | 9 | @Data 10 | public class Product { 11 | @NotBlank 12 | private String code; 13 | @NotBlank 14 | private String fallbackDate = "2025-12-31"; 15 | @NotBlank 16 | private String paidUpTo = "2025-12-31"; 17 | @NotNull 18 | private Boolean extended = false; 19 | } -------------------------------------------------------------------------------- /vmoptions/clion.vmoptions: -------------------------------------------------------------------------------- 1 | -Xms128m 2 | -Xmx3992m 3 | -XX:ReservedCodeCacheSize=512m 4 | -XX:+IgnoreUnrecognizedVMOptions 5 | -XX:+UseG1GC 6 | -XX:SoftRefLRUPolicyMSPerMB=50 7 | -XX:CICompilerCount=2 8 | -XX:+HeapDumpOnOutOfMemoryError 9 | -XX:-OmitStackTraceInFastThrow 10 | -ea 11 | -Dsun.io.useCanonCaches=false 12 | -Djdk.http.auth.tunneling.disabledSchemes="" 13 | -Djdk.attach.allowAttachSelf=true 14 | -Djdk.module.illegalAccess.silent=true 15 | -Dkotlinx.coroutines.debug=off 16 | -XX:ErrorFile=$USER_HOME/java_error_in_idea_%p.log 17 | -XX:HeapDumpPath=$USER_HOME/java_error_in_idea.hprof 18 | 19 | --add-opens=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED 20 | --add-opens=java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED 21 | 22 | -------------------------------------------------------------------------------- /vmoptions/goland.vmoptions: -------------------------------------------------------------------------------- 1 | -Xms128m 2 | -Xmx1024m 3 | -XX:ReservedCodeCacheSize=512m 4 | -XX:+IgnoreUnrecognizedVMOptions 5 | -XX:+UseG1GC 6 | -XX:SoftRefLRUPolicyMSPerMB=50 7 | -XX:CICompilerCount=2 8 | -XX:+HeapDumpOnOutOfMemoryError 9 | -XX:-OmitStackTraceInFastThrow 10 | -ea 11 | -Dsun.io.useCanonCaches=false 12 | -Djdk.http.auth.tunneling.disabledSchemes="" 13 | -Djdk.attach.allowAttachSelf=true 14 | -Djdk.module.illegalAccess.silent=true 15 | -Dkotlinx.coroutines.debug=off 16 | -XX:ErrorFile=$USER_HOME/java_error_in_idea_%p.log 17 | -XX:HeapDumpPath=$USER_HOME/java_error_in_idea.hprof 18 | 19 | --add-opens=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED 20 | --add-opens=java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED 21 | 22 | -------------------------------------------------------------------------------- /vmoptions/idea.vmoptions: -------------------------------------------------------------------------------- 1 | -Xms128m 2 | -Xmx3992m 3 | -XX:ReservedCodeCacheSize=512m 4 | -XX:+IgnoreUnrecognizedVMOptions 5 | -XX:+UseG1GC 6 | -XX:SoftRefLRUPolicyMSPerMB=50 7 | -XX:CICompilerCount=2 8 | -XX:+HeapDumpOnOutOfMemoryError 9 | -XX:-OmitStackTraceInFastThrow 10 | -ea 11 | -Dsun.io.useCanonCaches=false 12 | -Djdk.http.auth.tunneling.disabledSchemes="" 13 | -Djdk.attach.allowAttachSelf=true 14 | -Djdk.module.illegalAccess.silent=true 15 | -Dkotlinx.coroutines.debug=off 16 | -XX:ErrorFile=$USER_HOME/java_error_in_idea_%p.log 17 | -XX:HeapDumpPath=$USER_HOME/java_error_in_idea.hprof 18 | 19 | --add-opens=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED 20 | --add-opens=java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED 21 | 22 | -------------------------------------------------------------------------------- /vmoptions/rider.vmoptions: -------------------------------------------------------------------------------- 1 | -Xms128m 2 | -Xmx1024m 3 | -XX:ReservedCodeCacheSize=512m 4 | -XX:+IgnoreUnrecognizedVMOptions 5 | -XX:+UseG1GC 6 | -XX:SoftRefLRUPolicyMSPerMB=50 7 | -XX:CICompilerCount=2 8 | -XX:+HeapDumpOnOutOfMemoryError 9 | -XX:-OmitStackTraceInFastThrow 10 | -ea 11 | -Dsun.io.useCanonCaches=false 12 | -Djdk.http.auth.tunneling.disabledSchemes="" 13 | -Djdk.attach.allowAttachSelf=true 14 | -Djdk.module.illegalAccess.silent=true 15 | -Dkotlinx.coroutines.debug=off 16 | -XX:ErrorFile=$USER_HOME/java_error_in_idea_%p.log 17 | -XX:HeapDumpPath=$USER_HOME/java_error_in_idea.hprof 18 | 19 | --add-opens=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED 20 | --add-opens=java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED 21 | 22 | -------------------------------------------------------------------------------- /vmoptions/studio.vmoptions: -------------------------------------------------------------------------------- 1 | -Xms128m 2 | -Xmx1024m 3 | -XX:ReservedCodeCacheSize=512m 4 | -XX:+IgnoreUnrecognizedVMOptions 5 | -XX:+UseG1GC 6 | -XX:SoftRefLRUPolicyMSPerMB=50 7 | -XX:CICompilerCount=2 8 | -XX:+HeapDumpOnOutOfMemoryError 9 | -XX:-OmitStackTraceInFastThrow 10 | -ea 11 | -Dsun.io.useCanonCaches=false 12 | -Djdk.http.auth.tunneling.disabledSchemes="" 13 | -Djdk.attach.allowAttachSelf=true 14 | -Djdk.module.illegalAccess.silent=true 15 | -Dkotlinx.coroutines.debug=off 16 | -XX:ErrorFile=$USER_HOME/java_error_in_idea_%p.log 17 | -XX:HeapDumpPath=$USER_HOME/java_error_in_idea.hprof 18 | 19 | --add-opens=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED 20 | --add-opens=java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED 21 | 22 | -------------------------------------------------------------------------------- /vmoptions/webide.vmoptions: -------------------------------------------------------------------------------- 1 | -Xms128m 2 | -Xmx1024m 3 | -XX:ReservedCodeCacheSize=512m 4 | -XX:+IgnoreUnrecognizedVMOptions 5 | -XX:+UseG1GC 6 | -XX:SoftRefLRUPolicyMSPerMB=50 7 | -XX:CICompilerCount=2 8 | -XX:+HeapDumpOnOutOfMemoryError 9 | -XX:-OmitStackTraceInFastThrow 10 | -ea 11 | -Dsun.io.useCanonCaches=false 12 | -Djdk.http.auth.tunneling.disabledSchemes="" 13 | -Djdk.attach.allowAttachSelf=true 14 | -Djdk.module.illegalAccess.silent=true 15 | -Dkotlinx.coroutines.debug=off 16 | -XX:ErrorFile=$USER_HOME/java_error_in_idea_%p.log 17 | -XX:HeapDumpPath=$USER_HOME/java_error_in_idea.hprof 18 | 19 | --add-opens=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED 20 | --add-opens=java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED 21 | 22 | -------------------------------------------------------------------------------- /vmoptions/appcode.vmoptions: -------------------------------------------------------------------------------- 1 | -Xms128m 2 | -Xmx1024m 3 | -XX:ReservedCodeCacheSize=512m 4 | -XX:+IgnoreUnrecognizedVMOptions 5 | -XX:+UseG1GC 6 | -XX:SoftRefLRUPolicyMSPerMB=50 7 | -XX:CICompilerCount=2 8 | -XX:+HeapDumpOnOutOfMemoryError 9 | -XX:-OmitStackTraceInFastThrow 10 | -ea 11 | -Dsun.io.useCanonCaches=false 12 | -Djdk.http.auth.tunneling.disabledSchemes="" 13 | -Djdk.attach.allowAttachSelf=true 14 | -Djdk.module.illegalAccess.silent=true 15 | -Dkotlinx.coroutines.debug=off 16 | -XX:ErrorFile=$USER_HOME/java_error_in_idea_%p.log 17 | -XX:HeapDumpPath=$USER_HOME/java_error_in_idea.hprof 18 | 19 | --add-opens=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED 20 | --add-opens=java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED 21 | 22 | -------------------------------------------------------------------------------- /vmoptions/dataspell.vmoptions: -------------------------------------------------------------------------------- 1 | -Xms128m 2 | -Xmx1024m 3 | -XX:ReservedCodeCacheSize=512m 4 | -XX:+IgnoreUnrecognizedVMOptions 5 | -XX:+UseG1GC 6 | -XX:SoftRefLRUPolicyMSPerMB=50 7 | -XX:CICompilerCount=2 8 | -XX:+HeapDumpOnOutOfMemoryError 9 | -XX:-OmitStackTraceInFastThrow 10 | -ea 11 | -Dsun.io.useCanonCaches=false 12 | -Djdk.http.auth.tunneling.disabledSchemes="" 13 | -Djdk.attach.allowAttachSelf=true 14 | -Djdk.module.illegalAccess.silent=true 15 | -Dkotlinx.coroutines.debug=off 16 | -XX:ErrorFile=$USER_HOME/java_error_in_idea_%p.log 17 | -XX:HeapDumpPath=$USER_HOME/java_error_in_idea.hprof 18 | 19 | --add-opens=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED 20 | --add-opens=java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED 21 | 22 | -------------------------------------------------------------------------------- /vmoptions/gateway.vmoptions: -------------------------------------------------------------------------------- 1 | -Xms128m 2 | -Xmx1024m 3 | -XX:ReservedCodeCacheSize=512m 4 | -XX:+IgnoreUnrecognizedVMOptions 5 | -XX:+UseG1GC 6 | -XX:SoftRefLRUPolicyMSPerMB=50 7 | -XX:CICompilerCount=2 8 | -XX:+HeapDumpOnOutOfMemoryError 9 | -XX:-OmitStackTraceInFastThrow 10 | -ea 11 | -Dsun.io.useCanonCaches=false 12 | -Djdk.http.auth.tunneling.disabledSchemes="" 13 | -Djdk.attach.allowAttachSelf=true 14 | -Djdk.module.illegalAccess.silent=true 15 | -Dkotlinx.coroutines.debug=off 16 | -XX:ErrorFile=$USER_HOME/java_error_in_idea_%p.log 17 | -XX:HeapDumpPath=$USER_HOME/java_error_in_idea.hprof 18 | 19 | --add-opens=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED 20 | --add-opens=java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED 21 | 22 | -------------------------------------------------------------------------------- /vmoptions/phpstorm.vmoptions: -------------------------------------------------------------------------------- 1 | -Xms128m 2 | -Xmx1024m 3 | -XX:ReservedCodeCacheSize=512m 4 | -XX:+IgnoreUnrecognizedVMOptions 5 | -XX:+UseG1GC 6 | -XX:SoftRefLRUPolicyMSPerMB=50 7 | -XX:CICompilerCount=2 8 | -XX:+HeapDumpOnOutOfMemoryError 9 | -XX:-OmitStackTraceInFastThrow 10 | -ea 11 | -Dsun.io.useCanonCaches=false 12 | -Djdk.http.auth.tunneling.disabledSchemes="" 13 | -Djdk.attach.allowAttachSelf=true 14 | -Djdk.module.illegalAccess.silent=true 15 | -Dkotlinx.coroutines.debug=off 16 | -XX:ErrorFile=$USER_HOME/java_error_in_idea_%p.log 17 | -XX:HeapDumpPath=$USER_HOME/java_error_in_idea.hprof 18 | 19 | --add-opens=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED 20 | --add-opens=java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED 21 | 22 | -------------------------------------------------------------------------------- /vmoptions/pycharm.vmoptions: -------------------------------------------------------------------------------- 1 | -Xms128m 2 | -Xmx1024m 3 | -XX:ReservedCodeCacheSize=512m 4 | -XX:+IgnoreUnrecognizedVMOptions 5 | -XX:+UseG1GC 6 | -XX:SoftRefLRUPolicyMSPerMB=50 7 | -XX:CICompilerCount=2 8 | -XX:+HeapDumpOnOutOfMemoryError 9 | -XX:-OmitStackTraceInFastThrow 10 | -ea 11 | -Dsun.io.useCanonCaches=false 12 | -Djdk.http.auth.tunneling.disabledSchemes="" 13 | -Djdk.attach.allowAttachSelf=true 14 | -Djdk.module.illegalAccess.silent=true 15 | -Dkotlinx.coroutines.debug=off 16 | -XX:ErrorFile=$USER_HOME/java_error_in_idea_%p.log 17 | -XX:HeapDumpPath=$USER_HOME/java_error_in_idea.hprof 18 | 19 | --add-opens=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED 20 | --add-opens=java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED 21 | 22 | -------------------------------------------------------------------------------- /vmoptions/rubymine.vmoptions: -------------------------------------------------------------------------------- 1 | -Xms128m 2 | -Xmx1024m 3 | -XX:ReservedCodeCacheSize=512m 4 | -XX:+IgnoreUnrecognizedVMOptions 5 | -XX:+UseG1GC 6 | -XX:SoftRefLRUPolicyMSPerMB=50 7 | -XX:CICompilerCount=2 8 | -XX:+HeapDumpOnOutOfMemoryError 9 | -XX:-OmitStackTraceInFastThrow 10 | -ea 11 | -Dsun.io.useCanonCaches=false 12 | -Djdk.http.auth.tunneling.disabledSchemes="" 13 | -Djdk.attach.allowAttachSelf=true 14 | -Djdk.module.illegalAccess.silent=true 15 | -Dkotlinx.coroutines.debug=off 16 | -XX:ErrorFile=$USER_HOME/java_error_in_idea_%p.log 17 | -XX:HeapDumpPath=$USER_HOME/java_error_in_idea.hprof 18 | 19 | --add-opens=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED 20 | --add-opens=java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED 21 | 22 | -------------------------------------------------------------------------------- /vmoptions/webstorm.vmoptions: -------------------------------------------------------------------------------- 1 | -Xms128m 2 | -Xmx1024m 3 | -XX:ReservedCodeCacheSize=512m 4 | -XX:+IgnoreUnrecognizedVMOptions 5 | -XX:+UseG1GC 6 | -XX:SoftRefLRUPolicyMSPerMB=50 7 | -XX:CICompilerCount=2 8 | -XX:+HeapDumpOnOutOfMemoryError 9 | -XX:-OmitStackTraceInFastThrow 10 | -ea 11 | -Dsun.io.useCanonCaches=false 12 | -Djdk.http.auth.tunneling.disabledSchemes="" 13 | -Djdk.attach.allowAttachSelf=true 14 | -Djdk.module.illegalAccess.silent=true 15 | -Dkotlinx.coroutines.debug=off 16 | -XX:ErrorFile=$USER_HOME/java_error_in_idea_%p.log 17 | -XX:HeapDumpPath=$USER_HOME/java_error_in_idea.hprof 18 | 19 | --add-opens=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED 20 | --add-opens=java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED 21 | 22 | -------------------------------------------------------------------------------- /vmoptions/datagrip.vmoptions: -------------------------------------------------------------------------------- 1 | -Xms128m 2 | -Xmx1024m 3 | -XX:ReservedCodeCacheSize=512m 4 | -XX:+IgnoreUnrecognizedVMOptions 5 | -XX:+UseG1GC 6 | -XX:SoftRefLRUPolicyMSPerMB=50 7 | -XX:CICompilerCount=2 8 | -XX:+HeapDumpOnOutOfMemoryError 9 | -XX:-OmitStackTraceInFastThrow 10 | -ea 11 | -Dsun.io.useCanonCaches=false 12 | -Djdk.http.auth.tunneling.disabledSchemes="" 13 | -Djdk.attach.allowAttachSelf=true 14 | -Djdk.module.illegalAccess.silent=true 15 | -Dkotlinx.coroutines.debug=off 16 | -XX:ErrorFile=$USER_HOME/java_error_in_idea_%p.log 17 | -XX:HeapDumpPath=$USER_HOME/java_error_in_idea.hprof 18 | 19 | --add-opens=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED 20 | --add-opens=java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED 21 | 22 | 23 | -------------------------------------------------------------------------------- /vmoptions/devecostudio.vmoptions: -------------------------------------------------------------------------------- 1 | -Xms128m 2 | -Xmx1024m 3 | -XX:ReservedCodeCacheSize=512m 4 | -XX:+IgnoreUnrecognizedVMOptions 5 | -XX:+UseG1GC 6 | -XX:SoftRefLRUPolicyMSPerMB=50 7 | -XX:CICompilerCount=2 8 | -XX:+HeapDumpOnOutOfMemoryError 9 | -XX:-OmitStackTraceInFastThrow 10 | -ea 11 | -Dsun.io.useCanonCaches=false 12 | -Djdk.http.auth.tunneling.disabledSchemes="" 13 | -Djdk.attach.allowAttachSelf=true 14 | -Djdk.module.illegalAccess.silent=true 15 | -Dkotlinx.coroutines.debug=off 16 | -XX:ErrorFile=$USER_HOME/java_error_in_idea_%p.log 17 | -XX:HeapDumpPath=$USER_HOME/java_error_in_idea.hprof 18 | 19 | --add-opens=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED 20 | --add-opens=java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED 21 | 22 | -------------------------------------------------------------------------------- /vmoptions/jetbrains_client.vmoptions: -------------------------------------------------------------------------------- 1 | -Xms128m 2 | -Xmx1024m 3 | -XX:ReservedCodeCacheSize=512m 4 | -XX:+IgnoreUnrecognizedVMOptions 5 | -XX:+UseG1GC 6 | -XX:SoftRefLRUPolicyMSPerMB=50 7 | -XX:CICompilerCount=2 8 | -XX:+HeapDumpOnOutOfMemoryError 9 | -XX:-OmitStackTraceInFastThrow 10 | -ea 11 | -Dsun.io.useCanonCaches=false 12 | -Djdk.http.auth.tunneling.disabledSchemes="" 13 | -Djdk.attach.allowAttachSelf=true 14 | -Djdk.module.illegalAccess.silent=true 15 | -Dkotlinx.coroutines.debug=off 16 | -XX:ErrorFile=$USER_HOME/java_error_in_idea_%p.log 17 | -XX:HeapDumpPath=$USER_HOME/java_error_in_idea.hprof 18 | 19 | --add-opens=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED 20 | --add-opens=java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED 21 | 22 | -------------------------------------------------------------------------------- /vmoptions/jetbrainsclient.vmoptions: -------------------------------------------------------------------------------- 1 | -Xms128m 2 | -Xmx1024m 3 | -XX:ReservedCodeCacheSize=512m 4 | -XX:+IgnoreUnrecognizedVMOptions 5 | -XX:+UseG1GC 6 | -XX:SoftRefLRUPolicyMSPerMB=50 7 | -XX:CICompilerCount=2 8 | -XX:+HeapDumpOnOutOfMemoryError 9 | -XX:-OmitStackTraceInFastThrow 10 | -ea 11 | -Dsun.io.useCanonCaches=false 12 | -Djdk.http.auth.tunneling.disabledSchemes="" 13 | -Djdk.attach.allowAttachSelf=true 14 | -Djdk.module.illegalAccess.silent=true 15 | -Dkotlinx.coroutines.debug=off 16 | -XX:ErrorFile=$USER_HOME/java_error_in_idea_%p.log 17 | -XX:HeapDumpPath=$USER_HOME/java_error_in_idea.hprof 18 | 19 | --add-opens=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED 20 | --add-opens=java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED 21 | 22 | -------------------------------------------------------------------------------- /script/uninstall-current-user.vbs: -------------------------------------------------------------------------------- 1 | Set oShell = CreateObject("WScript.Shell") 2 | Set oEnv = oShell.Environment("USER") 3 | 4 | Dim sEnvKey, sEnvVal, aJBProducts 5 | aJBProducts = Array("idea", "clion", "phpstorm", "goland", "pycharm", "webstorm", "webide", "rider", "datagrip", "rubymine", "appcode", "dataspell", "gateway", "jetbrains_client", "jetbrainsclient", "studio", "devecostudio") 6 | 7 | MsgBox "It may take a few seconds to execute this script." & vbCrLf & vbCrLf & "Click 'OK' button and wait for the prompt of 'Done.' to pop up!" 8 | 9 | For Each sPrd in aJBProducts 10 | sEnvKey = UCase(sPrd) & "_VM_OPTIONS" 11 | sEnvVal = oShell.ExpandEnvironmentStrings("%" & sEnvKey & "%") 12 | If sEnvVal <> ("%" & sEnvKey & "%") Then 13 | oEnv.Remove(sEnvKey) 14 | End If 15 | Next 16 | 17 | MsgBox "Done." 18 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 3.2.0 9 | 10 | 11 | win.novice 12 | jetbra 13 | 0.0.1 14 | pom 15 | jetbra 16 | jetbra 17 | 18 | jetbra-agent 19 | jetbra-server 20 | jetbra-dist 21 | 22 | 23 | 17 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /jetbra-agent/src/main/java/win/novice/li/PKIXBuilderParametersAdvice.java: -------------------------------------------------------------------------------- 1 | package win.novice.li; 2 | 3 | import net.bytebuddy.asm.Advice; 4 | 5 | import java.lang.reflect.Method; 6 | import java.security.cert.TrustAnchor; 7 | import java.util.HashSet; 8 | import java.util.Set; 9 | 10 | public class PKIXBuilderParametersAdvice { 11 | 12 | 13 | @Advice.OnMethodEnter 14 | @SuppressWarnings("unchecked") 15 | public static void intercept(@Advice.Argument(value = 0, readOnly = false) Set trustAnchors) throws Exception { 16 | Class clazz = Class.forName("win.novice.li.TrustAnchorHolder", true, ClassLoader.getSystemClassLoader()); 17 | Method method = clazz.getDeclaredMethod("loadTrustAnchors"); 18 | Set loadedTrustAnchors = (Set)method.invoke(null); 19 | HashSet newTrustAnchors = new HashSet<>(trustAnchors); 20 | newTrustAnchors.addAll(loadedTrustAnchors); 21 | trustAnchors = newTrustAnchors; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /jetbra-agent/src/main/java/win/novice/li/SystemAdvice.java: -------------------------------------------------------------------------------- 1 | package win.novice.li; 2 | 3 | import net.bytebuddy.asm.Advice; 4 | 5 | public class SystemAdvice { 6 | 7 | 8 | // System.getProperty 9 | @Advice.OnMethodExit 10 | public static void intercept(@Advice.Argument(0) Object x, @Advice.Return(readOnly = false) String r) throws Exception { 11 | if (x.toString().equals("jb.vmOptionsFile")) { 12 | RuntimeException exception = new RuntimeException(); 13 | int nullCnt = 0; 14 | boolean hasReflect = false; 15 | for (StackTraceElement element : exception.getStackTrace()) { 16 | if (element.getFileName() == null) { 17 | nullCnt += 1; 18 | continue; 19 | } 20 | if (element.getFileName().equals("Method.java")) { 21 | hasReflect = true; 22 | } 23 | } 24 | if (nullCnt >= 3 && hasReflect) { 25 | r = null; 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 kangwei Li 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /script/uninstall-all-users.vbs: -------------------------------------------------------------------------------- 1 | If Not WScript.Arguments.Named.Exists("elevate") Then 2 | CreateObject("Shell.Application").ShellExecute WScript.FullName, """" & WScript.ScriptFullName & """ /elevate", "", "runas", 10 3 | WScript.Quit 4 | End If 5 | 6 | MsgBox "It may take a few seconds to execute this script." & vbCrLf & vbCrLf & "Click 'OK' button and wait for the prompt of 'Done.' to pop up!" 7 | 8 | Sub RemoveEnv(env) 9 | On Error Resume Next 10 | 11 | Dim sEnvKey, sEnvVal, aJBProducts 12 | aJBProducts = Array("idea", "clion", "phpstorm", "goland", "pycharm", "webstorm", "webide", "rider", "datagrip", "rubymine", "appcode", "dataspell", "gateway", "jetbrains_client", "jetbrainsclient", "studio", "devecostudio") 13 | 14 | For Each sPrd in aJBProducts 15 | sEnvKey = UCase(sPrd) & "_VM_OPTIONS" 16 | sEnvVal = oShell.ExpandEnvironmentStrings("%" & sEnvKey & "%") 17 | If sEnvVal <> ("%" & sEnvKey & "%") Then 18 | env.Remove(sEnvKey) 19 | End If 20 | Next 21 | End Sub 22 | 23 | Set oShell = CreateObject("WScript.Shell") 24 | 25 | RemoveEnv oShell.Environment("USER") 26 | RemoveEnv oShell.Environment("SYSTEM") 27 | 28 | MsgBox "Done." 29 | -------------------------------------------------------------------------------- /jetbra-server/src/main/java/win/novice/li/model/License.java: -------------------------------------------------------------------------------- 1 | package win.novice.li.model; 2 | 3 | import jakarta.validation.Valid; 4 | import jakarta.validation.constraints.Min; 5 | import jakarta.validation.constraints.NotBlank; 6 | import jakarta.validation.constraints.NotEmpty; 7 | import jakarta.validation.constraints.NotNull; 8 | import lombok.Data; 9 | 10 | import java.util.List; 11 | 12 | @Data 13 | public class License { 14 | private String licenseId; 15 | @NotBlank 16 | private String licenseeName = "Test"; 17 | @NotBlank 18 | private String assigneeName = "novice.li"; 19 | @NotNull 20 | private String assigneeEmail = ""; 21 | @NotNull 22 | private String licenseRestriction = ""; 23 | @NotNull 24 | private Boolean checkConcurrentUse = false; 25 | 26 | @NotEmpty 27 | private List<@Valid Product> products; 28 | @NotBlank 29 | private String metadata = "0120230102PPAA013009"; 30 | @NotBlank 31 | private String hash = "41472961/0:1563609451"; 32 | 33 | @NotNull 34 | @Min(1) 35 | private Integer gracePeriodDays = 7; 36 | @NotNull 37 | private Boolean autoProlongated = true; 38 | @NotNull 39 | private Boolean isAutoProlongated = true; 40 | } 41 | -------------------------------------------------------------------------------- /trust-crt/janetfilter.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIETDCCAjSgAwIBAgIBDTANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1KZXRQ 3 | cm9maWxlIENBMB4XDTIwMTAxOTA5MDU1M1oXDTIyMTAyMTA5MDU1M1owHzEdMBsG 4 | A1UEAwwUcHJvZDJ5LWZyb20tMjAyMDEwMTkwggEiMA0GCSqGSIb3DQEBAQUAA4IB 5 | DwAwggEKAoIBAQCUlaUFc1wf+CfY9wzFWEL2euKQ5nswqb57V8QZG7d7RoR6rwYU 6 | IXseTOAFq210oMEe++LCjzKDuqwDfsyhgDNTgZBPAaC4vUU2oy+XR+Fq8nBixWIs 7 | H668HeOnRK6RRhsr0rJzRB95aZ3EAPzBuQ2qPaNGm17pAX0Rd6MPRgjp75IWwI9e 8 | A6aMEdPQEVN7uyOtM5zSsjoj79Lbu1fjShOnQZuJcsV8tqnayeFkNzv2LTOlofU/ 9 | Tbx502Ro073gGjoeRzNvrynAP03pL486P3KCAyiNPhDs2z8/COMrxRlZW5mfzo0x 10 | sK0dQGNH3UoG/9RVwHG4eS8LFpMTR9oetHZBAgMBAAGjgZkwgZYwCQYDVR0TBAIw 11 | ADAdBgNVHQ4EFgQUJNoRIpb1hUHAk0foMSNM9MCEAv8wSAYDVR0jBEEwP4AUo562 12 | SGdCEjZBvW3gubSgUouX8bOhHKQaMBgxFjAUBgNVBAMMDUpldFByb2ZpbGUgQ0GC 13 | CQDSbLGDsoN54TATBgNVHSUEDDAKBggrBgEFBQcDATALBgNVHQ8EBAMCBaAwDQYJ 14 | KoZIhvcNAQELBQADggIBABKaDfYJk51mtYwUFK8xqhiZaYPd30TlmCmSAaGJ0eBp 15 | vkVeqA2jGYhAQRqFiAlFC63JKvWvRZO1iRuWCEfUMkdqQ9VQPXziE/BlsOIgrL6R 16 | lJfuFcEZ8TK3syIfIGQZNCxYhLLUuet2HE6LJYPQ5c0jH4kDooRpcVZ4rBxNwddp 17 | ctUO2te9UU5/FjhioZQsPvd92qOTsV+8Cyl2fvNhNKD1Uu9ff5AkVIQn4JU23ozd 18 | B/R5oUlebwaTE6WZNBs+TA/qPj+5/we9NH71WRB0hqUoLI2AKKyiPw++FtN4Su1v 19 | sdDlrAzDj9ILjpjJKA1ImuVcG329/WTYIKysZ1CWK3zATg9BeCUPAV1pQy8ToXOq 20 | +RSYen6winZ2OO93eyHv2Iw5kbn1dqfBw1BuTE29V2FJKicJSu8iEOpfoafwJISX 21 | mz1wnnWL3V/0NxTulfWsXugOoLfv0ZIBP1xH9kmf22jjQ2JiHhQZP7ZDsreRrOeI 22 | Q/c4yR8IQvMLfC0WKQqrHu5ZzXTH4NO3CwGWSlTY74kE91zXB5mwWAx1jig+UXYc 23 | 2w4RkVhy0//lOmVya/PEepuuTTI4+UJwC7qbVlh5zfhj8oTNUXgN0AOc+Q0/WFPl 24 | 1aw5VV/VrO8FCoB15lFVlpKaQ1Yh+DVU8ke+rt9Th0BCHXe0uZOEmH0nOnH/0onD 25 | -----END CERTIFICATE----- 26 | -------------------------------------------------------------------------------- /jetbra-dist/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | win.novice 8 | jetbra 9 | 0.0.1 10 | 11 | 12 | win.novice.li 13 | jetbra-dist 14 | 15 | 16 | 17 17 | 17 18 | UTF-8 19 | 20 | 21 | 22 | 23 | maven-assembly-plugin 24 | 25 | jetbra 26 | 27 | package.xml 28 | 29 | 30 | 31 | 32 | make-assembly 33 | package 34 | 35 | single 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /crt_and_key_gen.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | 3 | from cryptography import x509 4 | from cryptography.hazmat.backends import default_backend 5 | from cryptography.hazmat.primitives import hashes, serialization 6 | from cryptography.hazmat.primitives.asymmetric import rsa 7 | from cryptography.x509.oid import NameOID 8 | 9 | one_day = datetime.timedelta(days=1) 10 | ten_day = datetime.timedelta(days=3650) 11 | today = datetime.datetime.today() 12 | yesterday = today - one_day 13 | tomorrow = today + ten_day 14 | 15 | private_key = rsa.generate_private_key( 16 | public_exponent=65537, 17 | key_size=4096, 18 | backend=default_backend() 19 | ) 20 | public_key = private_key.public_key() 21 | builder = x509.CertificateBuilder() 22 | 23 | builder = builder.subject_name(x509.Name([ 24 | x509.NameAttribute(NameOID.COMMON_NAME, 'Novice'), 25 | ])) 26 | builder = builder.issuer_name(x509.Name([ 27 | x509.NameAttribute(NameOID.COMMON_NAME, 'JetProfile CA'), 28 | ])) 29 | builder = builder.not_valid_before(yesterday) 30 | builder = builder.not_valid_after(tomorrow) 31 | builder = builder.serial_number(x509.random_serial_number()) 32 | builder = builder.public_key(public_key) 33 | 34 | certificate = builder.sign( 35 | private_key=private_key, algorithm=hashes.SHA256(), 36 | backend=default_backend() 37 | ) 38 | 39 | private_bytes = private_key.private_bytes( 40 | encoding=serialization.Encoding.PEM, 41 | format=serialization.PrivateFormat.TraditionalOpenSSL, 42 | encryption_algorithm=serialization.NoEncryption()) 43 | public_bytes = certificate.public_bytes( 44 | encoding=serialization.Encoding.PEM) 45 | with open("ca.key", "wb") as fout: 46 | fout.write(private_bytes) 47 | with open("ca.crt", "wb") as fout: 48 | fout.write(public_bytes) 49 | -------------------------------------------------------------------------------- /trust-crt/jetbra.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEtTCCAp2gAwIBAgIUDyuccmylba71lZQAQic5TJiAhwwwDQYJKoZIhvcNAQEL 3 | BQAwGDEWMBQGA1UEAwwNSmV0UHJvZmlsZSBDQTAeFw0yMzA5MjkxNDA2MTJaFw0z 4 | MzA5MjcxNDA2MTJaMBExDzANBgNVBAMMBk5vdmljZTCCAiIwDQYJKoZIhvcNAQEB 5 | BQADggIPADCCAgoCggIBALenqcGP2ZxGkYqmKA9c4Hzf8+YD1smvmOxKjd+bmTLr 6 | utM/hXv1cj1rW3/lqyDtdDk7K6W8/TDq1CRrEt+Do6l30DxhAiC34aH8DmGwgq77 7 | xEoLimvH5LpePxflF+tbB1RZtFgFDOIYLdSQaKFH2JDgVKxhLiV3S6jniPhkCtWW 8 | rTs+E6vq4N15Bm3NnM5AJILqjtUbOjNfaxVq6RrOoTc0R3Fqqo6yvxo/+JYa2UnH 9 | IC+r2dbKuDLMUrtgnydEUdJNX0zH9FtcdELvr48uc9mY038TWUsZUK1pnQbxA2bP 10 | yA4qnYJ9IvUgO6LtLXvGFm137YQMS1N41AHDBOrwoNI8UoDX+qI3rM96biFOFvn7 11 | Edky7rByzybt3H+zxdojfjvpL1E0NO98BT9zfufHAaAxZtlmDOu5LDJe3CGurnyR 12 | MRExbtc+Qjl1mUh6tG4lakAwdsoxry0GdG72yaYyb9it53kaFks/T/s7Z7bRJzVF 13 | zQDV1Y4bzUtk43vKm2vztBVlQkBkZY5f2Jbe5Ig3b8swQzBnOT0mrL5SPUhwmQ6I 14 | xkEWztj55OEujBMmRr92oESuq9ZYMaeLidKWVR3/++HA8BRZaRGEKtSHZCbFEFdi 15 | hDxxJv9Xh6NuT/ewJ6HYp+0NQpFnUnJ72n8wV+tudpam7aKcdzVmz7cNwOhG2Ls7 16 | AgMBAAEwDQYJKoZIhvcNAQELBQADggIBAIdeaQfKni7tXtcywC3zJvGzaaj242pS 17 | WB1y40HW8jub0uHjTLsBPX27iA/5rb+rNXtUWX/f2K+DU4IgaIiiHhkDrMsw7piv 18 | azqwA9h7/uA0A5nepmTYf/HY4W6P2stbeqInNsFRZXS7Jg4Q5LgEtHKo/H8USjtV 19 | w9apmE3BCElkXRuelXMsSllpR/JEVv/8NPLmnHSY02q4KMVW2ozXtaAxSYQmZswy 20 | P1YnBcnRukoI4igobpcKQXwGoQCIUlec8LbFXYM9V2eNCwgABqd4r67m7QJq31Y/ 21 | 1TJysQdMH+hoPFy9rqNCxSq3ptpuzcYAk6qVf58PrrYH/6bHwiYPAayvvdzNPOhM 22 | 9OCwomfcazhK3y7HyS8aBLntTQYFf7vYzZxPMDybYTvJM+ClCNnVD7Q9fttIJ6eM 23 | XFsXb8YK1uGNjQW8Y4WHk1MCHuD9ZumWu/CtAhBn6tllTQWwNMaPOQvKf1kr1Kt5 24 | etrONY+B6O+Oi75SZbDuGz7PIF9nMPy4WB/8XgKdVFtKJ7/zLIPHgY8IKgbx/VTz 25 | 6uBhYo8wOf3xzzweMnn06UcfV3JGNvtMuV4vlkZNNxXeifsgzHugCvJX0nybhfBh 26 | fIqVyfK6t0eKJqrvp54XFEtJGR+lf3pBfTdcOI6QFEPKGZKoQz8Ck+BC/WBDtbjc 27 | /uYKczZ8DKZu 28 | -----END CERTIFICATE----- 29 | -------------------------------------------------------------------------------- /jetbra-server/src/main/resources/jetbra.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEtTCCAp2gAwIBAgIUDyuccmylba71lZQAQic5TJiAhwwwDQYJKoZIhvcNAQEL 3 | BQAwGDEWMBQGA1UEAwwNSmV0UHJvZmlsZSBDQTAeFw0yMzA5MjkxNDA2MTJaFw0z 4 | MzA5MjcxNDA2MTJaMBExDzANBgNVBAMMBk5vdmljZTCCAiIwDQYJKoZIhvcNAQEB 5 | BQADggIPADCCAgoCggIBALenqcGP2ZxGkYqmKA9c4Hzf8+YD1smvmOxKjd+bmTLr 6 | utM/hXv1cj1rW3/lqyDtdDk7K6W8/TDq1CRrEt+Do6l30DxhAiC34aH8DmGwgq77 7 | xEoLimvH5LpePxflF+tbB1RZtFgFDOIYLdSQaKFH2JDgVKxhLiV3S6jniPhkCtWW 8 | rTs+E6vq4N15Bm3NnM5AJILqjtUbOjNfaxVq6RrOoTc0R3Fqqo6yvxo/+JYa2UnH 9 | IC+r2dbKuDLMUrtgnydEUdJNX0zH9FtcdELvr48uc9mY038TWUsZUK1pnQbxA2bP 10 | yA4qnYJ9IvUgO6LtLXvGFm137YQMS1N41AHDBOrwoNI8UoDX+qI3rM96biFOFvn7 11 | Edky7rByzybt3H+zxdojfjvpL1E0NO98BT9zfufHAaAxZtlmDOu5LDJe3CGurnyR 12 | MRExbtc+Qjl1mUh6tG4lakAwdsoxry0GdG72yaYyb9it53kaFks/T/s7Z7bRJzVF 13 | zQDV1Y4bzUtk43vKm2vztBVlQkBkZY5f2Jbe5Ig3b8swQzBnOT0mrL5SPUhwmQ6I 14 | xkEWztj55OEujBMmRr92oESuq9ZYMaeLidKWVR3/++HA8BRZaRGEKtSHZCbFEFdi 15 | hDxxJv9Xh6NuT/ewJ6HYp+0NQpFnUnJ72n8wV+tudpam7aKcdzVmz7cNwOhG2Ls7 16 | AgMBAAEwDQYJKoZIhvcNAQELBQADggIBAIdeaQfKni7tXtcywC3zJvGzaaj242pS 17 | WB1y40HW8jub0uHjTLsBPX27iA/5rb+rNXtUWX/f2K+DU4IgaIiiHhkDrMsw7piv 18 | azqwA9h7/uA0A5nepmTYf/HY4W6P2stbeqInNsFRZXS7Jg4Q5LgEtHKo/H8USjtV 19 | w9apmE3BCElkXRuelXMsSllpR/JEVv/8NPLmnHSY02q4KMVW2ozXtaAxSYQmZswy 20 | P1YnBcnRukoI4igobpcKQXwGoQCIUlec8LbFXYM9V2eNCwgABqd4r67m7QJq31Y/ 21 | 1TJysQdMH+hoPFy9rqNCxSq3ptpuzcYAk6qVf58PrrYH/6bHwiYPAayvvdzNPOhM 22 | 9OCwomfcazhK3y7HyS8aBLntTQYFf7vYzZxPMDybYTvJM+ClCNnVD7Q9fttIJ6eM 23 | XFsXb8YK1uGNjQW8Y4WHk1MCHuD9ZumWu/CtAhBn6tllTQWwNMaPOQvKf1kr1Kt5 24 | etrONY+B6O+Oi75SZbDuGz7PIF9nMPy4WB/8XgKdVFtKJ7/zLIPHgY8IKgbx/VTz 25 | 6uBhYo8wOf3xzzweMnn06UcfV3JGNvtMuV4vlkZNNxXeifsgzHugCvJX0nybhfBh 26 | fIqVyfK6t0eKJqrvp54XFEtJGR+lf3pBfTdcOI6QFEPKGZKoQz8Ck+BC/WBDtbjc 27 | /uYKczZ8DKZu 28 | -----END CERTIFICATE----- 29 | -------------------------------------------------------------------------------- /jetbra-dist/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | all 5 | 6 | zip 7 | 8 | false 9 | 10 | 11 | ${project.parent.basedir}/script 12 | script 13 | 14 | * 15 | 16 | 17 | 18 | ${project.parent.basedir}/vmoptions 19 | vmoptions 20 | 21 | * 22 | 23 | 24 | 25 | ${project.parent.basedir}/trust-crt 26 | trust-crt 27 | 28 | * 29 | 30 | 31 | 32 | ${project.parent.basedir}/trust-crt 33 | trust-crt 34 | 35 | * 36 | 37 | 38 | 39 | 40 | 41 | ${project.parent.basedir}/jetbra-agent/target/jetbra-agent.jar 42 | jetbra-agent.jar 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /script/uninstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | OS_NAME=$(uname -s) 6 | JB_PRODUCTS="idea clion phpstorm goland pycharm webstorm webide rider datagrip rubymine appcode dataspell gateway jetbrains_client jetbrainsclient studio devecostudio" 7 | 8 | KDE_ENV_DIR="${HOME}/.config/plasma-workspace/env" 9 | 10 | PROFILE_PATH="${HOME}/.profile" 11 | ZSH_PROFILE_PATH="${HOME}/.zshrc" 12 | PLIST_PATH="${HOME}/Library/LaunchAgents/jetbrains.vmoptions.plist" 13 | 14 | if [ $OS_NAME = "Darwin" ]; then 15 | BASH_PROFILE_PATH="${HOME}/.bash_profile" 16 | else 17 | BASH_PROFILE_PATH="${HOME}/.bashrc" 18 | fi 19 | 20 | touch "${PROFILE_PATH}" 21 | touch "${BASH_PROFILE_PATH}" 22 | touch "${ZSH_PROFILE_PATH}" 23 | 24 | MY_VMOPTIONS_SHELL_NAME="jetbrains.vmoptions.sh" 25 | MY_VMOPTIONS_SHELL_FILE="${HOME}/.${MY_VMOPTIONS_SHELL_NAME}" 26 | 27 | rm -rf "${MY_VMOPTIONS_SHELL_FILE}" 28 | 29 | if [ $OS_NAME = "Darwin" ]; then 30 | for PRD in $JB_PRODUCTS; do 31 | ENV_NAME=$(echo $PRD | tr '[a-z]' '[A-Z]')"_VM_OPTIONS" 32 | 33 | launchctl unsetenv "${ENV_NAME}" 34 | done 35 | 36 | rm -rf "${PLIST_PATH}" 37 | 38 | sed -i '' '/___MY_VMOPTIONS_SHELL_FILE="${HOME}\/\.jetbrains\.vmoptions\.sh"; if /d' "${PROFILE_PATH}" >/dev/null 2>&1 39 | sed -i '' '/___MY_VMOPTIONS_SHELL_FILE="${HOME}\/\.jetbrains\.vmoptions\.sh"; if /d' "${BASH_PROFILE_PATH}" >/dev/null 2>&1 40 | sed -i '' '/___MY_VMOPTIONS_SHELL_FILE="${HOME}\/\.jetbrains\.vmoptions\.sh"; if /d' "${ZSH_PROFILE_PATH}" >/dev/null 2>&1 41 | 42 | echo 'done.' 43 | else 44 | sed -i '/___MY_VMOPTIONS_SHELL_FILE="${HOME}\/\.jetbrains\.vmoptions\.sh"; if /d' "${PROFILE_PATH}" >/dev/null 2>&1 45 | sed -i '/___MY_VMOPTIONS_SHELL_FILE="${HOME}\/\.jetbrains\.vmoptions\.sh"; if /d' "${BASH_PROFILE_PATH}" >/dev/null 2>&1 46 | sed -i '/___MY_VMOPTIONS_SHELL_FILE="${HOME}\/\.jetbrains\.vmoptions\.sh"; if /d' "${ZSH_PROFILE_PATH}" >/dev/null 2>&1 47 | 48 | rm -rf "${KDE_ENV_DIR}/${MY_VMOPTIONS_SHELL_NAME}" 49 | echo "done. you'd better log off first!" 50 | fi 51 | -------------------------------------------------------------------------------- /script/install-current-user.vbs: -------------------------------------------------------------------------------- 1 | Set oShell = CreateObject("WScript.Shell") 2 | Set oEnv = oShell.Environment("USER") 3 | Set oFS = CreateObject("Scripting.FileSystemObject") 4 | 5 | Dim sEnvKey, sEnvVal, aJBProducts 6 | aJBProducts = Array("idea", "clion", "phpstorm", "goland", "pycharm", "webstorm", "webide", "rider", "datagrip", "rubymine", "appcode", "dataspell", "gateway", "jetbrains_client", "jetbrainsclient", "studio", "devecostudio") 7 | 8 | Set re = New RegExp 9 | re.Global = True 10 | re.IgnoreCase = True 11 | re.Pattern = "^\-javaagent:.*[\/\\]jetbra\-agent\.jar.*" 12 | 13 | Dim sBasePath, sJarFile 14 | sBasePath = oFS.GetParentFolderName(oShell.CurrentDirectory) 15 | sJarFile = sBasePath & "\jetbra-agent.jar" 16 | 17 | If Not oFS.FileExists(sJarFile) Then 18 | MsgBox "jetbra-agent.jar not found", vbOKOnly Or vbCritical 19 | WScript.Quit -1 20 | End If 21 | 22 | MsgBox "It may take a few seconds to execute this script." & vbCrLf & vbCrLf & "Click 'OK' button and wait for the prompt of 'Done.' to pop up!" 23 | 24 | Dim sVmOptionsFile 25 | For Each sPrd in aJBProducts 26 | sEnvKey = UCase(sPrd) & "_VM_OPTIONS" 27 | sVmOptionsFile = sBasePath & "\vmoptions\" & sPrd & ".vmoptions" 28 | If oFS.FileExists(sVmOptionsFile) Then 29 | ProcessVmOptions sVmOptionsFile 30 | oEnv(sEnvKey) = sVmOptionsFile 31 | End If 32 | Next 33 | 34 | Sub ProcessVmOptions(ByVal file) 35 | Dim sLine, sNewContent, bMatch 36 | Set oFile = oFS.OpenTextFile(file, 1, 0) 37 | 38 | sNewContent = "" 39 | Do Until oFile.AtEndOfStream 40 | sLine = oFile.ReadLine 41 | bMatch = re.Test(sLine) 42 | If Not bMatch Then 43 | sNewContent = sNewContent & sLine & vbLf 44 | End If 45 | Loop 46 | oFile.Close 47 | 48 | sNewContent = sNewContent & "-javaagent:" & sJarFile & "=jetbrains" 49 | Set oFile = oFS.OpenTextFile(file, 2, 0) 50 | oFile.Write sNewContent 51 | oFile.Close 52 | End Sub 53 | 54 | MsgBox "Done." 55 | -------------------------------------------------------------------------------- /jetbra-agent/src/main/java/win/novice/li/AgentMain.java: -------------------------------------------------------------------------------- 1 | package win.novice.li; 2 | 3 | import net.bytebuddy.agent.builder.AgentBuilder; 4 | import net.bytebuddy.asm.Advice; 5 | import net.bytebuddy.matcher.ElementMatchers; 6 | 7 | import java.lang.instrument.Instrumentation; 8 | import java.util.Set; 9 | 10 | public class AgentMain { 11 | public static void premain(String agentArgs, Instrumentation inst) throws Exception { 12 | printLogo(); 13 | AgentBuilder agentBuilder = newAgentBuilder(); 14 | agentBuilder.type(ElementMatchers.named("java.security.cert.PKIXBuilderParameters")) 15 | .transform((builder, typeDescription, classLoader, module, protectionDomain) -> builder 16 | .visit(Advice.to(PKIXBuilderParametersAdvice.class) 17 | .on(ElementMatchers.isConstructor().and(ElementMatchers.takesArgument(0, Set.class))))) 18 | .asTerminalTransformation() 19 | .type(ElementMatchers.named("sun.net.www.http.HttpClient")) 20 | .transform((builder, typeDescription, classLoader, module, protectionDomain) -> builder 21 | .visit(Advice.to(HttpClientAdvice.class) 22 | .on(ElementMatchers.named("openServer")))) 23 | .asTerminalTransformation() 24 | .type(ElementMatchers.named("java.lang.System")) 25 | .transform((builder, typeDescription, classLoader, module, protectionDomain) -> builder 26 | .visit(Advice.to(SystemAdvice.class) 27 | .on(ElementMatchers.named("getProperty")))) 28 | .asTerminalTransformation() 29 | .installOn(inst); 30 | 31 | agentBuilder.installOn(inst); 32 | } 33 | 34 | static AgentBuilder newAgentBuilder() { 35 | return new AgentBuilder.Default() 36 | .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION) 37 | .with(AgentBuilder.InitializationStrategy.NoOp.INSTANCE) 38 | .with(AgentBuilder.TypeStrategy.Default.REDEFINE) 39 | .ignore(ElementMatchers.nameStartsWith("net.bytebuddy.")); 40 | } 41 | 42 | 43 | static void printLogo() { 44 | System.out.println(" _ _ _ \n" + 45 | " | | ___| |_| |__ _ __ __ _ \n" + 46 | " _ | |/ _ \\ __| '_ \\| '__/ _` |\n" + 47 | "| |_| | __/ |_| |_) | | | (_| |\n" + 48 | " \\___/ \\___|\\__|_.__/|_| \\__,_|"); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /jetbra-agent/src/main/java/win/novice/li/TrustAnchorHolder.java: -------------------------------------------------------------------------------- 1 | package win.novice.li; 2 | 3 | import java.io.File; 4 | import java.io.FileInputStream; 5 | import java.net.URI; 6 | import java.net.URL; 7 | import java.nio.file.Paths; 8 | import java.security.cert.CertificateFactory; 9 | import java.security.cert.TrustAnchor; 10 | import java.security.cert.X509Certificate; 11 | import java.util.HashSet; 12 | import java.util.Set; 13 | 14 | public class TrustAnchorHolder { 15 | public static Set TRUST_ANCHORS; 16 | 17 | 18 | public static Set loadTrustAnchors() throws Exception { 19 | if (TRUST_ANCHORS != null) { 20 | return TRUST_ANCHORS; 21 | } 22 | TRUST_ANCHORS = new HashSet<>(); 23 | 24 | String certDir; 25 | if (System.getenv("JB_HOME") != null) { 26 | certDir = System.getenv("JB_HOME"); 27 | } else { 28 | URI jarURI = getJarURI(); 29 | if (jarURI == null) { 30 | return TRUST_ANCHORS; 31 | } 32 | certDir = Paths.get(jarURI).getParent().resolve("trust-crt").toString(); 33 | } 34 | System.out.println("load crt from " + certDir); 35 | File dir = new File(certDir); 36 | if (dir.exists() && dir.isDirectory()) { 37 | File[] files = dir.listFiles(); 38 | if (files != null) { 39 | CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); 40 | for (File item : files) { 41 | if (item.getName().endsWith(".crt")) { 42 | X509Certificate cert = (X509Certificate) certificateFactory.generateCertificate(new FileInputStream(item)); 43 | TRUST_ANCHORS.add(new TrustAnchor(cert, null)); 44 | } 45 | } 46 | } 47 | } 48 | System.out.println("loaded " + TRUST_ANCHORS.size() + " crts"); 49 | return TRUST_ANCHORS; 50 | } 51 | 52 | public static URI getJarURI() throws Exception { 53 | URL url = TrustAnchorHolder.class.getProtectionDomain().getCodeSource().getLocation(); 54 | if (null != url) { 55 | return url.toURI(); 56 | } 57 | String resourcePath = "/jarLocation.txt"; 58 | url = TrustAnchorHolder.class.getResource(resourcePath); 59 | if (null == url) { 60 | return null; 61 | } 62 | String path = url.getPath(); 63 | path = path.substring(0, path.length() - resourcePath.length() - 1); 64 | return new URI(path); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /jetbra-agent/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | win.novice 8 | jetbra 9 | 0.0.1 10 | 11 | 12 | win.novice.li 13 | jetbra-agent 14 | 15 | 16 | 17 17 | 17 18 | UTF-8 19 | 20 | 21 | 22 | net.bytebuddy 23 | byte-buddy 24 | 1.14.8 25 | 26 | 27 | 28 | net.bytebuddy 29 | byte-buddy-agent 30 | 1.14.8 31 | 32 | 33 | 34 | 35 | 36 | 37 | maven-assembly-plugin 38 | 39 | false 40 | 41 | jar-with-dependencies 42 | 43 | 44 | 45 | win.novice.li.AgentMain 46 | true 47 | true 48 | 49 | 50 | jetbra-agent 51 | 52 | 53 | 54 | make-assembly 55 | package 56 | 57 | single 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /script/install-all-users.vbs: -------------------------------------------------------------------------------- 1 | If Not WScript.Arguments.Named.Exists("elevate") Then 2 | CreateObject("Shell.Application").ShellExecute WScript.FullName, """" & WScript.ScriptFullName & """ /elevate", "", "runas", 10 3 | WScript.Quit 4 | End If 5 | 6 | Set oShell = CreateObject("WScript.Shell") 7 | Set oEnvSystem = oShell.Environment("SYSTEM") 8 | Set oFS = CreateObject("Scripting.FileSystemObject") 9 | 10 | Dim sBasePath, sJarFile 11 | sBasePath = oFS.GetParentFolderName(oFS.GetParentFolderName(WScript.ScriptFullName)) 12 | sJarFile = sBasePath & "\jetbra-agent.jar" 13 | 14 | If Not oFS.FileExists(sJarFile) Then 15 | MsgBox "jetbra-agent.jar not found", vbOKOnly Or vbCritical 16 | WScript.Quit -1 17 | End If 18 | 19 | MsgBox "It may take a few seconds to execute this script." & vbCrLf & vbCrLf & "Click 'OK' button and wait for the prompt of 'Done.' to pop up!" 20 | 21 | Dim sEnvKey, sEnvVal, aJBProducts 22 | aJBProducts = Array("idea", "clion", "phpstorm", "goland", "pycharm", "webstorm", "webide", "rider", "datagrip", "rubymine", "appcode", "dataspell", "gateway", "jetbrains_client", "jetbrainsclient", "studio", "devecostudio") 23 | 24 | Set re = New RegExp 25 | re.Global = True 26 | re.IgnoreCase = True 27 | re.Pattern = "^\-javaagent:.*[\/\\]jetbra\-agent\.jar.*" 28 | 29 | Sub RemoveEnv(env) 30 | On Error Resume Next 31 | 32 | For Each sPrd in aJBProducts 33 | sEnvKey = UCase(sPrd) & "_VM_OPTIONS" 34 | sEnvVal = oShell.ExpandEnvironmentStrings("%" & sEnvKey & "%") 35 | If sEnvVal <> ("%" & sEnvKey & "%") Then 36 | env.Remove(sEnvKey) 37 | End If 38 | Next 39 | End Sub 40 | 41 | RemoveEnv oShell.Environment("USER") 42 | 43 | Dim sVmOptionsFile 44 | For Each sPrd in aJBProducts 45 | sEnvKey = UCase(sPrd) & "_VM_OPTIONS" 46 | sVmOptionsFile = sBasePath & "\vmoptions\" & sPrd & ".vmoptions" 47 | If oFS.FileExists(sVmOptionsFile) Then 48 | ProcessVmOptions sVmOptionsFile 49 | oEnvSystem(sEnvKey) = sVmOptionsFile 50 | End If 51 | Next 52 | 53 | Sub ProcessVmOptions(ByVal file) 54 | Dim sLine, sNewContent, bMatch 55 | Set oFile = oFS.OpenTextFile(file, 1, 0) 56 | 57 | sNewContent = "" 58 | Do Until oFile.AtEndOfStream 59 | sLine = oFile.ReadLine 60 | bMatch = re.Test(sLine) 61 | If Not bMatch Then 62 | sNewContent = sNewContent & sLine & vbLf 63 | End If 64 | Loop 65 | oFile.Close 66 | 67 | sNewContent = sNewContent & "-javaagent:" & sJarFile & "=jetbrains" 68 | Set oFile = oFS.OpenTextFile(file, 2, 0) 69 | oFile.Write sNewContent 70 | oFile.Close 71 | End Sub 72 | 73 | MsgBox "Done." 74 | -------------------------------------------------------------------------------- /jetbra-server/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | win.novice 8 | jetbra 9 | 0.0.1 10 | 11 | 12 | win.novice.li 13 | jetbra-server 14 | 15 | 16 | 17 17 | 17 18 | UTF-8 19 | 20 | 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-web 29 | 30 | 31 | org.springframework.boot 32 | spring-boot-starter-validation 33 | 34 | 35 | 36 | org.springframework.boot 37 | spring-boot-starter-test 38 | test 39 | 40 | 41 | org.bouncycastle 42 | bcpkix-jdk18on 43 | 1.72 44 | 45 | 46 | org.bouncycastle 47 | bcprov-jdk18on 48 | 1.72 49 | 50 | 51 | org.projectlombok 52 | lombok 53 | true 54 | 55 | 56 | 57 | 58 | 59 | 60 | org.springframework.boot 61 | spring-boot-maven-plugin 62 | 63 | 64 | 65 | org.projectlombok 66 | lombok 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /jetbra-server/src/main/resources/jetbra.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | 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 51 | -----END RSA PRIVATE KEY----- 52 | -------------------------------------------------------------------------------- /script/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | OS_NAME=$(uname -s) 6 | JB_PRODUCTS="idea clion phpstorm goland pycharm webstorm webide rider datagrip rubymine appcode dataspell gateway jetbrains_client jetbrainsclient studio devecostudio" 7 | 8 | BASE_PATH=$(dirname $( 9 | cd $(dirname "$0") 10 | pwd 11 | )) 12 | 13 | JAR_FILE_PATH="${BASE_PATH}/jetbra-agent.jar" 14 | 15 | if [ ! -f "${JAR_FILE_PATH}" ]; then 16 | echo 'jetbra-agent.jar not found' 17 | exit -1 18 | fi 19 | 20 | KDE_ENV_DIR="${HOME}/.config/plasma-workspace/env" 21 | LAUNCH_AGENTS_DIR="${HOME}/Library/LaunchAgents" 22 | 23 | PROFILE_PATH="${HOME}/.profile" 24 | ZSH_PROFILE_PATH="${HOME}/.zshrc" 25 | PLIST_PATH="${LAUNCH_AGENTS_DIR}/jetbrains.vmoptions.plist" 26 | 27 | if [ $OS_NAME = "Darwin" ]; then 28 | BASH_PROFILE_PATH="${HOME}/.bash_profile" 29 | 30 | mkdir -p "${LAUNCH_AGENTS_DIR}" 31 | echo 'Labeljetbrains.vmoptionsProgramArgumentssh-c' >"${PLIST_PATH}" 32 | else 33 | BASH_PROFILE_PATH="${HOME}/.bashrc" 34 | mkdir -p "${KDE_ENV_DIR}" 35 | fi 36 | 37 | touch "${PROFILE_PATH}" 38 | touch "${BASH_PROFILE_PATH}" 39 | touch "${ZSH_PROFILE_PATH}" 40 | 41 | MY_VMOPTIONS_SHELL_NAME="jetbrains.vmoptions.sh" 42 | MY_VMOPTIONS_SHELL_FILE="${HOME}/.${MY_VMOPTIONS_SHELL_NAME}" 43 | echo '#!/bin/sh' >"${MY_VMOPTIONS_SHELL_FILE}" 44 | 45 | EXEC_LINE='___MY_VMOPTIONS_SHELL_FILE="${HOME}/.jetbrains.vmoptions.sh"; if [ -f "${___MY_VMOPTIONS_SHELL_FILE}" ]; then . "${___MY_VMOPTIONS_SHELL_FILE}"; fi' 46 | 47 | for PRD in $JB_PRODUCTS; do 48 | VM_FILE_PATH="${BASE_PATH}/vmoptions/${PRD}.vmoptions" 49 | if [ ! -f "${VM_FILE_PATH}" ]; then 50 | continue 51 | fi 52 | 53 | if [ $OS_NAME = "Darwin" ]; then 54 | sed -i '' '/^\-javaagent:.*[\/\\]jetbra\-agent\.jar.*/d' "${VM_FILE_PATH}" 55 | else 56 | sed -i '/^\-javaagent:.*[\/\\]jetbra\-agent\.jar.*/d' "${VM_FILE_PATH}" 57 | fi 58 | 59 | echo "-javaagent:${JAR_FILE_PATH}=jetbrains" >>"${VM_FILE_PATH}" 60 | 61 | ENV_NAME=$(echo $PRD | tr '[a-z]' '[A-Z]')"_VM_OPTIONS" 62 | echo "export ${ENV_NAME}=\"${VM_FILE_PATH}\"" >>"${MY_VMOPTIONS_SHELL_FILE}" 63 | 64 | if [ $OS_NAME = "Darwin" ]; then 65 | launchctl setenv "${ENV_NAME}" "${VM_FILE_PATH}" 66 | echo "launchctl setenv \"${ENV_NAME}\" \"${VM_FILE_PATH}\"" >>"${PLIST_PATH}" 67 | fi 68 | done 69 | 70 | if [ $OS_NAME = "Darwin" ]; then 71 | sed -i '' '/___MY_VMOPTIONS_SHELL_FILE="${HOME}\/\.jetbrains\.vmoptions\.sh"; if /d' "${PROFILE_PATH}" >/dev/null 2>&1 72 | sed -i '' '/___MY_VMOPTIONS_SHELL_FILE="${HOME}\/\.jetbrains\.vmoptions\.sh"; if /d' "${BASH_PROFILE_PATH}" >/dev/null 2>&1 73 | sed -i '' '/___MY_VMOPTIONS_SHELL_FILE="${HOME}\/\.jetbrains\.vmoptions\.sh"; if /d' "${ZSH_PROFILE_PATH}" >/dev/null 2>&1 74 | 75 | echo 'RunAtLoad' >>"${PLIST_PATH}" 76 | else 77 | sed -i '/___MY_VMOPTIONS_SHELL_FILE="${HOME}\/\.jetbrains\.vmoptions\.sh"; if /d' "${PROFILE_PATH}" >/dev/null 2>&1 78 | sed -i '/___MY_VMOPTIONS_SHELL_FILE="${HOME}\/\.jetbrains\.vmoptions\.sh"; if /d' "${BASH_PROFILE_PATH}" >/dev/null 2>&1 79 | sed -i '/___MY_VMOPTIONS_SHELL_FILE="${HOME}\/\.jetbrains\.vmoptions\.sh"; if /d' "${ZSH_PROFILE_PATH}" >/dev/null 2>&1 80 | fi 81 | 82 | echo "${EXEC_LINE}" >>"${PROFILE_PATH}" 83 | echo "${EXEC_LINE}" >>"${BASH_PROFILE_PATH}" 84 | echo "${EXEC_LINE}" >>"${ZSH_PROFILE_PATH}" 85 | 86 | if [ $OS_NAME = "Darwin" ]; then 87 | echo 'done. the "kill Dock" command can fix the crash issue.' 88 | else 89 | ln -sf "${MY_VMOPTIONS_SHELL_FILE}" "${KDE_ENV_DIR}/${MY_VMOPTIONS_SHELL_NAME}" 90 | echo "done. you'd better log off first!" 91 | fi 92 | -------------------------------------------------------------------------------- /jetbra-server/src/main/java/win/novice/li/controller/LicenseController.java: -------------------------------------------------------------------------------- 1 | package win.novice.li.controller; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import lombok.SneakyThrows; 5 | import org.bouncycastle.jce.provider.BouncyCastleProvider; 6 | import org.bouncycastle.openssl.PEMKeyPair; 7 | import org.bouncycastle.openssl.PEMParser; 8 | import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; 9 | import org.springframework.core.io.ClassPathResource; 10 | import org.springframework.validation.annotation.Validated; 11 | import org.springframework.web.bind.annotation.PostMapping; 12 | import org.springframework.web.bind.annotation.RequestBody; 13 | import org.springframework.web.bind.annotation.RestController; 14 | import win.novice.li.model.License; 15 | 16 | import java.io.FileInputStream; 17 | import java.io.InputStreamReader; 18 | import java.nio.charset.StandardCharsets; 19 | import java.security.*; 20 | import java.security.cert.CertificateFactory; 21 | import java.security.cert.X509Certificate; 22 | import java.util.Base64; 23 | import java.util.HashMap; 24 | import java.util.Map; 25 | 26 | @RestController 27 | public class LicenseController { 28 | private static final PrivateKey PRIVATE_KEY = getPrivateKey(); 29 | private static final X509Certificate CRT = getCertificate(); 30 | 31 | private static final ObjectMapper MAPPER = new ObjectMapper(); 32 | 33 | @PostMapping("/generateLicense") 34 | @SneakyThrows 35 | public Map generateLicense(@RequestBody @Validated License license) { 36 | Map ans = new HashMap<>(); 37 | 38 | String licenseId = generateLicenseId(); 39 | license.setLicenseId(licenseId); 40 | 41 | String licensePart = MAPPER.writeValueAsString(license); 42 | byte[] licensePartBytes = licensePart.getBytes(StandardCharsets.UTF_8); 43 | String licensePartBase64 = Base64.getEncoder().encodeToString(licensePartBytes); 44 | 45 | 46 | Signature signature = Signature.getInstance("SHA1withRSA"); 47 | signature.initSign(PRIVATE_KEY); 48 | signature.update(licensePartBytes); 49 | byte[] signatureBytes = signature.sign(); 50 | String sigResultsBase64 = Base64.getEncoder().encodeToString(signatureBytes); 51 | 52 | String result = licenseId + "-" + licensePartBase64 + "-" + sigResultsBase64 + "-" + Base64.getEncoder().encodeToString(CRT.getEncoded()); 53 | 54 | ans.put("license",result); 55 | return ans; 56 | } 57 | 58 | 59 | private static final String ALLOWED_CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; 60 | private static final SecureRandom random = new SecureRandom(); 61 | 62 | public static String generateLicenseId() { 63 | int licenseLength = 10; 64 | StringBuilder sb = new StringBuilder(licenseLength); 65 | for (int i = 0; i < licenseLength; i++) { 66 | int randomIndex = random.nextInt(ALLOWED_CHARACTERS.length()); 67 | char randomChar = ALLOWED_CHARACTERS.charAt(randomIndex); 68 | sb.append(randomChar); 69 | } 70 | return sb.toString(); 71 | } 72 | 73 | 74 | @SneakyThrows 75 | static PrivateKey getPrivateKey() { 76 | ClassPathResource licenseKeyResource = new ClassPathResource("jetbra.key"); 77 | Security.addProvider(new BouncyCastleProvider()); 78 | PEMParser pemParser = new PEMParser(new InputStreamReader(licenseKeyResource.getInputStream())); 79 | JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC"); 80 | Object object = pemParser.readObject(); 81 | KeyPair kp = converter.getKeyPair((PEMKeyPair) object); 82 | return kp.getPrivate(); 83 | } 84 | 85 | @SneakyThrows 86 | static X509Certificate getCertificate() { 87 | CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); 88 | ClassPathResource crtResource = new ClassPathResource("jetbra.crt"); 89 | return (X509Certificate) certificateFactory.generateCertificate(crtResource.getInputStream()); 90 | } 91 | } 92 | 93 | -------------------------------------------------------------------------------- /jetbra.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name JetBra 3 | // @namespace https://github.com/novice88/jetbra 4 | // @version 1.1 5 | // @license MIT 6 | // @description 添加一个按钮,点击获取插件的激活码 7 | // @author novice.li 8 | // @match https://plugins.jetbrains.com/plugin/* 9 | // @grant GM_setClipboard 10 | // @grant GM_addStyle 11 | // @grant GM_xmlhttpRequest 12 | // @connect noviceli.win 13 | // @connect self 14 | // @connect localhost 15 | // ==/UserScript== 16 | 17 | var elmGetter = function() { 18 | const win = window.unsafeWindow || document.defaultView || window; 19 | const doc = win.document; 20 | const listeners = new WeakMap(); 21 | let mode = 'css'; 22 | let $; 23 | const elProto = win.Element.prototype; 24 | const matches = elProto.matches || 25 | elProto.matchesSelector || 26 | elProto.webkitMatchesSelector || 27 | elProto.mozMatchesSelector || 28 | elProto.oMatchesSelector; 29 | const MutationObs = win.MutationObserver || 30 | win.WebkitMutationObserver || 31 | win.MozMutationObserver; 32 | function addObserver(target, callback) { 33 | const observer = new MutationObs(mutations => { 34 | for (const mutation of mutations) { 35 | if (mutation.type === 'attributes') { 36 | callback(mutation.target); 37 | if (observer.canceled) return; 38 | } 39 | for (const node of mutation.addedNodes) { 40 | if (node instanceof Element) callback(node); 41 | if (observer.canceled) return; 42 | } 43 | } 44 | }); 45 | observer.canceled = false; 46 | observer.observe(target, {childList: true, subtree: true, attributes: true}); 47 | return () => { 48 | observer.canceled = true; 49 | observer.disconnect(); 50 | }; 51 | } 52 | function addFilter(target, filter) { 53 | let listener = listeners.get(target); 54 | if (!listener) { 55 | listener = { 56 | filters: new Set(), 57 | remove: addObserver(target, el => listener.filters.forEach(f => f(el))) 58 | }; 59 | listeners.set(target, listener); 60 | } 61 | listener.filters.add(filter); 62 | } 63 | function removeFilter(target, filter) { 64 | const listener = listeners.get(target); 65 | if (!listener) return; 66 | listener.filters.delete(filter); 67 | if (!listener.filters.size) { 68 | listener.remove(); 69 | listeners.delete(target); 70 | } 71 | } 72 | function query(all, selector, parent, includeParent, curMode) { 73 | switch (curMode) { 74 | case 'css': 75 | const checkParent = includeParent && matches.call(parent, selector); 76 | if (all) { 77 | const queryAll = parent.querySelectorAll(selector); 78 | return checkParent ? [parent, ...queryAll] : [...queryAll]; 79 | } 80 | return checkParent ? parent : parent.querySelector(selector); 81 | case 'jquery': 82 | let jNodes = $(includeParent ? parent : []); 83 | jNodes = jNodes.add([...parent.querySelectorAll('*')]).filter(selector); 84 | if (all) return $.map(jNodes, el => $(el)); 85 | return jNodes.length ? $(jNodes.get(0)) : null; 86 | case 'xpath': 87 | const ownerDoc = parent.ownerDocument || parent; 88 | selector += '/self::*'; 89 | if (all) { 90 | const xPathResult = ownerDoc.evaluate(selector, parent, null, 7, null); 91 | const result = []; 92 | for (let i = 0; i < xPathResult.snapshotLength; i++) { 93 | result.push(xPathResult.snapshotItem(i)); 94 | } 95 | return result; 96 | } 97 | return ownerDoc.evaluate(selector, parent, null, 9, null).singleNodeValue; 98 | } 99 | } 100 | function isJquery(jq) { 101 | return jq && jq.fn && typeof jq.fn.jquery === 'string'; 102 | } 103 | function getOne(selector, parent, timeout) { 104 | const curMode = mode; 105 | return new Promise(resolve => { 106 | const node = query(false, selector, parent, false, curMode); 107 | if (node) return resolve(node); 108 | let timer; 109 | const filter = el => { 110 | const node = query(false, selector, el, true, curMode); 111 | if (node) { 112 | removeFilter(parent, filter); 113 | timer && clearTimeout(timer); 114 | resolve(node); 115 | } 116 | }; 117 | addFilter(parent, filter); 118 | if (timeout > 0) { 119 | timer = setTimeout(() => { 120 | removeFilter(parent, filter); 121 | resolve(null); 122 | }, timeout); 123 | } 124 | }); 125 | } 126 | return { 127 | get currentSelector() { 128 | return mode; 129 | }, 130 | get(selector, ...args) { 131 | let parent = typeof args[0] !== 'number' && args.shift() || doc; 132 | if (mode === 'jquery' && parent instanceof $) parent = parent.get(0); 133 | const timeout = args[0] || 0; 134 | if (Array.isArray(selector)) { 135 | return Promise.all(selector.map(s => getOne(s, parent, timeout))); 136 | } 137 | return getOne(selector, parent, timeout); 138 | }, 139 | each(selector, ...args) { 140 | let parent = typeof args[0] !== 'function' && args.shift() || doc; 141 | if (mode === 'jquery' && parent instanceof $) parent = parent.get(0); 142 | const callback = args[0]; 143 | const curMode = mode; 144 | const refs = new WeakSet(); 145 | for (const node of query(true, selector, parent, false, curMode)) { 146 | refs.add(curMode === 'jquery' ? node.get(0) : node); 147 | if (callback(node, false) === false) return; 148 | } 149 | const filter = el => { 150 | for (const node of query(true, selector, el, true, curMode)) { 151 | const _el = curMode === 'jquery' ? node.get(0) : node; 152 | if (refs.has(_el)) break; 153 | refs.add(_el); 154 | if (callback(node, true) === false) { 155 | return removeFilter(parent, filter); 156 | } 157 | } 158 | }; 159 | addFilter(parent, filter); 160 | }, 161 | create(domString, ...args) { 162 | const returnList = typeof args[0] === 'boolean' && args.shift(); 163 | const parent = args[0]; 164 | const template = doc.createElement('template'); 165 | template.innerHTML = domString; 166 | const node = template.content.firstElementChild; 167 | if (!node) return null; 168 | parent ? parent.appendChild(node) : node.remove(); 169 | if (returnList) { 170 | const list = {}; 171 | node.querySelectorAll('[id]').forEach(el => list[el.id] = el); 172 | list[0] = node; 173 | return list; 174 | } 175 | return node; 176 | }, 177 | selector(desc) { 178 | switch (true) { 179 | case isJquery(desc): 180 | $ = desc; 181 | return mode = 'jquery'; 182 | case !desc || typeof desc.toLowerCase !== 'function': 183 | return mode = 'css'; 184 | case desc.toLowerCase() === 'jquery': 185 | for (const jq of [window.jQuery, window.$, win.jQuery, win.$]) { 186 | if (isJquery(jq)) { 187 | $ = jq; 188 | break; 189 | }; 190 | } 191 | return mode = $ ? 'jquery' : 'css'; 192 | case desc.toLowerCase() === 'xpath': 193 | return mode = 'xpath'; 194 | default: 195 | return mode = 'css'; 196 | } 197 | } 198 | }; 199 | }(); 200 | (async function () { 201 | 'use strict'; 202 | GM_addStyle(` 203 | .jetbra-button { 204 | background-color: #04AA6D; 205 | border: none; 206 | color: white; 207 | padding: 8px 24px; 208 | text-align: center; 209 | text-decoration: none; 210 | display: inline-block; 211 | border-radius: 16px; 212 | box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19); 213 | transition-duration: 0.4s; 214 | } 215 | 216 | .jetbra-button:hover { 217 | background-color: #057e47; 218 | color: white; 219 | } 220 | `); 221 | const backendBaseUrl = 'https://jetbra.noviceli.win' 222 | const metaTag = document.querySelector('meta[name="pluginId"]') 223 | if (!metaTag) { 224 | return 225 | } 226 | const pluginId = metaTag.getAttribute('content') 227 | 228 | let pluginDetail = await fetch('https://plugins.jetbrains.com/api/plugins/' + pluginId).then(r => r.json()); 229 | 230 | const parentElement = await elmGetter.get('.plugin-header__controls-panel > div:first-child'); 231 | 232 | let newElement = document.createElement('div'); 233 | newElement.classList.toggle('wt-col-inline'); 234 | newElement.innerHTML = ``; 235 | parentElement.appendChild(newElement) 236 | 237 | 238 | newElement.addEventListener('click', async () => { 239 | if (pluginDetail.purchaseInfo === undefined) { 240 | window.alert('此插件不是付费插件'); 241 | return; 242 | } 243 | let data = { 244 | "licenseeName": "Test", 245 | "assigneeName": "novice.li", 246 | "assigneeEmail": "", 247 | "licenseRestriction": "", 248 | "checkConcurrentUse": false, 249 | "products": [{ 250 | "code": pluginDetail.purchaseInfo.productCode, 251 | "fallbackDate": "2099-12-30", 252 | "paidUpTo": "2099-12-30", 253 | "extended": false 254 | }], 255 | "metadata": "0120230102PPAA013009", 256 | "hash": "41472961/0:1563609451", 257 | "gracePeriodDays": 7, 258 | "autoProlongated": true, 259 | "isAutoProlongated": true 260 | } 261 | GM_xmlhttpRequest({ 262 | method: 'POST', 263 | url: backendBaseUrl + '/generateLicense', 264 | headers: { 265 | 'Content-Type': 'application/json' 266 | }, 267 | data: JSON.stringify(data), 268 | onload: function (response) { 269 | let license = JSON.parse(response.responseText).license 270 | GM_setClipboard(license, 'text'); 271 | window.alert('激活码已复制到剪切版'); 272 | } 273 | }); 274 | }) 275 | })(); 276 | --------------------------------------------------------------------------------