我正在为嵌入式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上遇到了一些错误:

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

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


当前回答

我不能评论,所以我补充了一个单独的答案。我试图为NGINX创建一个自签名证书,这很简单,但当我想将其添加到Chrome白名单时,我遇到了一个问题。我的解决方案是创建一个根证书并用它签署一个子证书。

所以一步一步。创建文件config_ssl_c.cnf注意,配置文件有一个选项basicConstraints=CA:true,这意味着该证书应该是根证书。

这是一个很好的做法,因为您只需创建一次即可重复使用。

[ req ]
default_bits = 2048

prompt = no
distinguished_name=req_distinguished_name
req_extensions = v3_req

[ req_distinguished_name ]
countryName=UA
stateOrProvinceName=root region
localityName=root city
organizationName=Market(localhost)
organizationalUnitName=roote department
commonName=market.localhost
emailAddress=root_email@root.localhost

[ alternate_names ]
DNS.1        = market.localhost
DNS.2        = www.market.localhost
DNS.3        = mail.market.localhost
DNS.4        = ftp.market.localhost
DNS.5        = *.market.localhost

[ v3_req ]
keyUsage=digitalSignature
basicConstraints=CA:true
subjectKeyIdentifier = hash
subjectAltName = @alternate_names

子证书的下一个配置文件将调用config_ssl.cnf。

[ req ]
default_bits = 2048

prompt = no
distinguished_name=req_distinguished_name
req_extensions = v3_req

[ req_distinguished_name ]
countryName=UA
stateOrProvinceName=Kyiv region
localityName=Kyiv
organizationName=market place
organizationalUnitName=market place department
commonName=market.localhost
emailAddress=email@market.localhost

[ alternate_names ]
DNS.1        = market.localhost
DNS.2        = www.market.localhost
DNS.3        = mail.market.localhost
DNS.4        = ftp.market.localhost
DNS.5        = *.market.localhost

[ v3_req ]
keyUsage=digitalSignature
basicConstraints=CA:false
subjectAltName = @alternate_names
subjectKeyIdentifier = hash

第一步-创建根密钥和证书

openssl genrsa -out ca.key 2048
openssl req -new -x509 -key ca.key -out ca.crt -days 365 -config config_ssl_ca.cnf

第二步创建子密钥和文件CSR-证书签名请求。因为这样做的目的是通过root签名子证书并获得正确的证书

openssl genrsa -out market.key 2048
openssl req -new -sha256 -key market.key -config config_ssl.cnf -out market.csr

打开Linux终端并执行以下命令

echo 00 > ca.srl
touch index.txt

ca.srl文本文件包含下一个要使用的十六进制序列号。强制性的此文件必须存在并包含有效的序列号。

最后一步,再创建一个配置文件,并将其命名为config_ca.cnf

# we use 'ca' as the default section because we're usign the ca command
[ ca ]
default_ca = my_ca

[ my_ca ]
#  a text file containing the next serial number to use in hex. Mandatory.
#  This file must be present and contain a valid serial number.
serial = ./ca.srl

# the text database file to use. Mandatory. This file must be present though
# initially it will be empty.
database = ./index.txt

# specifies the directory where new certificates will be placed. Mandatory.
new_certs_dir = ./

# the file containing the CA certificate. Mandatory
certificate = ./ca.crt

# the file contaning the CA private key. Mandatory
private_key = ./ca.key

# the message digest algorithm. Remember to not use MD5
default_md = sha256

# for how many days will the signed certificate be valid
default_days = 365

# a section with a set of variables corresponding to DN fields
policy = my_policy

# MOST IMPORTANT PART OF THIS CONFIG
copy_extensions = copy

[ my_policy ]
# if the value is "match" then the field value must match the same field in the
# CA certificate. If the value is "supplied" then it must be present.
# Optional means it may be present. Any fields not mentioned are silently
# deleted.
countryName = match
stateOrProvinceName = supplied
organizationName = supplied
commonName = market.localhost
organizationalUnitName = optional
commonName = supplied

您可能会问,为什么这么难,为什么我们必须再创建一个配置来按根签名子证书。答案很简单,因为子证书必须具有SAN块-主题替代名称。如果我们使用“openssl x509”utils对子证书进行签名,则根证书将删除子证书中的SAN字段。因此,我们使用“opensslca”而不是“opensslx509”来避免删除SAN字段。我们创建一个新的配置文件,并告诉它复制所有扩展字段copy_extensions=copy。

openssl ca -config config_ca.cnf -out market.crt -in market.csr

该程序会问您两个问题:

签署证书?说“Y”1个证书请求中有1个已认证,是否提交?说“Y”

在终端中,您可以看到一个带有单词“Database”的句子,它表示您通过命令“touch”创建的文件index.txt。它将包含由“openssl-ca”util创建的所有证书的所有信息。要检查证书的有效性,请使用:

openssl rsa -in market.key -check

如果您想查看CRT中的内容:

openssl x509 -in market.crt -text -noout

如果您想了解CSR的内部内容:

openssl req -in market.csr -noout -text 

其他回答

现代浏览器现在如果缺少SAN(Subject Alternate Name),则会为格式良好的自签名证书抛出安全错误。OpenSSL没有提供命令行方式来指定这一点,因此许多开发人员的教程和书签突然过时了。

重新运行的最快方法是一个简短的独立conf文件:

创建OpenSSL配置文件(示例:req.cnf)[要求]distinguished_name=请求分类名称x509_extensions=v3_reqprompt=否[请求分配名称]C=我们ST=VAL=SomeCityO=我的公司OU=我的部门CN=www.company.com[v3_req]keyUsage=关键,数字签名,密钥协议extendedKeyUsage=服务器身份验证subjectAltName=@alt_names[备选名称]DNS.1=www.company.comDNS.2=公司网站DNS.3=公司网创建引用此配置文件的证书openssl req-x509-节点-天730-新密钥rsa:2048\-keyout cert.key-out cert.pem-config req.cnf-sha256

来自的配置示例https://support.citrix.com/article/CTX135602

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

创建自签名证书很容易。您只需使用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地址。

正如已经详细讨论过的,自签名证书不受Internet信任。您可以将自签名证书添加到许多浏览器,但不是所有浏览器。或者,您可以成为自己的证书颁发机构。

人们不想从证书颁发机构获得签名证书的主要原因是成本--赛门铁克每年收取995美元至1999美元的证书费用--仅针对用于内部网络的证书,赛门铁克每年收取399美元。如果您正在处理信用卡付款或为高利润公司的利润中心工作,那么该成本很容易被证明是合理的。对于一个在互联网上创建的个人项目,或者一个以最低预算运行的非营利组织,或者如果一个人在一个组织的成本中心工作,成本中心总是试图用更少的钱做更多的事情,这是很多人都无法承受的。

另一种方法是使用certbot(参见certbot)。Certbot是一个易于使用的自动客户端,可为web服务器获取和部署SSL/TLS证书。

如果您设置了certbot,则可以使其为您创建和维护Let's Encrypt证书颁发机构颁发的证书。

我在周末为我的组织做了这件事。我在服务器(Ubuntu 16.04)上安装了certbot所需的软件包,然后运行了设置和启用certbot所必需的命令。一个可能需要certbot的DNS插件-我们目前正在使用DigitalOcean,但可能很快会迁移到另一个服务。

请注意,有些指示不太正确,需要谷歌花点时间才能弄清楚。这第一次花了我相当多的时间,但现在我想我可以在几分钟内完成。

对于DigitalOcean,我遇到的一个问题是,系统提示我输入DigitalOcean凭据INI文件的路径。脚本所指的是应用程序和API页面以及该页面上的令牌/密钥选项卡。您需要为DigitalOcean的API创建或生成个人访问令牌(读写)——这是一个65个字符的十六进制字符串。然后需要将该字符串放入运行certbot的Web服务器上的文件中。该文件的第一行可以是注释(注释以#开头)。第二行是:

dns_digitalocean_token = 0000111122223333444455556666777788889999aaaabbbbccccddddeeeeffff

一旦我知道了如何为DigitalOcean的API设置读写令牌,就可以很容易地使用certbot来设置通配符证书。请注意,您不必设置通配符证书,而是可以指定要应用证书的每个域和子域。正是通配符证书需要包含来自DigitalOcean的个人访问令牌的凭据INI文件。

请注意,公钥证书(也称为身份证书或SSL证书)过期并需要续订。因此,您需要定期(重复)更新证书。certbot文档包括续订证书。

我的计划是编写一个脚本,使用openssl命令获取证书的到期日期,并在到期前30天或更短的时间内触发续订。然后我将把这个脚本添加到cron中,并每天运行一次。

以下是读取证书过期日期的命令:

root@prod-host:~# /usr/bin/openssl x509 -enddate -noout -in path-to-certificate-pem-file
notAfter=May 25 19:24:12 2019 GMT

我建议添加-sha256参数,以使用SHA-2哈希算法,因为主要浏览器正在考虑将“SHA-1证书”显示为不安全。

来自已接受答案的同一命令行-@diegows,添加了-sha256

openssl req-x509-sha256-newkey rsa:2048-keyout key.pem-out cert.pem-天XXX

更多信息请访问谷歌安全博客。

2018年5月更新。正如许多人在评论中指出的那样,使用SHA-2不会为自签名证书添加任何安全性。但我仍然建议使用它作为一个好习惯,不要使用过时/不安全的加密哈希函数。在“为什么最终实体证书之上的证书可以基于SHA-1?”中提供了完整的解释?。

这是我在本地箱上使用的脚本,用于在自签名证书中设置SAN(subjectAltName)。

此脚本采用域名(example.com)并在同一证书中为*.example.com和example.com生成SAN。对以下各节进行了评论。为脚本命名(例如generatessl.sh)并赋予其可执行权限。文件将被写入与脚本相同的目录。

Chrome 58及更高版本要求在自签名证书中设置SAN。

#!/usr/bin/env bash

# Set the TLD domain we want to use
BASE_DOMAIN="example.com"

# Days for the cert to live
DAYS=1095

# A blank passphrase
PASSPHRASE=""

# Generated configuration file
CONFIG_FILE="config.txt"

cat > $CONFIG_FILE <<-EOF
[req]
default_bits = 2048
prompt = no
default_md = sha256
x509_extensions = v3_req
distinguished_name = dn

[dn]
C = CA
ST = BC
L = Vancouver
O = Example Corp
OU = Testing Domain
emailAddress = webmaster@$BASE_DOMAIN
CN = $BASE_DOMAIN

[v3_req]
subjectAltName = @alt_names

[alt_names]
DNS.1 = *.$BASE_DOMAIN
DNS.2 = $BASE_DOMAIN
EOF

# The file name can be anything
FILE_NAME="$BASE_DOMAIN"

# Remove previous keys
echo "Removing existing certs like $FILE_NAME.*"
chmod 770 $FILE_NAME.*
rm $FILE_NAME.*

echo "Generating certs for $BASE_DOMAIN"

# Generate our Private Key, CSR and Certificate
# Use SHA-2 as SHA-1 is unsupported from Jan 1, 2017

openssl req -new -x509 -newkey rsa:2048 -sha256 -nodes -keyout "$FILE_NAME.key" -days $DAYS -out "$FILE_NAME.crt" -passin pass:$PASSPHRASE -config "$CONFIG_FILE"

# OPTIONAL - write an info to see the details of the generated crt
openssl x509 -noout -fingerprint -text < "$FILE_NAME.crt" > "$FILE_NAME.info"

# Protect the key
chmod 400 "$FILE_NAME.key"

该脚本还编写了一个信息文件,因此您可以检查新证书并验证SAN设置是否正确。

                ...
                28:dd:b8:1e:34:b5:b1:44:1a:60:6d:e3:3c:5a:c4:
                da:3d
            Exponent: 65537 (0x10001)
    X509v3 extensions:
        X509v3 Subject Alternative Name: 
            DNS:*.example.com, DNS:example.com
Signature Algorithm: sha256WithRSAEncryption
     3b:35:5a:d6:9e:92:4f:fc:f4:f4:87:78:cd:c7:8d:cd:8c:cc:
     ...

如果您使用的是Apache,则可以在配置文件中引用上述证书,如下所示:

<VirtualHost _default_:443>
    ServerName example.com
    ServerAlias www.example.com
    DocumentRoot /var/www/htdocs

    SSLEngine on
    SSLCertificateFile path/to/your/example.com.crt
    SSLCertificateKeyFile path/to/your/example.com.key
</VirtualHost>

请记住重新启动Apache(或Nginx或IIS)服务器以使新证书生效。