我正在为嵌入式Linux设备添加HTTPS支持。我已尝试使用以下步骤生成自签名证书:

openssl req -new > cert.csr
openssl rsa -in privkey.pem -out key.pem
openssl x509 -in cert.csr -out cert.pem -req -signkey key.pem -days 1001
cat key.pem>>cert.pem

这是可行的,但我在Google Chrome上遇到了一些错误:

这可能不是你要找的网站!站点的安全证书不受信任!

我错过了什么吗?这是构建自签名证书的正确方法吗?


当前回答

您可以在一个命令中完成此操作:

openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 365

如果您不想用密码保护您的私钥,也可以添加-node(“no DES”的缩写)。否则,它将提示您输入“至少4个字符”的密码。

天数参数(365)可以替换为任何数字以影响到期日期。然后它会提示您输入“国家名称”之类的信息,但您只需按Enter键并接受默认值即可。

添加-sbj'/CN=localhost'以抑制有关证书内容的问题(用所需域替换localhost)。

自签名证书不会通过任何第三方进行验证,除非您以前将其导入浏览器。如果需要更多的安全性,则应使用由证书颁发机构(CA)签名的证书。

其他回答

我错过了什么吗?这是构建自签名证书的正确方法吗?

创建自签名证书很容易。您只需使用openssl-req命令。创建一个可供最多客户机使用的客户端(如浏览器和命令行工具)可能很棘手。

这很困难,因为浏览器有自己的一组要求,而且它们比IETF更具限制性。浏览器使用的要求记录在CA/浏览器论坛上(见下文参考资料)。限制出现在两个关键领域:(1)信任锚和(2)DNS名称。

现代浏览器(如我们在2014/2015年使用的warez)需要一个链接到信任锚的证书,他们希望DNS名称在证书中以特定方式显示。浏览器正在积极反对自签名服务器证书。

有些浏览器不太容易导入自签名服务器证书。事实上,你不能使用某些浏览器,比如Android的浏览器。因此,完整的解决方案是成为你自己的权威。

在没有成为自己的权威机构的情况下,您必须获得正确的DNS名称,以使证书获得最大的成功机会。但我鼓励你成为自己的权威。成为你自己的权威很容易,它会回避所有的信任问题(谁比你自己更值得信任?)。


这可能不是你要找的网站!站点的安全证书不受信任!

这是因为浏览器使用预定义的信任锚点列表来验证服务器证书。自签名证书不会链接回受信任的锚点。

避免这种情况的最佳方法是:

创建自己的权限(即成为CA)为服务器创建证书签名请求(CSR)使用CA密钥签署服务器的CSR在服务器上安装服务器证书在客户端上安装CA证书

第1步-创建您自己的授权仅意味着使用CA创建自签名证书:正确且正确的密钥使用。这意味着Subject和Issuer是同一实体,CA在基本约束中设置为true(也应标记为关键),密钥用法为keyCertSign和crlSign(如果您使用的是CRL),Subject key Identifier(SKI)与Authority key Identifier相同。

要成为自己的证书颁发机构,请参阅*如何与证书颁发机构签署证书签名请求?堆栈溢出。然后,将CA导入浏览器使用的信任存储。

步骤2-4大致上是您现在为面向公共的服务器注册像Startcom或CAcert这样的CA服务时所做的。第1步和第5步允许您避开第三方权威机构,充当自己的权威机构(谁比自己更值得信任?)。

避免浏览器警告的第二个最佳方法是信任服务器的证书。但有些浏览器,比如Android的默认浏览器,不允许你这样做,所以它永远不会在平台上运行。

浏览器(和其他类似的用户代理)不信任自签名证书的问题将是物联网(IoT)中的一个大问题。例如,当你连接到恒温器或冰箱来编程时,会发生什么?答案是,就用户体验而言,没有什么好东西。

W3C的WebAppSec工作组正在着手研究这个问题。例如,请参阅提案:将HTTP标记为非安全。


如何使用OpenSSL创建自签名证书

下面的命令和配置文件创建自签名证书(它还向您展示了如何创建签名请求)。它们在一个方面与其他答案不同:用于自签名证书的DNS名称是主题备用名称(SAN),而不是通用名称(CN)。

DNS名称通过配置文件以subjectAltName=@alternate_names行(无法通过命令行执行)放置在SAN中。然后在配置文件中有一个alternate_name部分(您应该根据自己的喜好进行调整):

[ 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
# IP.1        = 127.0.0.1
# IP.2        = ::1

将DNS名称放在SAN中而不是CN中是很重要的,因为IETF和CA/浏览器论坛都指定了这种做法。它们还指定CN中的DNS名称已弃用(但并非禁止)。如果在CN中输入DNS名称,则必须根据CA/B策略将其包含在SAN中。因此,您不能避免使用主题备用名称。

如果您没有将DNS名称放在SAN中,则证书将无法在浏览器和其他用户代理下验证,这些用户代理遵循CA/浏览器论坛指南。

相关:浏览器遵循CA/浏览器论坛政策;而不是IETF策略。这也是使用OpenSSL创建的证书(通常遵循IETF)有时无法在浏览器下验证(浏览器遵循CA/B)的原因之一。它们是不同的标准,它们有不同的发布政策和不同的验证要求。


创建自签名证书(注意添加了-x509选项):

openssl req -config example-com.conf -new -x509 -sha256 -newkey rsa:2048 -nodes \
    -keyout example-com.key.pem -days 365 -out example-com.cert.pem

创建签名请求(请注意缺少-x509选项):

openssl req -config example-com.conf -new -sha256 -newkey rsa:2048 -nodes \
    -keyout example-com.key.pem -days 365 -out example-com.req.pem

打印自签名证书:

openssl x509 -in example-com.cert.pem -text -noout

打印签名请求:

openssl req -in example-com.req.pem -text -noout

配置文件(通过-config选项传递)

[ req ]
default_bits        = 2048
default_keyfile     = server-key.pem
distinguished_name  = subject
req_extensions      = req_ext
x509_extensions     = x509_ext
string_mask         = utf8only

# 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         = Country Name (2 letter code)
countryName_default     = US

stateOrProvinceName     = State or Province Name (full name)
stateOrProvinceName_default = NY

localityName            = Locality Name (eg, city)
localityName_default        = New York

organizationName         = Organization Name (eg, company)
organizationName_default    = Example, LLC

# 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          = Common Name (e.g. server FQDN or YOUR name)
commonName_default      = Example Company

emailAddress            = Email Address
emailAddress_default        = test@example.com

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

subjectKeyIdentifier        = hash
authorityKeyIdentifier    = keyid,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        = CA:FALSE
keyUsage            = digitalSignature, keyEncipherment
subjectAltName          = @alternate_names
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

# 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          = @alternate_names
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

您可能需要为Chrome执行以下操作。否则,Chrome可能会投诉公用名无效(ERR_CERT_Common_Name_invalid)。我不确定SAN中的IP地址与本例中的CN之间的关系。

# IPv4 localhost
# IP.1       = 127.0.0.1

# IPv6 localhost
# IP.2     = ::1

关于X.509/PKIX证书中DNS名称的处理,还有其他规则。有关规则,请参阅以下文件:

RFC 5280,Internet X.509公钥基础结构证书和证书吊销列表(CRL)配置文件RFC 6125,在传输层安全性(TLS)环境中使用X.509(PKIX)证书在互联网公钥基础设施中表示和验证基于域的应用服务身份RFC 6797,附录A,HTTP严格传输安全(HSTS)RFC 7469,HTTP公钥锁定扩展CA/浏览器论坛基线要求CA/浏览器论坛扩展验证指南

列出了RFC 6797和RFC 7469,因为它们比其他RFC和CA/B文档更具限制性。RFC 6797和7469也不允许IP地址。

在经历了大量的尝试和各种解决方案之后,我仍然面临这样一个问题:为localhost颁发自签名证书会给我带来错误

错误证书无效

在扩展细节时,chrome表示:

您现在无法访问localhost,因为网站已发送加密凭据。。。

唯一难看的方式是键入(直接在这个屏幕上,没有看到文本的任何光标):

(在键盘上键入)这是安全的

这让我继续。

直到我找到extendedKeyUsage=serverAuth,clientAuth

TL;博士

openssl genrsa-out localhost.key 2048openssl req-key localhost.key-new-out localhost.csr(单击输入所有内容,然后用localhost或其他FQDN填写通用名称(CN)。将以下内容放入名为v3.ext的文件中(根据需要进行编辑):

subjectKeyIdentifier   = hash
authorityKeyIdentifier = keyid:always,issuer:always
basicConstraints       = CA:TRUE
keyUsage               = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment, keyAgreement, keyCertSign
extendedKeyUsage       = serverAuth, clientAuth
subjectAltName         = DNS:localhost, DNS:localhost.localdomain
issuerAltName          = issuer:copy

openssl x509-req-in localhost.csr-signkey localhost.key-out localhost.pem-days 3650-sha256-extfile v3.ext

瞧!您可以访问网站,展开“高级”,然后单击“转到本地主机(不安全)”。

从2023年起,OpenSSL≥1.1.1,以下命令可满足您的所有需求,包括主题备用名称(SAN):

openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes \
  -keyout example.key -out example.crt -subj "/CN=example.com" \
  -addext "subjectAltName=DNS:example.com,DNS:www.example.net,IP:10.0.0.1"

在OpenSSL≤1.1.0的旧系统上,如Debian≤9或CentOS≤7,需要使用此命令的更长版本:

openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes \
  -keyout example.key -out example.crt -extensions san -config \
  <(echo "[req]"; 
    echo distinguished_name=req; 
    echo "[san]"; 
    echo subjectAltName=DNS:example.com,DNS:www.example.net,IP:10.0.0.1
    ) \
  -subj "/CN=example.com"

任一命令都创建一个证书

对于(子)域example.com和www.example.net(SAN)有效,对于IP地址10.0.0.1(SAN)也是有效的,相对强劲(截至2023年)有效期为3650天(约10年)。

将生成以下文件:

私钥:example.key证书:example.crt

所有信息都在命令行中提供。没有让你烦恼的互动输入。没有你必须处理的配置文件。所有必要的步骤都由单个OpenSSL调用执行:从私钥生成到自签名证书。


备注#1:加密参数

由于证书是自签名的,需要用户手动接受,因此使用短到期或弱加密是没有意义的。

未来,您可能希望使用超过4096位的RSA密钥和比sha256更强的哈希算法,但截至2023年,这些都是正常的值。它们足够强大,同时受到所有现代浏览器的支持。

备注#2:参数“-nodes”

理论上,您可以省略-nodes参数(这意味着“没有DES加密”),在这种情况下,example.key将使用密码进行加密。然而,这对于服务器安装几乎从来都没有用处,因为您要么必须将密码也存储在服务器上,要么必须在每次重新启动时手动输入密码。

备注#3:另请参见

直接在命令行上向openssl提供subjectAltName如何通过命令行向SSL证书添加多个电子邮件地址?有关MSYS_NO_PATHCONV的更多信息

openssl允许通过单个命令(-newkey)生成自签名证书指示生成私钥,-x509指示发出自签名证书而不是签名请求)::

openssl req -x509 -newkey rsa:4096 \
-keyout my.key -passout pass:123456 -out my.crt \
-days 365 \
-subj /CN=localhost/O=home/C=US/emailAddress=me@mail.internal \
-addext "subjectAltName = DNS:localhost,DNS:web.internal,email:me@mail.internal" \
-addext keyUsage=digitalSignature -addext extendedKeyUsage=serverAuth

您可以在单独的步骤中生成私钥和构造自签名证书:

openssl genrsa -out my.key -passout pass:123456 2048

openssl req -x509 \
-key my.key -passin pass:123456 -out my.csr \
-days 3650 \
-subj /CN=localhost/O=home/C=US/emailAddress=me@mail.internal \
-addext "subjectAltName = DNS:localhost,DNS:web.internal,email:me@mail.internal" \
-addext keyUsage=digitalSignature -addext extendedKeyUsage=serverAuth

查看生成的证书:

openssl x509 -text -noout -in my.crt

Java keytool创建PKCS#12存储::

keytool -genkeypair -keystore my.p12 -alias master \
-storetype pkcs12 -keyalg RSA -keysize 2048 -validity 3650 \
-storepass 123456 \
-dname "CN=localhost,O=home,C=US" \
-ext 'san=dns:localhost,dns:web.internal,email:me@mail.internal'

要导出自签名证书,请执行以下操作:

keytool -exportcert -keystore my.p12 -file my.crt \
-alias master -rfc -storepass 123456

查看生成的证书:

keytool -printcert -file my.crt

GnuTLS的certtool不允许从CLI传递不同的属性。我不喜欢乱搞配置文件((

您可以在一个命令中完成此操作:

openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 365

如果您不想用密码保护您的私钥,也可以添加-node(“no DES”的缩写)。否则,它将提示您输入“至少4个字符”的密码。

天数参数(365)可以替换为任何数字以影响到期日期。然后它会提示您输入“国家名称”之类的信息,但您只需按Enter键并接受默认值即可。

添加-sbj'/CN=localhost'以抑制有关证书内容的问题(用所需域替换localhost)。

自签名证书不会通过任何第三方进行验证,除非您以前将其导入浏览器。如果需要更多的安全性,则应使用由证书颁发机构(CA)签名的证书。