自签证书

This commit is contained in:
wanghongen
2023-06-12 17:52:27 +08:00
parent eaf6c30023
commit 2a1afee996
19 changed files with 278 additions and 51 deletions

View File

@@ -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);
// });
}
}

View File

@@ -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));
}
}

View File

@@ -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);
});
}

View File

@@ -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 结束

View File

@@ -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
View 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';
}
}
}