我正在为嵌入式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命令。创建一个可供最多客户机使用的客户端(如浏览器和命令行工具)可能很棘手。

这很困难,因为浏览器有自己的一组要求,而且它们比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地址。

其他回答

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

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 -new -key {private key file} -out {output file}

但是,会显示警告,因为浏览器无法通过使用已知的证书颁发机构(CA)验证证书来验证标识。

由于这是一个自签名证书,因此没有CA,您可以安全地忽略警告并继续。如果你想获得一个真正的证书,在公共互联网上任何人都可以识别,那么程序如下。

生成私钥使用该私钥创建CSR文件向CA提交CSR(威瑞信或其他公司等)在web服务器上安装从CA接收的证书根据cert类型将其他证书添加到身份验证链

我在《安全连接:使用OpenSSL创建安全证书》的一篇文章中了解了更多关于这一点的详细信息

生成密钥我将/etc/mysql用于证书存储,因为/etc/appamor.d/usr.sbin.mysqld包含/etc/mysql/*.pem r。苏多苏-cd/etc/mysqlopenssl genrsa-out ca-key.pem 2048;openssl req-new-x509-nodes-days 1000-key ca-key.pem-out ca-cert.pem;openssl req-newkey rsa:2048-天1000-节点-keyout server-key.pem-out server-req.pem;openssl x509-req-in server-req.pem-天1000-CA CA-cert.pem-CA密钥CA-key.pem-set_serial 01-out server-cert.pum;openssl req-newkey rsa:2048-天1000-nodes-keyout client-key.pem-out client-req.pem;openssl x509-req-in client-req.pem-days 1000-CA CA-cert.pem-CA密钥CA-key.pem-set_serial 01-out client-cert.pum;添加配置/etc/mysql/my.cnf[客户]ssl-ca=/etc/mysql/ca-cert.pemssl证书=/etc/mysql/client-cert.pemssl密钥=/etc/mysql/client-key.pem[mysqld]ssl-ca=/etc/mysql/ca-cert.pemssl证书=/etc/mysql/server-cert.pemssl密钥=/etc/mysql/server-key.pem

在我的设置中,Ubuntu服务器登录到:/var/log/mysql/error.log

后续注意事项:

SSL错误:无法从“…”获取证书如果MySQL不在appmors配置中,则可能会拒绝对证书文件的读取访问。如前面步骤^中所述,将所有证书保存为/etc/mysql/目录中的.pem文件,appamor默认批准该目录(或修改appamor/SELinux以允许访问存储它们的任何位置)SSL错误:无法获取私钥您的MySQL服务器版本可能不支持默认的rsa:2048格式将生成的rsa:2048转换为纯rsa:openssl rsa-in server-key.pem-out server-key.phpopenssl rsa-in client-key.pem-out client-key.php检查本地服务器是否支持SSL:mysql-u根-pmysql>显示变量,如“%ssl%”;+---------------+----------------------------+|变量名称|值|+---------------+----------------------------+|have_openssl|是||have_ssl|是||ssl-ca|/etc/mysql/ca-cert.pem||ssl_容量|||ssl_cert |/etc/mysql/server-cert.pem||ssl_密码|||ssl-key |/etc/mysql/server-key.pem|+---------------+----------------------------+验证与数据库的连接是否经过SSL加密:正在验证连接登录到MySQL实例时,可以发出查询:显示状态,如“Ssl_cypher”;如果您的连接未加密,结果将为空:mysql>显示状态,如“Ssl_cypher”;+---------------+-------+|变量名称|值|+---------------+-------+|Ssl_密码||+---------------+-------+一组1行(0.00秒)否则,它将显示正在使用的密码的非零长度字符串:mysql>显示状态,如“Ssl_cypher”;+---------------+--------------------+|变量名称|值|+---------------+--------------------+|Ssl_密码|DHE-RSA-AES256-SHA|+---------------+--------------------+一组1行(0.00秒)特定用户的连接需要ssl(“需要ssl”):安全套接字层告诉服务器只允许帐户使用SSL加密连接。授予测试的所有特权。*至“root”@“本地主机”需要SSL;要进行连接,客户端必须指定--ssl ca选项来验证服务器证书,还可以指定--ssl密钥和--ssl证书选项。如果未指定--ssl ca选项或--ssl capath选项,则客户端不会对服务器证书进行身份验证。


备用链接:《用SSL保护PHP与MySQL的连接》中的冗长教程。

生成10年无密码和证书的密钥,短方法:

openssl req  -x509 -nodes -new  -keyout server.key -out server.crt -days 3650 -subj "/C=/ST=/L=/O=/OU=web/CN=www.server.com"

对于标志-subj |,允许使用subject空值-subj“/C=/ST=/L=/O=/OU=web/CN=www.server.com”,但可以根据需要设置更多详细信息:

C-国家名称(2字母代码)ST-状态L-地点名称(如城市)O-组织名称OU-组织单位名称CN-通用名称-必填!

正如已经详细讨论过的,自签名证书不受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