主要参考 【如何为nginx配置https(免费证书)】【简书】作者:夜_雪
我使用的是自建证书的方法,在 MacOS、linux 上都可行。

一、生成证书、秘钥

生成秘钥 key

  1. openssl genrsa -aes256 -out server.key 2048

会要求输入两次相同的密码,输入即可。
后边生成证书等操作会使用到上面生成的秘钥,如果不想多次输入密码,可以执行以下命令:

  1. openssl rsa -in server.key -out server.key

创建服务器证书的申请文件

  1. openssl req -new -key server.key -out server.csr

会要求输入一些信息,比如:Country Name,中国的话填 CN 即可,其它的一些比如 Province Name、Locality Name、Organization Name 等等自己看着填即可,Common Name 一般填你要配置的域名。

创建 CA 证书

  1. openssl req -new -x509 -key server.key -out ca.crt -days 3650

此时,你可以得到一个ca.crt的证书,这个证书用来给自己的证书签名。

创建服务器公钥证书

  1. openssl x509 -req -days 3650 -in server.csr -CA ca.crt -CAkey server.key -CAcreateserial -out server.crt

ls 你的文件夹,可以看到一共生成了5个文件:
ca.crt、ca.srl、server.crt、server.csr、server.key
其中,server.crt 和 server.key 就是你的 nginx 需要的证书文件.

二、配置 Nginx

  1. server {
  2. listen 443 ssl; # ssl 开启 https
  3. server_name testhttps;
  4. ssl_certificate /etc/nginx/conf.d/keystore/server.crt; # 公钥证书位置
  5. ssl_certificate_key /etc/nginx/conf.d/keystore/server.key; # 私钥位置
  6. ssl_session_cache shared:SSL:10m;
  7. ssl_session_timeout 20m;
  8. ssl_protocols SSLv2 SSLv3 TLSv1;
  9. ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
  10. ssl_prefer_server_ciphers on;
  11. #charset koi8-r;
  12. #access_log /var/log/nginx/host.access.log main;
  13. location / {
  14. root /usr/share/nginx/html;
  15. index index.html index.htm;
  16. }
  17. }

参考上边

三、配置 Flutter Dio 客户端

参考 Dio README.md,有两种方式

方式 1

假设我们的后台服务使用的是自签名证书,证书格式是PEM格式,我们将证书的内容保存在本地字符串中,那么我们的校验逻辑如下:

  1. String PEM="XXXXX"; // certificate content
  2. (dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (client) {
  3. client.badCertificateCallback=(X509Certificate cert, String host, int port){
  4. if(cert.pem==PEM){ // Verify the certificate
  5. return true;
  6. }
  7. return false;
  8. };
  9. };

X509Certificate 证书的标准格式,包含了证书除私钥外所有信息,读者可以自行查阅文档。另外,上面的示例没有校验host,是因为只要服务器返回的证书内容和本地的保存一致就已经能证明是我们的服务器了(而不是中间人),host验证通常是为了防止证书和域名不匹配。
注意:由于 Nginx 返回的证书内容有换行,所以 PEM 也要在对应位置加上换行符(\n),举个栗子:

  1. const String PEM = '-----BEGIN CERTIFICATE-----\nMIIDrjCCApYCCQDK1YKFMM9+CjANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMC\nQ04xDjAMBgNVBAgMBUhlYmVpMRAwDgYDVQQHDAdCYW9kaW5nMRIwEAYDVQQKDAlk\ncmVhbXJlYWwxEjAQBgNVBAsMCWRyZWFtcmVhbDEdMBsGA1UEAwwUd3d3LmRyZWFt\ncmVhbC5vbmxpbmUxIDAeBgkqhkiG9w0BCQEWETE3NzM4NjcyMDRAcXEuY29tMB4X\nDTIwMDQxMzEwNTg0MVoXDTMwMDQxMTEwNTg0MVowgZgxCzAJBgNVBAYTAkNOMQ4w\nDAYDVQQIDAVIZWJlaTEQMA4GA1UEBwwHQmFvZGluZzESMBAGA1UECgwJZHJlYW1y\nZWFsMRIwEAYDVQQLDAlkcmVhbXJlYWwxHTAbBgNVBAMMFHd3dy5kcmVhbXJlYWwu\nb25saW5lMSAwHgYJKoZIhvcNAQkBFhExNzczODY3MjA0QHFxLmNvbTCCASIwDQYJ\nKoZIhvcNAQEBBQADggEPADCCAQoCggEBALmEiXGPStETkn335Q2RC64u19ustQZF\niAnyFcvcb+pQqzx18VhDaHh4cX8EqMf+XZNe09JZMcqv4l67XUQaBBHKdCRNwJbj\nLpL6CfGGT6Y8BgGBvnPtkfnD3ZXMqFSut50x4vx1bQKu6j9ngZIAQz/dzLWndLW7\n5xujSg/5+7WhYsuSKwlw2CB1UKlh4VhL/QCwvwuTBawiuTekra5X+jTcB1x+cYZX\nT0BtEOqXEavx6UW8cbEQS8B00DITvZ9H/qTpf50Hy5TZwMV1/M3UXYQY7CvUOBXo\n0bxAOagtUz6ubirgzoNmKlELbciudXWmpMMzV/lIA7U/fpIqv7n7H1UCAwEAATAN\nBgkqhkiG9w0BAQsFAAOCAQEAHV+VGcUX4U7hzdGLj7/icaFLvDUJ8XHxwLEpb83j\nZ+6k7g5zXiK1nQBbbxGyGsxaVA2U/X4tEEwVn3mDAZMfGYMDj+rg1v6Rjq6WG9I5\n2ePXob0QW00JtQQxQAfPkftz3+PsoMmNzYUNF479Tf/YjBqq9EmQ7b2v1dQrDOvd\nkmRFH/uIiWho97UruKORP+qeQ5K2JuQ6V4dRScJEqGNy9D87zQ0aLDopUcCfPsAj\nLEv0L9SK8hXq+6Vy+oqhkSrZTyVvw8GBQ8Wu2ahXLsOk6Y/2tCPzL7AUi6FWWzln\nWkqBQacGC5cN6PPSt4p6DsU7pTDIxtjuZrv/wsi4rAYLRg==\n-----END CERTIFICATE-----\n';d

方式 2

对于自签名的证书,我们也可以将其添加到本地证书信任链中,这样证书验证时就会自动通过,而不会再走到badCertificateCallback回调中:

  1. (dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (client) {
  2. SecurityContext sc = SecurityContext();
  3. //file is the path of certificate
  4. sc.setTrustedCertificates(file);
  5. HttpClient httpClient = HttpClient(context: sc);
  6. return httpClient;
  7. };

注意,通过 setTrustedCertificates() 设置的证书格式必须为 PEM 或 PKCS12,如果证书格式为 PKCS12,则需将证书密码传入,这样则会在代码中暴露证书密码,所以客户端证书校验不建议使用 PKCS12 格式的证书。

完。