1.前言
上篇提到了 tls指纹 ja4和ja3指纹已经一致,但ja3_text的顺序并非一致,为进一步优化指纹,需要保证tls extension顺序一致性。
- 拉取bc源码
- bc环境,修改bc源码,打包
- 修改tlsClien实现类
bc gradle打包限制太多,具体打包问题问ai即可解决
2.实现
首先看tlsClien实现类
我们继承了 DefaultTlsClient ,实现getClientExtensions,此方法返回为hashtable,hashtable的存取顺序并非统一,所以我们需要更换为LinkedMap
2.1 找到bc类的具体实现
bash
AbstractTlsClient
的

修改为这样,底下的实现部分随之修改
2.2 hashtable更换
将编译报错的地方一一修改,这里推荐 ctrl+shift+r 替换所有
bash
HashTableclient Extensions
为
bash
LinkedHashMap Extensions
然后再一一修改,大概几十个文件,改一个小时左右
2.3 tls请求顺序固定
ctrl+n 找到类TlsProtocol
搜索 writeExtensionsData并修改
bash
protected static void writeExtensionsData(LinkedHashMap extensions, int bindersSize, ByteArrayOutputStream buf)
throws IOException
{
/*
* NOTE: There are reports of servers that don't accept a zero-length extension as the last
* one, so we write out any zero-length ones first as a best-effort workaround.
*/
// writeSelectedExtensions(buf, extensions, true);
// writeSelectedExtensions(buf, extensions, false);
writeSelectedExtensions(buf, extensions);
writePreSharedKeyExtension(buf, extensions, bindersSize);
}
writeSelectedExtensions方法修改
bash
protected static void writeSelectedExtensions(OutputStream output, LinkedHashMap extensions)
throws IOException
{
Iterator keys = extensions.keySet().iterator();
while (keys.hasNext())
{
Integer key = (Integer)keys.next();
int extension_type = key.intValue();
// NOTE: Must be last; handled by 'writePreSharedKeyExtension'
if (ExtensionType.pre_shared_key == extension_type)
{
continue;
}
byte[] extension_data = (byte[])extensions.get(key);
TlsUtils.checkUint16(extension_type);
TlsUtils.writeUint16(extension_type, output);
TlsUtils.writeOpaque16(extension_data, output);
}
}
源方法是过滤空val发一次,再过滤非空val发一次,我们直接一次发送完,让其顺序一致
2.4 BcTlsClient实现修改
此拓展为简陋版本,非chrome实际扩展,实际扩展自己可以自己找找
bash
@Override
public LinkedHashMap<Integer, byte[]> getClientExtensions() throws IOException {
LinkedHashMap ext = TlsExtensionsUtils.ensureExtensionsInitialised(super.getClientExtensions());
ext.clear();
// ---------- 1) GREASE extension (43690) ----------
ext.put(6682, new byte[]{0x00}); // 占位:GREASE 自定义内容(Chrome 只是随便填)
// ---------- 17) server_name (0) ----------
Vector<ServerName> serverNames = new Vector<>();
serverNames.add(new ServerName(NameType.host_name, host.getBytes()));
TlsExtensionsUtils.addServerNameExtensionClient(ext, serverNames);
// ---------- 9) application_settings (17613) ----------
// 非标准扩展:这里用简单格式: [uint8 num_protocols][uint8 len][bytes protocol]...
{
ByteArrayOutputStream bos = new ByteArrayOutputStream();
TlsUtils.writeUint8(1, bos); // 1 protocol
byte[] p = "h2".getBytes();
TlsUtils.writeUint8(p.length, bos); // protocol length
bos.write(p, 0, p.length);
ext.put(17613, bos.toByteArray());
}
ext.put(43690, new byte[]{0x00}); // 占位:GREASE 自定义内容(Chrome 只是随便填)
return ext;
}
bc打包,整到项目里面
到此完毕
2.5 请求tls包对比

右边为chrome指纹,左边java指纹,基本上一致,中间4个指纹是会随请求改变的
akamai_hash未公开算法,不做研究,且java极难实现
3. notice
感谢chatgpt,bc分析很好用
依赖补充
bash
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
<!-- 请查看最新版本 -->
<version>1.83</version>
<scope>system</scope>
<systemPath>${project.basedir}/libs/bcprov-jdk18on-1.83.jar</systemPath>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bctls-jdk18on</artifactId>
<version>1.83</version> <!-- 替换为最新版本 -->
<scope>system</scope>
<systemPath>${project.basedir}/libs/bctls-jdk18on-1.83.jar</systemPath>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcutil-jdk18on</artifactId>
<version>1.83</version> <!-- 替换为最新版本 -->
<scope>system</scope>
<systemPath>${project.basedir}/libs/bcutil-jdk18on-1.83.jar</systemPath>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.http2</groupId>
<artifactId>http2-common</artifactId>
<version>11.0.21</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.http2</groupId>
<artifactId>http2-hpack</artifactId>
<version>11.0.21</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId>
<version>11.0.21</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-http</artifactId>
<version>11.0.21</version>
</dependency>