请求host不匹配时重新生成证书
This commit is contained in:
+1
-1
@@ -13,7 +13,7 @@ tokio = { version = "1.47.5", features = ["full"] }
|
||||
tokio-rustls = "0.26"
|
||||
rustls-pemfile = "2"
|
||||
jni = "0.22.4"
|
||||
rcgen = "0.14"
|
||||
rcgen = { version = "0.14", features = ["x509-parser"] }
|
||||
anyhow = "1"
|
||||
bytes = "1"
|
||||
tokio-util = "0.7.20"
|
||||
|
||||
+4
-147
@@ -1,156 +1,13 @@
|
||||
use rcgen::{
|
||||
CertificateParams, DistinguishedName, DnType, ExtendedKeyUsagePurpose, IsCa, KeyPair,
|
||||
KeyUsagePurpose, SanType,
|
||||
};
|
||||
use std::{env, fs, path::PathBuf};
|
||||
|
||||
const CA_KEY: &str = "-----BEGIN PRIVATE KEY-----
|
||||
MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAShZANiAAQTglHNUqyS
|
||||
mMHIqtl3Mh3rl+cJvQtMoKylXcitUdz8nRWJoVl+OlEg4e/WMcY+GDXKyuKYaaYu
|
||||
FjHoooGBq1ZhbcRdkYq8CfOrDmPPeSqk3O1zh743u6VpVJ8cArJw7Wc=
|
||||
-----END PRIVATE KEY-----";
|
||||
|
||||
const CA_CHAIN: &str = "-----BEGIN PRIVATE KEY-----
|
||||
MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAShZANiAAQTglHNUqyS
|
||||
mMHIqtl3Mh3rl+cJvQtMoKylXcitUdz8nRWJoVl+OlEg4e/WMcY+GDXKyuKYaaYu
|
||||
FjHoooGBq1ZhbcRdkYq8CfOrDmPPeSqk3O1zh743u6VpVJ8cArJw7Wc=
|
||||
-----END PRIVATE KEY-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDaDCCAu6gAwIBAgIPKWkmwiejoVJjkrGUgqUEMAoGCCqGSM49BAMDMFYxCzAJ
|
||||
BgNVBAYTAklOMSUwIwYDVQQKDBxlTXVkaHJhIFRlY2hub2xvZ2llcyBMaW1pdGVk
|
||||
MSAwHgYDVQQDDBdlbVNpZ24gUm9vdCBUTFMgQ0EgLSBHMzAeFw0yNTA0MzAxNTM3
|
||||
NDFaFw00MDA0MjgxODMwMDBaMEAxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtlTXVk
|
||||
aHJhIEluYzEbMBkGA1UEAwwSRU0gT1YgVExTIENBIC0gRzNDMHYwEAYHKoZIzj0C
|
||||
AQYFK4EEACIDYgAEE4JRzVKskpjByKrZdzId65fnCb0LTKCspV3IrVHc/J0ViaFZ
|
||||
fjpRIOHv1jHGPhg1ysrimGmmLhYx6KKBgatWYW3EXZGKvAnzqw5jz3kqpNztc4e+
|
||||
N7ulaVSfHAKycO1no4IBljCCAZIwHwYDVR0jBBgwFoAU4TqrIXzx401UrtRyt/2L
|
||||
WUIgkFQwHQYDVR0OBBYEFDTq6gzryQra+dDF4u2beMmHnhtsMB0GA1UdJQQWMBQG
|
||||
CCsGAQUFBwMBBggrBgEFBQcDAjBdBgNVHSAEVjBUMDkGCysGAQQBg44hAQABMCow
|
||||
KAYIKwYBBQUHAgEWHGh0dHA6Ly9yZXBvc2l0b3J5LmVtc2lnbi5jb20wDQYLKwYB
|
||||
BAGDjiEBAm4wCAYGZ4EMAQICMHUGCCsGAQUFBwEBBGkwZzAkBggrBgEFBQcwAYYY
|
||||
aHR0cDovL29jc3AtYy5lbXNpZ24uY29tMD8GCCsGAQUFBzAChjNodHRwOi8vcmVw
|
||||
b3NpdG9yeS5lbXNpZ24uY29tL2NlcnRzL0VNT1ZUTFNDQUczQy5wN2MwNwYDVR0f
|
||||
BDAwLjAsoCqgKIYmaHR0cDovL2NybC5lbXNpZ24uY29tLz9yb290dGxzY2FnMy5j
|
||||
cmwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwCgYIKoZIzj0E
|
||||
AwMDaAAwZQIxAMSZt2xsPeumRdeSMSQ3CbzIZeQ/q7GDCWfeE1Pq+mhgA6gRM44c
|
||||
NVF3xJ+wsxJk+wIwFJbaDMokD8Ei6eNOiA8yUMYvfzQyngmrhZQMdPCK1SewrR79
|
||||
4rSb9vGqxZzelQth
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDdDCCAvmgAwIBAgIQAJYaPvsTWVdu0EAFtGhKWzAKBggqhkjOPQQDAzBrMQsw
|
||||
CQYDVQQGEwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhy
|
||||
YSBUZWNobm9sb2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290
|
||||
IENBIC0gRzMwHhcNMjQwNzExMTU0MzM2WhcNMzkwNzA5MTgzMDAwWjBWMQswCQYD
|
||||
VQQGEwJJTjElMCMGA1UECgwcZU11ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRlZDEg
|
||||
MB4GA1UEAwwXZW1TaWduIFJvb3QgVExTIENBIC0gRzMwdjAQBgcqhkjOPQIBBgUr
|
||||
gQQAIgNiAARfD9w/q1cWxL6D9bMGH6CQ4u+gAV6GN9s+PwQx9Bl4hC+0iU8QOk8m
|
||||
WO/RwXQy10DCSxT8vSIJClHKp+Ao4qJKdYeV0SwZ7LMQziyLfxIUOY6Grja/sQFw
|
||||
LKYf8EdDXAyjggF1MIIBcTAfBgNVHSMEGDAWgBR8XQKEE9TMipuBzhccLikenEhj
|
||||
QjAdBgNVHQ4EFgQU4TqrIXzx401UrtRyt/2LWUIgkFQwHQYDVR0lBBYwFAYIKwYB
|
||||
BQUHAwEGCCsGAQUFBwMCMD0GA1UdIAQ2MDQwMgYEVR0gADAqMCgGCCsGAQUFBwIB
|
||||
FhxodHRwOi8vcmVwb3NpdG9yeS5lbXNpZ24uY29tMHgGCCsGAQUFBwEBBGwwajAi
|
||||
BggrBgEFBQcwAYYWaHR0cDovL29jc3AuZW1zaWduLmNvbTBEBggrBgEFBQcwAoY4
|
||||
aHR0cDovL3JlcG9zaXRvcnkuZW1zaWduLmNvbS9jZXJ0cy9lbVNpZ25FQ0NSb290
|
||||
Q0FHMy5jcnQwMwYDVR0fBCwwKjAooCagJIYiaHR0cDovL2NybC5lbXNpZ24uY29t
|
||||
P1Jvb3RDQUczLmNybDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIB
|
||||
ATAKBggqhkjOPQQDAwNpADBmAjEAt9CIpRGp46QHQYDUBnLKw+vvK0/h9vmilEAg
|
||||
TroUl/anH4x2rXHqiCtBTBfgKDegAjEA8WpG1LTZdAIL0FSYqxXeREaeTSxEofM0
|
||||
1jJH3jGvCXC79LU9UPJ2o3TU+UIj2gMV
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDzzCCAregAwIBAgIRAI1aSBGTnvRH13ERzdmEmukwDQYJKoZIhvcNAQELBQAw
|
||||
ezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
|
||||
A1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNV
|
||||
BAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczAeFw0yNTA1MTQwMDAwMDBaFw0y
|
||||
ODEyMzEyMzU5NTlaMEAxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtlTXVkaHJhIElu
|
||||
YzEbMBkGA1UEAwwSRU0gT1YgVExTIENBIC0gRzNDMHYwEAYHKoZIzj0CAQYFK4EE
|
||||
ACIDYgAEE4JRzVKskpjByKrZdzId65fnCb0LTKCspV3IrVHc/J0ViaFZfjpRIOHv
|
||||
1jHGPhg1ysrimGmmLhYx6KKBgatWYW3EXZGKvAnzqw5jz3kqpNztc4e+N7ulaVSf
|
||||
HAKycO1no4IBNTCCATEwHwYDVR0jBBgwFoAUoBEKIz6W8Qfs4q8p74Klf9AwpLQw
|
||||
HQYDVR0OBBYEFDTq6gzryQra+dDF4u2beMmHnhtsMA4GA1UdDwEB/wQEAwIBBjAS
|
||||
BgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcD
|
||||
AjAxBgNVHSAEKjAoMAgGBmeBDAECAjANBgsrBgEEAYOOIQEAATANBgsrBgEEAYOO
|
||||
IQECbjBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9B
|
||||
QUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDA0BggrBgEFBQcBAQQoMCYwJAYIKwYB
|
||||
BQUHMAGGGGh0dHA6Ly9vY3NwLmNvbW9kb2NhLmNvbTANBgkqhkiG9w0BAQsFAAOC
|
||||
AQEAHlbhZpjhkPEAQVhPHRtBDaHqiisVD20pQ1TUeSx3l62Qe7NQ0b7vWQy7DN0i
|
||||
KOu7BGpP5EnIjvXbz9FhpfPJ4FhKEt3tQ51rqXz2oD8vRaryvExWPobDUz3j7HN6
|
||||
OzBsVMIe3PD7AAkpky0x8zMSi/ep5R9OMzybmNptbtppyuB5ytgq+h7lQdRIP+CQ
|
||||
TaJX3qTgfMqzntYre5GljmEB5hU2/rSn8zoSU2APkJidlZFpghtgbD4I2VRHZvCw
|
||||
FFr/SrFM9pwrgbJNZ1MtyTSVgI5NlNb0aMfLR+/r6jJt3ROeY2yh2QQ2dx+Yqgdb
|
||||
uL5ChFWOQU+ErSDnUq7yr+pjBQ==
|
||||
-----END CERTIFICATE-----";
|
||||
|
||||
/// 证书里要覆盖的域名。抓包目标 host 必须在这里面,否则 App 会报证书不受信任。
|
||||
const SAN: &[&str] = &[
|
||||
// 国服
|
||||
"*.dailygn.com",
|
||||
// 日服
|
||||
"production-game-api.sekai.colorfulpalette.org",
|
||||
"*.sekai.colorfulpalette.org",
|
||||
// 国际服
|
||||
"n-production-game-api.sekai-en.com",
|
||||
"*.sekai-en.com",
|
||||
// 台服 / 韩服(cdn 和 https 两个域都会出现)
|
||||
"*.bytedgame.com",
|
||||
"mk-zian-obt-cdn.bytedgame.com",
|
||||
"mk-zian-obt-https.bytedgame.com",
|
||||
"mkkorea-obt-prod01-cdn.bytedgame.com",
|
||||
"mkkorea-obt-prod01-https.bytedgame.com",
|
||||
];
|
||||
#[path = "src/cert.rs"]
|
||||
mod cert;
|
||||
use cert::*;
|
||||
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||
|
||||
let ca_key = KeyPair::from_pem(CA_KEY).expect("parse CA key failed");
|
||||
let first_ca_pem = first_pem_certificate(CA_CHAIN);
|
||||
let issuer = rcgen::Issuer::from_ca_cert_pem(&first_ca_pem, ca_key)
|
||||
.expect("build Issuer from CA cert + key failed");
|
||||
|
||||
let mut leaf_params = CertificateParams::default();
|
||||
let mut dn = DistinguishedName::new();
|
||||
dn.push(DnType::CommonName, SAN[0]);
|
||||
leaf_params.distinguished_name = dn;
|
||||
|
||||
leaf_params.subject_alt_names = SAN
|
||||
.iter()
|
||||
.map(|d| SanType::DnsName((*d).try_into().unwrap()))
|
||||
.collect();
|
||||
|
||||
leaf_params.key_usages = vec![
|
||||
KeyUsagePurpose::DigitalSignature,
|
||||
KeyUsagePurpose::KeyEncipherment,
|
||||
];
|
||||
leaf_params.extended_key_usages = vec![ExtendedKeyUsagePurpose::ServerAuth];
|
||||
leaf_params.is_ca = IsCa::NoCa;
|
||||
// AKI 必须和签发者证书里的 SKI 一致,rcgen 的 Issuer 会自动从 CA 证书里取
|
||||
leaf_params.use_authority_key_identifier_extension = true;
|
||||
// 有效期落在 CA 的有效期内(2025-04-30 ~ 2040-04-28)
|
||||
leaf_params.not_before = rcgen::date_time_ymd(2025, 5, 1);
|
||||
leaf_params.not_after = rcgen::date_time_ymd(2038, 1, 1);
|
||||
|
||||
let leaf_key = KeyPair::generate().expect("generate leaf key failed");
|
||||
let leaf_cert = leaf_params
|
||||
.signed_by(&leaf_key, &issuer)
|
||||
.expect("sign leaf cert failed");
|
||||
|
||||
// leaf + 完整 CA 链(两个 G3C 交叉签名都带上,让验证器自己选路径)
|
||||
let fullchain_pem = format!("{}{}", leaf_cert.pem(), CA_CHAIN);
|
||||
let (fullchain_pem, leaf_key_pem) = sign(SAN.iter());
|
||||
fs::write(out_dir.join("fullchain.pem"), fullchain_pem).unwrap();
|
||||
|
||||
let leaf_key_pem = leaf_key.serialize_pem();
|
||||
fs::write(out_dir.join("leaf.key"), leaf_key_pem).unwrap();
|
||||
}
|
||||
|
||||
fn first_pem_certificate(pem: &str) -> String {
|
||||
let begin = "-----BEGIN CERTIFICATE-----";
|
||||
let end = "-----END CERTIFICATE-----";
|
||||
if let (Some(s), Some(e)) = (pem.find(begin), pem.find(end)) {
|
||||
pem[s..e + end.len()].to_string()
|
||||
} else {
|
||||
panic!("no certificate found in CA chain PEM");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
use crate::cert::{check_cert_included, sign};
|
||||
use crate::info;
|
||||
use rustls_pemfile::{certs, private_key};
|
||||
use std::io::BufReader;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub fn build_tls_acceptor(target_hosts: Vec<String>) -> Arc<rustls::ServerConfig> {
|
||||
let is_included_built_in = check_cert_included(target_hosts.iter().map(|s| s.as_str()));
|
||||
let (cert_chain, key) = if is_included_built_in {
|
||||
let fullchain = include_bytes!(concat!(env!("OUT_DIR"), "/fullchain.pem"));
|
||||
let leaf_key = include_bytes!(concat!(env!("OUT_DIR"), "/leaf.key"));
|
||||
let cert_chain = certs(&mut BufReader::new(&fullchain[..]))
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.unwrap();
|
||||
let key = private_key(&mut BufReader::new(&leaf_key[..]))
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
(cert_chain, key)
|
||||
} else {
|
||||
info!("Target hosts not match built-in cert, generating new cert");
|
||||
let target = target_hosts.iter().map(|s| s.as_str()).collect::<Vec<_>>();
|
||||
let (fullchain, leaf_key) = sign(target.iter());
|
||||
let cert_chain = certs(&mut BufReader::new(fullchain.as_bytes()))
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.unwrap();
|
||||
let key = private_key(&mut BufReader::new(leaf_key.as_bytes()))
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
(cert_chain, key)
|
||||
};
|
||||
|
||||
let mut config = rustls::ServerConfig::builder()
|
||||
.with_no_client_auth()
|
||||
.with_single_cert(cert_chain, key)
|
||||
.unwrap();
|
||||
|
||||
config.alpn_protocols = vec![b"http/1.1".to_vec()];
|
||||
|
||||
Arc::new(config)
|
||||
}
|
||||
+171
-19
@@ -1,24 +1,176 @@
|
||||
use rustls_pemfile::{certs, private_key};
|
||||
use std::io::BufReader;
|
||||
use std::sync::Arc;
|
||||
use rcgen::{
|
||||
CertificateParams, DistinguishedName, DnType, ExtendedKeyUsagePurpose, IsCa, KeyPair,
|
||||
KeyUsagePurpose, SanType,
|
||||
};
|
||||
|
||||
pub fn build_tls_acceptor() -> Arc<rustls::ServerConfig> {
|
||||
let fullchain = include_bytes!(concat!(env!("OUT_DIR"), "/fullchain.pem"));
|
||||
let leaf_key = include_bytes!(concat!(env!("OUT_DIR"), "/leaf.key"));
|
||||
const CA_KEY: &str = "-----BEGIN PRIVATE KEY-----
|
||||
MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAShZANiAAQTglHNUqyS
|
||||
mMHIqtl3Mh3rl+cJvQtMoKylXcitUdz8nRWJoVl+OlEg4e/WMcY+GDXKyuKYaaYu
|
||||
FjHoooGBq1ZhbcRdkYq8CfOrDmPPeSqk3O1zh743u6VpVJ8cArJw7Wc=
|
||||
-----END PRIVATE KEY-----";
|
||||
|
||||
let cert_chain = certs(&mut BufReader::new(&fullchain[..]))
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.unwrap();
|
||||
let key = private_key(&mut BufReader::new(&leaf_key[..]))
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
const CA_CHAIN: &str = "-----BEGIN PRIVATE KEY-----
|
||||
MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAShZANiAAQTglHNUqyS
|
||||
mMHIqtl3Mh3rl+cJvQtMoKylXcitUdz8nRWJoVl+OlEg4e/WMcY+GDXKyuKYaaYu
|
||||
FjHoooGBq1ZhbcRdkYq8CfOrDmPPeSqk3O1zh743u6VpVJ8cArJw7Wc=
|
||||
-----END PRIVATE KEY-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDaDCCAu6gAwIBAgIPKWkmwiejoVJjkrGUgqUEMAoGCCqGSM49BAMDMFYxCzAJ
|
||||
BgNVBAYTAklOMSUwIwYDVQQKDBxlTXVkaHJhIFRlY2hub2xvZ2llcyBMaW1pdGVk
|
||||
MSAwHgYDVQQDDBdlbVNpZ24gUm9vdCBUTFMgQ0EgLSBHMzAeFw0yNTA0MzAxNTM3
|
||||
NDFaFw00MDA0MjgxODMwMDBaMEAxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtlTXVk
|
||||
aHJhIEluYzEbMBkGA1UEAwwSRU0gT1YgVExTIENBIC0gRzNDMHYwEAYHKoZIzj0C
|
||||
AQYFK4EEACIDYgAEE4JRzVKskpjByKrZdzId65fnCb0LTKCspV3IrVHc/J0ViaFZ
|
||||
fjpRIOHv1jHGPhg1ysrimGmmLhYx6KKBgatWYW3EXZGKvAnzqw5jz3kqpNztc4e+
|
||||
N7ulaVSfHAKycO1no4IBljCCAZIwHwYDVR0jBBgwFoAU4TqrIXzx401UrtRyt/2L
|
||||
WUIgkFQwHQYDVR0OBBYEFDTq6gzryQra+dDF4u2beMmHnhtsMB0GA1UdJQQWMBQG
|
||||
CCsGAQUFBwMBBggrBgEFBQcDAjBdBgNVHSAEVjBUMDkGCysGAQQBg44hAQABMCow
|
||||
KAYIKwYBBQUHAgEWHGh0dHA6Ly9yZXBvc2l0b3J5LmVtc2lnbi5jb20wDQYLKwYB
|
||||
BAGDjiEBAm4wCAYGZ4EMAQICMHUGCCsGAQUFBwEBBGkwZzAkBggrBgEFBQcwAYYY
|
||||
aHR0cDovL29jc3AtYy5lbXNpZ24uY29tMD8GCCsGAQUFBzAChjNodHRwOi8vcmVw
|
||||
b3NpdG9yeS5lbXNpZ24uY29tL2NlcnRzL0VNT1ZUTFNDQUczQy5wN2MwNwYDVR0f
|
||||
BDAwLjAsoCqgKIYmaHR0cDovL2NybC5lbXNpZ24uY29tLz9yb290dGxzY2FnMy5j
|
||||
cmwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwCgYIKoZIzj0E
|
||||
AwMDaAAwZQIxAMSZt2xsPeumRdeSMSQ3CbzIZeQ/q7GDCWfeE1Pq+mhgA6gRM44c
|
||||
NVF3xJ+wsxJk+wIwFJbaDMokD8Ei6eNOiA8yUMYvfzQyngmrhZQMdPCK1SewrR79
|
||||
4rSb9vGqxZzelQth
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDdDCCAvmgAwIBAgIQAJYaPvsTWVdu0EAFtGhKWzAKBggqhkjOPQQDAzBrMQsw
|
||||
CQYDVQQGEwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhy
|
||||
YSBUZWNobm9sb2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290
|
||||
IENBIC0gRzMwHhcNMjQwNzExMTU0MzM2WhcNMzkwNzA5MTgzMDAwWjBWMQswCQYD
|
||||
VQQGEwJJTjElMCMGA1UECgwcZU11ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRlZDEg
|
||||
MB4GA1UEAwwXZW1TaWduIFJvb3QgVExTIENBIC0gRzMwdjAQBgcqhkjOPQIBBgUr
|
||||
gQQAIgNiAARfD9w/q1cWxL6D9bMGH6CQ4u+gAV6GN9s+PwQx9Bl4hC+0iU8QOk8m
|
||||
WO/RwXQy10DCSxT8vSIJClHKp+Ao4qJKdYeV0SwZ7LMQziyLfxIUOY6Grja/sQFw
|
||||
LKYf8EdDXAyjggF1MIIBcTAfBgNVHSMEGDAWgBR8XQKEE9TMipuBzhccLikenEhj
|
||||
QjAdBgNVHQ4EFgQU4TqrIXzx401UrtRyt/2LWUIgkFQwHQYDVR0lBBYwFAYIKwYB
|
||||
BQUHAwEGCCsGAQUFBwMCMD0GA1UdIAQ2MDQwMgYEVR0gADAqMCgGCCsGAQUFBwIB
|
||||
FhxodHRwOi8vcmVwb3NpdG9yeS5lbXNpZ24uY29tMHgGCCsGAQUFBwEBBGwwajAi
|
||||
BggrBgEFBQcwAYYWaHR0cDovL29jc3AuZW1zaWduLmNvbTBEBggrBgEFBQcwAoY4
|
||||
aHR0cDovL3JlcG9zaXRvcnkuZW1zaWduLmNvbS9jZXJ0cy9lbVNpZ25FQ0NSb290
|
||||
Q0FHMy5jcnQwMwYDVR0fBCwwKjAooCagJIYiaHR0cDovL2NybC5lbXNpZ24uY29t
|
||||
P1Jvb3RDQUczLmNybDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIB
|
||||
ATAKBggqhkjOPQQDAwNpADBmAjEAt9CIpRGp46QHQYDUBnLKw+vvK0/h9vmilEAg
|
||||
TroUl/anH4x2rXHqiCtBTBfgKDegAjEA8WpG1LTZdAIL0FSYqxXeREaeTSxEofM0
|
||||
1jJH3jGvCXC79LU9UPJ2o3TU+UIj2gMV
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDzzCCAregAwIBAgIRAI1aSBGTnvRH13ERzdmEmukwDQYJKoZIhvcNAQELBQAw
|
||||
ezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
|
||||
A1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNV
|
||||
BAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczAeFw0yNTA1MTQwMDAwMDBaFw0y
|
||||
ODEyMzEyMzU5NTlaMEAxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtlTXVkaHJhIElu
|
||||
YzEbMBkGA1UEAwwSRU0gT1YgVExTIENBIC0gRzNDMHYwEAYHKoZIzj0CAQYFK4EE
|
||||
ACIDYgAEE4JRzVKskpjByKrZdzId65fnCb0LTKCspV3IrVHc/J0ViaFZfjpRIOHv
|
||||
1jHGPhg1ysrimGmmLhYx6KKBgatWYW3EXZGKvAnzqw5jz3kqpNztc4e+N7ulaVSf
|
||||
HAKycO1no4IBNTCCATEwHwYDVR0jBBgwFoAUoBEKIz6W8Qfs4q8p74Klf9AwpLQw
|
||||
HQYDVR0OBBYEFDTq6gzryQra+dDF4u2beMmHnhtsMA4GA1UdDwEB/wQEAwIBBjAS
|
||||
BgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcD
|
||||
AjAxBgNVHSAEKjAoMAgGBmeBDAECAjANBgsrBgEEAYOOIQEAATANBgsrBgEEAYOO
|
||||
IQECbjBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9B
|
||||
QUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDA0BggrBgEFBQcBAQQoMCYwJAYIKwYB
|
||||
BQUHMAGGGGh0dHA6Ly9vY3NwLmNvbW9kb2NhLmNvbTANBgkqhkiG9w0BAQsFAAOC
|
||||
AQEAHlbhZpjhkPEAQVhPHRtBDaHqiisVD20pQ1TUeSx3l62Qe7NQ0b7vWQy7DN0i
|
||||
KOu7BGpP5EnIjvXbz9FhpfPJ4FhKEt3tQ51rqXz2oD8vRaryvExWPobDUz3j7HN6
|
||||
OzBsVMIe3PD7AAkpky0x8zMSi/ep5R9OMzybmNptbtppyuB5ytgq+h7lQdRIP+CQ
|
||||
TaJX3qTgfMqzntYre5GljmEB5hU2/rSn8zoSU2APkJidlZFpghtgbD4I2VRHZvCw
|
||||
FFr/SrFM9pwrgbJNZ1MtyTSVgI5NlNb0aMfLR+/r6jJt3ROeY2yh2QQ2dx+Yqgdb
|
||||
uL5ChFWOQU+ErSDnUq7yr+pjBQ==
|
||||
-----END CERTIFICATE-----";
|
||||
|
||||
let mut config = rustls::ServerConfig::builder()
|
||||
.with_no_client_auth()
|
||||
.with_single_cert(cert_chain, key)
|
||||
.unwrap();
|
||||
pub const SAN: &[&str] = &[
|
||||
// 国服
|
||||
"*.dailygn.com",
|
||||
// 日服
|
||||
"production-game-api.sekai.colorfulpalette.org",
|
||||
"*.sekai.colorfulpalette.org",
|
||||
// 国际服
|
||||
"n-production-game-api.sekai-en.com",
|
||||
"*.sekai-en.com",
|
||||
// 台服 / 韩服(cdn 和 https 两个域都会出现)
|
||||
"*.bytedgame.com",
|
||||
"mk-zian-obt-cdn.bytedgame.com",
|
||||
"mk-zian-obt-https.bytedgame.com",
|
||||
"mkkorea-obt-prod01-cdn.bytedgame.com",
|
||||
"mkkorea-obt-prod01-https.bytedgame.com",
|
||||
];
|
||||
|
||||
config.alpn_protocols = vec![b"http/1.1".to_vec()];
|
||||
#[allow(unused)] // caused in re-build in build.rs
|
||||
pub fn check_cert_included<'a>(target_hosts: impl Iterator<Item = &'a str>) -> bool {
|
||||
for host in target_hosts {
|
||||
if SAN.contains(&host) {
|
||||
continue;
|
||||
}
|
||||
let mut found = false;
|
||||
for included_host in SAN {
|
||||
if !included_host.starts_with("*.") {
|
||||
continue;
|
||||
}
|
||||
let without_star = &included_host[2..];
|
||||
let host_without_first = host.split_once(".");
|
||||
if let Some(parts) = host_without_first
|
||||
&& parts.1 == without_star
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
Arc::new(config)
|
||||
}
|
||||
pub fn sign<'a>(target_hosts: impl Iterator<Item = &'a &'a str>) -> (String, String) {
|
||||
let mut target_hosts = target_hosts.peekable();
|
||||
|
||||
let ca_key = KeyPair::from_pem(CA_KEY).expect("parse CA key failed");
|
||||
let first_ca_pem = first_pem_certificate(CA_CHAIN);
|
||||
let issuer = rcgen::Issuer::from_ca_cert_pem(&first_ca_pem, ca_key)
|
||||
.expect("build Issuer from CA cert + key failed");
|
||||
|
||||
let mut leaf_params = CertificateParams::default();
|
||||
let mut dn = DistinguishedName::new();
|
||||
dn.push(DnType::CommonName, target_hosts.peek().unwrap().to_string());
|
||||
leaf_params.distinguished_name = dn;
|
||||
|
||||
leaf_params.subject_alt_names = target_hosts
|
||||
.map(|d| SanType::DnsName((*d).try_into().unwrap()))
|
||||
.collect();
|
||||
|
||||
leaf_params.key_usages = vec![
|
||||
KeyUsagePurpose::DigitalSignature,
|
||||
KeyUsagePurpose::KeyEncipherment,
|
||||
];
|
||||
leaf_params.extended_key_usages = vec![ExtendedKeyUsagePurpose::ServerAuth];
|
||||
leaf_params.is_ca = IsCa::NoCa;
|
||||
// AKI 必须和签发者证书里的 SKI 一致,rcgen 的 Issuer 会自动从 CA 证书里取
|
||||
leaf_params.use_authority_key_identifier_extension = true;
|
||||
// 有效期落在 CA 的有效期内(2025-04-30 ~ 2040-04-28)
|
||||
leaf_params.not_before = rcgen::date_time_ymd(2025, 5, 1);
|
||||
leaf_params.not_after = rcgen::date_time_ymd(2038, 1, 1);
|
||||
|
||||
let leaf_key = KeyPair::generate().expect("generate leaf key failed");
|
||||
let leaf_cert = leaf_params
|
||||
.signed_by(&leaf_key, &issuer)
|
||||
.expect("sign leaf cert failed");
|
||||
|
||||
let fullchain_pem = format!("{}{}", leaf_cert.pem(), CA_CHAIN);
|
||||
let leaf_key_pem = leaf_key.serialize_pem();
|
||||
(fullchain_pem, leaf_key_pem)
|
||||
}
|
||||
|
||||
fn first_pem_certificate(pem: &str) -> String {
|
||||
let begin = "-----BEGIN CERTIFICATE-----";
|
||||
let end = "-----END CERTIFICATE-----";
|
||||
if let (Some(s), Some(e)) = (pem.find(begin), pem.find(end)) {
|
||||
pem[s..e + end.len()].to_string()
|
||||
} else {
|
||||
panic!("no certificate found in CA chain PEM");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ mod cert;
|
||||
mod mitm;
|
||||
mod mut_qstring;
|
||||
mod tun;
|
||||
mod acceptor;
|
||||
|
||||
use crate::mitm::{ProxyConfig, StepSpec, Waiter, Waiters, run_session};
|
||||
use jni::objects::{Global, JClass, JObject, JString};
|
||||
|
||||
+20
-13
@@ -1,8 +1,8 @@
|
||||
use crate::cert::build_tls_acceptor;
|
||||
use crate::mut_qstring::MutQString;
|
||||
use crate::tun::{BorrowedTunIo, RewindStream};
|
||||
use crate::{error, info, warn};
|
||||
use bytes::Bytes;
|
||||
use http::Uri;
|
||||
use http_body_util::{BodyExt, Full};
|
||||
use hyper::body::Incoming;
|
||||
use hyper_util::rt::TokioIo;
|
||||
@@ -14,13 +14,13 @@ use std::net::SocketAddr;
|
||||
use std::os::fd::RawFd;
|
||||
use std::sync::{Arc, Mutex as StdMutex, OnceLock};
|
||||
use std::time::Duration;
|
||||
use http::Uri;
|
||||
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
|
||||
use tokio::net::UdpSocket;
|
||||
use tokio::task::JoinSet;
|
||||
use tokio_rustls::StartHandshake;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tun_rs::BorrowedAsyncDevice;
|
||||
use crate::acceptor::build_tls_acceptor;
|
||||
|
||||
static ROOT_STORE: OnceLock<Arc<RootCertStore>> = OnceLock::new();
|
||||
|
||||
@@ -145,12 +145,11 @@ where
|
||||
return passthrough_cancel_wrapped(io, dst, None, upstream, &cancel).await;
|
||||
};
|
||||
|
||||
// 崩溃上报之类的域名直接拒绝,不建立上游连接
|
||||
if let Some(host) = sni.as_deref() {
|
||||
if is_blocked_host(host) {
|
||||
warn!("blocked host {host} ({dst:?}), connection rejected");
|
||||
return Ok(());
|
||||
}
|
||||
if let Some(host) = sni.as_deref()
|
||||
&& is_blocked_host(host)
|
||||
{
|
||||
warn!("blocked host {host} ({dst:?}), connection rejected");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let host_matched = sni
|
||||
@@ -382,7 +381,6 @@ fn force_full_mysekai(uri: &Uri) -> Option<Uri> {
|
||||
Uri::try_from(format!("{}?{}", uri.path(), query).as_str()).ok()
|
||||
}
|
||||
|
||||
|
||||
#[derive(serde::Deserialize, Debug, Clone)]
|
||||
pub struct StepSpec {
|
||||
pub id: String,
|
||||
@@ -450,9 +448,16 @@ impl ProxyConfig {
|
||||
None
|
||||
} else {
|
||||
let pass = url.password().unwrap_or("");
|
||||
Some(base64_encode(format!("{}:{}", url.username(), pass).as_bytes()))
|
||||
Some(base64_encode(
|
||||
format!("{}:{}", url.username(), pass).as_bytes(),
|
||||
))
|
||||
};
|
||||
Ok(Some(Self { tls, host, port, auth }))
|
||||
Ok(Some(Self {
|
||||
tls,
|
||||
host,
|
||||
port,
|
||||
auth,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -473,7 +478,9 @@ struct Upstream {
|
||||
|
||||
impl Upstream {
|
||||
fn new(proxy: Option<ProxyConfig>) -> Self {
|
||||
Self { proxy: proxy.map(Arc::new) }
|
||||
Self {
|
||||
proxy: proxy.map(Arc::new),
|
||||
}
|
||||
}
|
||||
|
||||
async fn connect(&self, dst: SocketAddr, host: Option<&str>) -> anyhow::Result<UpstreamIo> {
|
||||
@@ -571,7 +578,7 @@ pub async fn run_session(
|
||||
let mut ip_stack = ipstack::IpStack::new(ipstack_config, tun_io);
|
||||
let mut bridge_tasks = JoinSet::new();
|
||||
|
||||
let tls_acceptor = build_tls_acceptor();
|
||||
let tls_acceptor = build_tls_acceptor(target_hosts.clone());
|
||||
let upstream = Upstream::new(proxy);
|
||||
|
||||
loop {
|
||||
|
||||
Reference in New Issue
Block a user