Skip to content

Commit 13b0308

Browse files
committed
Fix Juicity pinned cert chain
1 parent 4784361 commit 13b0308

2 files changed

Lines changed: 34 additions & 19 deletions

File tree

app/src/main/java/io/nekohasekai/sagernet/fmt/juicity/JuicityFmt.kt

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import io.nekohasekai.sagernet.ktx.toLink
66
import io.nekohasekai.sagernet.ktx.urlSafe
77
import moe.matsuri.nb4a.SingBoxOptions
88
import moe.matsuri.nb4a.SingBoxOptions.Outbound_JuicityOptions
9+
import moe.matsuri.nb4a.utils.listByLineOrComma
910
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
11+
import java.util.Base64
1012

1113
fun parseJuicity(url: String): JuicityBean {
1214
val link = url.replace("juicity://", "https://").toHttpUrlOrNull() ?: error(
@@ -23,7 +25,9 @@ fun parseJuicity(url: String): JuicityBean {
2325
sni = it
2426
}
2527
link.queryParameter("pinned_certchain_sha256")?.let {
26-
pinnedCertchainSha256 = it
28+
normalizePinnedCertChainHash(it)?.let { hash ->
29+
pinnedCertchainSha256 = hash
30+
}
2731
}
2832
link.queryParameter("allow_insecure")?.let {
2933
if (it == "1" || it == "true") allowInsecure = true
@@ -38,7 +42,9 @@ fun JuicityBean.toUri(): String {
3842
builder.addQueryParameter("sni", sni)
3943
}
4044
if (pinnedCertchainSha256.isNotBlank()) {
41-
builder.addQueryParameter("pinned_certchain_sha256", pinnedCertchainSha256)
45+
normalizePinnedCertChainHash(pinnedCertchainSha256.listByLineOrComma().firstOrNull())?.let {
46+
builder.addQueryParameter("pinned_certchain_sha256", it)
47+
}
4248
}
4349
if (allowInsecure) {
4450
builder.addQueryParameter("allow_insecure", "1")
@@ -68,7 +74,18 @@ fun buildSingBoxOutboundJuicityBean(bean: JuicityBean): Outbound_JuicityOptions
6874
}
6975

7076
if (bean.pinnedCertchainSha256.isNotBlank()) {
71-
pin_cert_sha256 = bean.pinnedCertchainSha256
77+
normalizePinnedCertChainHash(bean.pinnedCertchainSha256.listByLineOrComma().firstOrNull())?.let {
78+
pin_cert_sha256 = it
79+
}
7280
}
7381
}
7482
}
83+
84+
private fun normalizePinnedCertChainHash(rawHash: String?): String? {
85+
val certChainHash = rawHash?.replace(":", "")?.takeIf { it.isNotEmpty() } ?: return null
86+
return when {
87+
certChainHash.length == 64 -> Base64.getUrlEncoder()
88+
.encodeToString(certChainHash.chunked(2).map { chunk -> chunk.toInt(16).toByte() }.toByteArray())
89+
else -> certChainHash.replace('/', '_').replace('+', '-')
90+
}
91+
}

libcore/protocol/juicity/outbound.go

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ package juicity
33
import (
44
"bytes"
55
"context"
6+
"crypto/sha256"
67
"crypto/x509"
7-
"crypto/sha256"
88
"encoding/base64"
99
"encoding/hex"
1010
"net"
@@ -16,12 +16,12 @@ import (
1616
"github.com/sagernet/sing-box/common/tls"
1717
C "github.com/sagernet/sing-box/constant"
1818
"github.com/sagernet/sing-box/log"
19+
"github.com/sagernet/sing-box/option"
1920
"github.com/sagernet/sing/common/bufio"
2021
E "github.com/sagernet/sing/common/exceptions"
2122
"github.com/sagernet/sing/common/logger"
2223
M "github.com/sagernet/sing/common/metadata"
2324
N "github.com/sagernet/sing/common/network"
24-
"github.com/sagernet/sing-box/option"
2525

2626
"github.com/dyhkwong/sing-juicity"
2727
"github.com/gofrs/uuid/v5"
@@ -156,18 +156,16 @@ func tryDecodeBase64(raw string) ([]byte, error) {
156156
return nil, E.Cause(E.Errors(errors...), "try decoding base64")
157157
}
158158

159-
// Simple implementation of cert chain hash - you may need to adjust this
160-
func certChainHash(rawCerts [][]byte) (hash []byte) {
161-
// This is a simplified implementation. You might need to implement
162-
// the exact hash calculation method used by juicity
163-
// For now, return the first cert's hash as placeholder
164-
// if len(rawCerts) > 0 {
165-
// return rawCerts[0][:32] // simplified
166-
// }
167-
// return nil
168-
for _, cert := range rawCerts {
169-
sum := sha256.Sum256(cert)
170-
hash = append(hash, sum[:]...)
171-
}
172-
return
159+
func certChainHash(rawCerts [][]byte) []byte {
160+
var chainHash []byte
161+
for _, cert := range rawCerts {
162+
certHash := sha256.Sum256(cert)
163+
if chainHash == nil {
164+
chainHash = certHash[:]
165+
} else {
166+
newHash := sha256.Sum256(append(chainHash, certHash[:]...))
167+
chainHash = newHash[:]
168+
}
169+
}
170+
return chainHash
173171
}

0 commit comments

Comments
 (0)