mirror of
https://github.com/wanghongenpin/proxypin.git
synced 2026-05-20 16:15:47 +08:00
自签证书
This commit is contained in:
@@ -22,6 +22,7 @@ Future<void> start({EventListener? listener}) async {
|
||||
|
||||
void setSystemProxy(int port) {
|
||||
if (Platform.isMacOS) {
|
||||
|
||||
// Process.run('networksetup', ['-getsecurewebproxy', 'Wi-Fi']).then((ProcessResult results) {
|
||||
// print(results.stdout);
|
||||
// });
|
||||
@@ -29,8 +30,8 @@ void setSystemProxy(int port) {
|
||||
// .then((ProcessResult results) {
|
||||
// print(results.stdout);
|
||||
// });
|
||||
Process.run('networksetup', ['-setwebproxy', 'Wi-Fi', '127.0.0.1', port.toString()]).then((ProcessResult results) {
|
||||
print(results.stdout);
|
||||
});
|
||||
// Process.run('networksetup', ['-setwebproxy', 'Wi-Fi', '127.0.0.1', port.toString()]).then((ProcessResult results) {
|
||||
// print(results.stdout);
|
||||
// });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ import 'dart:io';
|
||||
import 'dart:math';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:basic_utils/basic_utils.dart';
|
||||
import 'package:logger/logger.dart';
|
||||
import 'package:network/network/http/http.dart';
|
||||
import 'package:network/network/util/AttributeKeys.dart';
|
||||
@@ -33,13 +32,8 @@ abstract class ChannelHandler<T> {
|
||||
|
||||
void exceptionCaught(Channel channel, Object cause, {StackTrace? trace}) {
|
||||
HostAndPort? attribute = channel.getAttribute(AttributeKeys.host);
|
||||
X509CertificateData? x509certificateFromPem;
|
||||
if (attribute != null && CertificateManager.get(attribute.host) != null) {
|
||||
String cer = CertificateManager.get(attribute.host)!;
|
||||
x509certificateFromPem = X509Utils.x509CertificateFromPem(cer);
|
||||
}
|
||||
|
||||
log.e("error $attribute $channel ${x509certificateFromPem?.tbsCertificate?.subject}", cause, trace);
|
||||
log.e("error $attribute $channel", cause, trace);
|
||||
channel.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,11 +180,11 @@ class HostAndPort {
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is HostAndPort &&
|
||||
runtimeType == other.runtimeType &&
|
||||
scheme == other.scheme &&
|
||||
host == other.host &&
|
||||
port == other.port;
|
||||
other is HostAndPort &&
|
||||
runtimeType == other.runtimeType &&
|
||||
scheme == other.scheme &&
|
||||
host == other.host &&
|
||||
port == other.port;
|
||||
|
||||
@override
|
||||
int get hashCode => scheme.hashCode ^ host.hashCode ^ port.hashCode;
|
||||
@@ -214,6 +208,7 @@ abstract interface class Encoder<T> {
|
||||
/// 编解码器
|
||||
abstract class Codec<T> implements Decoder<T>, Encoder<T> {
|
||||
static const int defaultMaxInitialLineLength = 8192;
|
||||
static const int maxBodyLength = 1024000;
|
||||
}
|
||||
|
||||
class RawCodec extends Codec<Object> {
|
||||
@@ -243,13 +238,10 @@ class Network {
|
||||
Channel listen(Socket socket) {
|
||||
var channel = Channel(socket);
|
||||
_channelInitializer.call(channel);
|
||||
|
||||
channel.pipeline.channelActive(channel);
|
||||
|
||||
socket.listen((data) => _onEvent(data, channel),
|
||||
onError: (error, trace) => channel.pipeline.exceptionCaught(channel, error, trace: trace),
|
||||
onDone: () => channel.pipeline.channelInactive(channel));
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
@@ -308,6 +300,7 @@ class Client extends Network {
|
||||
return SecureSocket.connect(hostAndPort.host, hostAndPort.port, onBadCertificate: (certificate) => true)
|
||||
.then((socket) => listen(socket));
|
||||
}
|
||||
return Socket.connect(hostAndPort.host, hostAndPort.port).then((socket) => listen(socket));
|
||||
return Socket.connect(hostAndPort.host, hostAndPort.port, timeout: const Duration(seconds: 10))
|
||||
.then((socket) => listen(socket));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:network/network/http/http.dart';
|
||||
import 'package:network/network/http/http_headers.dart';
|
||||
import 'package:network/network/util/AttributeKeys.dart';
|
||||
@@ -36,6 +38,11 @@ class HttpChannelHandler extends ChannelHandler<HttpRequest> {
|
||||
@override
|
||||
void channelRead(Channel channel, HttpRequest msg) {
|
||||
forward(channel, msg).catchError((error, trace) {
|
||||
if (error is SocketException && (error.message.contains("Failed host lookup") || error.message.contains(" Operation timed out"))) {
|
||||
log.e("连接失败 ${error.message}");
|
||||
channel.close();
|
||||
return;
|
||||
}
|
||||
log.e("转发请求失败", error, trace);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -77,7 +77,6 @@ abstract class HttpCodec<T extends HttpMessage> implements Codec<T> {
|
||||
_state = State.done;
|
||||
}
|
||||
}
|
||||
|
||||
if (_state == State.done) {
|
||||
message.body = _convertBody();
|
||||
_state = State.readInitial;
|
||||
@@ -141,7 +140,8 @@ abstract class HttpCodec<T extends HttpMessage> implements Codec<T> {
|
||||
while (_httpParse.index < data.length) {
|
||||
var parseLine = _httpParse.parseLine(data);
|
||||
if (parseLine.isEmpty) {
|
||||
continue;
|
||||
_httpParse.index = 0;
|
||||
return;
|
||||
}
|
||||
int length = hexToInt(String.fromCharCodes(parseLine));
|
||||
//chunked编码结束 最后以length = 0 结束
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import 'dart:core';
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:basic_utils/basic_utils.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:network/network/util/x509.dart';
|
||||
|
||||
Future<void> main() async {
|
||||
var securityContext = await CertificateManager.getCertificateContext('www.baidu.com');
|
||||
print(securityContext);
|
||||
print(CertificateManager._caCert.tbsCertificate?.subject);
|
||||
print(CertificateManager._caCert.tbsCertificateSeqAsString);
|
||||
print(CertificateManager._caCert);
|
||||
await CertificateManager.getCertificateContext('www.jianshu.com');
|
||||
String cer = CertificateManager.get('www.jianshu.com')!;
|
||||
var x509certificateFromPem = X509Utils.x509CertificateFromPem(cer);
|
||||
print(x509certificateFromPem.plain!);
|
||||
}
|
||||
|
||||
class CertificateManager {
|
||||
@@ -40,7 +41,7 @@ class CertificateManager {
|
||||
if (!_initialized) {
|
||||
await _initCAConfig();
|
||||
}
|
||||
cer = generate(_serverKeyPair.publicKey as RSAPublicKey, _caPriKey, host);
|
||||
cer = generate(_caCert, _serverKeyPair.publicKey as RSAPublicKey, _caPriKey, host);
|
||||
_certificateMap[host] = cer;
|
||||
}
|
||||
|
||||
@@ -52,7 +53,7 @@ class CertificateManager {
|
||||
}
|
||||
|
||||
/// 生成证书
|
||||
static String generate(PublicKey serverPubKey, RSAPrivateKey caPriKey, String host) {
|
||||
static String generate(X509CertificateData caRoot, RSAPublicKey serverPubKey, RSAPrivateKey caPriKey, String host) {
|
||||
//根据CA证书subject来动态生成目标服务器证书的issuer和subject
|
||||
Map<String, String> x509Subject = {
|
||||
'C': 'CN',
|
||||
@@ -62,10 +63,10 @@ class CertificateManager {
|
||||
'OU': 'Proxy',
|
||||
};
|
||||
x509Subject['CN'] = host;
|
||||
var csr = X509Utils.generateRsaCsrPem(x509Subject, caPriKey, serverPubKey as RSAPublicKey, san: [host]);
|
||||
|
||||
Map<String, String> issuer = Map.from(_caCert.tbsCertificate!.subject);
|
||||
var csrPem = X509Utils.generateSelfSignedCertificate(caPriKey, csr, 365, sans: [host], issuer: issuer);
|
||||
var csrPem = X509Generate.generateSelfSignedCertificate(caRoot, serverPubKey, caPriKey, 365,
|
||||
sans: [host], serialNumber: Random().nextInt(1000000).toString(), issuer: issuer);
|
||||
return csrPem;
|
||||
}
|
||||
|
||||
@@ -75,11 +76,13 @@ class CertificateManager {
|
||||
}
|
||||
//从项目目录加入ca根证书
|
||||
var caPem = await rootBundle.loadString('assets/certs/ca.crt');
|
||||
// var caPem = await File('assets/certs/ca.crt').readAsString();
|
||||
_caCert = X509Utils.x509CertificateFromPem(caPem);
|
||||
//根据CA证书subject来动态生成目标服务器证书的issuer和subject
|
||||
|
||||
//从项目目录加入ca私钥
|
||||
var privateBytes = await rootBundle.load('assets/certs/ca_private.der');
|
||||
// var privateBytes = await File('assets/certs/ca_private.der').readAsBytes();
|
||||
_caPriKey = CryptoUtils.rsaPrivateKeyFromDERBytes(privateBytes.buffer.asUint8List());
|
||||
|
||||
_initialized = true;
|
||||
|
||||
198
lib/network/util/x509.dart
Normal file
198
lib/network/util/x509.dart
Normal file
@@ -0,0 +1,198 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:basic_utils/basic_utils.dart';
|
||||
import 'package:pointycastle/asn1/unsupported_object_identifier_exception.dart';
|
||||
import 'package:pointycastle/pointycastle.dart';
|
||||
|
||||
class X509Generate {
|
||||
static const String BEGIN_CERT = '-----BEGIN CERTIFICATE-----';
|
||||
static const String END_CERT = '-----END CERTIFICATE-----';
|
||||
|
||||
//所在国家
|
||||
static const String C = "2.5.4.6";
|
||||
static const String SERIAL_NUMBER = "2.5.4.5";
|
||||
static const String DN_QUALIFIER = "2.5.4.46";
|
||||
|
||||
///
|
||||
/// Generates a self signed certificate
|
||||
///
|
||||
/// * [privateKey] = The private key used for signing
|
||||
/// * [csr] = The CSR containing the DN and public key
|
||||
/// * [days] = The validity in days
|
||||
/// * [sans] = Subject alternative names to place within the certificate
|
||||
/// * [extKeyUsage] = The extended key usage definition
|
||||
/// * [serialNumber] = The serialnumber. If not set the default will be 1.
|
||||
/// * [issuer] = The issuer. If null, the issuer will be the subject of the given csr.
|
||||
///
|
||||
static String generateSelfSignedCertificate(
|
||||
X509CertificateData caRoot,
|
||||
RSAPublicKey publicKey,
|
||||
RSAPrivateKey privateKey,
|
||||
int days, {
|
||||
List<String>? sans,
|
||||
String serialNumber = '1',
|
||||
Map<String, String>? issuer,
|
||||
}) {
|
||||
var data = ASN1Sequence();
|
||||
|
||||
// Add version
|
||||
var version = ASN1Object(tag: 0xA0);
|
||||
version.valueBytes = ASN1Integer.fromtInt(2).encode();
|
||||
data.add(version);
|
||||
|
||||
// Add serial number
|
||||
data.add(ASN1Integer(BigInt.parse(serialNumber)));
|
||||
|
||||
// Add protocol
|
||||
var blockProtocol = ASN1Sequence();
|
||||
blockProtocol.add(ASN1ObjectIdentifier.fromIdentifierString(caRoot.signatureAlgorithm));
|
||||
blockProtocol.add(ASN1Null());
|
||||
data.add(blockProtocol);
|
||||
|
||||
issuer ??= Map.from(caRoot.tbsCertificate!.subject);
|
||||
|
||||
// Add Issuer
|
||||
var issuerSeq = ASN1Sequence();
|
||||
for (var k in issuer.keys) {
|
||||
var value = issuer[k];
|
||||
ASN1Object pString;
|
||||
if (C == k || SERIAL_NUMBER == k || k == DN_QUALIFIER) {
|
||||
pString = ASN1PrintableString(stringValue: value);
|
||||
} else {
|
||||
pString = ASN1UTF8String(utf8StringValue: value);
|
||||
}
|
||||
ASN1ObjectIdentifier oIdentifier;
|
||||
try {
|
||||
oIdentifier = ASN1ObjectIdentifier.fromName(k);
|
||||
} on UnsupportedObjectIdentifierException {
|
||||
oIdentifier = ASN1ObjectIdentifier.fromIdentifierString(k);
|
||||
}
|
||||
var innerSequence = ASN1Sequence(elements: [oIdentifier, pString]);
|
||||
var s = ASN1Set(elements: [innerSequence]);
|
||||
issuerSeq.add(s);
|
||||
}
|
||||
data.add(issuerSeq);
|
||||
|
||||
// Add Validity
|
||||
var validitySeq = ASN1Sequence();
|
||||
validitySeq.add(ASN1UtcTime(DateTime.now().toUtc()));
|
||||
validitySeq.add(ASN1UtcTime(DateTime.now().add(Duration(days: days)).toUtc()));
|
||||
data.add(validitySeq);
|
||||
|
||||
// Add Subject
|
||||
var subjectSeq = ASN1Sequence();
|
||||
for (var k in caRoot.tbsCertificate!.subject.keys) {
|
||||
var value = caRoot.tbsCertificate!.subject[k];
|
||||
ASN1Object pString;
|
||||
if (C == k || SERIAL_NUMBER == k || k == DN_QUALIFIER) {
|
||||
pString = ASN1PrintableString(stringValue: value);
|
||||
} else {
|
||||
pString = ASN1UTF8String(utf8StringValue: value);
|
||||
}
|
||||
var oIdentifier = ASN1ObjectIdentifier.fromIdentifierString(k);
|
||||
var innerSequence = ASN1Sequence(elements: [oIdentifier, pString]);
|
||||
var s = ASN1Set(elements: [innerSequence]);
|
||||
subjectSeq.add(s);
|
||||
}
|
||||
data.add(subjectSeq);
|
||||
|
||||
// Add Public Key
|
||||
data.add(_makePublicKeyBlock(publicKey));
|
||||
|
||||
// Add Extensions
|
||||
if (IterableUtils.isNotNullOrEmpty(sans)) {
|
||||
var extensionTopSequence = ASN1Sequence();
|
||||
|
||||
var sanList = ASN1Sequence();
|
||||
for (var s in sans!) {
|
||||
sanList.add(ASN1PrintableString(stringValue: s, tag: 0x82));
|
||||
}
|
||||
var octetString = ASN1OctetString(octets: sanList.encode());
|
||||
|
||||
var sanSequence = ASN1Sequence();
|
||||
sanSequence.add(ASN1ObjectIdentifier.fromIdentifierString('2.5.29.17'));
|
||||
sanSequence.add(octetString);
|
||||
extensionTopSequence.add(sanSequence);
|
||||
|
||||
var extObj = ASN1Object(tag: 0xA3);
|
||||
extObj.valueBytes = extensionTopSequence.encode();
|
||||
|
||||
data.add(extObj);
|
||||
}
|
||||
|
||||
var outer = ASN1Sequence();
|
||||
outer.add(data);
|
||||
outer.add(blockProtocol);
|
||||
var encode = _rsaSign(data.encode(), privateKey, _getDigestFromOi(caRoot.signatureAlgorithm));
|
||||
outer.add(ASN1BitString(stringValues: encode));
|
||||
|
||||
var chunks = StringUtils.chunk(base64Encode(outer.encode()), 64);
|
||||
|
||||
return '$BEGIN_CERT\n${chunks.join('\r\n')}\n$END_CERT';
|
||||
}
|
||||
|
||||
static Uint8List _rsaSign(Uint8List inBytes, RSAPrivateKey privateKey, String signingAlgorithm) {
|
||||
var signer = Signer('$signingAlgorithm/RSA');
|
||||
signer.init(true, PrivateKeyParameter<RSAPrivateKey>(privateKey));
|
||||
|
||||
var signature = signer.generateSignature(inBytes) as RSASignature;
|
||||
|
||||
return signature.bytes;
|
||||
}
|
||||
|
||||
///
|
||||
/// Create the public key ASN1Sequence for the csr.
|
||||
///
|
||||
static ASN1Sequence _makePublicKeyBlock(RSAPublicKey publicKey) {
|
||||
var blockEncryptionType = ASN1Sequence();
|
||||
blockEncryptionType.add(ASN1ObjectIdentifier.fromName('rsaEncryption'));
|
||||
blockEncryptionType.add(ASN1Null());
|
||||
|
||||
var publicKeySequence = ASN1Sequence();
|
||||
publicKeySequence.add(ASN1Integer(publicKey.modulus));
|
||||
publicKeySequence.add(ASN1Integer(publicKey.exponent));
|
||||
|
||||
var blockPublicKey = ASN1BitString(stringValues: publicKeySequence.encode());
|
||||
|
||||
var outer = ASN1Sequence();
|
||||
outer.add(blockEncryptionType);
|
||||
outer.add(blockPublicKey);
|
||||
|
||||
return outer;
|
||||
}
|
||||
|
||||
///
|
||||
/// Converts the hex string to bytes
|
||||
///
|
||||
static Uint8List _stringAsBytes(String s) {
|
||||
var list = StringUtils.chunk(s, 2);
|
||||
var bytes = <int>[];
|
||||
for (var e in list) {
|
||||
bytes.add(int.parse(e, radix: 16));
|
||||
}
|
||||
return Uint8List.fromList(bytes);
|
||||
}
|
||||
|
||||
static String _getDigestFromOi(String oi) {
|
||||
switch (oi) {
|
||||
case 'ecdsaWithSHA1':
|
||||
case 'sha1WithRSAEncryption':
|
||||
return 'SHA-1';
|
||||
case 'ecdsaWithSHA224':
|
||||
case 'sha224WithRSAEncryption':
|
||||
return 'SHA-224';
|
||||
case 'ecdsaWithSHA256':
|
||||
case 'sha256WithRSAEncryption':
|
||||
return 'SHA-256';
|
||||
case 'ecdsaWithSHA384':
|
||||
case 'sha384WithRSAEncryption':
|
||||
return 'SHA-384';
|
||||
case 'ecdsaWithSHA512':
|
||||
case 'sha512WithRSAEncryption':
|
||||
return 'SHA-512';
|
||||
default:
|
||||
return 'SHA-256';
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user