Skip to content

Commit 64a0e0c

Browse files
committed
certificate: Replace platform bridge with CGO JNI
1 parent 0794d64 commit 64a0e0c

13 files changed

Lines changed: 186 additions & 21 deletions

File tree

adapter/platform.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ type PlatformInterface interface {
2929
ClearDNSCache()
3030
RequestPermissionForWIFIState() error
3131
ReadWIFIState() WIFIState
32-
SystemCertificates() []string
3332

3433
UsePlatformConnectionOwnerFinder() bool
3534
FindConnectionOwner(request *FindConnectionOwnerRequest) (*ConnectionOwner, error)

box.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ func New(options Options) (*Box, error) {
187187
len(certificateOptions.Certificate) > 0 ||
188188
len(certificateOptions.CertificatePath) > 0 ||
189189
len(certificateOptions.CertificateDirectoryPath) > 0 {
190-
certificateStore, err := certificate.NewStore(ctx, logFactory.NewLogger("certificate"), certificateOptions)
190+
certificateStore, err := certificate.NewStore(logFactory.NewLogger("certificate"), certificateOptions)
191191
if err != nil {
192192
return nil, err
193193
}

common/certificate/store.go

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package certificate
22

33
import (
44
"bytes"
5-
"context"
65
"crypto/x509"
76
"io/fs"
87
"os"
@@ -16,7 +15,6 @@ import (
1615
"github.com/sagernet/sing-box/option"
1716
E "github.com/sagernet/sing/common/exceptions"
1817
"github.com/sagernet/sing/common/logger"
19-
"github.com/sagernet/sing/service"
2018
)
2119

2220
var _ adapter.CertificateStore = (*Store)(nil)
@@ -34,7 +32,7 @@ type Store struct {
3432
platform storePlatform
3533
}
3634

37-
func NewStore(ctx context.Context, logger logger.Logger, options option.CertificateOptions) (*Store, error) {
35+
func NewStore(logger logger.Logger, options option.CertificateOptions) (*Store, error) {
3836
storeType := options.Store
3937
if storeType == "" {
4038
storeType = C.CertificateStoreSystem
@@ -43,14 +41,10 @@ func NewStore(ctx context.Context, logger logger.Logger, options option.Certific
4341
switch storeType {
4442
case C.CertificateStoreSystem:
4543
systemPool = x509.NewCertPool()
46-
platformInterface := service.FromContext[adapter.PlatformInterface](ctx)
4744
var systemValid bool
48-
if platformInterface != nil {
49-
for _, cert := range platformInterface.SystemCertificates() {
50-
if systemPool.AppendCertsFromPEM([]byte(cert)) {
51-
systemValid = true
52-
}
53-
}
45+
for _, certificate := range systemCertificates() {
46+
systemPool.AddCert(certificate)
47+
systemValid = true
5448
}
5549
if !systemValid {
5650
certPool, err := x509.SystemCertPool()
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
#include <jni.h>
2+
#include <stdint.h>
3+
#include <stdlib.h>
4+
#include <string.h>
5+
6+
void *box_system_certificates_der(uintptr_t vmPtr, int *out_length) {
7+
*out_length = 0;
8+
9+
JavaVM *vm = (JavaVM *) vmPtr;
10+
JNIEnv *env = NULL;
11+
int attached = 0;
12+
jint getEnvResult = (*vm)->GetEnv(vm, (void **) &env, JNI_VERSION_1_6);
13+
if (getEnvResult == JNI_EDETACHED) {
14+
if ((*vm)->AttachCurrentThread(vm, &env, NULL) != JNI_OK) {
15+
return NULL;
16+
}
17+
attached = 1;
18+
} else if (getEnvResult != JNI_OK) {
19+
return NULL;
20+
}
21+
22+
unsigned char *result = NULL;
23+
int resultLength = 0;
24+
25+
jclass keyStoreClass = (*env)->FindClass(env, "java/security/KeyStore");
26+
jmethodID getInstance = (*env)->GetStaticMethodID(env, keyStoreClass, "getInstance", "(Ljava/lang/String;)Ljava/security/KeyStore;");
27+
jstring storeName = (*env)->NewStringUTF(env, "AndroidCAStore");
28+
jobject keyStore = (*env)->CallStaticObjectMethod(env, keyStoreClass, getInstance, storeName);
29+
if ((*env)->ExceptionCheck(env) || keyStore == NULL) {
30+
goto done;
31+
}
32+
33+
jmethodID load = (*env)->GetMethodID(env, keyStoreClass, "load", "(Ljava/io/InputStream;[C)V");
34+
(*env)->CallVoidMethod(env, keyStore, load, NULL, NULL);
35+
if ((*env)->ExceptionCheck(env)) {
36+
goto done;
37+
}
38+
39+
jmethodID aliasesMethod = (*env)->GetMethodID(env, keyStoreClass, "aliases", "()Ljava/util/Enumeration;");
40+
jmethodID getCertificate = (*env)->GetMethodID(env, keyStoreClass, "getCertificate", "(Ljava/lang/String;)Ljava/security/cert/Certificate;");
41+
jobject aliases = (*env)->CallObjectMethod(env, keyStore, aliasesMethod);
42+
if ((*env)->ExceptionCheck(env) || aliases == NULL) {
43+
goto done;
44+
}
45+
46+
jclass enumerationClass = (*env)->FindClass(env, "java/util/Enumeration");
47+
jmethodID hasMoreElements = (*env)->GetMethodID(env, enumerationClass, "hasMoreElements", "()Z");
48+
jmethodID nextElement = (*env)->GetMethodID(env, enumerationClass, "nextElement", "()Ljava/lang/Object;");
49+
50+
jclass certificateClass = (*env)->FindClass(env, "java/security/cert/Certificate");
51+
jmethodID getEncoded = (*env)->GetMethodID(env, certificateClass, "getEncoded", "()[B");
52+
53+
while ((*env)->CallBooleanMethod(env, aliases, hasMoreElements)) {
54+
jstring alias = (jstring) (*env)->CallObjectMethod(env, aliases, nextElement);
55+
jobject certificate = (*env)->CallObjectMethod(env, keyStore, getCertificate, alias);
56+
(*env)->DeleteLocalRef(env, alias);
57+
if ((*env)->ExceptionCheck(env) || certificate == NULL) {
58+
(*env)->ExceptionClear(env);
59+
continue;
60+
}
61+
jbyteArray encoded = (jbyteArray) (*env)->CallObjectMethod(env, certificate, getEncoded);
62+
(*env)->DeleteLocalRef(env, certificate);
63+
if ((*env)->ExceptionCheck(env) || encoded == NULL) {
64+
(*env)->ExceptionClear(env);
65+
continue;
66+
}
67+
jsize encodedLength = (*env)->GetArrayLength(env, encoded);
68+
unsigned char *grown = realloc(result, resultLength + encodedLength);
69+
if (grown == NULL) {
70+
(*env)->DeleteLocalRef(env, encoded);
71+
free(result);
72+
result = NULL;
73+
resultLength = 0;
74+
goto done;
75+
}
76+
result = grown;
77+
(*env)->GetByteArrayRegion(env, encoded, 0, encodedLength, (jbyte *) (result + resultLength));
78+
resultLength += encodedLength;
79+
(*env)->DeleteLocalRef(env, encoded);
80+
}
81+
82+
done:
83+
if ((*env)->ExceptionCheck(env)) {
84+
(*env)->ExceptionClear(env);
85+
}
86+
if (attached) {
87+
(*vm)->DetachCurrentThread(vm);
88+
}
89+
*out_length = resultLength;
90+
return result;
91+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//go:build android
2+
3+
package certificate
4+
5+
/*
6+
#include <stdint.h>
7+
#include <stdlib.h>
8+
extern void *box_system_certificates_der(uintptr_t vm, int *out_length);
9+
*/
10+
import "C"
11+
12+
import (
13+
"crypto/x509"
14+
15+
"github.com/sagernet/sing-box/common/jni"
16+
)
17+
18+
func systemCertificates() []*x509.Certificate {
19+
vm := jni.VM()
20+
if vm == 0 {
21+
return nil
22+
}
23+
var length C.int
24+
pointer := C.box_system_certificates_der(C.uintptr_t(vm), &length)
25+
if pointer == nil {
26+
return nil
27+
}
28+
defer C.free(pointer)
29+
certificates, err := x509.ParseCertificates(C.GoBytes(pointer, length))
30+
if err != nil {
31+
return nil
32+
}
33+
return certificates
34+
}

common/certificate/system_other.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//go:build !android || !cgo
2+
3+
package certificate
4+
5+
import "crypto/x509"
6+
7+
func systemCertificates() []*x509.Certificate {
8+
return nil
9+
}

common/jni/jni_android.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#include <jni.h>
2+
#include <stdint.h>
3+
4+
static JavaVM *javaVM;
5+
6+
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
7+
javaVM = vm;
8+
return JNI_VERSION_1_6;
9+
}
10+
11+
uintptr_t box_jni_vm(void) {
12+
return (uintptr_t) javaVM;
13+
}

common/jni/jni_android.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//go:build android
2+
3+
package jni
4+
5+
/*
6+
#include <stdint.h>
7+
extern uintptr_t box_jni_vm(void);
8+
*/
9+
import "C"
10+
11+
func VM() uintptr {
12+
return uintptr(C.box_jni_vm())
13+
}

common/jni/jni_other.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
//go:build !android || !cgo
2+
3+
package jni
4+
5+
func VM() uintptr {
6+
return 0
7+
}

experimental/libbox/config.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -131,10 +131,6 @@ func (s *platformInterfaceStub) ReadWIFIState() adapter.WIFIState {
131131
return adapter.WIFIState{}
132132
}
133133

134-
func (s *platformInterfaceStub) SystemCertificates() []string {
135-
return nil
136-
}
137-
138134
func (s *platformInterfaceStub) UsePlatformConnectionOwnerFinder() bool {
139135
return false
140136
}

0 commit comments

Comments
 (0)