
1. 项目概述为什么我们需要一个自定义的解密插件在渗透测试和安全审计的日常工作中我们经常遇到一个令人头疼的“黑盒”应用与服务器之间的通信数据被加密了。你打开Burp Suite的Proxy历史记录看到的请求和响应体不再是明文的JSON或XML而是一串串毫无意义的十六进制字符串或Base64编码的乱码。这就像戴着眼罩去摸象你只知道有数据在流动却完全不清楚里面到底传输了什么敏感信息、调用了哪些业务接口、是否存在逻辑漏洞。这种情况在金融、游戏、物联网以及一些对数据安全要求较高的移动App中尤为常见开发者会使用自定义的算法或标准的AES、RSA等对关键业务参数进行全程加密。这时候Burp Suite自带的Decoder和Comparer虽然强大但面对每个请求都需要手动复制、粘贴到外部工具解密、再粘贴回来分析的流程效率极其低下而且容易出错。更麻烦的是当你要测试一个需要连续发送多个加密请求的业务流程时比如登录后修改资料再下单这种手动操作几乎不可行。因此一个能够无缝集成到Burp Suite工作流中的自定义解密插件就成了安全测试人员的“刚需”。它能够自动识别经过代理的流量在Burp Suite的界面里实时地将加密的请求参数和响应结果解密为明文让测试者能够像处理普通HTTP流量一样进行重放、修改、扫描和漏洞挖掘。这个项目就是教你如何用Java亲手打造这样一把“万能钥匙”。2. 插件核心设计思路与Burp Suite扩展API解析在动手写代码之前我们必须先理解Burp Suite插件的运行机制和它能给我们提供的“操作空间”。Burp Suite通过IBurpExtender这个核心接口与插件进行交互这就像是一个插座的国际标准你的插件必须实现这个接口才能被Burp Suite识别并加载。2.1 理解Burp Suite的事件驱动模型Burp Suite的插件开发本质上是事件驱动的。你的插件会注册为一系列事件的监听器Listener当特定事件发生时Burp Suite会回调你插件中对应的方法并传入相关的数据对象你可以在这些回调方法里“做手脚”。对于我们这个解密插件最关键的两个扩展点是IHttpListener这是核心中的核心。它监听所有经过Burp Suite代理的HTTP/HTTPS流量。无论是浏览器发出的请求还是服务器返回的响应都会触发这个监听器。我们的解密逻辑主要就写在这里的回调方法processHttpMessage中。IMessageEditorTabFactory这个接口允许我们为Burp Suite的“消息编辑器”就是Proxy、Repeater里查看请求/响应详情那个窗口添加自定义的标签页。我们可以创建一个新的标签页专门用来显示解密后的明文这比在原始视图里替换内容更加清晰和安全因为原始加密数据得以保留。2.2 插件工作流程设计基于以上API我们的插件工作流程可以这样设计加载与注册插件启动实现IBurpExtender接口的registerExtenderCallbacks方法。在这个方法里我们获取Burp提供的IExtensionHelpers工具对象并用callbacks.registerHttpListener(this)将自己注册为HTTP监听器。流量拦截与判断每当有HTTP消息请求或响应经过时processHttpMessage方法被调用。我们首先需要判断这条消息是否是我们需要处理的“加密消息”。这通常通过检查URL路径、Content-Type头部、或者请求体/响应体的特征例如是否以特定前缀开头是否是JSON但包含encryptedData字段来实现。解密执行如果判断为加密流量则提取出加密的字符串可能从请求参数、Cookie、HTTP头或Body中调用我们预先编写好的解密函数如AES解密、RSA私钥解密、Base64解码后解密等。内容替换或展示方案AIHttpListener内直接修改直接在processHttpMessage中使用iRequestResponse.setRequest()或setResponse()方法将解密后的明文替换回原始的请求/响应体。这样做最直接后续所有模块Scanner, Intruder, Repeater看到的都是解密后的数据。但需极其谨慎因为这会永久改变流量如果解密出错可能导致服务端无法识别。方案BIMessageEditorTab内展示不修改原始消息而是创建一个新的编辑器标签页。在标签页的getUiComponent()方法中返回一个显示解密明文的文本区域。这是更推荐的做法它非侵入式只影响查看不影响实际发送的数据更安全可靠。密钥/算法管理插件需要有一个地方来配置解密所需的密钥、IV初始化向量、算法模式等。这可以通过在Burp Suite的界面上添加一个自定义的配置标签ITab接口来实现让用户能够方便地输入和修改这些敏感信息。注意在实际选择方案时我强烈建议初学者先从方案B自定义标签页开始。它风险低逻辑清晰能帮助你更好地理解Burp的UI扩展机制。等完全掌握后再根据实际测试场景的需要考虑是否引入方案A的流量修改功能。3. 开发环境搭建与项目初始化工欲善其事必先利其器。开发Burp Suite插件环境搭建有几步是绕不开的。3.1 JDK选择与Burp Extender API获取首先你需要安装Java Development Kit (JDK)。由于Burp Suite本身是基于Java开发的为了最好的兼容性建议使用与你的Burp Suite版本相匹配的JDK 8或JDK 11。你可以在命令行输入java -version来确认。接下来你需要获取Burp Suite的扩展API文件JAR包。这个文件就在Burp Suite的安装目录里。启动Burp Suite社区版或专业版均可。点击顶部菜单Extender-APIs。在APIs标签页中你会看到一个“Save interface files”的按钮。点击它选择一个目录例如D:\burp-apiBurp会生成一个名为burpsuite_api_vX.X.jar的文件X.X是版本号。这个JAR包包含了我们开发所需的所有接口如IBurpExtender,IHttpListener和工具类。3.2 创建Maven项目与管理依赖现代Java项目强烈推荐使用Maven或Gradle来管理依赖和构建。这里以Maven为例。你可以使用IDE如IntelliJ IDEA或Eclipse直接创建一个Maven项目。项目的核心是pom.xml文件我们需要做以下关键配置添加Burp Extender API依赖由于那个API JAR包不在公共的Maven仓库中我们需要将其安装到本地仓库或者使用system作用域直接引用本地路径。dependency groupIdnet.portswigger.burp.extender/groupId artifactIdburp-extender-api/artifactId version1.0/version !-- 版本号可自定义 -- scopesystem/scope systemPath${project.basedir}/lib/burpsuite_api_v2024.6.jar/systemPath /dependency你需要把下载的API JAR包复制到项目根目录的lib文件夹下并修改systemPath中的文件名与你实际的文件名一致。添加加解密库依赖Java自带的javax.crypto包功能强大但略显底层。为了更方便地处理AES、RSA等操作我们可以引入org.bouncycastleBouncy Castle这个强大的加密库或者使用Apache的commons-codec处理Base64编解码。dependency groupIdorg.bouncycastle/groupId artifactIdbcprov-jdk15on/artifactId version1.70/version /dependency dependency groupIdcommons-codec/groupId artifactIdcommons-codec/artifactId version1.16.0/version /dependency配置Maven编译插件Burp Suite插件需要打包成一个单独的JAR文件并且所有依赖除了Burp API最好被打包进去即生成“uber-jar”或“fat-jar”。我们可以使用maven-shade-plugin或maven-assembly-plugin来实现。同时需要指定编译的Java版本。build plugins plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-compiler-plugin/artifactId version3.11.0/version configuration source8/source target8/target /configuration /plugin plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-shade-plugin/artifactId version3.5.0/version executions execution phasepackage/phase goals goalshade/goal /goals /execution /executions /plugin /plugins /build3.3 编写插件主类骨架创建一个Java类例如DecryptorPlugin.java它需要实现IBurpExtender和IHttpListener接口。package com.yourcompany.burp.decryptor; import burp.*; import java.io.PrintWriter; public class DecryptorPlugin implements IBurpExtender, IHttpListener { private IBurpExtenderCallbacks callbacks; private IExtensionHelpers helpers; private PrintWriter stdout; private PrintWriter stderr; Override public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks) { // 保存回调对象和帮助对象 this.callbacks callbacks; this.helpers callbacks.getHelpers(); // 设置输出流用于在Burp的Extender输出台打印日志 stdout new PrintWriter(callbacks.getStdout(), true); stderr new PrintWriter(callbacks.getStderr(), true); // 设置插件名称 callbacks.setExtensionName(Custom Crypto Decryptor); // 注册自己为HTTP监听器 callbacks.registerHttpListener(this); stdout.println([] Custom Crypto Decryptor Plugin Loaded Successfully!); } Override public void processHttpMessage(int toolFlag, boolean messageIsRequest, IHttpRequestResponse messageInfo) { // toolFlag: 标识消息来自哪个工具 (如 TOOL_PROXY0x00000004) // messageIsRequest: true为请求false为响应 // messageInfo: 包含请求/响应原始数据及一些辅助信息 // 这里将是我们的核心解密逻辑 // 暂时先打印一条日志确认插件被正确调用 if (messageIsRequest) { stdout.println([*] Intercepted a Request from tool: toolFlag); } else { stdout.println([*] Intercepted a Response from tool: toolFlag); } } }完成以上步骤后运行mvn clean package在target目录下会生成一个类似your-plugin-1.0-shaded.jar的文件。在Burp Suite的Extender-Extensions标签页点击Add选择这个JAR包加载。如果一切顺利你会在下方的输出面板看到[] Custom Crypto Decryptor Plugin Loaded Successfully!的日志并且代理经过的每一个请求响应都会触发打印日志。至此你的插件开发环境和工作流水线就完全打通了。4. 核心解密逻辑的实现与集成现在我们来到了最核心的部分如何在一个具体的HTTP消息中识别出加密数据并将其解密。这里我们以一个最常见的场景为例一个移动端APP其POST请求的JSON Body中有一个名为data的字段该字段的值是一个经过AES-128-CBC加密并Base64编码的字符串。我们需要解密它。4.1 识别加密流量首先我们需要在processHttpMessage方法中增加过滤逻辑。不能对所有流量都进行解密尝试那样效率低下且可能出错。Override public void processHttpMessage(int toolFlag, boolean messageIsRequest, IHttpRequestResponse messageInfo) { // 只处理来自Proxy、Repeater或Intruder的流量忽略Scanner等 if (toolFlag ! IBurpExtenderCallbacks.TOOL_PROXY toolFlag ! IBurpExtenderCallbacks.TOOL_REPEATER toolFlag ! IBurpExtenderCallbacks.TOOL_INTRUDER) { return; } IRequestInfo reqInfo helpers.analyzeRequest(messageInfo); String url reqInfo.getUrl().toString(); // 示例只处理特定主机和路径的请求 if (!url.contains(api.vulnerable-app.com) || !url.contains(/secure/endpoint)) { return; } // 如果是请求且是POST方法尝试解密Body if (messageIsRequest reqInfo.getMethod().equalsIgnoreCase(POST)) { // 获取请求Body的字节数组 byte[] requestBytes messageInfo.getRequest(); int bodyOffset reqInfo.getBodyOffset(); byte[] bodyBytes Arrays.copyOfRange(requestBytes, bodyOffset, requestBytes.length); String bodyStr helpers.bytesToString(bodyBytes); // 尝试将Body解析为JSON并查找加密字段 try { // 这里可以使用简单的字符串查找或引入JSON库如Gson/Jackson // 为了简化示例我们假设bodyStr是JSON且包含data:加密字符串 if (bodyStr.contains(\data\:)) { // 提取加密字符串 (这是一个非常简单的正则实际应用需要更健壮的解析) // 假设格式是 data: Base64EncodedAESCipherText Pattern pattern Pattern.compile(\data\:\\s*\([^\])\); Matcher matcher pattern.matcher(bodyStr); if (matcher.find()) { String encryptedBase64 matcher.group(1); stdout.println([*] Found encrypted data field: encryptedBase64.substring(0, Math.min(20, encryptedBase64.length())) ...); // 调用解密函数 String decryptedPlaintext decryptAES(encryptedBase64); if (decryptedPlaintext ! null) { stdout.println([] Decrypted: decryptedPlaintext); // TODO: 在这里处理解密后的明文例如显示在自定义标签页 } } } } catch (Exception e) { stderr.println([-] Error parsing request body: e.getMessage()); } } // 响应解密的逻辑类似可以分析Content-Type和响应体结构 }4.2 实现AES解密函数接下来实现decryptAES方法。这里演示AES-128-CBC模式PKCS5Padding填充方式。密钥和IV需要根据目标应用实际情况替换。import org.apache.commons.codec.binary.Base64; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; private String decryptAES(String encryptedBase64) { try { // 1. Base64解码 byte[] encryptedData Base64.decodeBase64(encryptedBase64); // 2. 准备密钥和IV (!!! 这里需要替换成实际的密钥和IV !!!) String secretKey 0123456789abcdef; // 16字节密钥 for AES-128 String iv abcdefghijklmnop; // 16字节IV for CBC mode SecretKeySpec keySpec new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), AES); IvParameterSpec ivSpec new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8)); // 3. 初始化Cipher为解密模式 Cipher cipher Cipher.getInstance(AES/CBC/PKCS5Padding); cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); // 4. 执行解密 byte[] decryptedBytes cipher.doFinal(encryptedData); // 5. 将解密后的字节转换为字符串 return new String(decryptedBytes, StandardCharsets.UTF_8); } catch (Exception e) { stderr.println([-] AES Decryption failed: e.getMessage()); e.printStackTrace(stderr); return null; } }实操心得密钥和IV的管理是安全测试中的敏感环节。绝对不要将真实的测试目标密钥硬编码在插件代码中并上传到公开的代码仓库。最佳实践是在插件中提供一个配置界面通过实现ITab接口让测试人员在加载插件后手动输入密钥。或者从本地一个加密的配置文件读取。这既是职业操守也避免了因代码泄露导致目标系统密钥暴露的风险。4.3 创建自定义消息编辑器标签页为了安全、清晰地展示解密内容我们实现IMessageEditorTabFactory和IMessageEditorTab。// 在主类中注册Tab Factory public class DecryptorPlugin implements IBurpExtender, IHttpListener, IMessageEditorTabFactory { // ... 之前的成员变量和registerExtenderCallbacks方法 ... Override public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks) { // ... 之前的初始化代码 ... // 注册消息编辑器Tab工厂 callbacks.registerMessageEditorTabFactory(this); stdout.println([] Message Editor Tab Factory Registered.); } Override public IMessageEditorTab createNewInstance(IMessageEditorController controller, boolean editable) { // 返回我们自定义的Tab实例 return new DecryptorMessageEditorTab(controller, editable, helpers, stdout, stderr); } } // 自定义的Tab实现 class DecryptorMessageEditorTab implements IMessageEditorTab { private final JTextArea textArea; private byte[] currentMessage; private final IMessageEditorController controller; private final boolean editable; private final IExtensionHelpers helpers; private final PrintWriter stdout; public DecryptorMessageEditorTab(IMessageEditorController controller, boolean editable, IExtensionHelpers helpers, PrintWriter stdout, PrintWriter stderr) { this.controller controller; this.editable editable; this.helpers helpers; this.stdout stdout; this.textArea new JTextArea(); this.textArea.setEditable(false); // 我们的解密展示页通常设为只读 this.textArea.setFont(new Font(Monospaced, Font.PLAIN, 12)); } Override public String getTabCaption() { return Decrypted View; // 标签页显示的名称 } Override public java.awt.Component getUiComponent() { return new JScrollPane(textArea); // 返回一个带滚动条的文本区域 } Override public boolean isEnabled(byte[] content, boolean isRequest) { // 判断是否为此消息启用这个Tab。这里我们可以复用之前的流量识别逻辑。 // 简单示例如果内容是请求且包含特定主机则启用。 if (content null) return false; IRequestInfo reqInfo helpers.analyzeRequest(controller.getHttpService(), content); if (reqInfo ! null reqInfo.getUrl().toString().contains(api.vulnerable-app.com)) { return true; } // 也可以检查响应这里省略 return false; } Override public void setMessage(byte[] content, boolean isRequest) { this.currentMessage content; if (content null) { textArea.setText(); return; } // 这里是核心当用户点击我们这个Tab时Burp会调用此方法传入当前消息。 // 我们需要在这里执行解密并显示。 String displayText [Processing...]\n; try { if (isRequest) { IRequestInfo reqInfo helpers.analyzeRequest(controller.getHttpService(), content); int bodyOffset reqInfo.getBodyOffset(); byte[] bodyBytes Arrays.copyOfRange(content, bodyOffset, content.length); String bodyStr helpers.bytesToString(bodyBytes); // 调用之前写的解密逻辑这里需要把decryptAES方法移到能访问的地方或传递进来 String decrypted YourDecryptionUtil.decryptRequestBody(bodyStr); // 假设有一个工具类 if (decrypted ! null) { displayText Decrypted Request Body \n decrypted; } else { displayText [-] Decryption failed or no encrypted data found.; } } else { // 响应解密的逻辑类似 IResponseInfo respInfo helpers.analyzeResponse(content); int bodyOffset respInfo.getBodyOffset(); byte[] bodyBytes Arrays.copyOfRange(content, bodyOffset, content.length); String bodyStr helpers.bytesToString(bodyBytes); String decrypted YourDecryptionUtil.decryptResponseBody(bodyStr); if (decrypted ! null) { displayText Decrypted Response Body \n decrypted; } else { displayText [-] Decryption failed or no encrypted data found in response.; } } } catch (Exception e) { displayText [-] Error during decryption: e.getMessage(); } textArea.setText(displayText); textArea.setCaretPosition(0); // 滚动到顶部 } Override public byte[] getMessage() { // 因为我们只是展示不修改原始消息所以直接返回null。 // 如果想让Tab可编辑并返回修改后的消息可以在这里处理。 return null; } Override public boolean isModified() { // 我们的展示页不修改内容所以总是返回false。 return false; } Override public byte[] getSelectedData() { // 返回文本区域中用户选中的内容用于复制等操作。 return textArea.getSelectedText().getBytes(StandardCharsets.UTF_8); } }完成这部分代码后重新打包并加载插件。现在当你从Proxy历史记录或Repeater中打开一个目标请求时消息编辑器旁边会出现一个名为“Decrypted View”的新标签页点击它就能直接看到解密后的明文内容而原始的加密请求依然完好无损。5. 插件配置界面与密钥管理一个健壮的插件必须允许用户动态配置解密参数而不是写死在代码里。我们将通过实现ITab接口来为Burp Suite添加一个配置面板。public class DecryptorPlugin implements IBurpExtender, IHttpListener, IMessageEditorTabFactory, ITab { // ... 之前的成员变量 ... private JPanel configPanel; // Swing面板 private JTextField keyField; private JTextField ivField; private JComboBoxString algorithmComboBox; Override public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks) { // ... 之前的初始化代码 ... // 初始化UI initializeConfigUI(); // 注册自己为一个Tab callbacks.addSuiteTab(this); stdout.println([] Configuration Tab Added.); } private void initializeConfigUI() { configPanel new JPanel(new GridBagLayout()); GridBagConstraints gbc new GridBagConstraints(); gbc.insets new Insets(5, 5, 5, 5); gbc.fill GridBagConstraints.HORIZONTAL; gbc.gridx 0; gbc.gridy 0; // 算法选择 configPanel.add(new JLabel(Algorithm:), gbc); algorithmComboBox new JComboBox(new String[]{AES/CBC/PKCS5Padding, AES/ECB/PKCS5Padding, DESede/CBC/PKCS5Padding}); gbc.gridx 1; configPanel.add(algorithmComboBox, gbc); // 密钥输入 gbc.gridx 0; gbc.gridy; configPanel.add(new JLabel(Secret Key (Hex or Base64):), gbc); keyField new JTextField(32); gbc.gridx 1; configPanel.add(keyField, gbc); // IV输入 (对于ECB模式不需要) gbc.gridx 0; gbc.gridy; configPanel.add(new JLabel(IV (Hex, for CBC mode):), gbc); ivField new JTextField(32); gbc.gridx 1; configPanel.add(ivField, gbc); // 保存按钮 gbc.gridx 0; gbc.gridy; gbc.gridwidth 2; gbc.anchor GridBagConstraints.CENTER; JButton saveButton new JButton(Save Configuration); saveButton.addActionListener(e - saveConfiguration()); configPanel.add(saveButton, gbc); // 加载默认配置或上次保存的配置 loadConfiguration(); } private void saveConfiguration() { // 这里应该将配置算法、密钥、IV保存到Burp的持久化设置中或者一个本地文件。 // 使用 callbacks.saveExtensionSetting / loadExtensionSetting 是Burp推荐的方式。 String algo (String) algorithmComboBox.getSelectedItem(); String key keyField.getText(); String iv ivField.getText(); callbacks.saveExtensionSetting(algorithm, algo); callbacks.saveExtensionSetting(secret_key, key); callbacks.saveExtensionSetting(iv, iv); // 同时更新内存中的解密工具类的配置 YourDecryptionUtil.updateConfig(algo, key, iv); stdout.println([] Configuration saved.); JOptionPane.showMessageDialog(configPanel, Configuration saved successfully!, Info, JOptionPane.INFORMATION_MESSAGE); } private void loadConfiguration() { String algo callbacks.loadExtensionSetting(algorithm); String key callbacks.loadExtensionSetting(secret_key); String iv callbacks.loadExtensionSetting(iv); if (algo ! null) algorithmComboBox.setSelectedItem(algo); if (key ! null) keyField.setText(key); if (iv ! null) ivField.setText(iv); } // 实现 ITab 接口的方法 Override public String getTabCaption() { return Crypto Decryptor Config; } Override public Component getUiComponent() { return configPanel; } }现在你的Burp Suite界面上会多出一个名为“Crypto Decryptor Config”的标签页。测试人员可以在这里输入从逆向工程、静态分析或沟通中获得的目标应用的加密密钥和算法参数。点击保存后插件后续的解密操作都会使用这些新配置。这种方式使得插件可以灵活适配不同的测试目标而无需重新编译代码。6. 处理复杂场景与高级功能基本的解密功能实现后我们会发现真实世界的场景要复杂得多。下面探讨几个常见的高级需求及其实现思路。6.1 多种加密算法与动态识别目标应用可能不止使用一种加密算法或者不同接口使用不同的密钥。我们需要一个更强大的解密引擎。创建解密管理器设计一个DecryptionManager类它维护一个“规则列表”。每条规则包含匹配条件如URL正则、请求头特征、算法类型、密钥、IV等。规则配置在配置界面中允许用户添加、编辑、删除多条解密规则。每条规则可以独立启用或禁用。动态匹配在processHttpMessage或isEnabled方法中遍历所有启用的规则找到第一条匹配当前HTTP消息的规则然后使用该规则对应的算法和密钥进行解密。算法工厂实现一个CryptoFactory根据算法字符串如“AES-128-CBC”、“RSA-OAEP”动态创建对应的解密器对象。// 简化的规则示例 public class DecryptionRule { private String name; private Pattern urlPattern; private String algorithm; private String key; private String iv; private boolean enabled; // getters and setters... } // 在插件主类中 private ListDecryptionRule rules new ArrayList(); private String applyDecryption(IHttpRequestResponse messageInfo, boolean isRequest) { IRequestInfo reqInfo helpers.analyzeRequest(messageInfo); String url reqInfo.getUrl().toString(); for (DecryptionRule rule : rules) { if (rule.isEnabled() rule.getUrlPattern().matcher(url).find()) { // 使用此规则进行解密 return CryptoEngine.decrypt(extractCipherText(messageInfo, isRequest, rule), rule); } } return null; // 没有匹配的规则 }6.2 处理非对称加密RSA有些应用使用RSA非对称加密。通常客户端用服务器的公钥加密一个临时生成的对称密钥如AES密钥然后用这个对称密钥加密业务数据。服务器用私钥解密得到对称密钥再解密数据。对于测试者我们通常没有服务器的私钥。但如果我们通过逆向APP拿到了内嵌的公钥或者能中间人获取到公钥我们就可以模拟客户端加密过程。更常见的情况是我们需要用服务器的公钥来验证签名或者用我们自己的私钥来解密客户端发给我们的、用我们公钥加密的数据在测试我们自己服务端时。在插件中集成RSA解密需要使用java.security.KeyFactory和Cipher。private String decryptRSA(String encryptedBase64, String privateKeyPEM) throws Exception { // 移除PEM格式的头尾标记 privateKeyPEM privateKeyPEM.replace(-----BEGIN PRIVATE KEY-----, ) .replace(-----END PRIVATE KEY-----, ) .replaceAll(\\s, ); byte[] pkcs8EncodedKey Base64.decodeBase64(privateKeyPEM); PKCS8EncodedKeySpec keySpec new PKCS8EncodedKeySpec(pkcs8EncodedKey); KeyFactory kf KeyFactory.getInstance(RSA); PrivateKey privateKey kf.generatePrivate(keySpec); Cipher cipher Cipher.getInstance(RSA/ECB/PKCS1Padding); // 注意填充方式需与客户端一致 cipher.init(Cipher.DECRYPT_MODE, privateKey); byte[] encryptedData Base64.decodeBase64(encryptedBase64); byte[] decryptedData cipher.doFinal(encryptedData); return new String(decryptedData, StandardCharsets.UTF_8); }注意事项RSA解密对数据长度有限制取决于密钥长度。通常它只用于加密密钥或很短的数据。如果遇到解密失败首先检查填充方式PKCS1Padding, OAEP等这是最常见的坑。其次确认你拿到的是正确的私钥并且格式是PKCS#8。很多时候从各种工具导出的密钥是PKCS#1格式需要转换。6.3 与Burp其他工具链协作解密插件最大的价值在于能让Burp Suite的其他核心工具在明文状态下工作。Scanner漏洞扫描如果你在IHttpListener中直接修改了请求/响应体方案A那么Scanner扫描的就是解密后的明文能发现更多逻辑漏洞。但要注意如果修改了原始请求结构可能会影响扫描的准确性。Intruder爆破这是插件发挥威力的地方。你可以在Intruder中对解密后的明文中的某个字段如用户ID进行标记§§然后进行爆破。插件需要在每次Intruder发送请求前将整个Payload包含你标记的明文部分重新加密。这需要更精细的控制可能需要实现IIntruderPayloadProcessor接口。这个接口允许你对Intruder生成的Payload进行自定义处理。Repeater重放我们的自定义消息编辑器Tab已经让Repeater的查看和调试变得非常方便。你可以在解密视图中看到明文修改后如果需要发送则需要一个反向的“加密”过程。这可以通过在Repeater的Tab中增加一个“Encrypt Send”按钮来实现或者更复杂地实现一个IHttpListener来拦截从Repeater发出的、在解密Tab中修改过的请求并将其重新加密。实现IIntruderPayloadProcessor的示例如下public class EncryptionPayloadProcessor implements IIntruderPayloadProcessor { private final IExtensionHelpers helpers; public EncryptionPayloadProcessor(IExtensionHelpers helpers) { this.helpers helpers; } Override public String getProcessorName() { return Encrypt payload; } Override public byte[] processPayload(byte[] currentPayload, byte[] originalPayload, byte[] baseValue) { // currentPayload: Intruder当前要替换进去的值明文 // baseValue: 被标记位置的原始值可能是加密的也可能是明文的 // 我们需要构造一个完整的请求其中将baseValue替换为加密后的currentPayload // 这里逻辑比较复杂需要结合具体的请求结构 // 通常做法是先解密原始请求得到模板替换标记部分再整体加密。 String processedRequest yourEncryptionLogic(currentPayload); return helpers.stringToBytes(processedRequest); } } // 在registerExtenderCallbacks中注册callbacks.registerIntruderPayloadProcessor(this);7. 调试、打包与发布7.1 插件调试技巧开发Burp插件最直接的调试方式就是使用stdout和stderr打印日志。所有打印的信息都会在Burp的Extender标签页的“Output”或“Errors”子标签中看到。详细日志在关键决策点、解密前后、异常捕获处打印信息。例如stdout.println([*] Trying to match rule: rule.getName());错误堆栈在catch块中使用e.printStackTrace(stderr);打印完整的异常堆栈这对于定位加密库的细微错误至关重要。使用系统断点高级如果你使用IDE如IntelliJ IDEA可以以调试模式启动Burp Suite然后将IDE的调试器附加到Burp的Java进程上。这需要一些额外的JVM参数配置例如在Burp启动时添加-agentlib:jdwptransportdt_socket,servery,suspendn,address5005但可以实现单步调试效率极高。7.2 打包与依赖管理我们之前配置的maven-shade-plugin会创建一个包含所有依赖除Burp API外的“uber JAR”。这是最常用的分发方式。检查生成的JAR使用jar tf target/your-plugin-1.0-shaded.jar命令或解压工具确认META-INF/MANIFEST.MF文件中的Main-Class属性是否被正确移除Burp插件不需要Main-Class并且你的主类如com.yourcompany.burp.decryptor.DecryptorPlugin的类文件在根目录下。处理依赖冲突如果你的插件引入了某个库如Bouncy Castle而Burp Suite自身也带了不同版本的同一库可能会引发NoSuchMethodError或ClassNotFoundException。这时需要使用maven-shade-plugin的 relocation 功能将你的依赖包名重命名。configuration relocations relocation patternorg.bouncycastle/pattern shadedPatterncom.yourcompany.shaded.bouncycastle/shadedPattern /relocation /relocations /configuration代码混淆可选对于商业或敏感插件可以考虑使用ProGuard等工具对代码进行混淆增加逆向难度。7.3 发布与分享完成开发和测试后你可以将插件的JAR文件分享给团队成员。编写README创建一个简单的说明文档解释插件的功能、配置方法如何设置密钥、适用的加密场景以及已知的限制。版本管理在代码中使用语义化版本号如1.0.0并在插件加载时打印出来便于问题追踪。考虑开源如果插件不包含敏感的业务逻辑或密钥可以考虑在GitHub等平台开源。这不仅能帮助他人也能收获社区的反馈和改进。记得在开源前务必移除所有硬编码的、与特定测试目标相关的密钥和配置。开发一个功能完善的Burp Suite自定义解密插件是一个将密码学知识、Java编程能力和对HTTP协议及Burp Suite框架理解相结合的过程。从最初简单的AES解密到支持多规则、非对称加密、与Intruder联动每一步的深化都让这个工具在实战中变得更加强大和顺手。当你能够流畅地解密目标应用的流量并利用Burp Suite的全套工具对其进行安全测试时那种“拨云见日”的感觉正是安全研究员工作乐趣和价值的体现。记住密钥管理无小事测试授权要牢记在合规的范围内让技术为我们所用。