如何通过Fluke自身生成的签名证书进行SSL固定?

时间:2018-07-13 11:00:13

标签: flutter

我正在寻找SSL固定,并使用自行生成的证书来快速运行我们的api。

4 个答案:

答案 0 :(得分:4)

这个问题没有足够的细节,所以这个答案基于一些假设:

  1. 您的API是HTTPS
  2. 您正在谈论验证服务器端自签名HTTPS证书
  3. 您将package:http用作http客户端
  4. 没有客户端证书

package:http在后​​台使用dart:io HttpClient,而HttpClient具有多种功能可用于证书验证。由于客户端将不信任自签名服务器证书,因此客户端将调用badCertificateCallback,让您自己验证服务器证书,例如:

HttpClient httpClient = new HttpClient()
  ..badCertificateCallback =
  ((X509Certificate cert, String host, int port) {
    // tests that cert is self signed, correct subject and correct date(s) 
    return (cert.issuer == cert.subject &&
        cert.subject == 'MySelfSignedCertCN' &&
        cert.endValidity.millisecondsSinceEpoch == 1234567890);
  });

IOClient ioClient = new IOClient(httpClient);
// use ioClient to perform get/post operations from package:http

// don't forget to call ioClient.close() when done
// note, this also closes the underlying HttpClient

答案 1 :(得分:1)

您可以使用SSL Pinning Plugin来执行此操作。只需将您的自签名证书指纹放在以下电话中即可:

await SslPinningPlugin.check(serverURL: url, headerHttp : new Map(), allowedSHA1Fingerprint: new List<String>, timeout : 50);

该呼叫返回:

  • 成功后,返回字符串“ CONNECTION_SECURE”
  • 发生错误时,返回字符串“ CONNECTION_INSECURE”

答案 2 :(得分:0)

每年创建一个自签名证书,并使客户仅信任今年和去年的证书:

import 'dart:io'
    show
        BytesBuilder,
        File,
        HttpClient,
        HttpClientRequest,
        HttpClientResponse,
        HttpHeaders,
        HttpRequest,
        HttpServer,
        InternetAddress,
        Process,
        stderr,
        stdout,
        SecurityContext;
import 'dart:convert' show utf8;

Future<void> shellCommand(String command) async {
  print('Executing command $command');
  final Process process = await Process.start('sh', ['-c', command]);
  stdout.addStream(process.stdout);
  stderr.addStream(process.stderr);
  final int exitCode = await process.exitCode;
  if (exitCode != 0) {
    throw new Exception('Process exited with status $exitCode');
  }
}

void main() async {
  // Last year's certificate:
  await shellCommand(
      'openssl req -newkey rsa:2048 -passout pass:password -keyout privatekey2018.pem -subj "/CN=localhost" -days 731 -x509 -out certificate2018.pem');
  // This year's certificate:
  await shellCommand(
      'openssl req -newkey rsa:2048 -passout pass:password -keyout privatekey2019.pem -subj "/CN=localhost" -days 731 -x509 -out certificate2019.pem');

  final SecurityContext serverSecurityContext = new SecurityContext();
  serverSecurityContext.useCertificateChainBytes(
      await new File('certificate2019.pem').readAsBytes());
  serverSecurityContext.usePrivateKey('privatekey2019.pem',
      password: 'password');
  final HttpServer httpServer = await HttpServer.bindSecure(
      InternetAddress.loopbackIPv4, 0, serverSecurityContext);
  httpServer.listen((HttpRequest request) {
    request.response.write('body1');
    request.response.close();
  });
  print('Server listening at https://localhost:${httpServer.port}/');

  print('Making request.');
  final SecurityContext clientSecurityContext =
      new SecurityContext(withTrustedRoots: false);
  clientSecurityContext.setTrustedCertificatesBytes(
      await new File('certificate2018.pem').readAsBytes());
  clientSecurityContext.setTrustedCertificatesBytes(
      await new File('certificate2019.pem').readAsBytes());
  final HttpClient httpClient = new HttpClient(context: clientSecurityContext);
  final HttpClientRequest request = await httpClient.getUrl(Uri(
      scheme: 'https', host: 'localhost', port: httpServer.port, path: '/'));
  final HttpClientResponse response = await request.close();
  final List<int> bytes = await response.fold(new BytesBuilder(),
      (BytesBuilder bytesBuilder, List<int> bytes) {
    bytesBuilder.add(bytes);
    return bytesBuilder;
  }).then((BytesBuilder bytesBuilder) => bytesBuilder.takeBytes());
  final String contenType =
      response.headers.value(HttpHeaders.contentTypeHeader) ?? '';
  print('${response.statusCode} ${response.reasonPhrase} '
      'content-type="$contenType" body="${utf8.decode(bytes)}"');

  httpServer.close(force: true);
}

创建自签名证书,并使客户端仅信任那些证书,并忽略请求中的主机名。当使用Terraform将服务器部署到AWS Elastic Beanstalk时,这很有用。服务器二进制Blob必须包含证书,但是直到部署完成才知道服务器的主机名。

import 'dart:io'
    show
        BytesBuilder,
        File,
        HttpClient,
        HttpClientRequest,
        HttpClientResponse,
        HttpHeaders,
        HttpRequest,
        HttpServer,
        InternetAddress,
        Process,
        stderr,
        stdout,
        SecurityContext,
        X509Certificate;
import 'dart:convert' show utf8;

Future<void> shellCommand(String command) async {
  print('Executing command $command');
  final Process process = await Process.start('sh', ['-c', command]);
  stdout.addStream(process.stdout);
  stderr.addStream(process.stderr);
  final int exitCode = await process.exitCode;
  if (exitCode != 0) {
    throw new Exception('Process exited with status $exitCode');
  }
}

void main() async {
  // Last year's certificate:
  await shellCommand(
      'openssl req -newkey rsa:2048 -passout pass:password -keyout privatekey2018.pem -subj "/CN=localhost" -days 731 -x509 -out certificate2018.pem');
  // This year's certificate:
  await shellCommand(
      'openssl req -newkey rsa:2048 -passout pass:password -keyout privatekey2019.pem -subj "/CN=localhost" -days 731 -x509 -out certificate2019.pem');

  final SecurityContext serverSecurityContext = new SecurityContext();
  serverSecurityContext.useCertificateChainBytes(
      await new File('certificate2019.pem').readAsBytes());
  serverSecurityContext.usePrivateKey('privatekey2019.pem',
      password: 'password');
  final HttpServer httpServer = await HttpServer.bindSecure(
      InternetAddress.loopbackIPv4, 0, serverSecurityContext);
  httpServer.listen((HttpRequest request) {
    request.response.write('body1');
    request.response.close();
  });
  print('Server listening at https://localhost:${httpServer.port}/');

  print('Making request.');
  final SecurityContext clientSecurityContext =
      new SecurityContext(withTrustedRoots: false);
  final HttpClient httpClient = new HttpClient(context: clientSecurityContext);
  final List<String> certificatePemStrings = [
    await new File('certificate2018.pem').readAsString(),
    await new File('certificate2019.pem').readAsString()
  ];
  httpClient.badCertificateCallback =
      (X509Certificate cert, String host, int port) => certificatePemStrings
          .any((certificatePemString) => cert.pem == certificatePemString);
  final HttpClientRequest request = await httpClient.getUrl(Uri(
      scheme: 'https', host: 'localhost', port: httpServer.port, path: '/'));
  final HttpClientResponse response = await request.close();
  final List<int> bytes = await response.fold(new BytesBuilder(),
      (BytesBuilder bytesBuilder, List<int> bytes) {
    bytesBuilder.add(bytes);
    return bytesBuilder;
  }).then((BytesBuilder bytesBuilder) => bytesBuilder.takeBytes());
  final String contenType =
      response.headers.value(HttpHeaders.contentTypeHeader) ?? '';
  print('${response.statusCode} ${response.reasonPhrase} '
      'content-type="$contenType" body="${utf8.decode(bytes)}"');

  httpServer.close(force: true);
}

在安全的笔记本电脑上创建证书颁发机构文件,并将其保存在安全的可移动驱动器中。每当需要新证书时,请获取可移动驱动器并生成并签署新服务器证书。这是运行openssl命令的示例,以及如何将Dart配置为仅信任由证书颁发机构签名的证书:

import 'dart:io'
    show
        BytesBuilder,
        File,
        HttpClient,
        HttpClientRequest,
        HttpClientResponse,
        HttpHeaders,
        HttpRequest,
        HttpServer,
        InternetAddress,
        Process,
        SecurityContext,
        stderr,
        stdout;
import 'dart:convert' show utf8;

Future<void> shellCommand(String command) async {
  print('Executing command $command');
  final Process process = await Process.start('sh', ['-c', command]);
  stdout.addStream(process.stdout);
  stderr.addStream(process.stderr);
  final int exitCode = await process.exitCode;
  if (exitCode != 0) {
    throw new Exception('Process exited with status $exitCode');
  }
}

void main() async {
  // Last year's certificates:
  await shellCommand(
      'openssl req -newkey rsa:2048 -nodes -keyout ca2018.privatekey.pem -subj "/OU=CA" -days 731 -x509 -out ca2018.certificate.pem');
  // This year's certificates:
  await shellCommand(
      'openssl req -newkey rsa:2048 -nodes -keyout ca2019.privatekey.pem -subj "/OU=CA" -days 731 -x509 -out ca2019.certificate.pem');
  await shellCommand(
      'openssl req -newkey rsa:2048 -passout pass:password -keyout privatekey.pem -subj "/CN=localhost" -days 731 -sha256 -new -out csr2019.pem');
  await shellCommand(
      'openssl x509 -req -in csr2019.pem -CA ca2019.certificate.pem -CAkey ca2019.privatekey.pem -set_serial 1 -days 730 -sha256 -out certificate2019.pem');
  await shellCommand(
      'cat certificate2019.pem ca2019.certificate.pem > certificate2019.chain.pem');

  final SecurityContext serverSecurityContext = new SecurityContext();
  serverSecurityContext.useCertificateChainBytes(
      await new File('certificate2019.chain.pem').readAsBytes());
  serverSecurityContext.usePrivateKey('privatekey.pem', password: 'password');
  final HttpServer httpServer = await HttpServer.bindSecure(
      InternetAddress.loopbackIPv4, 0, serverSecurityContext);
  httpServer.listen((HttpRequest request) {
    request.response.write('body1');
    request.response.close();
  });
  print('Server listening at https://localhost:${httpServer.port}/');

  print('Making request.');
  final SecurityContext clientSecurityContext =
      new SecurityContext(withTrustedRoots: false);
  clientSecurityContext.setTrustedCertificatesBytes(
      await new File('ca2018.certificate.pem').readAsBytes());
  clientSecurityContext.setTrustedCertificatesBytes(
      await new File('ca2019.certificate.pem').readAsBytes());
  final HttpClient httpClient = new HttpClient(context: clientSecurityContext);
  final HttpClientRequest request = await httpClient.getUrl(Uri(
      scheme: 'https', host: 'localhost', port: httpServer.port, path: '/'));
  final HttpClientResponse response = await request.close();
  final List<int> bytes = await response.fold(new BytesBuilder(),
      (BytesBuilder bytesBuilder, List<int> bytes) {
    bytesBuilder.add(bytes);
    return bytesBuilder;
  }).then((BytesBuilder bytesBuilder) => bytesBuilder.takeBytes());
  final String contenType =
      response.headers.value(HttpHeaders.contentTypeHeader) ?? '';
  print('${response.statusCode} ${response.reasonPhrase} '
      'content-type="$contenType" body="${utf8.decode(bytes)}"');

  httpServer.close(force: true);
}

答案 3 :(得分:-1)

您可以使用此lib:

https://pub.dev/packages/http_certificate_pinning

具有用于DIO的拦截器和用于HTTP的客户端

 // Add CertificatePinningInterceptor in dio Client
  Dio getClient(String baseUrl, List<String> allowedSHAFingerprints){
      var dio =  Dio(BaseOptions(baseUrl: baseUrl))
        ..interceptors.add(CertificatePinningInterceptor(allowedSHAFingerprints));
      return dio;
  }
```