一张自签名证书管控Chrome、Android和iOS?

22

又一篇关于自签名证书的问题,但我已经尝试了几天来找到在最新版本的Chrome、Android和iOS开发环境中创建可用的自签名证书的最佳/正确方式。

我在这里和其他地方找到的说明已经过时,至少对其中一个平台而言是如此。

以下是我找到的最佳方法,但它只适用于Chrome和Android。

openssl req -new -newkey rsa:2048 -days 3650 -nodes -x509 -subj "/C=US/ST=Oklahoma/L=Stillwater/O=My Company/OU=Engineering" -keyout ca.key -out ca.crt
openssl genrsa -out "test.key" 2048
openssl req -new -key test.key -out test.csr -config openssl.cnf
openssl x509 -req -days 3650 -in test.csr -CA ca.crt -CAkey ca.key -CAcreateserial -extensions v3_req -extfile openssl.cnf -out test.crt
openssl x509 -inform PEM -outform DER -in test.crt -out test.der.crt

openssl.cnf的内容:

[req]
default_bits = 2048
encrypt_key  = no # Change to encrypt the private key using des3 or similar
default_md   = sha256
prompt       = no
utf8         = yes

# Specify the DN here so we aren't prompted (along with prompt = no above).

distinguished_name = req_distinguished_name

# Extensions for SAN IP and SAN DNS

req_extensions = v3_req

# Be sure to update the subject to match your organization.

[req_distinguished_name]
C  = US
ST = Oklahoma
L  = Stillwater
O  = My Company
OU = Engineering
CN = test.com

# Allow client and server auth. You may want to only allow server auth.
# Link to SAN names.

[v3_req]
basicConstraints     = CA:TRUE
subjectKeyIdentifier = hash
keyUsage             = digitalSignature, keyEncipherment
extendedKeyUsage     = clientAuth, serverAuth
subjectAltName       = @alt_names

# Alternative names are specified as IP.# and DNS.# for IP addresses and
# DNS accordingly.

[alt_names]
DNS.1 = test.com

在我开发服务器上安装了test.crt和test.key之后,这种方法对于Chrome非常有效:只需将test.crt添加到我的Mac钥匙链中并为其打开“始终信任”即可。
对于Android也非常有效:将test.der.crt发送到设备并点击安装。最重要的是:它出现在设置/加密和凭据/受信任的凭据下的“用户”选项卡中。这对于在我的Android应用程序中使用networkSecurityConfig至关重要。
不幸的是,它在iOS上没有起作用:
- 我通过将证书拖到Xcode模拟器中来安装它。 - 我必须安装test.crt和ca.crt两个证书。如果我只安装了test.crt,则保持“未验证”状态,因为ca.crt是根证书。 - 它未出现在“设置/关于/证书信任设置”下,这是我打开它所需的。 - 当我的应用程序尝试使用NSMutableURLRequest访问我的服务器时,它会收到“TIC SSL Trust Error”的错误消息,并包括10个键值对:
- NSURLErrorFailingURLPeerTrustErrorKey= - _kCFStreamErrorDomainKey=3 - _kCFStreamErrorCodeKey=-9813 - NSErrorPeerCertificateChainKey=1 element, and NSLocalizedDescription=The certificate for this server is invalid. You might be connecting to a server that is pretending to be “test.com” which could put your confidential information at risk.
有什么办法可以更改我所做的内容,以便我可以在iOS下打开“证书信任设置”?
注意1:由于其他关于-9813错误代码的问题的答案表明可能缺少中间证书,因此我将ca.crt添加到我的Apache配置中以设置SSLCaCertificateFile。对于Chrome和Android仍然可以正常工作,但在iOS中出现了完全相同的错误。
谢谢!

我会在没有 DNS.2 = localhost 的情况下测试 OsX,以查看是否是因为它被忽略了,他们与移动平台相比,在不安全的本地服务器方面存在更多问题。 - lossleader
谢谢您的建议。我从头开始重新执行了所有步骤,没有使用本地主机,但是在iOS上仍然存在相同的问题。我在上面的原始问题中提供了更多详细信息。 - ScottyB
1
我终于弄清楚了我上面那些指令的问题,导致证书在iOS上无法正常工作。“subj”参数在第一步骤中需要包括CN(通用名称),该值等于openssl.cnf文件中给定的值。我会在我的回答中提供详细信息。 - ScottyB
2个回答

23

为了与iOS 13和Android 8兼容,此答案已更新(并简化)。现在感谢用户fixitnowyes在2019年10月6日发布的答案,详情请见https://discussions.apple.com/thread/250666160

只需一个openssl命令即可创建一个适用于Chrome、Android和iOS的自签名证书:

openssl req -config openssl.cnf -new -x509 -days 825 -out ca.crt

这将输出ca.crt和ca.key。请注意,825天是iOS 13+允许的最长持续时间,并且必须在openssl命令中指定。我无法确定openssl.cnf中的天数设置是否有效,请注意。
使用以下命令检查证书信息:
openssl x509 -in ca.crt -text -noout

openssl.cnf的内容:

[ req ]
default_bits        = 2048
default_keyfile     = ca.key
default_md          = sha256
default_days        = 825
encrypt_key         = no
distinguished_name  = subject
req_extensions      = req_ext
x509_extensions     = x509_ext
string_mask         = utf8only
prompt              = no

# The Subject DN can be formed using X501 or RFC 4514 (see RFC 4519 for a description).
#   Its sort of a mashup. For example, RFC 4514 does not provide emailAddress.

[ subject ]
countryName                 = US
stateOrProvinceName         = Oklahoma
localityName                = Stillwater
organizationName            = My Company
OU                          = Engineering

# Use a friendly name here because it's presented to the user. The server's DNS
#   names are placed in Subject Alternate Names. Plus, DNS names here is deprecated
#   by both IETF and CA/Browser Forums. If you place a DNS name here, then you
#   must include the DNS name in the SAN too (otherwise, Chrome and others that
#   strictly follow the CA/Browser Baseline Requirements will fail).

commonName              = test.com
emailAddress            = me@home.com

# Section x509_ext is used when generating a self-signed certificate. I.e., openssl req -x509 ...

[ x509_ext ]
subjectKeyIdentifier      = hash
authorityKeyIdentifier    = keyid:always,issuer

# You only need digitalSignature below. *If* you don't allow
#   RSA Key transport (i.e., you use ephemeral cipher suites), then
#   omit keyEncipherment because that's key transport.

basicConstraints        = critical, CA:TRUE
keyUsage            = critical, digitalSignature, keyEncipherment, cRLSign, keyCertSign
subjectAltName          = DNS:test.com
extendedKeyUsage = serverAuth

# RFC 5280, Section 4.2.1.12 makes EKU optional
#   CA/Browser Baseline Requirements, Appendix (B)(3)(G) makes me confused
#   In either case, you probably only need serverAuth.

extendedKeyUsage    = TLS Web Server Authentication

# Section req_ext is used when generating a certificate signing request. I.e., openssl req ...

[ req_ext ]
subjectKeyIdentifier        = hash
basicConstraints        = CA:FALSE
keyUsage            = digitalSignature, keyEncipherment
subjectAltName          = DNS:test.com
nsComment           = "OpenSSL Generated Certificate"

# RFC 5280, Section 4.2.1.12 makes EKU optional
#   CA/Browser Baseline Requirements, Appendix (B)(3)(G) makes me confused
#   In either case, you probably only need serverAuth.
# extendedKeyUsage    = serverAuth, clientAuth

# [ alternate_names ]
# DNS.1       = example.com
# DNS.2       = www.example.com
# DNS.3       = mail.example.com
# DNS.4       = ftp.example.com


# Add these if you need them. But usually you don't want them or
#   need them in production. You may need them for development.
# DNS.5       = localhost
# DNS.6       = localhost.localdomain
# DNS.7       = 127.0.0.1

# IPv6 localhost
# DNS.8     = ::1

创建证书后...

服务器安装:

  1. 在服务器上安装 ca.crt 和 ca.key。
  2. 重新启动服务器。

Chrome / Safari 安装:

  1. 将 ca.crt 添加到您的 Mac 的 KeyChain Access 中的 System 钥匙串中(或 PC 等效)。
  2. 将其设置为“始终信任”(在 Mac 上),以便它可以在 Chrome 和 Safari 中使用。

iOS 设备安装:

  1. 将 ca.crt 发送到您的设备并下载到 Files 中
  2. 转到 Files 并打开 ca.crt
  3. 转到“通用 / VPN 与设备管理”,找到您的证书(按域列出),并安装它
  4. 转到“通用 / 关于本机 / 证书信任设置”,并打开该证书

iOS Xcode 模拟器安装:

  1. 将 ca.crt 拖到模拟器中。请注意,没有任何确认说明已发生任何事情。
  2. 不需要转到“设置 / 通用 / 关于本机 / 证书信任设置”并启用它。它应该已经启用。

Android 安装:

  1. 将 ca.crt 发送到您的 Gmail 账户,然后在 Android 模拟器中登录到 Gmail 并点击安装。
  2. 它应该出现在“用户”标签下的“设置 / 锁屏和安全 / 加密和凭据 / 可信凭据”中。

2
有没有一种方法可以说“信任此根CA及其所有子级”,而不是手动添加每个单独的域证书?我在macOS上成功将ca.crt添加到钥匙串并标记为受信任,但这似乎在Android上无法正常工作:即使成功添加了它(domain.crt确实出现了),ca.crt也不会出现在“用户”选项卡中。 - Jumpa
移动 Chrome 在真实的安卓设备上的流程是什么?我的 ca.cert 应该放在哪里? - woodz
这应该与模拟器的过程相同。将其发送到您自己的电子邮件并从设备登录您的电子邮件帐户,然后点击它。您还可以将其放在Google驱动器上,并从设备上打开它。 - ScottyB
感谢上帝,我已经寻找了几个小时...不知道为什么在每个设备上都要这么复杂。 - ubuntuaskdanidani
1
@Jumpa或其他想要创建根CA的人,您必须使用openssl的“ca”命令来签署您的服务器证书。我能够通过遵循这个博客系列摸索出一条路:https://www.flexlabs.org/2019/07/private-ca-2-issuing-certificates,请参阅第1部分以设置CA,使其正常工作。 - teleclimber
非常好的答案,但似乎在Firefox上无法工作(请参见:https://stackoverflow.com/questions/75501215/self-signed-certificate-not-working-in-firefox)。有任何想法为什么会这样? - frontend_dev

1

我将分享我的批处理文件,用于创建可在Windows、iOS、Android、Chrome、FireFox和Safari上运行的自签名证书。我已经在最新版本的Chrome、Firefox以及移动设备上的本机浏览器(Android 8.1、最新版本的iOS)上进行了测试。

我发现接受的答案并没有涵盖所有情况——在某些Firefox移动版本中,我仍然需要接受一个异常。尽管接受异常会显示锁定图标,但在全屏或独立模式下安装为PWA时,这会导致URL显示。我的目标是支持一个私有本地网络上没有任何例外的内部网站,以便在Proper PWA安装。

我发现覆盖了所有情况(实际设备)的方法如下:

  • 创建CA
  • 创建签名请求
  • 使用签名请求创建CERT
  • 将CERT绑定到网站
  • 在所有设备上安装CA。

注意:批处理文件会创建很多文件,但其中许多仅是不同格式以支持不同的场景。批处理文件将要求您多次输入密码。不要因输入多个密码而感到困惑-随着批处理文件进行步骤,它始终验证在第一步创建的初始密码。如果在批处理文件结束时通过暂停遇到错误,请不要紧张。

SelfCert.bat

:: Please install openssl in c:\openssl
:: The path to config should be C:\openssl\openssl.cnf
:: Otherwise you will need to add -config "path to openssl.cnf" when making requests

:: Change to current directory of batch file
cd %~dp0

:: GENERATING CA
:: 1.Generate RSA
openssl genrsa -aes256 -out ca-key.pem 4096

:: 2.Generate a public CA Cert
openssl req -new -x509 -sha256 -days 365 -key ca-key.pem -out ca.pem

:: GENERATING CERTIFICATE
:: 1. Create a RSA key
openssl genrsa -out cert-key.pem 4096

:: 2. Create a Certificate Signing Request (CSR)
openssl req -new -sha256 -subj "/CN=yourcngoeshere" -key cert-key.pem -out cert.csr

:: 3. Create a extfile with all the alternative names
:: OBSOLETE HANDLED IN C#
:: echo "subjectAltName=DNS:your-dns.record,IP:XXX.XXX.XXX.XXX" >> extfile.cnf

:: 4. Create the Certificate
openssl x509 -req -sha256 -days 365 -in cert.csr -CA ca.pem -CAkey ca-key.pem -out cert.pem -extfile extfile.cnf -CAcreateserial

:: At this point the chian is completed.Now we will create different formats for uses cases and add to trusted root.

:: 5. Create a crt for Android
openssl x509 -outform der -in ca.pem -out ca.crt

:: 6. Create a pfx for IIS, Kestrel
openssl pkcs12 -export -out cert.pfx -inkey cert-key.pem -in cert.pem -certfile ca.pem

:: 7. Add the certification to our trusted root (iOS can add crt or pem)
certutil.exe -addstore root ca.pem

:: Remember in iOS to enable the certificate after installing it
:: Settings > General > About > Certificate Trust Settings

extfile.cnf

在运行批处理文件之前,我创建了这个文件,但您可以取消注释 生成证书 下的第3步。

subjectAltName=DNS:your-dns.record,IP:XXX.XXX.XXX.XXX

服务器/客户端 - Windows

  • 在Windows上,CA证书将添加到批处理文件的最后一行“certutil.exe -addstore root ca.pem”中的受信任根证书存储。

  • 在IIS和Kestrel上,我将cert.pfx分配给网站。

builder.WebHost.ConfigureKestrel(opt => { opt.ListenAnyIP(6001, listOpt => { listOpt.UseHttps(@"path to cert.pfx", "pfxpassword");});});

客户端 - Android

  • 在Android上,我通过电子邮件发送ca.crt。某些版本的Android似乎对类型很挑剔。从电子邮件中点击附件就足以安装它。
  • 通过导航到设置 > 安全性和位置 > 加密和凭据 > 受信任的凭据 > 用户来验证是否已安装。
    • 如果没有安装,则可能需要转到设置 > 安全性和位置 > 加密和凭据 > 从SD卡安装 > 下载

客户端 - iOS

  • 在iOS上,我通过电子邮件发送ca.crtca.pem。请务必按照安装后的提示说明启用CA。
    • 通常在下载后,您需要转到设置 > 通用 > 描述文件 > 安装
    • 安装后,您需要转到设置 > 通用 > 关于本机 > 证书信任设置 > 启用证书
两天前,我深入研究了认证链,由于没有密码学方面的经验,这真的让我头痛不已。我要特别感谢Christian Lempa。我只是将他的命令放入批处理文件中,并在最后将它们转换为不同的格式。希望这能帮助到某些人。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接