├── .gitignore ├── BurpExtender-ocsp.png ├── FakeBurpCert ├── .gitignore ├── build.gradle ├── cert.txt ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── libs │ └── javassist.jar ├── release │ └── FakeBurpCert-v2.2.jar ├── settings.gradle └── src │ ├── main │ ├── java │ │ └── fake │ │ │ └── cert │ │ │ └── FakeBurpCert.java │ └── resources │ │ ├── fake │ │ └── resources │ │ │ ├── createMap.jav │ │ │ ├── fakeBouncyCert.jav │ │ │ └── fakeCert.jav │ │ └── resources │ │ └── release.properties │ └── test │ └── java │ └── fake │ └── cert │ └── FakeBurpCertTest.java ├── Readme-ja.md ├── Readme.md └── SimpleOCSPServer ├── .gitignore ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── libs └── BurpExtensionCommons-v0.5.3.0.jar ├── release └── SimpleOCSPServer-v2.2.jar ├── settings.gradle └── src ├── main ├── java │ ├── burp │ │ └── BurpExtender.java │ └── server │ │ └── ocsp │ │ ├── OCSPProperty.java │ │ ├── OCSPServerTab.form │ │ ├── OCSPServerTab.java │ │ ├── OCSPWrap.java │ │ ├── OptionProperty.java │ │ ├── SimpleJettyServer.java │ │ └── Version.java └── resources │ ├── burp │ └── resources │ │ └── release.properties │ └── server │ └── ocsp │ └── resources │ ├── Resource.properties │ ├── Resource_ja.properties │ └── folder_image.png └── test ├── java └── server │ └── ocsp │ ├── OCSPUtilTest.java │ └── SimpleJettyServerTest.java └── resources └── resources ├── burpca.p12 └── req.der /.gitignore: -------------------------------------------------------------------------------- 1 | /FakeBurpCert/nbproject/ -------------------------------------------------------------------------------- /BurpExtender-ocsp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raise-isayan/FakeCert/5d180b60628fcd5f367822bddb6198e79338301e/BurpExtender-ocsp.png -------------------------------------------------------------------------------- /FakeBurpCert/.gitignore: -------------------------------------------------------------------------------- 1 | /.gradle 2 | /build 3 | *.zip 4 | -------------------------------------------------------------------------------- /FakeBurpCert/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | apply plugin: 'jacoco' 3 | apply plugin: 'application' 4 | 5 | repositories { 6 | mavenCentral() 7 | } 8 | 9 | sourceCompatibility = '1.8' // -source 10 | targetCompatibility = '1.8' // -target 11 | 12 | tasks.withType(JavaCompile) { 13 | options.encoding = 'UTF-8' 14 | } 15 | 16 | processResources { 17 | filesMatching ('**/*.properties') { 18 | expand(project.properties) 19 | } 20 | } 21 | 22 | clean.doFirst { 23 | delete fileTree('release') { 24 | include '*.jar' 25 | } 26 | } 27 | 28 | jar { 29 | manifest { 30 | attributes 'Premain-Class': 'fake.cert.FakeBurpCert' 31 | attributes 'Boot-Class-Path': 'javassist.jar' 32 | } 33 | 34 | destinationDirectory = file('release') 35 | archiveVersion = "v${release_version_major}" 36 | } 37 | 38 | task release(type: Zip, dependsOn: ['build']) { 39 | archiveBaseName ="${rootProject.name}_v${release_version_major}.${release_version_minor}" 40 | destinationDirectory = file("${projectDir}") 41 | from rootProject.rootDir 42 | include '*' 43 | include 'gradle/**' 44 | include 'image/**' 45 | include 'libs/**' 46 | include 'src/**' 47 | include 'release/**' 48 | exclude 'build' 49 | exclude '.git' 50 | exclude '.gradle' 51 | exclude '*.zip' 52 | } 53 | 54 | dependencies { 55 | // javassist.jar 56 | implementation fileTree(dir: 'libs', include: ['*.jar']) 57 | 58 | // UnitTest 59 | // https://mvnrepository.com/artifact/org.bouncycastle/bcpkix-jdk15on 60 | testImplementation 'org.bouncycastle:bcpkix-jdk15on:1.64' 61 | // https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on 62 | testImplementation 'org.bouncycastle:bcprov-jdk15on:1.64' 63 | 64 | // Use JUnit Jupiter for testing. 65 | // https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api 66 | testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.2' 67 | testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.8.2' 68 | 69 | } 70 | -------------------------------------------------------------------------------- /FakeBurpCert/cert.txt: -------------------------------------------------------------------------------- 1 | CN=www\.example\.com(,|$) x509.info.subject CN=www.example.com, OU=piyo, O=fuga, C=hoge 2 | CN=www\.example\.com(,|$) x509.info.serialNumber 11223344 3 | CN=www\.example\.com(,|$) x509.info.validity yyyy/MM/dd 2017/01/01 2027/12/31 4 | CN=www\.example\.com(,|$) x509.info.extensions.SubjectAlternativeName abc.example.com 5 | CN=www\.example\.com(,|$) x509.info.extensions.AuthorityInfoAccess.ocsp http://www.example.com:8888/ 6 | -------------------------------------------------------------------------------- /FakeBurpCert/gradle.properties: -------------------------------------------------------------------------------- 1 | netbeans.org-netbeans-modules-javascript2-requirejs.enabled=true 2 | release_version_major=2.2 3 | release_version_minor=0.0 4 | netbeans.hint.jdkPlatform=JDK_1.8 5 | -------------------------------------------------------------------------------- /FakeBurpCert/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raise-isayan/FakeCert/5d180b60628fcd5f367822bddb6198e79338301e/FakeBurpCert/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /FakeBurpCert/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /FakeBurpCert/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /FakeBurpCert/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /FakeBurpCert/libs/javassist.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raise-isayan/FakeCert/5d180b60628fcd5f367822bddb6198e79338301e/FakeBurpCert/libs/javassist.jar -------------------------------------------------------------------------------- /FakeBurpCert/release/FakeBurpCert-v2.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raise-isayan/FakeCert/5d180b60628fcd5f367822bddb6198e79338301e/FakeBurpCert/release/FakeBurpCert-v2.2.jar -------------------------------------------------------------------------------- /FakeBurpCert/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'FakeBurpCert' 2 | -------------------------------------------------------------------------------- /FakeBurpCert/src/main/java/fake/cert/FakeBurpCert.java: -------------------------------------------------------------------------------- 1 | package fake.cert; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.ByteArrayOutputStream; 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.lang.instrument.ClassFileTransformer; 8 | import java.lang.instrument.IllegalClassFormatException; 9 | import java.lang.instrument.Instrumentation; 10 | import java.nio.charset.StandardCharsets; 11 | import java.security.ProtectionDomain; 12 | import javassist.ClassPool; 13 | import javassist.CtClass; 14 | import javassist.CtConstructor; 15 | import javassist.CtField; 16 | import javassist.CtMethod; 17 | 18 | public class FakeBurpCert { 19 | 20 | private static ClassPool classPool; 21 | private static boolean debug; 22 | 23 | public static void premain(final String agentArgs, Instrumentation instrumentation) throws Exception { 24 | classPool = ClassPool.getDefault(); 25 | debug = "debug".equals(agentArgs); 26 | 27 | instrumentation.addTransformer(new ClassFileTransformer() { 28 | 29 | @Override 30 | public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, 31 | ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { 32 | try { 33 | if (className != null && className.equals("sun/security/x509/X509CertImpl")) { 34 | if (debug) { 35 | System.out.println("className:" + className); 36 | } 37 | CtClass ctClass = classPool.makeClass(new ByteArrayInputStream(classfileBuffer)); 38 | // 変換テーブル作成メソッドを追加 39 | ctClass.addMethod(CtMethod.make(buildResourceCommand(FAKE_CREATEMAP_COMMAND), ctClass)); 40 | // 変換テーブルのフィールドを追加 41 | CtField f = CtField.make("static java.util.Map translateMaps;", ctClass); 42 | ctClass.addField(f, "createMap()"); 43 | // 変換処理を行うメソッドを追加 44 | CtMethod translateTableMethod = CtMethod.make(buildResourceCommand(FAKE_CERT_COMMAND), ctClass); 45 | ctClass.addMethod(translateTableMethod); 46 | 47 | CtClass ctX509CertInfo = classPool.makeClass("sun.security.x509.X509CertInfo"); 48 | CtConstructor ctConstructor = ctClass.getDeclaredConstructor(new CtClass[]{ctX509CertInfo}); 49 | StringBuilder command = new StringBuilder(); 50 | command.append("{ $1 = sun.security.x509.X509CertImpl.burpCertInjection($1); }"); 51 | ctConstructor.insertBefore(command.toString()); 52 | return ctClass.toBytecode(); 53 | } else if (className != null && className.equals("org/bouncycastle/asn1/x509/V3TBSCertificateGenerator")) { 54 | if (debug) { 55 | System.out.println("className:" + className); 56 | } 57 | CtClass ctClass = classPool.makeClass(new ByteArrayInputStream(classfileBuffer)); 58 | // 変換テーブル作成メソッドを追加 59 | ctClass.addMethod(CtMethod.make(buildResourceCommand(FAKE_CREATEMAP_COMMAND), ctClass)); 60 | 61 | // 変換テーブルのフィールドを追加 62 | CtField f = CtField.make("static java.util.Map translateMaps;", ctClass); 63 | ctClass.addField(f, "createMap()"); 64 | // 変換処理を行うメソッドを追加 65 | CtMethod translateTableMethod = CtMethod.make(buildResourceCommand(FAKE_BUNCY_CERT_COMMAND), ctClass); 66 | ctClass.addMethod(translateTableMethod); 67 | 68 | CtMethod ctLoadMethod = ctClass.getDeclaredMethod("generateTBSCertificate"); 69 | StringBuilder command = new StringBuilder(); 70 | command.append("{ burpCertInjection(); }"); 71 | ctLoadMethod.insertBefore(command.toString()); 72 | return ctClass.toBytecode(); 73 | 74 | // burp v2020.6 以降において以下の処理があると、HTTPSにおいて接続時にエラーとなってしまうためコメント 75 | // } else if (debug && className != null && className.equals("java/security/KeyStore")) { 76 | // System.out.println("className:" + className); 77 | // CtClass ctClass = classPool.makeClass(new ByteArrayInputStream(classfileBuffer)); 78 | // StringBuilder command = new StringBuilder(); 79 | // command.append("{ System.out.println(\"pwd\\\"\" + new String($2) + \"\\\"\"); }"); 80 | // CtMethod ctLoadMethod = ctClass.getDeclaredMethod("load"); 81 | // ctLoadMethod.insertBefore(command.toString()); 82 | // return ctClass.toBytecode(); 83 | } else if (debug && className != null) { 84 | // if (className.startsWith("org/bouncycastle/")) { 85 | // System.out.println("className:" + className); 86 | // } 87 | System.out.println("className:" + className); 88 | } 89 | 90 | } catch (Exception ex) { 91 | ex.printStackTrace(); 92 | IllegalClassFormatException e = new IllegalClassFormatException(ex.getMessage()); 93 | e.initCause(ex); 94 | throw e; 95 | } 96 | 97 | return null; 98 | } 99 | 100 | }); 101 | 102 | } 103 | 104 | public final static String FAKE_CREATEMAP_COMMAND = "/fake/resources/createMap.jav"; 105 | public final static String FAKE_CERT_COMMAND = "/fake/resources/fakeCert.jav"; 106 | public final static String FAKE_BUNCY_CERT_COMMAND = "/fake/resources/fakeBouncyCert.jav"; 107 | 108 | public static String buildResourceCommand(String resourcePath) { 109 | StringBuilder command = new StringBuilder(); 110 | try(InputStream inStream = FakeBurpCert.class.getResourceAsStream(resourcePath)) { 111 | command.append(new String(readAllBytes(inStream), StandardCharsets.ISO_8859_1)); 112 | } catch (IOException ex) { 113 | ex.printStackTrace(); 114 | } 115 | return command.toString(); 116 | } 117 | 118 | /* InputStream.readAllBytes は JDK 9 からサポート */ 119 | public static byte[] readAllBytes(InputStream stream) throws IOException { 120 | ByteArrayOutputStream bostm = new ByteArrayOutputStream(); 121 | byte[] buff = new byte[1024]; 122 | int len = 0; 123 | while ((len = stream.read(buff)) >= 0) { 124 | bostm.write(buff, 0, len); 125 | } 126 | return bostm.toByteArray(); 127 | } 128 | 129 | } 130 | -------------------------------------------------------------------------------- /FakeBurpCert/src/main/resources/fake/resources/createMap.jav: -------------------------------------------------------------------------------- 1 | public static java.util.Map createMap() { 2 | java.util.Map map = new java.util.LinkedHashMap(); 3 | java.io.BufferedReader reader = null; 4 | try { 5 | reader = new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream("cert.txt"), "UTF-8")); 6 | String line = null; 7 | while ((line = reader.readLine()) != null) { 8 | if (line.startsWith("#")) { 9 | continue; 10 | } 11 | String[] inputs = line.split("\t", 3); 12 | java.util.List item = new java.util.ArrayList(); 13 | item.add(new java.util.AbstractMap.SimpleEntry(inputs[1], (inputs.length > 2) ? inputs[2] : "")); 14 | java.util.List attrs = (java.util.List) map.putIfAbsent(java.util.regex.Pattern.compile(inputs[0]), item); 15 | if (attrs != null) { 16 | attrs.addAll(item); 17 | } 18 | } 19 | } catch (java.io.IOException ex) { 20 | ex.printStackTrace(); 21 | } finally { 22 | if (reader != null) { 23 | try { 24 | reader.close(); 25 | } catch (java.io.IOException ex) { 26 | } 27 | } 28 | } 29 | return map; 30 | } 31 | -------------------------------------------------------------------------------- /FakeBurpCert/src/main/resources/fake/resources/fakeBouncyCert.jav: -------------------------------------------------------------------------------- 1 | public void burpCertInjection() { 2 | java.util.Iterator iterator = translateMaps.keySet().iterator(); 3 | 4 | String subjectCN = subject.toString(); 5 | while (iterator.hasNext()) { 6 | java.util.regex.Pattern regexSubject = (java.util.regex.Pattern) iterator.next(); 7 | java.util.regex.Matcher m = regexSubject.matcher(subjectCN); 8 | if (m.find()) { 9 | java.lang.System.out.println("subject:" + subjectCN + "\t"); 10 | java.util.List attrs = (java.util.List) translateMaps.get(regexSubject); 11 | for (int i = 0; i < attrs.size(); i++) { 12 | java.util.Map.Entry entry = (java.util.Map.Entry) attrs.get(i); 13 | String key = ((String) entry.getKey()); 14 | java.lang.System.out.print("attr[" + key + "] "); 15 | if (key.equals("x509.info.subject")) { 16 | String value = ((String)entry.getValue()).trim(); 17 | java.lang.System.out.println("\treplace_subject:[" + value + "] "); 18 | subject = new org.bouncycastle.asn1.x500.X500Name(value); 19 | } else if (key.equals("x509.info.serialNumber")) { 20 | String value = ((String)entry.getValue()).trim(); 21 | serialNumber = new org.bouncycastle.asn1.ASN1Integer(new java.math.BigInteger(value, 16)); 22 | } else if (key.equals("x509.info.validity")) { 23 | try { 24 | String value = ((String)entry.getValue()).trim(); 25 | java.lang.System.out.println("\treplace_date:[" + value + "] "); 26 | String[] list = value.split("\t", 3); 27 | if (list.length != 3) { 28 | new java.text.ParseException("x509.info.validity is value error: [dateFormat]\t[fromDate]\t[toDate]", list.length); 29 | } 30 | java.text.SimpleDateFormat format = new java.text.SimpleDateFormat(list[0]); 31 | java.util.Date fromDate = format.parse(list[1]); 32 | java.util.Date toDate = format.parse(list[2]); 33 | startDate = new org.bouncycastle.asn1.x509.Time(fromDate); 34 | endDate = new org.bouncycastle.asn1.x509.Time(toDate); 35 | } catch (java.text.ParseException ex) { 36 | ex.printStackTrace(); 37 | } 38 | } else if (key.startsWith("x509.info.extensions.SubjectAlternativeName")) { 39 | try { 40 | String value = ((String)entry.getValue()).trim(); 41 | java.lang.System.out.println("\treplace_san:" + value); 42 | 43 | org.bouncycastle.asn1.x509.ExtensionsGenerator extensionsGenerator = new org.bouncycastle.asn1.x509.ExtensionsGenerator(); 44 | org.bouncycastle.asn1.ASN1ObjectIdentifier [] oids = extensions.getCriticalExtensionOIDs(); 45 | // not SAN Exension 46 | for (int k = 0; k < oids.length; k++) { 47 | if (!org.bouncycastle.asn1.x509.Extension.subjectAlternativeName.equals(oids[i])) { 48 | extensionsGenerator.addExtension(extensions.getExtension(oids[i])); 49 | } 50 | } 51 | org.bouncycastle.asn1.x509.Extension sunExtenson = extensions.getExtension(org.bouncycastle.asn1.x509.Extension.subjectAlternativeName); 52 | if (sunExtenson == null) { 53 | if (!value.isEmpty()) { 54 | org.bouncycastle.asn1.x509.GeneralNames dnsNames = new org.bouncycastle.asn1.x509.GeneralNames(new org.bouncycastle.asn1.x509.GeneralName(org.bouncycastle.asn1.x509.GeneralName.dNSName, value)); 55 | extensionsGenerator.addExtension(org.bouncycastle.asn1.x509.Extension.subjectAlternativeName, false, dnsNames); 56 | } 57 | } 58 | else { 59 | org.bouncycastle.asn1.x509.GeneralNames gns = org.bouncycastle.asn1.x509.GeneralNames.fromExtensions(extensions, org.bouncycastle.asn1.x509.Extension.subjectAlternativeName); 60 | java.util.List dnsNameList = new java.util.ArrayList(); 61 | if (!key.endsWith(".clear")) { 62 | dnsNameList.addAll(java.util.Arrays.asList(gns.getNames())); 63 | } 64 | if (!value.isEmpty()) { 65 | dnsNameList.add(new org.bouncycastle.asn1.x509.GeneralName(org.bouncycastle.asn1.x509.GeneralName.dNSName, value)); 66 | } 67 | org.bouncycastle.asn1.x509.GeneralNames dnsNames = new org.bouncycastle.asn1.x509.GeneralNames((org.bouncycastle.asn1.x509.GeneralName[])dnsNameList.toArray(new org.bouncycastle.asn1.x509.GeneralName[dnsNameList.size()])); 68 | extensionsGenerator.addExtension(org.bouncycastle.asn1.x509.Extension.subjectAlternativeName, false, dnsNames); 69 | } 70 | extensions = extensionsGenerator.generate(); 71 | } catch (java.io.IOException ex) { 72 | ex.printStackTrace(); 73 | } catch (java.lang.Exception ex) { 74 | ex.printStackTrace(); 75 | } 76 | } else if (key.equals("x509.info.extensions.AuthorityInfoAccess.ocsp")) { 77 | try { 78 | String value = ((String)entry.getValue()).trim(); 79 | java.lang.System.out.println("ocsp:" + value); 80 | 81 | org.bouncycastle.asn1.x509.ExtensionsGenerator extensionsGenerator = new org.bouncycastle.asn1.x509.ExtensionsGenerator(); 82 | org.bouncycastle.asn1.ASN1ObjectIdentifier [] oids = extensions.getCriticalExtensionOIDs(); 83 | for (int k = 0; k < oids.length; k++) { 84 | if (!org.bouncycastle.asn1.x509.Extension.authorityInfoAccess.equals(oids[i])) { 85 | extensionsGenerator.addExtension(extensions.getExtension(oids[i])); 86 | } 87 | } 88 | org.bouncycastle.asn1.x509.Extension sunExtenson = extensions.getExtension(org.bouncycastle.asn1.x509.Extension.authorityInfoAccess); 89 | if (sunExtenson == null) { 90 | org.bouncycastle.asn1.x509.AuthorityInformationAccess authInfo = new org.bouncycastle.asn1.x509.AuthorityInformationAccess(org.bouncycastle.asn1.x509.AccessDescription.id_ad_ocsp, new org.bouncycastle.asn1.x509.GeneralName(org.bouncycastle.asn1.x509.GeneralName.uniformResourceIdentifier, value)); 91 | extensionsGenerator.addExtension(org.bouncycastle.asn1.x509.Extension.authorityInfoAccess, false, authInfo); 92 | } 93 | else { 94 | org.bouncycastle.asn1.x509.AuthorityInformationAccess authInfo = new org.bouncycastle.asn1.x509.AuthorityInformationAccess(org.bouncycastle.asn1.x509.AccessDescription.id_ad_ocsp, new org.bouncycastle.asn1.x509.GeneralName(org.bouncycastle.asn1.x509.GeneralName.uniformResourceIdentifier, value)); 95 | extensionsGenerator.addExtension(org.bouncycastle.asn1.x509.Extension.authorityInfoAccess, false, authInfo); 96 | } 97 | extensions = extensionsGenerator.generate(); 98 | } catch (java.io.IOException ex) { 99 | ex.printStackTrace(); 100 | } catch (java.lang.Exception ex) { 101 | ex.printStackTrace(); 102 | } 103 | 104 | } 105 | } 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /FakeBurpCert/src/main/resources/fake/resources/fakeCert.jav: -------------------------------------------------------------------------------- 1 | public static sun.security.x509.X509CertInfo burpCertInjection(sun.security.x509.X509CertInfo certInfo) { 2 | java.util.Iterator iterator = translateMaps.keySet().iterator(); 3 | try { 4 | String subject = String.valueOf(certInfo.get(sun.security.x509.X509CertInfo.SUBJECT)); 5 | while (iterator.hasNext()) { 6 | java.util.regex.Pattern regexSubject = (java.util.regex.Pattern) iterator.next(); 7 | java.util.regex.Matcher m = regexSubject.matcher(subject); 8 | if (m.find()) { 9 | java.lang.System.out.println("subject:" + subject + "\t"); 10 | java.util.List attrs = (java.util.List) translateMaps.get(regexSubject); 11 | for (int i = 0; i < attrs.size(); i++) { 12 | java.util.Map.Entry entry = (java.util.Map.Entry) attrs.get(i); 13 | String key = ((String) entry.getKey()); 14 | java.lang.System.out.print("attr[" + key + "] "); 15 | if (key.equals("x509.info.subject")) { 16 | String value = ((String)entry.getValue()).trim(); 17 | java.lang.System.out.println("\treplace_subject:[" + value + "] "); 18 | certInfo.set(sun.security.x509.X509CertInfo.SUBJECT, new sun.security.x509.CertificateSubjectName(new sun.security.x509.X500Name(value))); 19 | } else if(key.equals("x509.info.serialNumber")) { 20 | String value = ((String)entry.getValue()).trim(); 21 | certInfo.set(sun.security.x509.X509CertInfo.SERIAL_NUMBER, new sun.security.x509.CertificateSerialNumber(new java.math.BigInteger(value, 16))); 22 | } else if (key.equals("x509.info.validity")) { 23 | String value = ((String)entry.getValue()).trim(); 24 | java.lang.System.out.println("\treplace_date:[" + value + "] "); 25 | String[] list = value.split("\t", 3); 26 | if (list.length != 3) { 27 | new java.text.ParseException("x509.info.validity is value error: [dateFormat]\t[fromDate]\t[toDate]", list.length); 28 | } 29 | java.text.SimpleDateFormat format = new java.text.SimpleDateFormat(list[0]); 30 | java.util.Date fromDate = format.parse(list[1]); 31 | java.util.Date toDate = format.parse(list[2]); 32 | sun.security.x509.CertificateValidity interval = new sun.security.x509.CertificateValidity(fromDate, toDate); 33 | certInfo.set(sun.security.x509.X509CertInfo.VALIDITY, interval); 34 | } else if (key.startsWith("x509.info.extensions.SubjectAlternativeName")) { 35 | String value = ((String)entry.getValue()).trim(); 36 | java.lang.System.out.println("\treplace_san:" + value); 37 | sun.security.x509.CertificateExtensions ext = (sun.security.x509.CertificateExtensions) certInfo.get(sun.security.x509.X509CertInfo.EXTENSIONS); 38 | if (ext == null) { 39 | ext = new sun.security.x509.CertificateExtensions(); 40 | } 41 | sun.security.x509.SubjectAlternativeNameExtension san = (sun.security.x509.SubjectAlternativeNameExtension) ext.get(sun.security.x509.SubjectAlternativeNameExtension.NAME); 42 | if (san == null) { 43 | if (!value.isEmpty()) { 44 | sun.security.x509.GeneralNames alternativeNames = new sun.security.x509.GeneralNames(); 45 | sun.security.x509.DNSName dnsName = new sun.security.x509.DNSName(value); 46 | alternativeNames.add(new sun.security.x509.GeneralName(dnsName)); 47 | san = new sun.security.x509.SubjectAlternativeNameExtension(alternativeNames); 48 | } 49 | } 50 | else { 51 | sun.security.x509.GeneralNames alternativeNames = san.get(sun.security.x509.SubjectAlternativeNameExtension.SUBJECT_NAME); 52 | if (alternativeNames == null) { 53 | alternativeNames = new sun.security.x509.GeneralNames(); 54 | } 55 | if (key.endsWith(".clear")) { 56 | alternativeNames = new sun.security.x509.GeneralNames(); 57 | if (ext.get(sun.security.x509.SubjectAlternativeNameExtension.NAME) != null) ext.delete(sun.security.x509.SubjectAlternativeNameExtension.NAME); 58 | if (san.get(sun.security.x509.SubjectAlternativeNameExtension.SUBJECT_NAME) != null) san.delete(sun.security.x509.SubjectAlternativeNameExtension.SUBJECT_NAME); 59 | } 60 | if (!value.isEmpty()) { 61 | sun.security.x509.DNSName dnsName = new sun.security.x509.DNSName(value); 62 | alternativeNames.add(new sun.security.x509.GeneralName(dnsName)); 63 | } 64 | if (alternativeNames.size() > 0) { 65 | san.set(sun.security.x509.SubjectAlternativeNameExtension.SUBJECT_NAME, alternativeNames); 66 | } 67 | } 68 | if (san != null && san.get(sun.security.x509.SubjectAlternativeNameExtension.SUBJECT_NAME) != null) { 69 | ext.set(sun.security.x509.SubjectAlternativeNameExtension.NAME, san); 70 | } 71 | if (ext.getElements().hasMoreElements()) { 72 | certInfo.set(sun.security.x509.X509CertInfo.EXTENSIONS, ext); 73 | } 74 | else { 75 | if (certInfo.get(sun.security.x509.X509CertInfo.EXTENSIONS) != null) certInfo.delete(sun.security.x509.X509CertInfo.EXTENSIONS); 76 | } 77 | 78 | } else if (key.equals("x509.info.extensions.AuthorityInfoAccess.ocsp")) { 79 | sun.security.x509.CertificateExtensions ext = (sun.security.x509.CertificateExtensions) certInfo.get(sun.security.x509.X509CertInfo.EXTENSIONS); 80 | if (ext == null) { 81 | ext = new sun.security.x509.CertificateExtensions(); 82 | } 83 | String value = ((String)entry.getValue()).trim(); 84 | java.lang.System.out.println("ocsp:" + value); 85 | java.util.List adList = new java.util.ArrayList(); 86 | adList.add(new sun.security.x509.AccessDescription(sun.security.x509.AccessDescription.Ad_OCSP_Id, new sun.security.x509.GeneralName(new sun.security.x509.URIName(value)))); 87 | ext.set(sun.security.x509.AuthorityInfoAccessExtension.NAME, new sun.security.x509.AuthorityInfoAccessExtension(adList)); 88 | certInfo.set(sun.security.x509.X509CertInfo.EXTENSIONS, ext); 89 | } 90 | } 91 | } 92 | } 93 | } catch (java.security.cert.CertificateException e) { 94 | e.printStackTrace(); 95 | } catch (java.io.IOException e) { 96 | e.printStackTrace(); 97 | } catch (java.text.ParseException e) { 98 | e.printStackTrace(); 99 | } 100 | return certInfo; 101 | } 102 | -------------------------------------------------------------------------------- /FakeBurpCert/src/main/resources/resources/release.properties: -------------------------------------------------------------------------------- 1 | # FakeBurpCert build xml properties 2 | 3 | # version 4 | version=${release_version_major}.${release_version_minor} 5 | -------------------------------------------------------------------------------- /FakeBurpCert/src/test/java/fake/cert/FakeBurpCertTest.java: -------------------------------------------------------------------------------- 1 | package fake.cert; 2 | 3 | import org.junit.jupiter.api.AfterAll; 4 | import org.junit.jupiter.api.AfterEach; 5 | import org.junit.jupiter.api.Test; 6 | import static org.junit.jupiter.api.Assertions.*; 7 | import org.junit.jupiter.api.BeforeAll; 8 | import org.junit.jupiter.api.BeforeEach; 9 | 10 | /** 11 | * 12 | * @author isayan 13 | */ 14 | public class FakeBurpCertTest { 15 | 16 | public FakeBurpCertTest() { 17 | } 18 | 19 | @BeforeAll 20 | public static void setUpClass() { 21 | } 22 | 23 | @AfterAll 24 | public static void tearDownClass() { 25 | } 26 | 27 | @BeforeEach 28 | public void setUp() { 29 | } 30 | 31 | @AfterEach 32 | public void tearDown() { 33 | } 34 | 35 | 36 | /******************************************************************************/ 37 | 38 | /** 39 | * 40 | * @return 41 | */ 42 | public static java.util.Map createMap() { 43 | java.util.Map map = new java.util.LinkedHashMap(); 44 | java.io.BufferedReader reader = null; 45 | try { 46 | reader = new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream("cert.txt"), "UTF-8")); 47 | String line = null; 48 | while ((line = reader.readLine()) != null) { 49 | if (line.startsWith("#")) { 50 | continue; 51 | } 52 | String[] inputs = line.split("\t", 3); 53 | java.util.List item = new java.util.ArrayList(); 54 | item.add(new java.util.AbstractMap.SimpleEntry(inputs[1], (inputs.length > 2) ? inputs[2] : "")); 55 | java.util.List attrs = (java.util.List) map.putIfAbsent(java.util.regex.Pattern.compile(inputs[0]), item); 56 | if (attrs != null) { 57 | attrs.addAll(item); 58 | } 59 | } 60 | } catch (java.io.IOException ex) { 61 | ex.printStackTrace(); 62 | } finally { 63 | if (reader != null) { 64 | try { 65 | reader.close(); 66 | } catch (java.io.IOException ex) { 67 | } 68 | } 69 | } 70 | return map; 71 | } 72 | 73 | /******************************************************************************/ 74 | 75 | org.bouncycastle.asn1.ASN1Integer serialNumber; 76 | org.bouncycastle.asn1.x509.AlgorithmIdentifier signature; 77 | org.bouncycastle.asn1.x500.X500Name issuer; 78 | org.bouncycastle.asn1.x509.Time startDate, endDate; 79 | org.bouncycastle.asn1.x500.X500Name subject; 80 | org.bouncycastle.asn1.x509.SubjectPublicKeyInfo subjectPublicKeyInfo; 81 | org.bouncycastle.asn1.x509.Extensions extensions; 82 | 83 | static java.util.Map translateMaps = createMap(); 84 | 85 | public void burpCertInjection() { 86 | java.util.Iterator iterator = translateMaps.keySet().iterator(); 87 | 88 | String subjectCN = subject.toString(); 89 | while (iterator.hasNext()) { 90 | java.util.regex.Pattern regexSubject = (java.util.regex.Pattern) iterator.next(); 91 | java.util.regex.Matcher m = regexSubject.matcher(subjectCN); 92 | if (m.find()) { 93 | java.lang.System.out.println("subject:" + subjectCN + "\t"); 94 | java.util.List attrs = (java.util.List) translateMaps.get(regexSubject); 95 | for (int i = 0; i < attrs.size(); i++) { 96 | java.util.Map.Entry entry = (java.util.Map.Entry) attrs.get(i); 97 | String key = ((String) entry.getKey()); 98 | java.lang.System.out.print("attr[" + key + "] "); 99 | if (key.equals("x509.info.subject")) { 100 | String value = ((String)entry.getValue()).trim(); 101 | java.lang.System.out.println("\treplace_subject:[" + value + "] "); 102 | subject = new org.bouncycastle.asn1.x500.X500Name(value); 103 | } else if (key.equals("x509.info.serialNumber")) { 104 | String value = ((String)entry.getValue()).trim(); 105 | serialNumber = new org.bouncycastle.asn1.ASN1Integer(new java.math.BigInteger(value, 16)); 106 | } else if (key.equals("x509.info.validity")) { 107 | try { 108 | String value = ((String)entry.getValue()).trim(); 109 | java.lang.System.out.println("\treplace_date:[" + value + "] "); 110 | String[] list = value.split("\t", 3); 111 | if (list.length != 3) { 112 | new java.text.ParseException("x509.info.validity is value error: [dateFormat]\t[fromDate]\t[toDate]", list.length); 113 | } 114 | java.text.SimpleDateFormat format = new java.text.SimpleDateFormat(list[0]); 115 | java.util.Date fromDate = format.parse(list[1]); 116 | java.util.Date toDate = format.parse(list[2]); 117 | startDate = new org.bouncycastle.asn1.x509.Time(fromDate); 118 | endDate = new org.bouncycastle.asn1.x509.Time(toDate); 119 | } catch (java.text.ParseException ex) { 120 | ex.printStackTrace(); 121 | } 122 | } else if (key.startsWith("x509.info.extensions.SubjectAlternativeName")) { 123 | try { 124 | String value = ((String)entry.getValue()).trim(); 125 | java.lang.System.out.println("\treplace_san:" + value); 126 | 127 | org.bouncycastle.asn1.x509.ExtensionsGenerator extensionsGenerator = new org.bouncycastle.asn1.x509.ExtensionsGenerator(); 128 | org.bouncycastle.asn1.ASN1ObjectIdentifier [] oids = extensions.getCriticalExtensionOIDs(); 129 | // not SAN Exension 130 | for (int k = 0; k < oids.length; k++) { 131 | if (!org.bouncycastle.asn1.x509.Extension.subjectAlternativeName.equals(oids[i])) { 132 | extensionsGenerator.addExtension(extensions.getExtension(oids[i])); 133 | } 134 | } 135 | org.bouncycastle.asn1.x509.Extension sunExtenson = extensions.getExtension(org.bouncycastle.asn1.x509.Extension.subjectAlternativeName); 136 | if (sunExtenson == null) { 137 | if (!value.isEmpty()) { 138 | org.bouncycastle.asn1.x509.GeneralNames dnsNames = new org.bouncycastle.asn1.x509.GeneralNames(new org.bouncycastle.asn1.x509.GeneralName(org.bouncycastle.asn1.x509.GeneralName.dNSName, value)); 139 | extensionsGenerator.addExtension(org.bouncycastle.asn1.x509.Extension.subjectAlternativeName, false, dnsNames); 140 | } 141 | } 142 | else { 143 | org.bouncycastle.asn1.x509.GeneralNames gns = org.bouncycastle.asn1.x509.GeneralNames.fromExtensions(extensions, org.bouncycastle.asn1.x509.Extension.subjectAlternativeName); 144 | java.util.List dnsNameList = new java.util.ArrayList(); 145 | if (!key.endsWith(".clear")) { 146 | dnsNameList.addAll(java.util.Arrays.asList(gns.getNames())); 147 | } 148 | if (!value.isEmpty()) { 149 | dnsNameList.add(new org.bouncycastle.asn1.x509.GeneralName(org.bouncycastle.asn1.x509.GeneralName.dNSName, value)); 150 | } 151 | org.bouncycastle.asn1.x509.GeneralNames dnsNames = new org.bouncycastle.asn1.x509.GeneralNames((org.bouncycastle.asn1.x509.GeneralName[])dnsNameList.toArray(new org.bouncycastle.asn1.x509.GeneralName[dnsNameList.size()])); 152 | extensionsGenerator.addExtension(org.bouncycastle.asn1.x509.Extension.subjectAlternativeName, false, dnsNames); 153 | } 154 | extensions = extensionsGenerator.generate(); 155 | } catch (java.io.IOException ex) { 156 | ex.printStackTrace(); 157 | } catch (java.lang.Exception ex) { 158 | ex.printStackTrace(); 159 | } 160 | } else if (key.equals("x509.info.extensions.AuthorityInfoAccess.ocsp")) { 161 | try { 162 | String value = ((String)entry.getValue()).trim(); 163 | java.lang.System.out.println("ocsp:" + value); 164 | 165 | org.bouncycastle.asn1.x509.ExtensionsGenerator extensionsGenerator = new org.bouncycastle.asn1.x509.ExtensionsGenerator(); 166 | org.bouncycastle.asn1.ASN1ObjectIdentifier [] oids = extensions.getCriticalExtensionOIDs(); 167 | for (int k = 0; k < oids.length; k++) { 168 | if (!org.bouncycastle.asn1.x509.Extension.authorityInfoAccess.equals(oids[i])) { 169 | extensionsGenerator.addExtension(extensions.getExtension(oids[i])); 170 | } 171 | } 172 | org.bouncycastle.asn1.x509.Extension sunExtenson = extensions.getExtension(org.bouncycastle.asn1.x509.Extension.authorityInfoAccess); 173 | if (sunExtenson == null) { 174 | org.bouncycastle.asn1.x509.AuthorityInformationAccess authInfo = new org.bouncycastle.asn1.x509.AuthorityInformationAccess(org.bouncycastle.asn1.x509.AccessDescription.id_ad_ocsp, new org.bouncycastle.asn1.x509.GeneralName(org.bouncycastle.asn1.x509.GeneralName.uniformResourceIdentifier, value)); 175 | extensionsGenerator.addExtension(org.bouncycastle.asn1.x509.Extension.authorityInfoAccess, false, authInfo); 176 | } 177 | else { 178 | org.bouncycastle.asn1.x509.AuthorityInformationAccess authInfo = new org.bouncycastle.asn1.x509.AuthorityInformationAccess(org.bouncycastle.asn1.x509.AccessDescription.id_ad_ocsp, new org.bouncycastle.asn1.x509.GeneralName(org.bouncycastle.asn1.x509.GeneralName.uniformResourceIdentifier, value)); 179 | extensionsGenerator.addExtension(org.bouncycastle.asn1.x509.Extension.authorityInfoAccess, false, authInfo); 180 | } 181 | extensions = extensionsGenerator.generate(); 182 | } catch (java.io.IOException ex) { 183 | ex.printStackTrace(); 184 | } catch (java.lang.Exception ex) { 185 | ex.printStackTrace(); 186 | } 187 | 188 | } 189 | } 190 | } 191 | } 192 | } 193 | 194 | /******************************************************************************/ 195 | 196 | public static sun.security.x509.X509CertInfo burpCertInjection(sun.security.x509.X509CertInfo certInfo) { 197 | java.util.Iterator iterator = translateMaps.keySet().iterator(); 198 | try { 199 | String subject = String.valueOf(certInfo.get(sun.security.x509.X509CertInfo.SUBJECT)); 200 | while (iterator.hasNext()) { 201 | java.util.regex.Pattern regexSubject = (java.util.regex.Pattern) iterator.next(); 202 | java.util.regex.Matcher m = regexSubject.matcher(subject); 203 | if (m.find()) { 204 | java.lang.System.out.println("subject:" + subject + "\t"); 205 | java.util.List attrs = (java.util.List) translateMaps.get(regexSubject); 206 | for (int i = 0; i < attrs.size(); i++) { 207 | java.util.Map.Entry entry = (java.util.Map.Entry) attrs.get(i); 208 | String key = ((String) entry.getKey()); 209 | java.lang.System.out.print("attr[" + key + "] "); 210 | if (key.equals("x509.info.subject")) { 211 | String value = ((String)entry.getValue()).trim(); 212 | java.lang.System.out.println("\treplace_subject:[" + value + "] "); 213 | certInfo.set(sun.security.x509.X509CertInfo.SUBJECT, new sun.security.x509.CertificateSubjectName(new sun.security.x509.X500Name(value))); 214 | } else if(key.equals("x509.info.serialNumber")) { 215 | String value = ((String)entry.getValue()).trim(); 216 | certInfo.set(sun.security.x509.X509CertInfo.SERIAL_NUMBER, new sun.security.x509.CertificateSerialNumber(new java.math.BigInteger(value, 16))); 217 | } else if (key.equals("x509.info.validity")) { 218 | String value = ((String)entry.getValue()).trim(); 219 | java.lang.System.out.println("\treplace_date:[" + value + "] "); 220 | String[] list = value.split("\t", 3); 221 | if (list.length != 3) { 222 | new java.text.ParseException("x509.info.validity is value error: [dateFormat]\t[fromDate]\t[toDate]", list.length); 223 | } 224 | java.text.SimpleDateFormat format = new java.text.SimpleDateFormat(list[0]); 225 | java.util.Date fromDate = format.parse(list[1]); 226 | java.util.Date toDate = format.parse(list[2]); 227 | sun.security.x509.CertificateValidity interval = new sun.security.x509.CertificateValidity(fromDate, toDate); 228 | certInfo.set(sun.security.x509.X509CertInfo.VALIDITY, interval); 229 | } else if (key.startsWith("x509.info.extensions.SubjectAlternativeName")) { 230 | String value = ((String)entry.getValue()).trim(); 231 | java.lang.System.out.println("\treplace_san:" + value); 232 | sun.security.x509.CertificateExtensions ext = (sun.security.x509.CertificateExtensions) certInfo.get(sun.security.x509.X509CertInfo.EXTENSIONS); 233 | if (ext == null) { 234 | ext = new sun.security.x509.CertificateExtensions(); 235 | } 236 | sun.security.x509.SubjectAlternativeNameExtension san = (sun.security.x509.SubjectAlternativeNameExtension) ext.get(sun.security.x509.SubjectAlternativeNameExtension.NAME); 237 | if (san == null) { 238 | if (!value.isEmpty()) { 239 | sun.security.x509.GeneralNames alternativeNames = new sun.security.x509.GeneralNames(); 240 | sun.security.x509.DNSName dnsName = new sun.security.x509.DNSName(value); 241 | alternativeNames.add(new sun.security.x509.GeneralName(dnsName)); 242 | san = new sun.security.x509.SubjectAlternativeNameExtension(alternativeNames); 243 | } 244 | } 245 | else { 246 | sun.security.x509.GeneralNames alternativeNames = san.get(sun.security.x509.SubjectAlternativeNameExtension.SUBJECT_NAME); 247 | if (alternativeNames == null) { 248 | alternativeNames = new sun.security.x509.GeneralNames(); 249 | } 250 | if (key.endsWith(".clear")) { 251 | alternativeNames = new sun.security.x509.GeneralNames(); 252 | if (ext.get(sun.security.x509.SubjectAlternativeNameExtension.NAME) != null) ext.delete(sun.security.x509.SubjectAlternativeNameExtension.NAME); 253 | if (san.get(sun.security.x509.SubjectAlternativeNameExtension.SUBJECT_NAME) != null) san.delete(sun.security.x509.SubjectAlternativeNameExtension.SUBJECT_NAME); 254 | } 255 | if (!value.isEmpty()) { 256 | sun.security.x509.DNSName dnsName = new sun.security.x509.DNSName(value); 257 | alternativeNames.add(new sun.security.x509.GeneralName(dnsName)); 258 | } 259 | if (alternativeNames.size() > 0) { 260 | san.set(sun.security.x509.SubjectAlternativeNameExtension.SUBJECT_NAME, alternativeNames); 261 | } 262 | } 263 | if (san != null && san.get(sun.security.x509.SubjectAlternativeNameExtension.SUBJECT_NAME) != null) { 264 | ext.set(sun.security.x509.SubjectAlternativeNameExtension.NAME, san); 265 | } 266 | if (ext.getElements().hasMoreElements()) { 267 | certInfo.set(sun.security.x509.X509CertInfo.EXTENSIONS, ext); 268 | } 269 | else { 270 | if (certInfo.get(sun.security.x509.X509CertInfo.EXTENSIONS) != null) certInfo.delete(sun.security.x509.X509CertInfo.EXTENSIONS); 271 | } 272 | 273 | } else if (key.equals("x509.info.extensions.AuthorityInfoAccess.ocsp")) { 274 | sun.security.x509.CertificateExtensions ext = (sun.security.x509.CertificateExtensions) certInfo.get(sun.security.x509.X509CertInfo.EXTENSIONS); 275 | if (ext == null) { 276 | ext = new sun.security.x509.CertificateExtensions(); 277 | } 278 | String value = ((String)entry.getValue()).trim(); 279 | java.lang.System.out.println("ocsp:" + value); 280 | java.util.List adList = new java.util.ArrayList(); 281 | adList.add(new sun.security.x509.AccessDescription(sun.security.x509.AccessDescription.Ad_OCSP_Id, new sun.security.x509.GeneralName(new sun.security.x509.URIName(value)))); 282 | ext.set(sun.security.x509.AuthorityInfoAccessExtension.NAME, new sun.security.x509.AuthorityInfoAccessExtension(adList)); 283 | certInfo.set(sun.security.x509.X509CertInfo.EXTENSIONS, ext); 284 | } 285 | } 286 | } 287 | } 288 | } catch (java.security.cert.CertificateException e) { 289 | e.printStackTrace(); 290 | } catch (java.io.IOException e) { 291 | e.printStackTrace(); 292 | } catch (java.text.ParseException e) { 293 | e.printStackTrace(); 294 | } 295 | return certInfo; 296 | } 297 | 298 | } 299 | -------------------------------------------------------------------------------- /Readme-ja.md: -------------------------------------------------------------------------------- 1 | Burp suite 証明書変更ツール 2 | ============= 3 | Language/[English](Readme.md) 4 | 5 | このツールは、PortSwigger社のBurp Suiteが動的に生成する証明書の変更や含まれていない情報を追加するためのツールです。 6 | 7 | 本ツールには2つのプロジェクトが含まれています。 8 | 9 | FakeBurpCert 10 | _____________ 11 | 12 | Burp suite の作成する証明書に変更を加えます。 13 | 現時点において以下のいずれかのことが可能です。 14 | 15 | 1. CNの変更 16 | 2. SerialNumberの指定 17 | 3. 証明書の有効期限の指定 18 | 4. SAN(Subject Alternative Name)の変更もしくは追加 19 | 5. OCSP URI の追加 20 | 21 | SimpleOCSPServer 22 | _____________ 23 | 24 | 簡易 OCSP レスポンダ サーバです。 25 | 26 | # 使用方法 27 | 28 | ## FakeBurpCert 29 | 30 | 以下のファイルをBurpのjarファイルがあるフォルダにおきます。 31 | 32 | * FakeBurpCert.jar 33 | * cert.txt 34 | * javassist.jar 35 | 36 | Burp suiteのjarファイルがあるフォルダにて、(-jar オプションより前に) -javaagentコマンドラインオプションを指定して起動します。 37 | 38 | ``` 39 | java -javaagent:FakeBurpCert.jar -Xmx1024m -jar burpsuite_free_v1.7.06.jar 40 | ``` 41 | 42 | ## cert.txt ファイルの変更 43 | 44 | cert.txt には 証明書を変更するためのルールを記載します。 45 | 46 | このファイルは以下の形式にて記載します。 47 | なお、文字コードはUTF-8で記載する必要があります。 48 | 49 | ``` 50 | # 行頭が#はコメント扱い 51 | CN=www\.example\.com(,$) x509.info.subject CN=www.example.jp, OU=piyo CA, O=fuga, C=hoge 52 | CN=www\.example\.com(,$) x509.info.serialNumber 11223344AABB 53 | CN=www\.example\.com(,$) x509.info.validity yyyy/MM/dd 2017/01/01 2027/12/31 54 | CN=www\.example\.com(,$) x509.info.extensions.SubjectAlternativeName www.example.com 55 | CN=www\.example\.com(,$) x509.info.extensions.AuthorityInfoAccess.ocsp http://www.example.com:8888/ 56 | ``` 57 | ルールは複数行記載することが可能です。 58 | 59 | 各項目はタブ区切りとなっておりそれぞれ以下の値を設定します。 60 | 61 | 1カラム目 : 変更を加えたい証明書のsubjectにマッチする正規表現を記載、マッチした場合に処理が行われます。 62 | 63 | 2カラム目 : 追加もしくは更新するフィールドの種類を記載 64 | 65 | 3カラム目 : 追加もしくは更新する値 66 | 67 | 2カラム目に指定できるフィールドの種類は以下になります。 68 | 69 | * x509.info.subject 70 | * 証明書のsubject ... 変更後のSubjectを記載。CNについても変更可能です。 71 | 72 | * x509.info.serialNumber 73 | * 証明書のserialNumber ... 16進数で記載します。 74 | 75 | * x509.info.validity 76 | * 証明書の有効期限 ... [dateFormatPattern] [fromDate] [toDate]の順で記載。各項目はタブ区切りです。 77 | * dateFormatPattern ... SimpleDateFormatにて利用可能な日付フォーマットを指定可能です。 78 | * fromDate ... 開始日(日付フォーマットの書式にて記載) 79 | * toDate ... 終了日(日付フォーマットの書式にて記載) 80 | 81 | * x509.info.extensions.SubjectAlternativeName 82 | * 証明書のSAN ... 追加するSANを記載します。既存のSANの変更や削除はされずに指定したSANの追加となります。 83 | 84 | * x509.info.extensions.SubjectAlternativeName.clear 85 | * 既存のSANをクリアします。値が指定されてない場合は、SANが指定されていない証明書となり、 86 | 値が指定された場合は、既存のSANをクリアした後に指定された値が追加されます。 87 | 88 | * x509.info.extensions.AuthorityInfoAccess.ocsp 89 | * 証明書のOCSP URI ... URI を記載 90 | 91 | ちなみにBurp suiteが動的に生成する証明書のsubjectは以下のような形式になってます。 92 | 93 | ``` 94 | CN=www.example.jp, OU=PortSwigger CA, O=PortSwigger, C=PortSwigger 95 | ``` 96 | 97 | Burp suiteのバージョンv2020.2からは、証明書のライブラリにBouncy Castleが利用されるようになっており以下の形式となっております。 98 | 99 | ``` 100 | C=PortSwigger,O=PortSwigger,OU=PortSwigger CA,CN=www.example.com 101 | ``` 102 | 103 | 証明書のOCSP URI を記載した場合はクライアントアプリから検証処理のリクエストが発生する場合があります。 104 | このOCSPの検証処理に対応するために簡易 OCSP レスポンダ サーバ(SimpleOCSPServer)を用意しています。 105 | 106 | ## SimpleOCSPServer 107 | 108 | 簡易 OCSP レスポンダサーバを起動します。 109 | 110 | 以下のコマンドで起動できます。 111 | 112 | ``` 113 | java -jar SimpleOCSPServer.jar -cafile=burp_ca.p12 -password=testca -port=8888 114 | ``` 115 | 116 | 証明書が複数格納されてる場合は、aliasで証明書を指定できます。 117 | 118 | ``` 119 | java -jar SimpleOCSPServer.jar -cafile=burp_ca.p12 -alias=cacert -password=testca -port=8888 120 | ``` 121 | 122 | * -cafile 123 | * 秘密鍵を含むCA証明書(PKCS12形式) 124 | * -password 125 | * CA証明書のパスワード 126 | * -alias 127 | * 証明書のエイリアスを指定します。省略可能です。省略時は最初に見つかった証明書になります。 128 | * -port 129 | * 待ち受けポートになります。省略可能です。省略時は8888になります。 130 | 131 | Burp suite のCAは以下の手順でExportできます。 132 | 133 | 1. [Options]タブの[CA Certificate]をクリック 134 | 2. Exportの[Certificate and private key in PKCS#12 keystore]を選択して次へ 135 | 3. ファイル名とパスワードを指定して次へ指定してエクスポート 136 | 137 | SimpleOCSPServer は BurpExtender にも対応しています。 138 | 139 | ![OCSP Server](BurpExtender-ocsp.png) 140 | 141 | * [Start] ボタン 142 | * OCSP Server を起動します。もう一度クリックすると停止します。 143 | * Automatic start at loading 144 | * チェック時、Burp suite 起動時に自動で OCSP Server を起動します。 145 | * Listen port 146 | * OCSP Server の待ち受けポートになります。 147 | * CA Certificate 148 | * Burp suite default CA 149 | * Burp suite がデフォルトで利用している CA を利用します。 150 | * Use custom CA file 151 | * 秘密鍵を含む CA 証明書(PKCS12形式)を指定します。 152 | 153 | Burp suite の Extenderは以下の手順で読み込めます。 154 | 155 | 1. [Extender]タブの[add]をクリック 156 | 2. [Select file ...]をクリックし、SimpleOCSPServer.jar を選択する。 157 | 3. 「Next」をクリックし、エラーがでてないことを確認後、「Close」にてダイヤログを閉じる。 158 | 159 | # 動作環境 160 | 161 | ## Burp suite 162 | * v1.7以上 (http://www.portswigger.net/burp/) 163 | * v2020.2 (最終確認バージョン) 164 | 165 | ## 開発環境 166 | * NetBeans 12.4 (https://netbeans.apache.org/) 167 | 168 | ## ビルド 169 | NetBeans にてビルドもしくは gradle にてビルドします。 170 | 171 | 172 | ``` 173 | gradlew release 174 | ``` 175 | 176 | ## 必須ライブラリ 177 | ビルドには別途 [BurpExtensionCommons](https://github.com/raise-isayan/BurpExtensionCommons) のライブラリを必要とします。 178 | * BurpExtensionCommons v0.4.x 179 | 180 | ## 利用ライブラリ 181 | * Jassist 3.26.0 (https://www.javassist.org/) 182 | * Mozilla Public License Version 1.1, GNU Lesser General Public License Version 2.1, Apache License Version 2.0 183 | * https://github.com/jboss-javassist/javassist 184 | 185 | * Google gson (https://github.com/google/gson) 186 | * Apache License 2.0 187 | * https://github.com/google/gson/blob/master/LICENSE 188 | 189 | * Universal Chardet for java (https://code.google.com/archive/p/juniversalchardet/) 190 | * MPL 1.1 191 | * https://code.google.com/archive/p/juniversalchardet/ 192 | 193 | * BouncyCastle 1.6.4 (http://bouncycastle.org/) 194 | * MIT license 195 | * https://www.bouncycastle.org/license.html 196 | 197 | * Jetty 9 (https://www.eclipse.org/jetty/) 198 | * Apache License 2.0, Eclipse Public License 1.0 199 | * https://www.eclipse.org/jetty/licenses.html 200 | 201 | * Use Icon (http://www.famfamfam.com/lab/icons/silk/) 202 | * Creative Commons Attribution 2.5 License 203 | * http://www.famfamfam.com/lab/icons/silk/ 204 | 205 | ## 注意点 206 | このツールは、私個人が勝手に開発したもので、PortSwigger社は一切関係ありません。本ツールを使用したことによる不具合等についてPortSwiggerに問い合わせないようお願いします。 207 | このツールは内部でJava実行環境のバイトコードを変更します。Oracle社のJava実行環境で使用した場合、[バイナリ・コードライセンス](http://www.oracle.com/technetwork/java/javase/terms/license/index.html)に違反する可能性があります。ライセンスを確認の上、[OpenJDK](http://openjdk.java.net/)等その他のJava実行環境での実行を推奨します。 208 | 209 | ## 謝辞 210 | このツールのアイデアのベースとなったツールに[Belle (Burp Suite 非公式日本語化ツール) ](https://github.com/ankokuty/Belle) があり影響を受けています。 211 | Belleの作者には本ツール作成にあたりアドバイスをいただきました。この場を借りてお礼を申し上げます。 212 | 213 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | Burp suite Certificate modification tool 2 | ============= 3 | Language/[Japanese](Readme-ja.md) 4 | 5 | This tool is used to modify or add information that is not included in the dynamically generated certificates in PortSwiggers Burp Suite. 6 | 7 | This tool contains two projects. 8 | 9 | FakeBurpCert 10 | _____________ 11 | 12 | Change the certificates that the Burp suite creates. 13 | You can currently do one of the following 14 | 15 | 1. modification of CN 16 | 2. set the serial number 17 | 3. set the date of the certificate 18 | 4. modification or add a SAN (Subject Alternative Name). 19 | 5. add an OCSP URI. 20 | 21 | SimpleOCSPServer 22 | _____________ 23 | 24 | A simple OCSP responder server. 25 | 26 | # Usage 27 | 28 | ## FakeBurpCert 29 | 30 | Put the following file in the folder with the Burp jar file. 31 | 32 | * FakeBurpCert.jar 33 | * cert.txt 34 | * javassist.jar 35 | 36 | In the folder containing the Burp suite jar files, start it with the -javaagent command line option (before the -jar option). 37 | 38 | ``` 39 | java -javaagent:FakeBurpCert.jar -Xmx1024m -jar burpsuite_free_v1.7.06.jar 40 | ``` 41 | 42 | ## cert.txt file modification 43 | 44 | The cert.txt file contains the rules for modifying the certificate. 45 | 46 | This file should be written in the following format. 47 | Note that the character encoding must be UTF-8. 48 | 49 | ``` 50 | # A line beginning with # is treated as a comment. 51 | CN=www\.example\.com(,$) x509.info.subject CN=www.example.jp, OU=piyo CA, O=fuga, C=hoge 52 | CN=www\.example\.com(,$) x509.info.serialNumber 11223344AABB 53 | CN=www\.example\.com(,$) x509.info.validity yyyy/MM/dd 2017/01/01 2027/12/31 54 | CN=www\.example\.com(,$) x509.info.extensions.SubjectAlternativeName www.example.com 55 | CN=www\.example\.com(,$) x509.info.extensions.AuthorityInfoAccess.ocsp http://www.example.com:8888/ 56 | ``` 57 | rules can be stated on multiple lines. 58 | 59 | each item is tab-delimited, and you can set the following values for each. 60 | 61 | Column 1: Enter a regular expression that matches the subject of the certificate you want to change, and if it matches, it will be processed. 62 | 63 | Column 2: Describe the type of field to be added or updated 64 | 65 | Column 3: Value to be added or updated 66 | 67 | The types of fields that can be specified in the 2 column are as follows 68 | 69 | * x509.info.subject 70 | * Certificate subject ... The subject of the modified certificate, which can be changed for CN. 71 | 72 | * x509.info.serialNumber 73 | * Certificate serialNumber ... Hexadecimal notation.. 74 | 75 | * x509.info.validity 76 | * Certificate Expiration Date ... [dateFormatPattern] [fromDate] [toDate] in that order. 77 | Each item is tab-delimited. 78 | * dateFormatPattern ... Allows you to specify the date formats available in SimpleDateFormat 79 | * fromDate ... Start date (in date format) 80 | * toDate ... End date (in date format) 81 | 82 | * x509.info.extensions.SubjectAlternativeName 83 | * Certificate SAN ... Describe the SAN to be added. It does not change or delete the existing SAN, but adds the specified SAN. 84 | 85 | * x509.info.extensions.SubjectAlternativeName.clear 86 | * Clears the existing SAN. If no value is specified, the certificate has no SAN specified and 87 | If a value is specified, the specified value is added after the existing SAN is cleared. 88 | 89 | * x509.info.extensions.AuthorityInfoAccess.ocsp 90 | * OCSP URI of the certificate ... Include the URI. 91 | 92 | By the way, the subject of a certificate generated dynamically by the Burp suite is in the following format 93 | 94 | ``` 95 | CN=www.example.jp, OU=PortSwigger CA, O=PortSwigger, C=PortSwigger 96 | ``` 97 | 98 | Starting with version 2020.2 of the Burp suite, Bouncy Castle is used in the library of certificates, in the following format 99 | 100 | 101 | ``` 102 | C=PortSwigger,O=PortSwigger,OU=PortSwigger CA,CN=www.example.com 103 | ``` 104 | 105 | If the OCSP URI of a certificate is included, a request for a validation process may be generated from a client application. 106 | A simple OCSP responder server (SimpleOCSPServer) is provided to handle this OCSP verification process. 107 | 108 | ## SimpleOCSPServer 109 | 110 | Start the Simple OCSP responder server. 111 | 112 | You can start the responder server by the following command 113 | 114 | 115 | ``` 116 | java -jar SimpleOCSPServer.jar -cafile=burp_ca.p12 -password=testca -port=8888 117 | ``` 118 | 119 | If multiple certificates are stored, you can specify them in alias. 120 | 121 | ``` 122 | java -jar SimpleOCSPServer.jar -cafile=burp_ca.p12 -alias=cacert -password=testca -port=8888 123 | ``` 124 | 125 | * -cafile 126 | * CA certificate with a private key (PKCS12 format) 127 | * -password 128 | * CA certificate password. 129 | * -alias 130 | * Specifies an alias for the certificate. This is optional. If omitted. the first certificate found. 131 | * -port 132 | * This is a standby port. Can be omitted. The default is 8888. 133 | 134 | The CA of Burp suite can be exported by the following procedure. 135 | 136 | 1. Click [CA Certificate] on the [Options] tab. 137 | 2. Select the export [Certificate and Private Key in PKCS#12 Keystore] and go to the next 138 | 3. Export by specifying the file name and password to the next 139 | 140 | SimpleOCSPServer also supports BurpExtender. 141 | 142 | ![OCSP Server](BurpExtender-ocsp.png) 143 | 144 | * [Start] button 145 | * Start the OCSP Server. Click again to stop it. 146 | * Automatic start at loading 147 | * When checked, OCSP Server is automatically started when the Burp suite is started. 148 | * Listen port 149 | * The port to be used to listen for OCSP Server. 150 | * CA Certificate 151 | * Burp suite default CA 152 | * Use the CA that the Burp suite uses by default. 153 | * Use custom CA file 154 | * Specifies a CA certificate (PKCS12 format) that contains a private key. 155 | 156 | Extenders in the Burp suite can be loaded as follows 157 | 158 | 1. Click [add] on the [Extender] tab. 159 | 2. Select file ... and select SimpleOCSPServer.jar. 160 | 3. Click [Next], make sure there are no errors, and then click [Close] to close the dialog. 161 | 162 | # operating environment 163 | 164 | ## Burp suite 165 | * v1.7 or higher (http://www.portswigger.net/burp/) 166 | * v2020.2 (last version) 167 | 168 | ## development environment 169 | * NetBeans 12.4 (https://netbeans.apache.org/) 170 | 171 | ## build 172 | Build with NetBeans or build with gradle. 173 | 174 | ``` 175 | gradlew release 176 | ``` 177 | 178 | ## Required library 179 | Building requires a [BurpExtensionCommons](https://github.com/raise-isayan/BurpExtensionCommons) library. 180 | * BurpExtensionCommons v0.4.x 181 | 182 | ## Library 183 | * Jassist 3.26.0 (https://www.javassist.org/) 184 | * Mozilla Public License Version 1.1, GNU Lesser General Public License Version 2.1, Apache License Version 2.0 185 | * https://github.com/jboss-javassist/javassist 186 | 187 | * Google gson 2.8.5 (https://github.com/google/gson) 188 | * Apache License 2.0 189 | * https://github.com/google/gson/ 190 | 191 | * Universal Chardet for java (https://code.google.com/archive/p/juniversalchardet/) 192 | * MPL 1.1 193 | * https://code.google.com/archive/p/juniversalchardet/ 194 | 195 | * BouncyCastle 1.6.4 (http://bouncycastle.org/) 196 | * MIT license 197 | * https://www.bouncycastle.org/license.html 198 | 199 | * Jetty 9 (https://www.eclipse.org/jetty/) 200 | * Apache License 2.0, Eclipse Public License 1.0 201 | * https://www.eclipse.org/jetty/licenses.html 202 | 203 | * Use Icon (http://www.famfamfam.com/lab/icons/silk/) 204 | * Creative Commons Attribution 2.5 License 205 | * http://www.famfamfam.com/lab/icons/silk/ 206 | 207 | ## Note 208 | This tool was developed by me personally and PortSwigger is not affiliated with it in any way. Please do not ask PortSwigger about any problems caused by using this tool. 209 | 210 | This tool internally modifies the bytecode of the Java Runtime Environment, when used with Oracle's Java Runtime Environment [Binary Code License] (http://www.oracle.com/technetwork/java/javase/terms/license/index.html). We recommend that you check the license and run the program on a Java runtime 211 | 212 | ## thanks 213 | The idea for this tool was inspired by [Belle (Unofficial Burp Suite)](https://github.com/ankokuty/Belle). 214 | I would like to thank the author of Belle for her advice on creating this tool. I would like to take this opportunity to thank the author of this tool for his advice. 215 | 216 | -------------------------------------------------------------------------------- /SimpleOCSPServer/.gitignore: -------------------------------------------------------------------------------- 1 | /.gradle 2 | /build 3 | *.zip 4 | -------------------------------------------------------------------------------- /SimpleOCSPServer/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | apply plugin: 'jacoco' 3 | apply plugin: 'application' 4 | 5 | repositories { 6 | mavenCentral() 7 | } 8 | 9 | sourceCompatibility = '11' // -source 10 | targetCompatibility = '11' // -target 11 | 12 | mainClassName = 'server.ocsp.SimpleJettyServer' 13 | 14 | tasks.withType(JavaCompile) { 15 | options.encoding = 'UTF-8' 16 | } 17 | 18 | processResources { 19 | filteringCharset = 'UTF-8' 20 | filesMatching ('**/*.properties') { 21 | expand(project.properties) 22 | // naitive2ascii 23 | filter(org.apache.tools.ant.filters.EscapeUnicode) 24 | } 25 | } 26 | 27 | clean.doFirst { 28 | delete fileTree('release') { 29 | include '*.jar' 30 | } 31 | } 32 | 33 | jar { 34 | // Keep jar clean: 35 | exclude 'META-INF/*.SF', 'META-INF/*.DSA', 'META-INF/*.RSA', 'META-INF/*.MF', 'META-INF/**', 'about.html', 'module-info.class' 36 | 37 | manifest { 38 | attributes 'Main-Class': mainClassName 39 | } 40 | 41 | from { 42 | configurations.runtimeClasspath.filter{ it.exists() }.collect { it.isDirectory() ? it : zipTree(it) } 43 | } 44 | 45 | destinationDirectory = file('release') 46 | archiveVersion = "v${release_version_major}" 47 | } 48 | 49 | task release(type: Zip, dependsOn: ['build']) { 50 | archiveBaseName ="${rootProject.name}_v${release_version_major}.${release_version_minor}" 51 | destinationDirectory = file("${projectDir}") 52 | from rootProject.rootDir 53 | include '*' 54 | include 'gradle/**' 55 | include 'image/**' 56 | include 'libs/**' 57 | include 'src/**' 58 | include 'release/**' 59 | exclude 'build' 60 | exclude '.git' 61 | exclude '.gradle' 62 | exclude '*.zip' 63 | } 64 | 65 | dependencies { 66 | // https://github.com/raise-isayan/BurpExtensionCommons 67 | implementation fileTree(dir: 'libs', include: ['*.jar']) 68 | // https://mvnrepository.com/artifact/com.google.code.gson/gson 69 | implementation 'com.google.code.gson:gson:2.9.0' 70 | // https://mvnrepository.com/artifact/com.googlecode.juniversalchardet/juniversalchardet 71 | implementation 'com.googlecode.juniversalchardet:juniversalchardet:1.0.3' 72 | 73 | // https://mvnrepository.com/artifact/net.portswigger.burp.extender/burp-extender-api 74 | compileOnly 'net.portswigger.burp.extender:burp-extender-api:2.3' 75 | // https://mvnrepository.com/artifact/org.eclipse.jetty/jetty-server 76 | implementation 'org.eclipse.jetty:jetty-server:9.4.48.v20220622' 77 | // https://mvnrepository.com/artifact/org.bouncycastle/bcpkix-jdk15on 78 | implementation 'org.bouncycastle:bcpkix-jdk15on:1.64' 79 | // https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on 80 | implementation 'org.bouncycastle:bcprov-jdk15on:1.64' 81 | 82 | // UnitTest 83 | testImplementation fileTree(dir: 'libs', include: ['*.jar']) 84 | // https://mvnrepository.com/artifact/net.portswigger.burp.extender/burp-extender-api 85 | testImplementation 'net.portswigger.burp.extender:burp-extender-api:2.3' 86 | // https://mvnrepository.com/artifact/org.eclipse.jetty/jetty-client 87 | testImplementation 'org.eclipse.jetty:jetty-client:9.4.48.v20220622' 88 | testImplementation 'junit:junit:4.13.2' 89 | 90 | } 91 | -------------------------------------------------------------------------------- /SimpleOCSPServer/gradle.properties: -------------------------------------------------------------------------------- 1 | netbeans.org-netbeans-modules-javascript2-requirejs.enabled=true 2 | release_version_major=2.2 3 | release_version_minor=0.0 4 | -------------------------------------------------------------------------------- /SimpleOCSPServer/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raise-isayan/FakeCert/5d180b60628fcd5f367822bddb6198e79338301e/SimpleOCSPServer/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /SimpleOCSPServer/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /SimpleOCSPServer/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /SimpleOCSPServer/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /SimpleOCSPServer/libs/BurpExtensionCommons-v0.5.3.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raise-isayan/FakeCert/5d180b60628fcd5f367822bddb6198e79338301e/SimpleOCSPServer/libs/BurpExtensionCommons-v0.5.3.0.jar -------------------------------------------------------------------------------- /SimpleOCSPServer/release/SimpleOCSPServer-v2.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raise-isayan/FakeCert/5d180b60628fcd5f367822bddb6198e79338301e/SimpleOCSPServer/release/SimpleOCSPServer-v2.2.jar -------------------------------------------------------------------------------- /SimpleOCSPServer/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'SimpleOCSPServer' 2 | -------------------------------------------------------------------------------- /SimpleOCSPServer/src/main/java/burp/BurpExtender.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | 3 | import extension.burp.BurpExtenderImpl; 4 | import extension.helpers.ConvertUtil; 5 | import extension.helpers.json.JsonUtil; 6 | import java.beans.PropertyChangeEvent; 7 | import java.beans.PropertyChangeListener; 8 | import java.io.IOException; 9 | import java.util.Map; 10 | import java.util.logging.Level; 11 | import java.util.logging.Logger; 12 | import server.ocsp.OCSPServerTab; 13 | import server.ocsp.OptionProperty; 14 | 15 | /** 16 | * 17 | * @author isayan 18 | */ 19 | public class BurpExtender extends BurpExtenderImpl { 20 | private final static Logger logger = Logger.getLogger(BurpExtender.class.getName()); 21 | 22 | public BurpExtender() { 23 | } 24 | 25 | private final java.util.ResourceBundle BUNDLE = java.util.ResourceBundle.getBundle("burp/resources/release"); 26 | 27 | @SuppressWarnings("unchecked") 28 | public static BurpExtender getInstance() { 29 | return BurpExtenderImpl.getInstance(); 30 | } 31 | 32 | private OCSPServerTab ocspTab; 33 | 34 | @Override 35 | public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks) { 36 | super.registerExtenderCallbacks(callbacks); 37 | callbacks.setExtensionName(String.format("%s", BUNDLE.getString("projname"))); 38 | 39 | // 設定ファイル読み込み 40 | Map settings = this.option.loadConfigSetting(); 41 | String configJSON = getCallbacks().loadExtensionSetting("configJSON"); 42 | if (configJSON != null) { 43 | settings = jsonStringToMap(ConvertUtil.decompressZlibBase64(configJSON)); 44 | } 45 | this.ocspTab = new OCSPServerTab(); 46 | String settingValue = settings.getOrDefault(this.ocspTab.getSettingName(), this.ocspTab.defaultSetting()); 47 | this.ocspTab.saveSetting(settingValue); 48 | this.ocspTab.addPropertyChangeListener(newPropertyChangeListener()); 49 | callbacks.addSuiteTab(this.ocspTab); 50 | callbacks.registerExtensionStateListener(this.ocspTab); 51 | } 52 | 53 | public PropertyChangeListener newPropertyChangeListener() { 54 | final Map settings = this.option.loadConfigSetting(); 55 | return new PropertyChangeListener() { 56 | @Override 57 | public void propertyChange(PropertyChangeEvent evt) { 58 | if (OptionProperty.OCSP_PROPERTY.equals(evt.getPropertyName())) { 59 | String settingValue = ocspTab.loadSetting(); 60 | settings.put(ocspTab.getSettingName(), settingValue); 61 | applyOptionProperty(); 62 | } 63 | } 64 | }; 65 | } 66 | 67 | private final OptionProperty option = new OptionProperty(); 68 | 69 | public OptionProperty getProperty() { 70 | return this.option; 71 | } 72 | 73 | 74 | protected void applyOptionProperty() { 75 | try { 76 | final Map settings = this.option.loadConfigSetting(); 77 | String configJSON = mapToJsonString(settings); 78 | getCallbacks().saveExtensionSetting("configJSON", ConvertUtil.compressZlibBase64(configJSON)); 79 | } catch (Exception ex) { 80 | logger.log(Level.SEVERE, ex.getMessage(), ex); 81 | } 82 | } 83 | 84 | public String mapToJsonString(Map settings) { 85 | return JsonUtil.jsonToString(settings, true); 86 | } 87 | 88 | public Map jsonStringToMap(String json) { 89 | final Map settings = JsonUtil.jsonFromString(json, Map.class, true); 90 | return settings; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /SimpleOCSPServer/src/main/java/server/ocsp/OCSPProperty.java: -------------------------------------------------------------------------------- 1 | package server.ocsp; 2 | 3 | import com.google.gson.annotations.Expose; 4 | import extension.burp.IPropertyConfig; 5 | import extension.helpers.json.JsonUtil; 6 | import java.io.File; 7 | 8 | /** 9 | * 10 | * @author isayan 11 | */ 12 | public class OCSPProperty { 13 | 14 | @Expose 15 | private int listenPort = 8888; 16 | 17 | /** 18 | * @return the listenPort 19 | */ 20 | public int getListenPort() { 21 | return this.listenPort; 22 | } 23 | 24 | /** 25 | * @param listenPort the listenPort to set 26 | */ 27 | public void setListenPort(int listenPort) { 28 | this.listenPort = listenPort; 29 | } 30 | 31 | public static enum CACertificateType { 32 | BurpCA, CustomCA 33 | }; 34 | 35 | @Expose 36 | private CACertificateType caCertificateType = CACertificateType.BurpCA; 37 | 38 | /** 39 | * @return the caCACertificateType 40 | */ 41 | public CACertificateType getCACertificateType() { 42 | return this.caCertificateType; 43 | } 44 | 45 | /** 46 | * @param caCertificateType the caCertificateType to set 47 | */ 48 | public void setCACertificateType(CACertificateType caCertificateType) { 49 | this.caCertificateType = caCertificateType; 50 | } 51 | 52 | @Expose 53 | private File caFile = null; 54 | 55 | /** 56 | * @return the caFile 57 | */ 58 | public File getCaFile() { 59 | return this.caFile; 60 | } 61 | 62 | /** 63 | * @param caFile the caFile to set 64 | */ 65 | public void setCaFile(File caFile) { 66 | this.caFile = caFile; 67 | } 68 | 69 | @Expose 70 | private String password = ""; 71 | 72 | /** 73 | * @return the password 74 | */ 75 | public String getPassword() { 76 | return this.password; 77 | } 78 | 79 | /** 80 | * @param password the password to set 81 | */ 82 | public void setPassword(String password) { 83 | this.password = password; 84 | } 85 | 86 | @Expose 87 | private boolean autoStart = false; 88 | 89 | /** 90 | * @return the autoStart 91 | */ 92 | public boolean isAutoStart() { 93 | return this.autoStart; 94 | } 95 | 96 | /** 97 | * @param autoStart the autoStart to set 98 | */ 99 | public void setAutoStart(boolean autoStart) { 100 | this.autoStart = autoStart; 101 | } 102 | 103 | public void setProperty(OCSPProperty property) { 104 | this.listenPort = property.listenPort; 105 | this.caCertificateType = property.caCertificateType; 106 | this.caFile = property.caFile; 107 | this.password = property.password; 108 | this.autoStart = property.autoStart; 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /SimpleOCSPServer/src/main/java/server/ocsp/OCSPServerTab.form: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 |
264 | -------------------------------------------------------------------------------- /SimpleOCSPServer/src/main/java/server/ocsp/OCSPServerTab.java: -------------------------------------------------------------------------------- 1 | package server.ocsp; 2 | 3 | import burp.BurpExtender; 4 | import burp.IExtensionStateListener; 5 | import burp.ITab; 6 | import extension.burp.IPropertyConfig; 7 | import extension.helpers.CertUtil; 8 | import extension.helpers.StringUtil; 9 | import extension.helpers.SwingUtil; 10 | import extension.helpers.json.JsonUtil; 11 | import java.awt.Component; 12 | import java.awt.TrayIcon; 13 | import java.io.ByteArrayInputStream; 14 | import java.io.File; 15 | import java.io.FileInputStream; 16 | import java.io.FileNotFoundException; 17 | import java.io.IOException; 18 | import java.lang.Thread.UncaughtExceptionHandler; 19 | import java.net.BindException; 20 | import java.security.KeyStore; 21 | import java.security.KeyStoreException; 22 | import java.security.NoSuchAlgorithmException; 23 | import java.security.PrivateKey; 24 | import java.security.UnrecoverableKeyException; 25 | import java.security.cert.CertificateException; 26 | import java.security.cert.X509Certificate; 27 | import java.util.Base64; 28 | import java.util.logging.Level; 29 | import java.util.logging.Logger; 30 | import java.util.prefs.Preferences; 31 | import javax.swing.JFileChooser; 32 | import javax.swing.JOptionPane; 33 | import javax.swing.filechooser.FileFilter; 34 | import javax.swing.filechooser.FileNameExtensionFilter; 35 | import server.ocsp.OCSPProperty.CACertificateType; 36 | 37 | /** 38 | * 39 | * @author isayan 40 | */ 41 | public class OCSPServerTab extends javax.swing.JPanel 42 | implements ITab, IPropertyConfig, IExtensionStateListener, UncaughtExceptionHandler { 43 | private final static Logger logger = Logger.getLogger(OCSPServerTab.class.getName()); 44 | 45 | /** 46 | * Creates new form OCSPServerTab 47 | */ 48 | public OCSPServerTab() { 49 | initComponents(); 50 | customizeComponents(); 51 | } 52 | 53 | /** 54 | * This method is called from within the constructor to initialize the form. WARNING: Do NOT modify this code. The 55 | * content of this method is always regenerated by the Form Editor. 56 | */ 57 | @SuppressWarnings("unchecked") 58 | // //GEN-BEGIN:initComponents 59 | private void initComponents() { 60 | 61 | btnGrpCACertificate = new javax.swing.ButtonGroup(); 62 | btnServerStart = new javax.swing.JToggleButton(); 63 | chkAutoStart = new javax.swing.JCheckBox(); 64 | lblListenPort = new javax.swing.JLabel(); 65 | spnListenPort = new javax.swing.JSpinner(); 66 | pnlCaCertificate = new javax.swing.JPanel(); 67 | rdoBurpCA = new javax.swing.JRadioButton(); 68 | rdoCustomCA = new javax.swing.JRadioButton(); 69 | pnlCustomCA = new javax.swing.JPanel(); 70 | txtCAFile = new javax.swing.JTextField(); 71 | btnSelectCustomCA = new javax.swing.JButton(); 72 | txtCApassword = new javax.swing.JPasswordField(); 73 | chkNonMaskPassword = new javax.swing.JCheckBox(); 74 | lblPassword = new javax.swing.JLabel(); 75 | 76 | btnServerStart.setText("Start"); 77 | btnServerStart.addActionListener(new java.awt.event.ActionListener() { 78 | public void actionPerformed(java.awt.event.ActionEvent evt) { 79 | btnServerStartActionPerformed(evt); 80 | } 81 | }); 82 | 83 | java.util.ResourceBundle bundle = java.util.ResourceBundle.getBundle("server/ocsp/resources/Resource"); // NOI18N 84 | chkAutoStart.setText(bundle.getString("server.ocsp.tab.auto_start")); // NOI18N 85 | chkAutoStart.addChangeListener(new javax.swing.event.ChangeListener() { 86 | public void stateChanged(javax.swing.event.ChangeEvent evt) { 87 | chkAutoStartStateChanged(evt); 88 | } 89 | }); 90 | 91 | lblListenPort.setText(bundle.getString("server.ocsp.tab.listen_port")); // NOI18N 92 | 93 | spnListenPort.setModel(new javax.swing.SpinnerNumberModel(8888, 1024, 65535, 1)); 94 | spnListenPort.setEditor(new javax.swing.JSpinner.NumberEditor(spnListenPort, "#0")); 95 | spnListenPort.addChangeListener(new javax.swing.event.ChangeListener() { 96 | public void stateChanged(javax.swing.event.ChangeEvent evt) { 97 | spnListenPortStateChanged(evt); 98 | } 99 | }); 100 | 101 | pnlCaCertificate.setBorder(javax.swing.BorderFactory.createTitledBorder("CA Certificate")); 102 | 103 | btnGrpCACertificate.add(rdoBurpCA); 104 | rdoBurpCA.setSelected(true); 105 | rdoBurpCA.setText(bundle.getString("server.ocsp.tab.dafault_CA")); // NOI18N 106 | rdoBurpCA.addChangeListener(new javax.swing.event.ChangeListener() { 107 | public void stateChanged(javax.swing.event.ChangeEvent evt) { 108 | rdoBurpCAStateChanged(evt); 109 | } 110 | }); 111 | 112 | btnGrpCACertificate.add(rdoCustomCA); 113 | rdoCustomCA.setText(bundle.getString("server.ocsp.tab.custom_CA")); // NOI18N 114 | rdoCustomCA.addChangeListener(new javax.swing.event.ChangeListener() { 115 | public void stateChanged(javax.swing.event.ChangeEvent evt) { 116 | rdoCustomCAStateChanged(evt); 117 | } 118 | }); 119 | 120 | btnSelectCustomCA.setIcon(new javax.swing.ImageIcon(getClass().getResource("/server/ocsp/resources/folder_image.png"))); // NOI18N 121 | btnSelectCustomCA.addActionListener(new java.awt.event.ActionListener() { 122 | public void actionPerformed(java.awt.event.ActionEvent evt) { 123 | btnSelectCustomCAActionPerformed(evt); 124 | } 125 | }); 126 | 127 | txtCApassword.setToolTipText(""); 128 | 129 | chkNonMaskPassword.setText(bundle.getString("server.ocsp.tab.nonmask_password")); // NOI18N 130 | chkNonMaskPassword.addChangeListener(new javax.swing.event.ChangeListener() { 131 | public void stateChanged(javax.swing.event.ChangeEvent evt) { 132 | chkNonMaskPasswordStateChanged(evt); 133 | } 134 | }); 135 | 136 | lblPassword.setText(bundle.getString("server.ocsp.tab.password")); // NOI18N 137 | 138 | javax.swing.GroupLayout pnlCustomCALayout = new javax.swing.GroupLayout(pnlCustomCA); 139 | pnlCustomCA.setLayout(pnlCustomCALayout); 140 | pnlCustomCALayout.setHorizontalGroup( 141 | pnlCustomCALayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 142 | .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, pnlCustomCALayout.createSequentialGroup() 143 | .addGroup(pnlCustomCALayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) 144 | .addGroup(pnlCustomCALayout.createSequentialGroup() 145 | .addGap(17, 17, 17) 146 | .addComponent(txtCAFile)) 147 | .addGroup(pnlCustomCALayout.createSequentialGroup() 148 | .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) 149 | .addComponent(lblPassword) 150 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) 151 | .addGroup(pnlCustomCALayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 152 | .addComponent(chkNonMaskPassword) 153 | .addComponent(txtCApassword, javax.swing.GroupLayout.PREFERRED_SIZE, 289, javax.swing.GroupLayout.PREFERRED_SIZE)))) 154 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) 155 | .addComponent(btnSelectCustomCA) 156 | .addGap(66, 66, 66)) 157 | ); 158 | pnlCustomCALayout.setVerticalGroup( 159 | pnlCustomCALayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 160 | .addGroup(pnlCustomCALayout.createSequentialGroup() 161 | .addContainerGap() 162 | .addGroup(pnlCustomCALayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 163 | .addComponent(txtCAFile, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) 164 | .addComponent(btnSelectCustomCA)) 165 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) 166 | .addGroup(pnlCustomCALayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) 167 | .addComponent(txtCApassword, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) 168 | .addComponent(lblPassword)) 169 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) 170 | .addComponent(chkNonMaskPassword) 171 | .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) 172 | ); 173 | 174 | javax.swing.GroupLayout pnlCaCertificateLayout = new javax.swing.GroupLayout(pnlCaCertificate); 175 | pnlCaCertificate.setLayout(pnlCaCertificateLayout); 176 | pnlCaCertificateLayout.setHorizontalGroup( 177 | pnlCaCertificateLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 178 | .addGroup(pnlCaCertificateLayout.createSequentialGroup() 179 | .addContainerGap() 180 | .addGroup(pnlCaCertificateLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 181 | .addComponent(pnlCustomCA, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) 182 | .addGroup(pnlCaCertificateLayout.createSequentialGroup() 183 | .addComponent(rdoBurpCA, javax.swing.GroupLayout.PREFERRED_SIZE, 153, javax.swing.GroupLayout.PREFERRED_SIZE) 184 | .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) 185 | .addGroup(pnlCaCertificateLayout.createSequentialGroup() 186 | .addComponent(rdoCustomCA, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) 187 | .addGap(273, 273, 273)))) 188 | ); 189 | pnlCaCertificateLayout.setVerticalGroup( 190 | pnlCaCertificateLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 191 | .addGroup(pnlCaCertificateLayout.createSequentialGroup() 192 | .addContainerGap() 193 | .addComponent(rdoBurpCA) 194 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) 195 | .addComponent(rdoCustomCA) 196 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) 197 | .addComponent(pnlCustomCA, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) 198 | .addContainerGap(52, Short.MAX_VALUE)) 199 | ); 200 | 201 | javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); 202 | this.setLayout(layout); 203 | layout.setHorizontalGroup( 204 | layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 205 | .addGroup(layout.createSequentialGroup() 206 | .addContainerGap() 207 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 208 | .addComponent(pnlCaCertificate, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) 209 | .addGroup(layout.createSequentialGroup() 210 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 211 | .addGroup(layout.createSequentialGroup() 212 | .addComponent(lblListenPort, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE) 213 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) 214 | .addComponent(spnListenPort, javax.swing.GroupLayout.PREFERRED_SIZE, 88, javax.swing.GroupLayout.PREFERRED_SIZE)) 215 | .addGroup(layout.createSequentialGroup() 216 | .addComponent(btnServerStart, javax.swing.GroupLayout.PREFERRED_SIZE, 67, javax.swing.GroupLayout.PREFERRED_SIZE) 217 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) 218 | .addComponent(chkAutoStart))) 219 | .addGap(0, 342, Short.MAX_VALUE))) 220 | .addContainerGap()) 221 | ); 222 | layout.setVerticalGroup( 223 | layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 224 | .addGroup(layout.createSequentialGroup() 225 | .addContainerGap() 226 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 227 | .addComponent(chkAutoStart) 228 | .addComponent(btnServerStart)) 229 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) 230 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) 231 | .addComponent(lblListenPort) 232 | .addComponent(spnListenPort, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) 233 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) 234 | .addComponent(pnlCaCertificate, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) 235 | .addContainerGap()) 236 | ); 237 | }// //GEN-END:initComponents 238 | 239 | private java.util.ResourceBundle bundle = java.util.ResourceBundle.getBundle("server/ocsp/resources/Resource"); // NOI18N 240 | 241 | public KeyStore loadKeyStore() throws IOException, KeyStoreException { 242 | try { 243 | final KeyStore ks = KeyStore.getInstance("PKCS12"); 244 | String password = getCAPassword(); 245 | if (this.rdoBurpCA.isSelected()) { 246 | Preferences prefs = Preferences.userNodeForPackage(burp.IBurpExtender.class); 247 | byte[] caCartByte = Base64.getDecoder().decode(prefs.get("caCert", "")); 248 | ks.load(new ByteArrayInputStream(caCartByte), password.toCharArray()); 249 | return ks; 250 | } else { 251 | File pkcs_ca = new File(this.txtCAFile.getText()); 252 | ks.load(new FileInputStream(pkcs_ca), password.toCharArray()); 253 | return ks; 254 | } 255 | } catch (NoSuchAlgorithmException | CertificateException ex) { 256 | throw new IOException(ex); 257 | } 258 | } 259 | 260 | public String getCAPassword() { 261 | if (this.rdoBurpCA.isSelected()) { 262 | return "/burp/media/ps.p12"; 263 | } else { 264 | return String.valueOf(this.txtCApassword.getPassword()); 265 | } 266 | } 267 | 268 | @Override 269 | public void uncaughtException(Thread t, Throwable e) { 270 | if (e instanceof BindException) { 271 | this.btnServerStart.setSelected(false); 272 | JOptionPane.showMessageDialog(this, e.getMessage(), getTabCaption(), JOptionPane.ERROR_MESSAGE); 273 | BurpExtender.issueAlert(getTabCaption(), e.getMessage(), TrayIcon.MessageType.ERROR); 274 | } else { 275 | BurpExtender.issueAlert(getTabCaption(), e.getMessage(), TrayIcon.MessageType.ERROR); 276 | } 277 | 278 | } 279 | 280 | // private SimpleOCSPServer.ThreadWrap thredServer = null; 281 | private SimpleJettyServer thredServer = null; 282 | 283 | private void btnServerStartActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnServerStartActionPerformed 284 | if (this.btnServerStart.isSelected()) { 285 | if (this.thredServer == null || (this.thredServer != null && !this.thredServer.isRunning())) { 286 | try { 287 | KeyStore ks = this.loadKeyStore(); 288 | String password = this.getCAPassword(); 289 | String alias = CertUtil.getFirstAlias(ks); 290 | PrivateKey issuerPrivateKey = (PrivateKey) ks.getKey(alias, password.toCharArray()); 291 | X509Certificate issuerCert = (X509Certificate) ks.getCertificate(alias); 292 | this.thredServer = new SimpleJettyServer(); 293 | this.thredServer.startServer(issuerPrivateKey, issuerCert, (int) this.spnListenPort.getValue()); 294 | } catch (FileNotFoundException ex) { 295 | JOptionPane.showMessageDialog(this, "File not found:" + this.txtCAFile.getText(), getTabCaption(), JOptionPane.ERROR_MESSAGE); 296 | BurpExtender.issueAlert(getTabCaption(), StringUtil.getStackTraceMessage(ex), TrayIcon.MessageType.ERROR); 297 | this.btnServerStart.setSelected(false); 298 | } catch (IOException ex) { 299 | JOptionPane.showMessageDialog(this, ex.getMessage(), getTabCaption(), JOptionPane.ERROR_MESSAGE); 300 | BurpExtender.issueAlert(getTabCaption(), StringUtil.getStackTraceMessage(ex), TrayIcon.MessageType.ERROR); 301 | this.btnServerStart.setSelected(false); 302 | logger.log(Level.SEVERE, ex.getMessage(), ex); 303 | } catch (KeyStoreException ex) { 304 | JOptionPane.showMessageDialog(this, ex.getMessage(), getTabCaption(), JOptionPane.ERROR_MESSAGE); 305 | BurpExtender.issueAlert(getTabCaption(), StringUtil.getStackTraceMessage(ex), TrayIcon.MessageType.ERROR); 306 | this.btnServerStart.setSelected(false); 307 | logger.log(Level.SEVERE, ex.getMessage(), ex); 308 | } catch (NoSuchAlgorithmException ex) { 309 | JOptionPane.showMessageDialog(this, ex.getMessage(), getTabCaption(), JOptionPane.ERROR_MESSAGE); 310 | BurpExtender.issueAlert(getTabCaption(), StringUtil.getStackTraceMessage(ex), TrayIcon.MessageType.ERROR); 311 | this.btnServerStart.setSelected(false); 312 | logger.log(Level.SEVERE, ex.getMessage(), ex); 313 | } catch (UnrecoverableKeyException ex) { 314 | JOptionPane.showMessageDialog(this, ex.getMessage(), getTabCaption(), JOptionPane.ERROR_MESSAGE); 315 | BurpExtender.issueAlert(getTabCaption(), StringUtil.getStackTraceMessage(ex), TrayIcon.MessageType.ERROR); 316 | this.btnServerStart.setSelected(false); 317 | logger.log(Level.SEVERE, ex.getMessage(), ex); 318 | } catch (Exception ex) { 319 | JOptionPane.showMessageDialog(this, ex.getMessage(), getTabCaption(), JOptionPane.ERROR_MESSAGE); 320 | BurpExtender.issueAlert(getTabCaption(), StringUtil.getStackTraceMessage(ex), TrayIcon.MessageType.ERROR); 321 | this.btnServerStart.setSelected(false); 322 | logger.log(Level.SEVERE, ex.getMessage(), ex); 323 | } 324 | } 325 | } else { 326 | this.stopThreadServer(); 327 | } 328 | // Enable 329 | if (this.btnServerStart.isSelected()) { 330 | this.btnServerStart.setText(bundle.getString("server.ocsp.tab.stop")); 331 | this.spnListenPort.setEnabled(false); 332 | this.chkAutoStart.setEnabled(false); 333 | SwingUtil.setContainerEnable(this.pnlCaCertificate, false); 334 | SwingUtil.setContainerEnable(this.pnlCustomCA, this.rdoCustomCA.isSelected()); 335 | } else { 336 | this.btnServerStart.setText(bundle.getString("server.ocsp.tab.start")); 337 | this.spnListenPort.setEnabled(true); 338 | this.chkAutoStart.setEnabled(true); 339 | SwingUtil.setContainerEnable(this.pnlCaCertificate, true); 340 | SwingUtil.setContainerEnable(this.pnlCustomCA, this.rdoCustomCA.isSelected()); 341 | } 342 | }//GEN-LAST:event_btnServerStartActionPerformed 343 | 344 | private void spnListenPortStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_spnListenPortStateChanged 345 | this.firePropertyChange(OptionProperty.OCSP_PROPERTY, null, this.getOCSPProperty()); 346 | }//GEN-LAST:event_spnListenPortStateChanged 347 | 348 | private void rdoBurpCAStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_rdoBurpCAStateChanged 349 | this.firePropertyChange(OptionProperty.OCSP_PROPERTY, null, this.getOCSPProperty()); 350 | }//GEN-LAST:event_rdoBurpCAStateChanged 351 | 352 | private void rdoCustomCAStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_rdoCustomCAStateChanged 353 | SwingUtil.setContainerEnable(this.pnlCustomCA, this.rdoCustomCA.isSelected()); 354 | this.firePropertyChange(OptionProperty.OCSP_PROPERTY, null, this.getOCSPProperty()); 355 | }//GEN-LAST:event_rdoCustomCAStateChanged 356 | 357 | private void chkAutoStartStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_chkAutoStartStateChanged 358 | this.firePropertyChange(OptionProperty.OCSP_PROPERTY, null, this.getOCSPProperty()); 359 | }//GEN-LAST:event_chkAutoStartStateChanged 360 | 361 | private final static FileFilter FILTER_PKCS12 = new FileNameExtensionFilter("certificate file(*.p12;*.pfx)", "p12", "pfx"); 362 | 363 | private void btnSelectCustomCAActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnSelectCustomCAActionPerformed 364 | JFileChooser filechooser = new JFileChooser(); 365 | filechooser.setFileSelectionMode(JFileChooser.FILES_ONLY); 366 | filechooser.addChoosableFileFilter(FILTER_PKCS12); 367 | filechooser.setFileFilter(FILTER_PKCS12); 368 | filechooser.setSelectedFile(new File(this.txtCAFile.getText())); 369 | int selected = filechooser.showOpenDialog(this); 370 | if (selected == JFileChooser.APPROVE_OPTION) { 371 | File file = filechooser.getSelectedFile(); 372 | this.txtCAFile.setText(file.getAbsolutePath()); 373 | } 374 | }//GEN-LAST:event_btnSelectCustomCAActionPerformed 375 | 376 | private void chkNonMaskPasswordStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_chkNonMaskPasswordStateChanged 377 | if (this.chkNonMaskPassword.isSelected()) { 378 | this.txtCApassword.setEchoChar((char) 0); 379 | } else { 380 | this.txtCApassword.setEchoChar('*'); 381 | } 382 | }//GEN-LAST:event_chkNonMaskPasswordStateChanged 383 | 384 | // Variables declaration - do not modify//GEN-BEGIN:variables 385 | private javax.swing.ButtonGroup btnGrpCACertificate; 386 | private javax.swing.JButton btnSelectCustomCA; 387 | private javax.swing.JToggleButton btnServerStart; 388 | private javax.swing.JCheckBox chkAutoStart; 389 | private javax.swing.JCheckBox chkNonMaskPassword; 390 | private javax.swing.JLabel lblListenPort; 391 | private javax.swing.JLabel lblPassword; 392 | private javax.swing.JPanel pnlCaCertificate; 393 | private javax.swing.JPanel pnlCustomCA; 394 | private javax.swing.JRadioButton rdoBurpCA; 395 | private javax.swing.JRadioButton rdoCustomCA; 396 | private javax.swing.JSpinner spnListenPort; 397 | private javax.swing.JTextField txtCAFile; 398 | private javax.swing.JPasswordField txtCApassword; 399 | // End of variables declaration//GEN-END:variables 400 | 401 | @Override 402 | public String getTabCaption() { 403 | return "OCSP Server"; 404 | } 405 | 406 | @Override 407 | public Component getUiComponent() { 408 | return this; 409 | } 410 | 411 | private void customizeComponents() { 412 | this.btnServerStart.setText(bundle.getString("server.ocsp.tab.start")); 413 | if (this.isAutoStart()) { 414 | this.btnServerStart.doClick(); 415 | } 416 | SwingUtil.setContainerEnable(this.pnlCustomCA, this.rdoCustomCA.isSelected()); 417 | } 418 | 419 | public void setOCSPProperty(OCSPProperty ocspProperty) { 420 | this.chkAutoStart.setSelected(ocspProperty.isAutoStart()); 421 | this.spnListenPort.setValue(ocspProperty.getListenPort()); 422 | if (ocspProperty.getCACertificateType() == CACertificateType.BurpCA) { 423 | this.rdoBurpCA.setSelected(true); 424 | } else { 425 | this.rdoCustomCA.setSelected(true); 426 | this.txtCAFile.setText(String.valueOf(ocspProperty.getCaFile())); 427 | this.txtCApassword.setText(ocspProperty.getPassword()); 428 | } 429 | } 430 | 431 | public OCSPProperty getOCSPProperty() { 432 | final OCSPProperty ocspProperty = new OCSPProperty(); 433 | ocspProperty.setAutoStart(this.chkAutoStart.isSelected()); 434 | ocspProperty.setListenPort((int) this.spnListenPort.getValue()); 435 | if (this.rdoBurpCA.isSelected()) { 436 | ocspProperty.setCACertificateType(CACertificateType.BurpCA); 437 | } else { 438 | ocspProperty.setCACertificateType(CACertificateType.CustomCA); 439 | } 440 | ocspProperty.setCaFile(new File(this.txtCAFile.getText())); 441 | ocspProperty.setPassword(String.valueOf(this.txtCApassword.getPassword())); 442 | return ocspProperty; 443 | } 444 | 445 | private boolean isAutoStart() { 446 | return this.chkAutoStart.isSelected(); 447 | } 448 | 449 | protected void stopThreadServer() { 450 | if (this.thredServer != null) { 451 | this.thredServer.stopServer(); 452 | this.thredServer = null; 453 | } 454 | } 455 | 456 | @Override 457 | public void extensionUnloaded() { 458 | this.stopThreadServer(); 459 | } 460 | 461 | @Override 462 | public String getSettingName() { 463 | return this.getClass().getName(); 464 | } 465 | 466 | @Override 467 | public void saveSetting(String value) { 468 | OCSPProperty ocspProperty = JsonUtil.jsonFromString(value, OCSPProperty.class, true); 469 | this.setOCSPProperty(ocspProperty); 470 | } 471 | 472 | @Override 473 | public String loadSetting() { 474 | OCSPProperty ocspProperty = this.getOCSPProperty(); 475 | return JsonUtil.jsonToString(ocspProperty, true); 476 | } 477 | 478 | @Override 479 | public String defaultSetting() { 480 | OCSPProperty ocspProperty = new OCSPProperty(); 481 | return JsonUtil.jsonToString(ocspProperty, true); 482 | } 483 | 484 | } 485 | -------------------------------------------------------------------------------- /SimpleOCSPServer/src/main/java/server/ocsp/OCSPWrap.java: -------------------------------------------------------------------------------- 1 | package server.ocsp; 2 | 3 | import java.io.IOException; 4 | import java.io.UnsupportedEncodingException; 5 | import java.net.URLDecoder; 6 | import java.nio.charset.StandardCharsets; 7 | import java.security.PrivateKey; 8 | import java.security.Security; 9 | import java.security.cert.CertificateEncodingException; 10 | import java.security.cert.X509Certificate; 11 | import java.util.Date; 12 | import org.bouncycastle.cert.X509CertificateHolder; 13 | import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder; 14 | import org.bouncycastle.cert.ocsp.BasicOCSPResp; 15 | import org.bouncycastle.cert.ocsp.BasicOCSPRespBuilder; 16 | import org.bouncycastle.cert.ocsp.CertificateStatus; 17 | import org.bouncycastle.cert.ocsp.OCSPException; 18 | import org.bouncycastle.cert.ocsp.OCSPReq; 19 | import org.bouncycastle.cert.ocsp.OCSPResp; 20 | import org.bouncycastle.cert.ocsp.OCSPRespBuilder; 21 | import org.bouncycastle.cert.ocsp.Req; 22 | import org.bouncycastle.cert.ocsp.RespID; 23 | import org.bouncycastle.cert.ocsp.jcajce.JcaBasicOCSPRespBuilder; 24 | import org.bouncycastle.jce.provider.BouncyCastleProvider; 25 | import org.bouncycastle.operator.ContentSigner; 26 | import org.bouncycastle.operator.DigestCalculatorProvider; 27 | import org.bouncycastle.operator.OperatorCreationException; 28 | import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; 29 | import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; 30 | import org.bouncycastle.util.encoders.Base64; 31 | 32 | /** 33 | * 34 | * @author isayan 35 | */ 36 | public class OCSPWrap { 37 | 38 | static { 39 | if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) { 40 | Security.addProvider(new BouncyCastleProvider()); 41 | } 42 | } 43 | 44 | public OCSPWrap() { 45 | } 46 | 47 | public static byte [] decodeOCSPUrl(String path) { 48 | try { 49 | String url = URLDecoder.decode(path, StandardCharsets.ISO_8859_1.name()); 50 | byte [] b64 = Base64.decode(url); 51 | return b64; 52 | } catch (UnsupportedEncodingException ex) { 53 | 54 | } 55 | return new byte [] {}; 56 | } 57 | 58 | public static byte[] genOCSPRespEncoded(byte[] ocspRequest, PrivateKey issuerPrivateKey, X509Certificate issuerCert) throws IOException { 59 | try { 60 | OCSPResp resp = genOCSPResp(ocspRequest, issuerPrivateKey, issuerCert); 61 | return resp.getEncoded(); 62 | } catch (OCSPException ex) { 63 | throw new IOException(ex.getMessage(), ex); 64 | } 65 | } 66 | 67 | protected static OCSPResp genOCSPResp(byte [] ocspRequest, PrivateKey issuerPrivateKey, X509Certificate issuerCert) throws OCSPException, IOException { 68 | OCSPReq ocspReq = new OCSPReq(ocspRequest); 69 | Req[] reqIDList = ocspReq.getRequestList(); 70 | OCSPResp resp = genOCSPResp(reqIDList, issuerPrivateKey, issuerCert); 71 | return resp; 72 | } 73 | 74 | // final static AlgorithmIdentifier HASH_SHA256 = AlgorithmIdentifier.getInstance("2.16.840.1.101.3.4.2.1"); // SHA-256 75 | protected static OCSPResp genOCSPResp(Req[] reqIDList, PrivateKey issuerPrivateKey, X509Certificate issuerCert) throws OCSPException { 76 | OCSPResp ocspResp = null; 77 | try { 78 | X509CertificateHolder issuerCertHolder = new JcaX509CertificateHolder(issuerCert); 79 | DigestCalculatorProvider digCalcProv = new JcaDigestCalculatorProviderBuilder().setProvider(BouncyCastleProvider.PROVIDER_NAME).build(); 80 | 81 | X509CertificateHolder[] chain = new X509CertificateHolder[]{issuerCertHolder}; 82 | // RespID.HASH_SHA1 only 83 | BasicOCSPRespBuilder respGen = new JcaBasicOCSPRespBuilder(issuerCert.getPublicKey(), digCalcProv.get(RespID.HASH_SHA1)); 84 | 85 | for (Req reqID : reqIDList) { 86 | respGen.addResponse(reqID.getCertID(), CertificateStatus.GOOD); 87 | } 88 | 89 | ContentSigner signer = new JcaContentSignerBuilder("SHA256withRSA").setProvider(BouncyCastleProvider.PROVIDER_NAME).build(issuerPrivateKey); 90 | BasicOCSPResp resp = respGen.build(signer, chain, new Date()); 91 | OCSPRespBuilder rGen = new OCSPRespBuilder(); 92 | ocspResp = rGen.build(OCSPRespBuilder.SUCCESSFUL, resp); 93 | 94 | } catch (OperatorCreationException ex) { 95 | throw new OCSPException(ex.getMessage(), ex); 96 | } catch (CertificateEncodingException ex) { 97 | throw new OCSPException(ex.getMessage(), ex); 98 | } 99 | return ocspResp; 100 | } 101 | 102 | } -------------------------------------------------------------------------------- /SimpleOCSPServer/src/main/java/server/ocsp/OptionProperty.java: -------------------------------------------------------------------------------- 1 | package server.ocsp; 2 | 3 | import com.google.gson.annotations.Expose; 4 | import extension.burp.IOptionProperty; 5 | import extension.helpers.json.JsonUtil; 6 | import java.io.File; 7 | import java.io.IOException; 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | /** 12 | * 13 | * @author raise.isayan 14 | */ 15 | public final class OptionProperty implements IOptionProperty { 16 | public final static String OCSP_PROPERTY = "OCSPPropery"; 17 | 18 | private final Map config = new HashMap(); 19 | 20 | 21 | @Override 22 | public void saveConfigSetting(Map map) { 23 | this.config.putAll(map); 24 | } 25 | 26 | @Override 27 | public Map loadConfigSetting() { 28 | return this.config; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /SimpleOCSPServer/src/main/java/server/ocsp/SimpleJettyServer.java: -------------------------------------------------------------------------------- 1 | package server.ocsp; 2 | 3 | import extension.helpers.CertUtil; 4 | import extension.helpers.StringUtil; 5 | import java.io.ByteArrayOutputStream; 6 | import java.io.File; 7 | import java.io.FileInputStream; 8 | import java.io.IOException; 9 | import java.io.InputStream; 10 | import java.io.OutputStream; 11 | import java.io.PrintStream; 12 | import java.net.HttpURLConnection; 13 | import java.security.KeyStore; 14 | import java.security.PrivateKey; 15 | import java.security.cert.X509Certificate; 16 | import java.util.Enumeration; 17 | import java.util.Locale; 18 | import java.util.logging.Level; 19 | import java.util.logging.Logger; 20 | import javax.servlet.ServletException; 21 | import javax.servlet.http.HttpServletRequest; 22 | import javax.servlet.http.HttpServletResponse; 23 | import org.eclipse.jetty.server.Handler; 24 | import org.eclipse.jetty.server.Request; 25 | 26 | import org.eclipse.jetty.server.Server; 27 | import org.eclipse.jetty.server.ServerConnector; 28 | import org.eclipse.jetty.server.handler.AbstractHandler; 29 | import org.eclipse.jetty.server.handler.ContextHandler; 30 | import org.eclipse.jetty.server.handler.ContextHandlerCollection; 31 | 32 | /** 33 | * 34 | * @author isayan 35 | */ 36 | public class SimpleJettyServer { 37 | private final static Logger logger = Logger.getLogger(SimpleJettyServer.class.getName()); 38 | 39 | protected final static java.util.ResourceBundle BUNDLE = java.util.ResourceBundle.getBundle("server/ocsp/resources/Resource"); 40 | 41 | public SimpleJettyServer() { 42 | } 43 | 44 | private boolean debug = false; 45 | private Server server = null; 46 | 47 | /** 48 | * @return the debug 49 | */ 50 | public boolean isDebug() { 51 | return debug; 52 | } 53 | 54 | /** 55 | * @param debug the debug to set 56 | */ 57 | public void setDebug(boolean debug) { 58 | this.debug = debug; 59 | } 60 | 61 | 62 | public void startServer(PrivateKey issuerPrivateKey, X509Certificate issuerCert, int listenPort) throws Exception { 63 | System.out.println("start listen port:" + listenPort); 64 | this.server = createServer(listenPort); 65 | 66 | ContextHandler contextRoot = new ContextHandler("/"); 67 | contextRoot.setHandler(new MyHandler(issuerPrivateKey, issuerCert)); 68 | 69 | ContextHandlerCollection contexts = new ContextHandlerCollection(); 70 | contexts.setHandlers(new Handler[] { contextRoot }); 71 | 72 | server.setHandler(contexts); 73 | server.setDumpAfterStart(this.debug); 74 | server.start(); 75 | } 76 | 77 | public boolean isRunning() { 78 | return (this.server != null); 79 | } 80 | 81 | public void joinServer() { 82 | if (this.server != null) { 83 | try { 84 | this.server.join(); 85 | } catch (InterruptedException ex) { 86 | logger.log(Level.SEVERE, ex.getMessage(), ex); 87 | } 88 | } 89 | } 90 | 91 | 92 | public void stopServer() { 93 | if (this.server != null) { 94 | try { 95 | this.server.stop(); 96 | } catch (Exception ex) { 97 | logger.log(Level.SEVERE, ex.getMessage(), ex); 98 | } 99 | } 100 | this.server = null; 101 | } 102 | 103 | private static void usage() { 104 | System.out.println(""); 105 | System.out.println(String.format("Usage: java -jar %s.jar [option] <-cafile=> <-password=> [-port=]", SimpleJettyServer.class.getSimpleName())); 106 | System.out.println("[option]"); 107 | System.out.println("\t" + BUNDLE.getString("server.ocsp.main.arg.help")); 108 | System.out.println("\t" + BUNDLE.getString("server.ocsp.main.arg.version")); 109 | System.out.println("\t" + BUNDLE.getString("server.ocsp.main.arg.debug")); 110 | System.out.println("[command]"); 111 | System.out.println("\t" + BUNDLE.getString("server.ocsp.main.arg.cafile")); 112 | System.out.println("\t" + BUNDLE.getString("server.ocsp.main.arg.password")); 113 | System.out.println("\t" + BUNDLE.getString("server.ocsp.main.arg.alias")); 114 | System.out.println("\t" + BUNDLE.getString("server.ocsp.main.arg.port")); 115 | System.out.println(""); 116 | } 117 | 118 | class MyHandler extends AbstractHandler { 119 | 120 | private PrivateKey issuerPrivateKey = null; 121 | private X509Certificate issuerCert = null; 122 | 123 | public MyHandler(PrivateKey issuerPrivateKey, X509Certificate issuerCert) { 124 | this.issuerPrivateKey = issuerPrivateKey; 125 | this.issuerCert = issuerCert; 126 | } 127 | 128 | @Override 129 | public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { 130 | try { 131 | // RFC2560 132 | String method = baseRequest.getMethod(); 133 | if ("GET".equals(method)) { 134 | doGet(baseRequest, request, response); 135 | } else if ("POST".equals(method)) { 136 | doPost(baseRequest, request, response); 137 | } 138 | } catch (Exception ex) { 139 | doThrowable(response, ex); 140 | } 141 | } 142 | 143 | public void doGet(Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException { 144 | String uri = baseRequest.getRequestURI(); 145 | if (uri.equals("/")) { 146 | doSuccessResponse(response); 147 | } 148 | else { 149 | String[] paths = uri.split("/"); 150 | byte[] b64 = OCSPWrap.decodeOCSPUrl(paths[paths.length - 1]); 151 | doResponse(response, b64); 152 | } 153 | } 154 | 155 | public void doPost(Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException { 156 | try (ByteArrayOutputStream bostm = new ByteArrayOutputStream()) { 157 | byte buff[] = new byte[2048]; 158 | try (InputStream is = request.getInputStream()) { 159 | int len = 0; 160 | while ((len = is.read(buff)) > -1) { 161 | bostm.write(buff, 0, len); 162 | } 163 | } 164 | doResponse(response, bostm.toByteArray()); 165 | } 166 | } 167 | 168 | public void doSuccessResponse(HttpServletResponse response) throws IOException { 169 | response.setContentType("text/html"); 170 | response.setStatus(HttpURLConnection.HTTP_OK); 171 | try (PrintStream os = new PrintStream(response.getOutputStream())) { 172 | os.println("" + 173 | "

Success

" + 174 | "

Support RFC2560

" + 175 | "

GET" + 176 | "

GET {url}/{url-encoding of base-64 encoding of the DER encoding of the OCSPRequest}
" + 177 | "

POST" + 178 | "

POST {url}/ HTTP/1.1\n" +
179 |                             "Host: 192.0.2.11\n" +
180 |                             "Content-Length: ...\n" +
181 |                             "\n" +
182 |                             "{DER encoding of the OCSPRequest}
" + 183 | "" + 184 | "

GET/POST Response" + 185 | "

{CertStatus returns only Good}
" + 186 | ""); 187 | } 188 | } 189 | 190 | public void doResponse(HttpServletResponse response, byte[] ocspRequest) throws IOException { 191 | byte[] resp = OCSPWrap.genOCSPRespEncoded(ocspRequest, issuerPrivateKey, issuerCert); 192 | response.setContentType("application/ocsp-response"); 193 | response.setStatus(HttpURLConnection.HTTP_OK); 194 | response.setContentLength(resp.length); 195 | try (OutputStream os = response.getOutputStream()) { 196 | os.write(resp); 197 | } 198 | } 199 | 200 | public void doThrowable(HttpServletResponse response, Exception ex) throws IOException { 201 | StringBuilder errmsg = new StringBuilder(); 202 | errmsg.append(ex.getMessage()); 203 | errmsg.append(StringUtil.getStackTrace(ex)); 204 | response.setContentType("text/plain"); 205 | response.setStatus(HttpURLConnection.HTTP_INTERNAL_ERROR); 206 | response.setContentLength(errmsg.length()); 207 | try (PrintStream os = new PrintStream(response.getOutputStream())) { 208 | os.println(errmsg.toString()); 209 | } 210 | logger.log(Level.SEVERE, ex.getMessage(), ex); 211 | } 212 | 213 | } 214 | 215 | public static Server createServer(int port) { 216 | Server server = new Server(); 217 | ServerConnector connector = new ServerConnector(server); 218 | connector.setPort(port); 219 | server.addConnector(connector); 220 | return server; 221 | } 222 | 223 | private final static java.util.ResourceBundle RELEASE = java.util.ResourceBundle.getBundle("burp/resources/release"); 224 | 225 | private static String getVersion() { 226 | return RELEASE.getString("version"); 227 | } 228 | 229 | /** 230 | * @param args the command line arguments 231 | */ 232 | public static void main(String[] args) { 233 | int defaultPort = 8888; 234 | File pkcs_ca = null; 235 | String password = null; 236 | String alias = null; 237 | boolean debug = false; 238 | 239 | try { 240 | for (String arg : args) { 241 | // single parameter 242 | if ("-v".equals(arg)) { 243 | System.out.println("Version: " + getVersion()); 244 | System.out.println("Language: " + Locale.getDefault().getLanguage()); 245 | System.exit(0); 246 | } 247 | if ("-h".equals(arg)) { 248 | usage(); 249 | System.exit(0); 250 | } 251 | String[] param = arg.split("=", 2); 252 | if (param.length < 2) { 253 | // single parameter 254 | if ("-d".equals(arg)) { 255 | debug = true; 256 | } 257 | } 258 | else { 259 | // multi parameter 260 | if ("-cafile".equals(param[0])) { 261 | pkcs_ca = new File(param[1]); 262 | } else if ("-password".equals(param[0])) { 263 | password = param[1]; 264 | } else if ("-alias".equals(param[0])) { 265 | alias = param[1]; 266 | } else if ("-port".equals(param[0])) { 267 | defaultPort = Integer.parseInt(param[1]); 268 | } 269 | } 270 | } 271 | 272 | // 必須チェック 273 | if (pkcs_ca == null || password == null) { 274 | System.out.println("-cafile or -password argument err "); 275 | usage(); 276 | return; 277 | } 278 | 279 | // 存在チェック 280 | if (!(pkcs_ca.exists() && pkcs_ca.isFile())) { 281 | System.out.println("-cafile: File not found = " + pkcs_ca); 282 | return; 283 | } 284 | 285 | KeyStore ks = KeyStore.getInstance("PKCS12"); 286 | ks.load(new FileInputStream(pkcs_ca), password.toCharArray()); 287 | 288 | // ailias 存在確認 289 | if (alias != null && !ks.isKeyEntry(alias)) { 290 | System.out.println("-alias: alias of keystore entry not found = " + alias); 291 | System.out.println("alias included in certificate:"); 292 | Enumeration aliases = ks.aliases(); 293 | while (aliases.hasMoreElements()) { 294 | System.out.println(" " + aliases.nextElement()); 295 | } 296 | return; 297 | } 298 | // 最初にみつかったalias 299 | if (alias == null) { 300 | alias = CertUtil.getFirstAlias(ks); 301 | } 302 | 303 | PrivateKey issuerPrivateKey = (PrivateKey) ks.getKey(alias, password.toCharArray()); 304 | X509Certificate issuerCert = (X509Certificate) ks.getCertificate(alias); 305 | 306 | SimpleJettyServer server = new SimpleJettyServer(); 307 | server.setDebug(debug); 308 | server.startServer(issuerPrivateKey, issuerCert, defaultPort); 309 | server.joinServer(); 310 | 311 | } catch (Exception ex) { 312 | String errmsg = String.format("%s: %s", ex.getClass().getName(), ex.getMessage()); 313 | System.out.println(errmsg); 314 | logger.log(Level.SEVERE, ex.getMessage(), ex); 315 | usage(); 316 | } 317 | } 318 | 319 | } 320 | -------------------------------------------------------------------------------- /SimpleOCSPServer/src/main/java/server/ocsp/Version.java: -------------------------------------------------------------------------------- 1 | package server.ocsp; 2 | 3 | import extension.view.base.CustomVersion; 4 | 5 | 6 | /** 7 | * 8 | * @author isayan 9 | */ 10 | public final class Version extends CustomVersion { 11 | 12 | private final java.util.ResourceBundle BUNDLE = java.util.ResourceBundle.getBundle("burp/resources/release"); 13 | 14 | private Version() { 15 | String ver = BUNDLE.getString("version"); 16 | parseVersion(ver); 17 | } 18 | 19 | private static Version version = null; 20 | 21 | /** 22 | * Versionインスタンスの取得 23 | * 24 | * @return バージョン 25 | */ 26 | public static synchronized Version getInstance() { 27 | if (version == null) { 28 | version = new Version(); 29 | } 30 | return version; 31 | } 32 | 33 | public String getProjectName() { 34 | String projname = BUNDLE.getString("projname"); 35 | return projname; 36 | } 37 | 38 | private final static String VERSION_INFO_FMT = "Product Version: %s %s\n"; 39 | 40 | public String getVersionInfo() { 41 | return String.format(VERSION_INFO_FMT, 42 | getProjectName(), Version.getInstance().getVersion()); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /SimpleOCSPServer/src/main/resources/burp/resources/release.properties: -------------------------------------------------------------------------------- 1 | # SimpleOCSPServer build xml properties 2 | 3 | # name 4 | projname=${project.name} 5 | 6 | # version 7 | version=${release_version_major}.${release_version_minor} 8 | -------------------------------------------------------------------------------- /SimpleOCSPServer/src/main/resources/server/ocsp/resources/Resource.properties: -------------------------------------------------------------------------------- 1 | server.ocsp.main.arg.help=-h: output help. 2 | server.ocsp.main.arg.version=-v: output version. 3 | server.ocsp.main.arg.debug=-d: output debug log. 4 | server.ocsp.main.arg.cafile=-cafile: CA certificate including secret key (PKCS 12 format) 5 | server.ocsp.main.arg.password=-password: CA certificate password 6 | server.ocsp.main.arg.alias=-alias: Specify an alias for the certificate. It is optional. The default is the first certificate found. 7 | server.ocsp.main.arg.port=-port: It becomes a listen port. It is optional. The default is 8888. 8 | server.ocsp.tab.start=Start 9 | server.ocsp.tab.stop=Stop 10 | server.ocsp.tab.listen_port=Listen port: 11 | server.ocsp.tab.auto_start=Automatic start at loading 12 | server.ocsp.tab.nonmask_password=non mask password 13 | server.ocsp.tab.password=password: 14 | server.ocsp.tab.custom_CA=Use custom CA file (pkcs12) 15 | server.ocsp.tab.dafault_CA=Burp suite default CA 16 | -------------------------------------------------------------------------------- /SimpleOCSPServer/src/main/resources/server/ocsp/resources/Resource_ja.properties: -------------------------------------------------------------------------------- 1 | server.ocsp.main.arg.help=-h: \u30d8\u30eb\u30d7\u3092\u51fa\u529b\u3057\u307e\u3059\u3002 2 | server.ocsp.main.arg.version=-v: \u30d0\u30fc\u30b8\u30e7\u30f3\u3092\u51fa\u529b\u3057\u307e\u3059\u3002 3 | server.ocsp.main.arg.debug= -d: \u30c7\u30d0\u30c3\u30b0\u30ed\u30b0\u3092\u51fa\u529b\u3057\u307e\u3059\u3002 4 | server.ocsp.main.arg.cafile=-cafile: \u79d8\u5bc6\u9375\u3092\u542b\u3080CA\u8a3c\u660e\u66f8(PKCS12\u5f62\u5f0f) 5 | server.ocsp.main.arg.password=-password: CA\u8a3c\u660e\u66f8\u306e\u30d1\u30b9\u30ef\u30fc\u30c9 6 | server.ocsp.main.arg.alias=-alias: \u8a3c\u660e\u66f8\u306e\u30a8\u30a4\u30ea\u30a2\u30b9\u3092\u6307\u5b9a\u3002\u7701\u7565\u53ef\u80fd\u3067\u3059\u3002\u7701\u7565\u6642\u306f\u6700\u521d\u306b\u898b\u3064\u304b\u3063\u305f\u8a3c\u660e\u66f8\u3067\u3059\u3002 7 | server.ocsp.main.arg.port=-port: \u5f85\u3061\u53d7\u3051\u30dd\u30fc\u30c8\u306b\u306a\u308a\u307e\u3059\u3002\u7701\u7565\u53ef\u80fd\u3067\u3059\u3002\u7701\u7565\u6642\u306f8888\u3067\u3059\u3002 8 | server.ocsp.tab.start=\u958b\u59cb 9 | server.ocsp.tab.stop=\u505c\u6b62 10 | server.ocsp.tab.listen_port=\u5f85\u53d7\u3051\u30dd\u30fc\u30c8: 11 | server.ocsp.tab.auto_start=\u30ed\u30fc\u30c7\u30a3\u30f3\u30b0\u6642\u306e\u81ea\u52d5\u8d77\u52d5 12 | server.ocsp.tab.nonmask_password=\u975e\u30de\u30b9\u30af\u30d1\u30b9\u30ef\u30fc\u30c9 13 | server.ocsp.tab.password=\u30d1\u30b9\u30ef\u30fc\u30c9: 14 | server.ocsp.tab.custom_CA=\u30ab\u30b9\u30bf\u30e0 CA \u30d5\u30a1\u30a4\u30eb (pkcs12) 15 | server.ocsp.tab.dafault_CA=Burp suite \u30c7\u30d5\u30a9\u30eb\u30c8 CA 16 | -------------------------------------------------------------------------------- /SimpleOCSPServer/src/main/resources/server/ocsp/resources/folder_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raise-isayan/FakeCert/5d180b60628fcd5f367822bddb6198e79338301e/SimpleOCSPServer/src/main/resources/server/ocsp/resources/folder_image.png -------------------------------------------------------------------------------- /SimpleOCSPServer/src/test/java/server/ocsp/OCSPUtilTest.java: -------------------------------------------------------------------------------- 1 | package server.ocsp; 2 | 3 | import extension.helpers.CertUtil; 4 | import extension.helpers.FileUtil; 5 | import java.io.File; 6 | import java.io.FileInputStream; 7 | import java.math.BigInteger; 8 | import java.net.URLEncoder; 9 | import java.nio.charset.StandardCharsets; 10 | import java.security.KeyStore; 11 | import java.security.PrivateKey; 12 | import java.security.cert.X509Certificate; 13 | import java.util.logging.Logger; 14 | import org.bouncycastle.cert.ocsp.OCSPReq; 15 | import org.bouncycastle.cert.ocsp.OCSPResp; 16 | import org.bouncycastle.cert.ocsp.Req; 17 | import org.bouncycastle.util.encoders.Base64; 18 | import org.junit.After; 19 | import org.junit.AfterClass; 20 | import org.junit.Before; 21 | import org.junit.BeforeClass; 22 | import org.junit.Test; 23 | import static org.junit.Assert.*; 24 | 25 | /** 26 | * 27 | * @author isayan 28 | */ 29 | public class OCSPUtilTest { 30 | private final static Logger logger = Logger.getLogger(OCSPUtilTest.class.getName()); 31 | 32 | public OCSPUtilTest() { 33 | } 34 | 35 | @BeforeClass 36 | public static void setUpClass() { 37 | } 38 | 39 | @AfterClass 40 | public static void tearDownClass() { 41 | } 42 | 43 | @Before 44 | public void setUp() { 45 | } 46 | 47 | @After 48 | public void tearDown() { 49 | } 50 | 51 | /** 52 | * Test of genOCSPResp method, of class OCSPUtil. 53 | */ 54 | @Test 55 | public void testGenOCSPResp() throws Exception { 56 | System.out.println("genOCSPResp"); 57 | String caFileName = OCSPUtilTest.class.getResource("/resources/burpca.p12").getPath(); 58 | String password = "testca"; 59 | String reqFileName = OCSPUtilTest.class.getResource("/resources/req.der").getPath(); 60 | 61 | KeyStore ks = KeyStore.getInstance("PKCS12"); 62 | ks.load(new FileInputStream(caFileName), password.toCharArray()); 63 | 64 | String alias = CertUtil.getFirstAlias(ks); 65 | PrivateKey issuerPrivateKey = (PrivateKey) ks.getKey(alias, password.toCharArray()); 66 | X509Certificate issuerCert = (X509Certificate) ks.getCertificate(alias); 67 | 68 | OCSPReq ocspReq = new OCSPReq(FileUtil.bytesFromFile(new File(reqFileName))); 69 | Req[] reqIDList = ocspReq.getRequestList(); 70 | 71 | assertEquals(1, reqIDList.length); 72 | assertEquals(new BigInteger("ED30220789C5A11D", 16), reqIDList[0].getCertID().getSerialNumber()); 73 | 74 | OCSPResp resp = OCSPWrap.genOCSPResp(reqIDList, issuerPrivateKey, issuerCert); 75 | assertEquals(OCSPResp.SUCCESSFUL, resp.getStatus()); 76 | 77 | } 78 | 79 | /** 80 | * Test of testDecodeOCSPUrl method, of class OCSPUtil. 81 | */ 82 | @Test 83 | public void testDecodeOCSPUrl() throws Exception { 84 | System.out.println("testDecodeOCSPUrl"); 85 | String reqFileName = OCSPUtilTest.class.getResource("../../resources/req.der").getPath(); 86 | byte[] buff = FileUtil.bytesFromFile(new File(reqFileName)); 87 | String ocspURL = URLEncoder.encode(Base64.toBase64String(buff), StandardCharsets.ISO_8859_1); 88 | System.out.println("ocspURL:" + ocspURL); 89 | assertEquals("MG8wbTBGMEQwQjAJBgUrDgMCGgUABBS8yTAf%2BzfS3dkE3w3iAWPnuQhToAQU%2FZW%2FipZVJr947vgLStV5ogOLEg4CCQDtMCIHicWhHaIjMCEwHwYJKwYBBQUHMAECBBIEEFHb1rA7VYnQLA6o0rruOps%3D", ocspURL); 90 | byte[] decode = OCSPWrap.decodeOCSPUrl(ocspURL); 91 | assertArrayEquals(buff, decode); 92 | } 93 | 94 | /** 95 | * Test of getFirstAlias method, of class OCSPUtil. 96 | */ 97 | @Test 98 | public void testGetURL() throws Exception { 99 | System.out.println("getURL"); 100 | String uri = "/base64_encode"; 101 | String[] paths = uri.split("/"); 102 | assertEquals("base64_encode", paths[paths.length - 1]); 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /SimpleOCSPServer/src/test/java/server/ocsp/SimpleJettyServerTest.java: -------------------------------------------------------------------------------- 1 | // https://bugs.openjdk.java.net/browse/JDK-8214516 2 | package server.ocsp; 3 | 4 | import extension.helpers.CertUtil; 5 | import extension.helpers.FileUtil; 6 | import java.io.File; 7 | import java.io.FileInputStream; 8 | import java.math.BigInteger; 9 | import java.net.HttpURLConnection; 10 | import java.net.URL; 11 | import java.security.KeyStore; 12 | import java.security.PrivateKey; 13 | import java.security.cert.X509Certificate; 14 | import java.util.logging.Logger; 15 | import org.bouncycastle.cert.ocsp.BasicOCSPResp; 16 | import org.bouncycastle.cert.ocsp.CertificateStatus; 17 | import org.bouncycastle.cert.ocsp.OCSPResp; 18 | import org.bouncycastle.cert.ocsp.SingleResp; 19 | import org.eclipse.jetty.client.HttpClient; 20 | import org.eclipse.jetty.client.api.ContentResponse; 21 | import org.eclipse.jetty.client.api.Request; 22 | import org.eclipse.jetty.client.util.BytesContentProvider; 23 | import org.junit.After; 24 | import org.junit.AfterClass; 25 | import org.junit.Before; 26 | import org.junit.BeforeClass; 27 | import org.junit.Test; 28 | import static org.junit.Assert.*; 29 | 30 | /** 31 | * 32 | * @author isayan 33 | */ 34 | public class SimpleJettyServerTest { 35 | private final static Logger logger = Logger.getLogger(SimpleJettyServerTest.class.getName()); 36 | 37 | public SimpleJettyServerTest() { 38 | } 39 | 40 | @BeforeClass 41 | public static void setUpClass() { 42 | } 43 | 44 | @AfterClass 45 | public static void tearDownClass() { 46 | } 47 | 48 | @Before 49 | public void setUp() { 50 | } 51 | 52 | @After 53 | public void tearDown() { 54 | } 55 | 56 | /** 57 | * Test of genOCSPResp method, of class OCSPUtil. 58 | */ 59 | @Test 60 | public void testServerOCSPReq() throws Exception { 61 | System.out.println("serverOCSPReq"); 62 | String caFileName = OCSPUtilTest.class.getResource("/resources/burpca.p12").getPath(); 63 | String password = "testca"; 64 | int defaultPort = 8765; 65 | 66 | KeyStore ks = KeyStore.getInstance("PKCS12"); 67 | ks.load(new FileInputStream(caFileName), password.toCharArray()); 68 | 69 | String alias = CertUtil.getFirstAlias(ks); 70 | 71 | PrivateKey issuerPrivateKey = (PrivateKey) ks.getKey(alias, password.toCharArray()); 72 | X509Certificate issuerCert = (X509Certificate) ks.getCertificate(alias); 73 | 74 | SimpleJettyServer server = new SimpleJettyServer(); 75 | server.startServer(issuerPrivateKey, issuerCert, defaultPort); 76 | 77 | for (int i = 0; i < 10; i++) { 78 | if (server.isRunning()) { 79 | break; 80 | } 81 | Thread.sleep(1000); 82 | } 83 | if (!server.isRunning()) { 84 | fail(); 85 | } 86 | else { 87 | 88 | HttpClient httpClient = new HttpClient(); 89 | httpClient.setFollowRedirects(false); 90 | httpClient.start(); 91 | 92 | String reqFileName = OCSPUtilTest.class.getResource("/resources/req.der").getPath(); 93 | 94 | { 95 | // 疎通確認用 96 | // URLのTOPは例外で正常を返す 97 | URL url = new URL("http", "127.0.0.1", defaultPort, "/"); 98 | Request request = httpClient.newRequest(url.toURI()); 99 | ContentResponse response = request.send(); 100 | int status = response.getStatus(); 101 | assertEquals(HttpURLConnection.HTTP_OK, status); 102 | } 103 | { 104 | URL url = new URL("http", "127.0.0.1", defaultPort, "/MG8wbTBGMEQwQjAJBgUrDgMCGgUABBS8yTAf%2BzfS3dkE3w3iAWPnuQhToAQU%2FZW%2FipZVJr947vgLStV5ogOLEg4CCQDtMCIHicWhHaIjMCEwHwYJKwYBBQUHMAECBBIEEFHb1rA7VYnQLA6o0rruOps%3D"); 105 | Request request = httpClient.newRequest(url.toURI()); 106 | ContentResponse response = request.send(); 107 | int status = response.getStatus(); 108 | assertEquals(status, HttpURLConnection.HTTP_OK); 109 | OCSPResp resp = new OCSPResp(response.getContent()); 110 | assertEquals(OCSPResp.SUCCESSFUL, resp.getStatus()); 111 | 112 | BasicOCSPResp basicOCSPResp = (BasicOCSPResp)resp.getResponseObject(); 113 | SingleResp[] singleResps = basicOCSPResp.getResponses(); 114 | for (SingleResp singleResp : singleResps) { 115 | assertEquals(new BigInteger("ED30220789C5A11D", 16),singleResp.getCertID().getSerialNumber()); 116 | assertEquals(CertificateStatus.GOOD, singleResp.getCertStatus()); 117 | } 118 | } 119 | { 120 | URL url = new URL("http", "127.0.0.1", defaultPort, "/"); 121 | Request request = httpClient.POST(url.toURI()); 122 | request.content(new BytesContentProvider(new byte[] {})); 123 | ContentResponse response = request.send(); 124 | int status = response.getStatus(); 125 | assertEquals(HttpURLConnection.HTTP_INTERNAL_ERROR, status); 126 | } 127 | { 128 | URL url = new URL("http", "127.0.0.1", defaultPort, "/"); 129 | Request request = httpClient.POST(url.toURI()); 130 | request.content(new BytesContentProvider(FileUtil.bytesFromFile(new File(reqFileName)))); 131 | ContentResponse response = request.send(); 132 | int status = response.getStatus(); 133 | assertEquals(status, HttpURLConnection.HTTP_OK); 134 | OCSPResp resp = new OCSPResp(response.getContent()); 135 | assertEquals(OCSPResp.SUCCESSFUL, resp.getStatus()); 136 | BasicOCSPResp basicOCSPResp = (BasicOCSPResp)resp.getResponseObject(); 137 | SingleResp[] singleResps = basicOCSPResp.getResponses(); 138 | for (SingleResp singleResp : singleResps) { 139 | assertEquals(new BigInteger("ED30220789C5A11D", 16),singleResp.getCertID().getSerialNumber()); 140 | assertEquals(CertificateStatus.GOOD, singleResp.getCertStatus()); 141 | } 142 | } 143 | 144 | httpClient.stop(); 145 | 146 | } 147 | server.stopServer(); 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /SimpleOCSPServer/src/test/resources/resources/burpca.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raise-isayan/FakeCert/5d180b60628fcd5f367822bddb6198e79338301e/SimpleOCSPServer/src/test/resources/resources/burpca.p12 -------------------------------------------------------------------------------- /SimpleOCSPServer/src/test/resources/resources/req.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raise-isayan/FakeCert/5d180b60628fcd5f367822bddb6198e79338301e/SimpleOCSPServer/src/test/resources/resources/req.der --------------------------------------------------------------------------------