java http sign snip
关联文章 加解密技术
# case
private static String sign(String urlString, HttpRequestClient.Method method, byte[] body) {
Map.Entry<String, String> randomValue = pubicKeyEntries.get(generator.nextInt(pubicKeyEntries.size()));
String time = String.valueOf((System.currentTimeMillis() - InnerLogicManager.get().getServerTimestampDelta()) / 1000);
final List<String> contentOfSign = new ArrayList<>(6);
contentOfSign.add(time);
contentOfSign.add(method.name());
try {
URL url = new URL(urlString);
String path = url.getPath();
if (path == null) {
contentOfSign.add("");
} else {
contentOfSign.add(path);
}
String query = url.getQuery();
if (query == null) {
contentOfSign.add("");
} else {
contentOfSign.add(query);
}
} catch (MalformedURLException e) {
Logger.e(e);
}
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(body);
String bodySign = bytesToHex(hash);
contentOfSign.add(bodySign);
} catch (NoSuchAlgorithmException e) {
Logger.e(e);
}
String sign = DeviceDependence.current.getDeviceInject().hmac(key, Strings.joinStrings(contentOfSign, '\n'));
final List<String> content = new ArrayList<>();
content.add("version=" + version);
content.add("type=0"); // type = 0 , 只签名不加密, 防止非法调用接口
content.add("key=" + Base64.encodeToString(key.getBytes(), Base64.DEFAULT));
content.add("time=" + time);
String encryptedContent = DeviceDependence.current.getDeviceInject().rsaEncode(
Strings.joinStrings(content, ';'), randomValue.getValue());
return "key=" + randomValue.getKey() + ";secret=" + encryptedContent + ";signature=" + sign;
}
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
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
这个 sign 方法通过多种方式来保证安全性,确保请求的完整性、身份验证和防止重放攻击。以下是具体的安全性保证措施:
最终返回的字符串包括随机选择的公钥标识符、加密的内容和 HMAC 签名。这种组合方式确保了请求的完整性、身份验证以及数据的加密保护
- 完整性: SHA-256 哈希值和 HMAC 签名保证请求数据的完整性,防止数据被篡改
- 身份验证: HMAC 签名确保只有持有正确密钥的客户端才能生成合法签名,防止未授权的请求
- 防重放攻击: 时间戳的使用防止请求被重放,确保每次请求都是唯一的
- 加密保护: RSA 加密保护敏感信息在传输过程中的机密性,防止数据泄露
这些措施共同确保了请求在传输过程中的安全性,防止各种攻击和数据泄露
# 实现分析
# 1.时间戳
String time = String.valueOf((System.currentTimeMillis() - InnerLogicManager.get().getServerTimestampDelta()) / 1000);
1
使用当前时间戳(减去服务器时间差)作为签名的一部分,可以防止重放攻击,因为签名包含了特定时间的信息,每次请求的时间戳都是唯一的
# 2.签名内容集合
final List<String> contentOfSign = new ArrayList<>(6);
contentOfSign.add(time);
contentOfSign.add(method.name());
contentOfSign.add(path);
contentOfSign.add(query);
contentOfSign.add(bodySign);
1
2
3
4
5
6
2
3
4
5
6
将时间戳、HTTP 方法、URL 路径、查询参数和请求体的哈希值(SHA-256)组成一个集合,用于生成签名。这确保了请求的完整性,防止请求在传输过程中被篡改
# 3.SHA-256 哈希
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(body);
String bodySign = bytesToHex(hash);
contentOfSign.add(bodySign);
1
2
3
4
2
3
4
计算请求体的 SHA-256 哈希值,并将其加入签名内容集合中,确保请求体的完整性和唯一性
# 4.HMAC 签名
String sign = DeviceDependence.current.getDeviceInject().hmac(key, Strings.joinStrings(contentOfSign, '\n'));
1
使用 HMAC(基于密钥的哈希消息认证码)算法对签名内容集合进行签名,确保签名的真实性和完整性。只有持有正确密钥的双方才能生成和验证此签名
# 5.RSA 加密
String encryptedContent = DeviceDependence.current.getDeviceInject().rsaEncode(
Strings.joinStrings(content, ';'), randomValue.getValue());
1
2
2
将请求中的关键信息(版本、类型、密钥、时间戳)进行拼接后,用 RSA 公钥加密。RSA 加密是一种非对称加密算法,确保数据在传输过程中不被第三方窃取和篡改
# 6.组合签名和加密内容
return "key=" + randomValue.getKey() + ";secret=" + encryptedContent + ";signature=" + sign;
1
# 破解评估
评估上述 API 在客户端中的破解难度涉及考虑攻击者可能使用的不同技术和方法。以下是对可能的攻击途径和破解难度的详细评估:
- 时间戳防重放攻击:
- 攻击难度: 中等
- 分析: 攻击者需要获取有效的时间戳并在短时间内重放请求。尽管时间戳能防止简单的重放攻击,但如果攻击者能截获并迅速重放请求,仍可能成功。因此,时间戳的保护强度在于其有效期的短暂性
- SHA-256 哈希值计算:
- 攻击难度: 低
- 分析:SHA-256 哈希函数是公开的,攻击者可以轻松计算任何数据的 SHA-256 哈希值。然而,这部分并不会直接暴露密钥信息,主要用于确保数据完整性
- HMAC 签名:
- 攻击难度: 高
- 分析: HMAC 签名需要一个秘密密钥。假设密钥存储在客户端且保护得当(如通过硬编码或混淆),攻击者需要获取该密钥才能生成有效的签名。提取密钥通常需要逆向工程客户端代码或从设备存储中读取,这些操作较为复杂
- RSA 加密:
- 攻击难度: 高
- 分析:RSA 加密使用公钥加密敏感信息。即使攻击者能截获加密的数据,也难以解密,除非获取私钥。私钥通常不会存储在客户端,而是保存在服务器端,因此攻击者难以直接破解加密数据
- 整体破解难度:
- 攻击难度: 中到高
- 分析: 攻击者需要通过多种复杂技术手段,包括逆向工程、内存分析、代码注入等,才能破解整个签名过程。以下是一些具体的破解方法及其难度:
- 逆向工程和代码注入: 攻击者可能尝试逆向工程客户端应用,以理解并提取密钥和签名算法。这通常需要熟练的逆向工程技术和工具
- 内存分析: 攻击者可以使用调试工具或内存分析工具(如 Frida)动态分析应用运行时的内存,捕获密钥和其他敏感信息。这需要高级技术和深入的应用知识
- 代码混淆和反混淆: 如果客户端代码经过混淆处理,破解难度将大幅增加。然而,经验丰富的攻击者仍可能通过反混淆和动态调试破解代码
综合考虑上述保护措施和可能的攻击方法,该 API 在客户端的破解难度总体较高。虽然部分防护机制(如 SHA-256 哈希和时间戳)较易绕过,但核心的 HMAC 签名和 RSA 加密有效地提升了破解难度。为了进一步提高安全性,可以采用以下措施:
- 使用代码混淆工具保护客户端代码,增加逆向工程难度
- 在客户端尽量少存储或传递敏感信息,减少被攻击者提取的可能性
- 实现动态密钥管理机制,定期更新密钥,降低密钥泄露后的风险
即使如此,再强的客户端安全措施也无法完全避免被攻破的可能性,因此,服务器端的安全防护同样至关重要
上次更新: 2025/10/11, 09:25:12