mirror of
https://github.com/shtorm-7/sing-box-extended.git
synced 2026-09-15 21:00:27 +00:00
certificate: Replace platform bridge with CGO JNI
This commit is contained in:
@@ -29,7 +29,6 @@ type PlatformInterface interface {
|
|||||||
ClearDNSCache()
|
ClearDNSCache()
|
||||||
RequestPermissionForWIFIState() error
|
RequestPermissionForWIFIState() error
|
||||||
ReadWIFIState() WIFIState
|
ReadWIFIState() WIFIState
|
||||||
SystemCertificates() []string
|
|
||||||
|
|
||||||
UsePlatformConnectionOwnerFinder() bool
|
UsePlatformConnectionOwnerFinder() bool
|
||||||
FindConnectionOwner(request *FindConnectionOwnerRequest) (*ConnectionOwner, error)
|
FindConnectionOwner(request *FindConnectionOwnerRequest) (*ConnectionOwner, error)
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ func New(options Options) (*Box, error) {
|
|||||||
len(certificateOptions.Certificate) > 0 ||
|
len(certificateOptions.Certificate) > 0 ||
|
||||||
len(certificateOptions.CertificatePath) > 0 ||
|
len(certificateOptions.CertificatePath) > 0 ||
|
||||||
len(certificateOptions.CertificateDirectoryPath) > 0 {
|
len(certificateOptions.CertificateDirectoryPath) > 0 {
|
||||||
certificateStore, err := certificate.NewStore(ctx, logFactory.NewLogger("certificate"), certificateOptions)
|
certificateStore, err := certificate.NewStore(logFactory.NewLogger("certificate"), certificateOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package certificate
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
@@ -16,7 +15,6 @@ import (
|
|||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/logger"
|
"github.com/sagernet/sing/common/logger"
|
||||||
"github.com/sagernet/sing/service"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ adapter.CertificateStore = (*Store)(nil)
|
var _ adapter.CertificateStore = (*Store)(nil)
|
||||||
@@ -34,7 +32,7 @@ type Store struct {
|
|||||||
platform storePlatform
|
platform storePlatform
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewStore(ctx context.Context, logger logger.Logger, options option.CertificateOptions) (*Store, error) {
|
func NewStore(logger logger.Logger, options option.CertificateOptions) (*Store, error) {
|
||||||
storeType := options.Store
|
storeType := options.Store
|
||||||
if storeType == "" {
|
if storeType == "" {
|
||||||
storeType = C.CertificateStoreSystem
|
storeType = C.CertificateStoreSystem
|
||||||
@@ -43,14 +41,10 @@ func NewStore(ctx context.Context, logger logger.Logger, options option.Certific
|
|||||||
switch storeType {
|
switch storeType {
|
||||||
case C.CertificateStoreSystem:
|
case C.CertificateStoreSystem:
|
||||||
systemPool = x509.NewCertPool()
|
systemPool = x509.NewCertPool()
|
||||||
platformInterface := service.FromContext[adapter.PlatformInterface](ctx)
|
|
||||||
var systemValid bool
|
var systemValid bool
|
||||||
if platformInterface != nil {
|
for _, certificate := range systemCertificates() {
|
||||||
for _, cert := range platformInterface.SystemCertificates() {
|
systemPool.AddCert(certificate)
|
||||||
if systemPool.AppendCertsFromPEM([]byte(cert)) {
|
systemValid = true
|
||||||
systemValid = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if !systemValid {
|
if !systemValid {
|
||||||
certPool, err := x509.SystemCertPool()
|
certPool, err := x509.SystemCertPool()
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
#include <jni.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
void *box_system_certificates_der(uintptr_t vmPtr, int *out_length) {
|
||||||
|
*out_length = 0;
|
||||||
|
|
||||||
|
JavaVM *vm = (JavaVM *) vmPtr;
|
||||||
|
JNIEnv *env = NULL;
|
||||||
|
int attached = 0;
|
||||||
|
jint getEnvResult = (*vm)->GetEnv(vm, (void **) &env, JNI_VERSION_1_6);
|
||||||
|
if (getEnvResult == JNI_EDETACHED) {
|
||||||
|
if ((*vm)->AttachCurrentThread(vm, &env, NULL) != JNI_OK) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
attached = 1;
|
||||||
|
} else if (getEnvResult != JNI_OK) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char *result = NULL;
|
||||||
|
int resultLength = 0;
|
||||||
|
|
||||||
|
jclass keyStoreClass = (*env)->FindClass(env, "java/security/KeyStore");
|
||||||
|
jmethodID getInstance = (*env)->GetStaticMethodID(env, keyStoreClass, "getInstance", "(Ljava/lang/String;)Ljava/security/KeyStore;");
|
||||||
|
jstring storeName = (*env)->NewStringUTF(env, "AndroidCAStore");
|
||||||
|
jobject keyStore = (*env)->CallStaticObjectMethod(env, keyStoreClass, getInstance, storeName);
|
||||||
|
if ((*env)->ExceptionCheck(env) || keyStore == NULL) {
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
jmethodID load = (*env)->GetMethodID(env, keyStoreClass, "load", "(Ljava/io/InputStream;[C)V");
|
||||||
|
(*env)->CallVoidMethod(env, keyStore, load, NULL, NULL);
|
||||||
|
if ((*env)->ExceptionCheck(env)) {
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
jmethodID aliasesMethod = (*env)->GetMethodID(env, keyStoreClass, "aliases", "()Ljava/util/Enumeration;");
|
||||||
|
jmethodID getCertificate = (*env)->GetMethodID(env, keyStoreClass, "getCertificate", "(Ljava/lang/String;)Ljava/security/cert/Certificate;");
|
||||||
|
jobject aliases = (*env)->CallObjectMethod(env, keyStore, aliasesMethod);
|
||||||
|
if ((*env)->ExceptionCheck(env) || aliases == NULL) {
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
jclass enumerationClass = (*env)->FindClass(env, "java/util/Enumeration");
|
||||||
|
jmethodID hasMoreElements = (*env)->GetMethodID(env, enumerationClass, "hasMoreElements", "()Z");
|
||||||
|
jmethodID nextElement = (*env)->GetMethodID(env, enumerationClass, "nextElement", "()Ljava/lang/Object;");
|
||||||
|
|
||||||
|
jclass certificateClass = (*env)->FindClass(env, "java/security/cert/Certificate");
|
||||||
|
jmethodID getEncoded = (*env)->GetMethodID(env, certificateClass, "getEncoded", "()[B");
|
||||||
|
|
||||||
|
while ((*env)->CallBooleanMethod(env, aliases, hasMoreElements)) {
|
||||||
|
jstring alias = (jstring) (*env)->CallObjectMethod(env, aliases, nextElement);
|
||||||
|
jobject certificate = (*env)->CallObjectMethod(env, keyStore, getCertificate, alias);
|
||||||
|
(*env)->DeleteLocalRef(env, alias);
|
||||||
|
if ((*env)->ExceptionCheck(env) || certificate == NULL) {
|
||||||
|
(*env)->ExceptionClear(env);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
jbyteArray encoded = (jbyteArray) (*env)->CallObjectMethod(env, certificate, getEncoded);
|
||||||
|
(*env)->DeleteLocalRef(env, certificate);
|
||||||
|
if ((*env)->ExceptionCheck(env) || encoded == NULL) {
|
||||||
|
(*env)->ExceptionClear(env);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
jsize encodedLength = (*env)->GetArrayLength(env, encoded);
|
||||||
|
unsigned char *grown = realloc(result, resultLength + encodedLength);
|
||||||
|
if (grown == NULL) {
|
||||||
|
(*env)->DeleteLocalRef(env, encoded);
|
||||||
|
free(result);
|
||||||
|
result = NULL;
|
||||||
|
resultLength = 0;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
result = grown;
|
||||||
|
(*env)->GetByteArrayRegion(env, encoded, 0, encodedLength, (jbyte *) (result + resultLength));
|
||||||
|
resultLength += encodedLength;
|
||||||
|
(*env)->DeleteLocalRef(env, encoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
if ((*env)->ExceptionCheck(env)) {
|
||||||
|
(*env)->ExceptionClear(env);
|
||||||
|
}
|
||||||
|
if (attached) {
|
||||||
|
(*vm)->DetachCurrentThread(vm);
|
||||||
|
}
|
||||||
|
*out_length = resultLength;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
//go:build android
|
||||||
|
|
||||||
|
package certificate
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
extern void *box_system_certificates_der(uintptr_t vm, int *out_length);
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/x509"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/common/jni"
|
||||||
|
)
|
||||||
|
|
||||||
|
func systemCertificates() []*x509.Certificate {
|
||||||
|
vm := jni.VM()
|
||||||
|
if vm == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var length C.int
|
||||||
|
pointer := C.box_system_certificates_der(C.uintptr_t(vm), &length)
|
||||||
|
if pointer == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
defer C.free(pointer)
|
||||||
|
certificates, err := x509.ParseCertificates(C.GoBytes(pointer, length))
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return certificates
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
//go:build !android || !cgo
|
||||||
|
|
||||||
|
package certificate
|
||||||
|
|
||||||
|
import "crypto/x509"
|
||||||
|
|
||||||
|
func systemCertificates() []*x509.Certificate {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
#include <jni.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
static JavaVM *javaVM;
|
||||||
|
|
||||||
|
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
|
||||||
|
javaVM = vm;
|
||||||
|
return JNI_VERSION_1_6;
|
||||||
|
}
|
||||||
|
|
||||||
|
uintptr_t box_jni_vm(void) {
|
||||||
|
return (uintptr_t) javaVM;
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
//go:build android
|
||||||
|
|
||||||
|
package jni
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <stdint.h>
|
||||||
|
extern uintptr_t box_jni_vm(void);
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
func VM() uintptr {
|
||||||
|
return uintptr(C.box_jni_vm())
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
//go:build !android || !cgo
|
||||||
|
|
||||||
|
package jni
|
||||||
|
|
||||||
|
func VM() uintptr {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
@@ -131,10 +131,6 @@ func (s *platformInterfaceStub) ReadWIFIState() adapter.WIFIState {
|
|||||||
return adapter.WIFIState{}
|
return adapter.WIFIState{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *platformInterfaceStub) SystemCertificates() []string {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *platformInterfaceStub) UsePlatformConnectionOwnerFinder() bool {
|
func (s *platformInterfaceStub) UsePlatformConnectionOwnerFinder() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,10 +18,13 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/common/certificate"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/bufio"
|
"github.com/sagernet/sing/common/bufio"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
"github.com/sagernet/sing/common/logger"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
"github.com/sagernet/sing/protocol/socks"
|
"github.com/sagernet/sing/protocol/socks"
|
||||||
"github.com/sagernet/sing/protocol/socks/socks5"
|
"github.com/sagernet/sing/protocol/socks/socks5"
|
||||||
@@ -69,6 +72,7 @@ type httpClient struct {
|
|||||||
tls tls.Config
|
tls tls.Config
|
||||||
client http.Client
|
client http.Client
|
||||||
transport http.Transport
|
transport http.Transport
|
||||||
|
store *certificate.Store
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHTTPClient() HTTPClient {
|
func NewHTTPClient() HTTPClient {
|
||||||
@@ -78,6 +82,13 @@ func NewHTTPClient() HTTPClient {
|
|||||||
client.transport.TLSHandshakeTimeout = C.TCPTimeout
|
client.transport.TLSHandshakeTimeout = C.TCPTimeout
|
||||||
client.transport.TLSClientConfig = &client.tls
|
client.transport.TLSClientConfig = &client.tls
|
||||||
client.transport.DisableKeepAlives = true
|
client.transport.DisableKeepAlives = true
|
||||||
|
if C.IsAndroid {
|
||||||
|
store, err := certificate.NewStore(logger.NOP(), option.CertificateOptions{})
|
||||||
|
if err != nil {
|
||||||
|
panic(E.Cause(err, "initialize certificate store"))
|
||||||
|
}
|
||||||
|
client.tls.RootCAs = store.Pool()
|
||||||
|
}
|
||||||
return client
|
return client
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,6 +161,9 @@ func (c *httpClient) NewRequest() HTTPRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *httpClient) Close() {
|
func (c *httpClient) Close() {
|
||||||
|
if c.store != nil {
|
||||||
|
c.store.Close()
|
||||||
|
}
|
||||||
c.transport.CloseIdleConnections()
|
c.transport.CloseIdleConnections()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ type PlatformInterface interface {
|
|||||||
UnderNetworkExtension() bool
|
UnderNetworkExtension() bool
|
||||||
IncludeAllNetworks() bool
|
IncludeAllNetworks() bool
|
||||||
ReadWIFIState() *WIFIState
|
ReadWIFIState() *WIFIState
|
||||||
SystemCertificates() StringIterator
|
|
||||||
ClearDNSCache()
|
ClearDNSCache()
|
||||||
SendNotification(notification *Notification) error
|
SendNotification(notification *Notification) error
|
||||||
StartNeighborMonitor(listener NeighborUpdateListener) error
|
StartNeighborMonitor(listener NeighborUpdateListener) error
|
||||||
|
|||||||
@@ -175,10 +175,6 @@ func (w *platformInterfaceWrapper) ReadWIFIState() adapter.WIFIState {
|
|||||||
return (adapter.WIFIState)(*wifiState)
|
return (adapter.WIFIState)(*wifiState)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *platformInterfaceWrapper) SystemCertificates() []string {
|
|
||||||
return iteratorToArray[string](w.iif.SystemCertificates())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *platformInterfaceWrapper) UsePlatformConnectionOwnerFinder() bool {
|
func (w *platformInterfaceWrapper) UsePlatformConnectionOwnerFinder() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user