【问题标题】:How to generate a self-signed SSL certificate using OpenSSL?如何使用 OpenSSL 生成自签名 SSL 证书?
【发布时间】:2016-12-04 09:17:36
【问题描述】:

我正在向嵌入式 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:

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

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

【问题讨论】:

  • 自签名证书被认为对 Internet 不安全。 Firefox 会将该站点视为具有无效证书,而 Chrome 会认为该连接是纯 HTTP。更多详情:gerv.net/security/self-signed-certs
  • 您需要将 CA 证书导入浏览器并告诉浏览器您信任该证书 - 或者 - 由浏览器信任的大型无偿组织之一对其进行签名- 或 - 忽略警告并点击过去。我自己喜欢最后一个选项。
  • 您不应该像那样使用“库存”OpenSSL 设置。这是因为您不能将 DNS 名称放在使用者备用名称 (SAN) 中。您需要提供一个带有alternate_names 部分的配置文件,并使用-config 选项传递它。此外,IETF 和 CA/浏览器论坛均不赞成(但不禁止)将 DNS 名称放在通用名称 (CN) 中。 CN 中的任何 DNS 名称也必须存在于 SAN 中。没有办法避免使用 SAN。请参阅下面的答案。
  • 除了@jww 的评论。 2017 年 5 月 Chrome 不再接受没有(emtpy)SAN 的证书:“此站点的证书不包含包含域名或 IP 地址的主题备用名称扩展。”
  • 如今,只要您的网络服务器可以通过 Internet 上的端口 80 上的 FQDN 访问,您就可以使用 LetsEncrypt 并获得免费的完整 CA 证书(有效期为 90 天,可以自动续订)不会给出任何浏览器警告/消息。 www.letsencrypt.com

标签: ssl openssl certificate ssl-certificate x509certificate


【解决方案1】:

您可以在一个命令中做到这一点:

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

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

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

添加-subj '/CN=localhost' 以隐藏有关证书内容的问题(将localhost 替换为您想要的域)。

除非您之前将自签名证书导入浏览器,否则不会通过任何第三方验证自签名证书。如果您需要更高的安全性,您应该使用由certificate authority (CA) 签名的证书。

【讨论】:

  • 如果您想自己验证任何事情,这里是the documentation
  • 与第 3 方签署如何提供更高的安全性?
  • 对于在自动化中使用它的任何其他人,这里是该主题的所有常用参数:-subj "/C=US/ST=Oregon/L=Portland/O=Company Name/OU=Org/CN=www.example.com"
  • @JamesMills 我的意思是,想想看——如果一个看起来阴暗的家伙在他的货车侧面写着“免费糖果”邀请你进来,你完全会三思而后行对此要保持警惕——但如果你信任的人——比如真的信任——都喜欢“老兄,他是合法的”,那么你就会全心全意地享用免费的糖果。
  • 记得使用-sha256生成基于SHA-256的证书。
【解决方案2】:

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

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

这很困难,因为浏览器有自己的一套要求,而且比IETF 更严格。浏览器使用的要求记录在CA/Browser Forums(请参阅下面的参考资料)。限制出现在两个关键领域:(1) 信任锚,和 (2) DNS 名称。

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

某些浏览器无法完全轻松地导入自签名服务器证书。事实上,你不能用一些浏览器,比如Android的浏览器。所以完整的解决方案是成为你自己的权威。

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


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

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

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

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

第 1 步 - 创建您自己的权限 只是意味着创建一个带有 CA: true 和正确使用密钥的自签名证书。这意味着 SubjectIssuer 是同一个实体,CA 在 Basic Constraints 中设置为 true(也应标记为关键),密钥用法为 keyCertSigncrlSign(如果您使用 CRL),Subject Key Identifier (SKI) 与 Authority Key Identifier (AKI) 相同)。

要成为您自己的证书颁发机构,请参阅 Stack Overflow 上的 *How do you sign a certificate signing request with your certification authority?。然后,将您的 CA 导入浏览器使用的 Trust Store。

步骤 2 到 4 大致是您现在为面向公众的服务器所做的,当您征用像 StartcomCAcert 这样的 CA 的服务时。第 1 步和第 5 步可以让您避开第三方权威,并充当自己的权威(谁比自己更值得信任?)。

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

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

W3C 的 WebAppSec 工作组开始研究这个问题。例如,请参阅Proposal: Marking HTTP As Non-Secure


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

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

通过配置文件将 DNS 名称放置在 SAN 中,其中包含 subjectAltName = @alternate_names 行(无法通过命令行执行此操作)。然后在配置文件中有一个alternate_names 部分(你应该调整它以适应你的口味):

[ 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 名称(但不禁止)。 如果您将 DNS 名称放在 CN 中,那么它必须根据 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 may complain a Common Name is invalid (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 6797 和 RFC 7469,因为它们比其他 RFC 和 CA/B 文档更严格。 RFC 6797 和 7469也不允许允许 IP 地址。

【讨论】:

  • 是否可以在alternate_names 部分使用通配符?特别是子子域。我有一个问题在这里引用这个答案:serverfault.com/questions/711596/…
  • 我刚刚回复了他的具体问题。当答案如此简单时,我认为添加这么长的安全描述是没有意义的
  • @diegows - 您的答案不完整或不正确。它不正确的原因在您不想阅读的长篇文章中讨论:)
  • 谢谢!我发现你的帖子很有帮助。仅供参考,我最近在玩 Vault,发现它坚持使用 IP.x 127.0.0.1 而不是 DNS.x 127……我没有检查这是否符合标准。
  • 谢谢@jww。你说,“1.创建自己的权限(即成为CA)”,然后说,“5.在客户端安装CA证书”。如果根密钥被泄露,恶意人员可以使用该密钥为任何域签署证书,如果他们诱骗您访问他们的网站,他们现在可以进行中间人攻击。有没有办法创建根 CA,使其只能签署中间 CA 而不能签署证书?然后,您可以使用名称约束来保护您的中间 CA。
【解决方案3】:

截至 2022 年,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.comwww.example.net (SAN),
  • 也适用于 IP 地址10.0.0.1 (SAN),
  • 相对强劲(截至 2022 年)和
  • 有效期为3650 天(~10 年)。

生成以下文件:

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

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

备注 #1:加密参数

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

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

备注#2:参数“-nodes

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

备注 #3:另见

【讨论】:

  • 我尝试在 mingw64 的 Windows 上使用 oneliner #2(现代),但我遇到了 -subj 参数的错误。 ` $ openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes -keyout localhost.key -out localhost.crt -subj '/CN=localhost' -addext subjectAltName=DNS:localhost,IP:127.0.0.1 正在生成一个 RSA 私钥 [...] 将新私钥写入 'localhost.key' ----- 名称应采用 /type0=value0/type1=value1/type2=... 格式,其中字符可能是被\逃脱。此名称不是那种格式:'C:/Program Files/Git/CN=localhost' 提出证书请求时出现问题 `
  • 我无法弄清楚扩展为 C:/Program Files/Git/CN=localhost 的 arg /CN=localhost 到底是什么原因,所以我只是在普通 cmd 中运行了整个命令.exe,它工作得很好。以防万一有人为此苦苦挣扎。
  • @FranklinYu 你确定 rsa:2048 在 10 年后就足够了吗?因为那是有效期。如前所述,使用短期到期或弱加密是没有意义的。大多数 2048 位 RSA 密钥的有效期最长为 1-3 年。关于 OpenSSL 1.1.1,我仍然将 sha256 留在其中,因此如果您想要更强的哈希值,更改会更加明确和明显。
  • 1000 +1s 用于创建使用新所需 SAN 的“单线”,而无需创建包含大量样板的冗长配置文件。干得好!
  • 截至 2020 年 8 月 31 日,我可以保证这行得通!!!非常感谢!我真的很想看到一份参考资料,用简单的术语解释为什么它会以这样的速度发展。我的一部分想知道这是否仅仅是因为创建自签名证书的想法对大型科技公司产生了反作用。 10 年或 20 年后需要什么?这太疯狂了,这证明了这类关于 openssl 的问题所产生的活动量。
【解决方案4】:

这里是@diegows's answer 中描述的选项,更详细的描述来自the documentation

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

PKCS#10 证书请求和证书生成实用程序。

-x509

此选项输出自签名证书而不是证书请求。 这通常用于生成测试证书或自签名根 CA。

-newkey arg

此选项创建一个新的证书请求和一个新的私钥。论据 采取几种形式之一。 rsa:nbits,其中 nbits 是位数, 生成一个 nbits 大小的 RSA 密钥。

-keyout filename

这给出了将新创建的私钥写入的文件名。

-out filename

这指定了默认写入的输出文件名或标准输出。

-days n

当使用 -x509 选项时,它指定认证的天数 证书。默认为 30 天。

-nodes

如果指定了此选项,则如果创建了私钥,它将不会被加密。

文档其实比上面的更详细;我只是在这里总结了一下。

【讨论】:

【解决方案5】:

我无法发表评论,所以我将把它作为一个单独的答案。我发现接受的单行答案存在一些问题:

  • 单行在密钥中包含密码。
  • 单行使用 SHA-1,在许多浏览器中会在控制台中引发警告。

这是一个简化版本,它删除了密码,提高了安全性以抑制警告,并在 cmets 中包含一个建议,以传递 -subj 以删除完整的问题列表:

openssl genrsa -out server.key 2048
openssl rsa -in server.key -out server.key
openssl req -sha256 -new -key server.key -out server.csr -subj '/CN=localhost'
openssl x509 -req -sha256 -days 365 -in server.csr -signkey server.key -out server.crt

将“localhost”替换为您需要的任何域。您需要一个接一个地运行前两个命令,因为 OpenSSL 会提示输入密码。

将两者组合成一个 .pem 文件:

cat server.crt server.key > cert.pem

【讨论】:

  • 我需要github.com/molnarg/node-http2 的开发证书,而这个答案是最好的。
  • 将证书和密钥合并到一个文件中:cat server.crt server.key &gt;foo-cert.pem。适用于openssl-1.0.2d/demos/ssl/ 中的示例
  • 我这样生成的证书还在使用SHA1。
  • Tks,非常适合在 FreeBSD 10 OpenLDAP 2.4TLS 上创建自签名证书
  • key.pem 文件呢?
【解决方案6】:

如果现代浏览器缺少 SAN(主题备用名称),则现代浏览器现在会为其他格式良好的自签名证书引发安全错误。 OpenSSL 没有提供命令行方式来指定这一点,所以很多开发者的教程和书签突然就过时了。

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

  1. 创建一个 OpenSSL 配置文件(例如:req.cnf

    [req]
    distinguished_name = req_distinguished_name
    x509_extensions = v3_req
    prompt = no
    [req_distinguished_name]
    C = US
    ST = VA
    L = SomeCity
    O = MyCompany
    OU = MyDivision
    CN = www.company.com
    [v3_req]
    keyUsage = critical, digitalSignature, keyAgreement
    extendedKeyUsage = serverAuth
    subjectAltName = @alt_names
    [alt_names]
    DNS.1 = www.company.com
    DNS.2 = company.com
    DNS.3 = company.net
    
  2. 创建引用此配置文件的证书

    openssl req -x509 -nodes -days 730 -newkey rsa:2048 \
     -keyout cert.key -out cert.pem -config req.cnf -sha256
    

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

【讨论】:

  • 在删除导致错误的最后一个参数 -extensions 'v3_req' 后,它对我有用。在 Windows 上使用 OpenSSL。最后,我设法解决了这个问题!谢谢。
  • @Kyopaxa 你是对的 - 该参数与 cnf 文件的第 3 行是多余的;已更新。
  • 稳健的方式。谢谢。我建议添加-sha256
  • 您现在可以在命令行上使用-extension 'subjectAltName = DNS:dom.ain, DNS:oth.er' 指定SAN,参见github.com/openssl/openssl/pull/4986
  • 看起来这个选项现在被称为-addext
【解决方案7】:

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

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

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

Google Security blog 中的更多信息。

2018 年 5 月更新。 许多人在 cmets 中指出,使用 SHA-2 不会为自签名证书增加任何安全性。但我仍然建议使用它作为不使用过时/不安全的加密哈希函数的好习惯。 Why is it fine for certificates above the end-entity certificate to be SHA-1 based? 中提供了完整的解释。

【讨论】:

  • 如果是自签名密钥,无论如何都会产生浏览器错误,所以这并不重要
  • @Mark,这很重要,因为 SHA-2 更安全
  • 将 cert.pem 重命名为 cert.cer 后在 windows 中打开证书说指纹算法仍然是 Sha1,但签名哈希算法是 sha256。
  • “世界级加密 * 零认证 = 零安全”gerv.net/security/self-signed-certs
  • 请注意,自签名证书上使用的签名算法与决定它是否可信无关。根 CA 证书是自签名的。截至 2018 年 5 月,仍有许多 SHA-1 签名的活动根 CA 证书。因为证书是否信任自己以及该证书如何验证该信任都无关紧要。您要么信任它所说的 的根/自签名证书,要么不信任。见security.stackexchange.com/questions/91913/…
【解决方案8】:

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

此脚本采用域名 (example.com) 并在同一证书中为 *.example.com 和 example.com 生成 SAN。下面的部分进行了评论。命名脚本(例如generate-ssl.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)服务器以使新证书生效。

【讨论】:

  • 适用于 macOS High Siera 和 Chrome 58
  • 我仍然不确定 CN 如何影响整体设置?我正在尝试以localhost127.0.0.1:port# 运行它,对于这样的事情,对应的CN 是什么。
  • @DJ2 我会设置 BASE_DOMAIN=“localhost”
【解决方案9】:

我无法发表评论,所以我添加了一个单独的答案。 我尝试为 NGINX 创建一个自签名证书,这很容易,但是当我想将它添加到 Chrome 白名单时,我遇到了问题。我的解决方案是创建一个根证书并通过它签署一个子证书。

所以一步一步来。 创建文件 config_ssl_ca.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

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

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

程序会问你 2 个问题:

  1. 签署证书?说“Y”
  2. 1 个证书请求中的 1 个已通过认证,提交?说“Y”

在终端中你可以看到一个带有“Database”这个词的句子,它的意思是你通过命令“touch”创建的文件index.txt。它将包含您通过“openssl ca”实用程序创建的所有证书的所有信息。 检查证书有效使用:

openssl rsa -in market.key -check

如果你想看看 CRT 里面有什么:

openssl x509 -in market.crt -text -noout

如果您想了解 CSR 的内容:

openssl req -in market.csr -noout -text 

【讨论】:

  • 虽然这个过程看起来很复杂,但这正是我们需要的 .dev 域,因为这个域不支持自签名证书并且 Chrome 和 Firefox 正在强制 HSTS。我所做的是遵循以下步骤,即创建 CA,创建证书并使用我的 CA 对其进行签名,最后在浏览器中信任我的 CA。谢谢。
  • 你的通用名是错误的。不是名字/姓氏。它是您的域名 cn,即 www.yoursite.com 。见ssl.com/faqs/common-name
  • 没问题。有一些文件也写着名字(你的名字),这有点误导。但通用名称应该是实际的域。从这个意义上说,他们试图说的是(您的“域名”名称)。当通过创建证书的交互式方法运行时,它确实说 cn=domain 示例。所以 commonname 应该是 domain
【解决方案10】:

2017 年单线:

openssl req \
-newkey rsa:2048 \
-x509 \
-nodes \
-keyout server.pem \
-new \
-out server.pem \
-subj /CN=localhost \
-reqexts SAN \
-extensions SAN \
-config <(cat /System/Library/OpenSSL/openssl.cnf \
    <(printf '[SAN]\nsubjectAltName=DNS:localhost')) \
-sha256 \
-days 3650

这也适用于 Chrome 57,因为它提供了 SAN,而无需其他配置文件。 它取自here的答案。

这将创建一个包含私钥和证书的单个 .pem 文件。如果需要,您可以将它们移动到单独的 .pem 文件中。

【讨论】:

  • 对于 Linux 用户,您需要更改配置的路径。例如在当前的 Ubuntu /etc/ssl/openssl.conf 上工作
  • 对于不需要您指定 openssl.cnf 位置的单行代码,请参阅:stackoverflow.com/a/41366949/19163
【解决方案11】:

单行版本 2017:

CentOS:

openssl req -x509 -nodes -sha256 -newkey rsa:2048 \
-keyout localhost.key -out localhost.crt \
-days 3650 \
-subj "CN=localhost" \
-reqexts SAN -extensions SAN \
-config <(cat /etc/pki/tls/openssl.cnf <(printf "\n[SAN]\nsubjectAltName=IP:127.0.0.1,DNS:localhost"))

Ubuntu:

openssl req -x509 -nodes -sha256 -newkey rsa:2048 \
-keyout localhost.key -out localhost.crt \
-days 3650 \
-subj "/CN=localhost" \
-reqexts SAN -extensions SAN \
-config <(cat /etc/ssl/openssl.cnf <(printf "\n[SAN]\nsubjectAltName=IP:127.0.0.1,DNS:localhost"))

编辑:在 Ubuntu 的“subj”选项前添加了斜线。

【讨论】:

    【解决方案12】:

    您的一般程序是正确的。该命令的语法如下。

    openssl req -new -key {private key file} -out {output file}
    

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

    由于这是自签名证书,因此没有 CA,您可以放心地忽略警告并继续操作。如果您想获得公共 Internet 上任何人都可以识别的真实证书,那么过程如下。

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

    我在 Securing the Connection: Creating a Security Certificate with OpenSSL

    的帖子中有更多详细信息

    【讨论】:

      【解决方案13】:

      生成密钥

      我使用/etc/mysql 进行证书存储,因为/etc/apparmor.d/usr.sbin.mysqld 包含/etc/mysql/*.pem r

      sudo su -
      cd /etc/mysql
      openssl 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 -days 1000 -nodes -keyout server-key.pem -out server-req.pem;
      openssl x509 -req -in server-req.pem -days 1000 -CA ca-cert.pem -CAkey ca-key.pem -set_serial 01 -out server-cert.pem;
      openssl req -newkey rsa:2048 -days 1000 -nodes -keyout client-key.pem -out client-req.pem;
      openssl x509 -req -in client-req.pem -days 1000 -CA ca-cert.pem -CAkey ca-key.pem -set_serial 01 -out client-cert.pem;
      

      添加配置

      /etc/mysql/my.cnf

      [client]
      ssl-ca=/etc/mysql/ca-cert.pem
      ssl-cert=/etc/mysql/client-cert.pem
      ssl-key=/etc/mysql/client-key.pem
      
      [mysqld]
      ssl-ca=/etc/mysql/ca-cert.pem
      ssl-cert=/etc/mysql/server-cert.pem
      ssl-key=/etc/mysql/server-key.pem
      

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

      跟进说明:

      • SSL error: Unable to get certificate from '...'

        MySQL might be denied read access to your certificate file if it is not in apparmors configuration。正如前面步骤中提到的^,将我们所有的证书保存为/etc/mysql/ 目录中的.pem 文件,该目录默认由apparmor 批准(或修改您的apparmor/SELinux 以允许访问您存储它们的任何位置。)

      • SSL error: Unable to get private key

        Your MySQL server version may not support the default rsa:2048 format

        将生成的rsa:2048 转换为普通的rsa

        openssl rsa -in server-key.pem -out server-key.pem
        openssl rsa -in client-key.pem -out client-key.pem
        
      • Check if local server supports SSL:

        mysql -u root -p
        mysql> show variables like "%ssl%";
        +---------------+----------------------------+
        | Variable_name | Value                      |
        +---------------+----------------------------+
        | have_openssl  | YES                        |
        | have_ssl      | YES                        |
        | ssl_ca        | /etc/mysql/ca-cert.pem     |
        | ssl_capath    |                            |
        | ssl_cert      | /etc/mysql/server-cert.pem |
        | ssl_cipher    |                            |
        | ssl_key       | /etc/mysql/server-key.pem  |
        +---------------+----------------------------+
        
      • Verifying a connection to the database is SSL encrypted:

        验证连接

        登录到 MySQL 实例后,您可以发出查询:

        show status like 'Ssl_cipher';
        

        如果您的连接未加密,则结果将为空白:

        mysql> show status like 'Ssl_cipher';
        +---------------+-------+
        | Variable_name | Value |
        +---------------+-------+
        | Ssl_cipher    |       |
        +---------------+-------+
        1 row in set (0.00 sec)
        

        否则,它会为正在使用的密码显示一个非零长度的字符串:

        mysql> show status like 'Ssl_cipher';
        +---------------+--------------------+
        | Variable_name | Value              |
        +---------------+--------------------+
        | Ssl_cipher    | DHE-RSA-AES256-SHA |
        +---------------+--------------------+
        1 row in set (0.00 sec)
        
      • Require ssl for specific user's connection('需要 ssl'):

        • SSL

        告诉服务器只允许帐户的 SSL 加密连接。

        GRANT ALL PRIVILEGES ON test.* TO 'root'@'localhost'
          REQUIRE SSL;
        

        要连接,客户端必须指定 --ssl-ca 选项来验证服务器证书,并且可以另外指定 --ssl-key 和 --ssl-cert 选项。如果既没有指定 --ssl-ca 选项也没有指定 --ssl-capath 选项,则客户端不会验证服务器证书。


      备用链接:Secure PHP Connections to MySQL with SSL 中的冗长教程。

      【讨论】:

      • -1;这在很大程度上与所提出的问题相切,并且在明确其引用的来源方面也做得不好。
      • 这显示了配置 CA,由 CA 签名的服务器/客户端证书,配置它们以供 mysqld 在具有 apparmor 的主机上读取。它举例说明了在同一台机器上托管 ca、服务器和客户端并将该 ca 的权限危险地暴露给 mysqld 进程的相当无用的情况。除了在测试环境中测试 ssl 配置之外,此设置实际上没有任何意义。对于操作内部 CA,我建议使用 gnuttls 工具链而不是 openssl help.ubuntu.com/community/GnuTLS,并且在解决 mysqld+apparmor 案例之前对 tls 有很好的了解
      【解决方案14】:

      一个班轮 FTW。我喜欢保持简单。为什么不使用一个包含所有所需参数的命令呢?这就是我喜欢的方式 - 这会创建一个 x509 证书及其 PEM 密钥:

      openssl req -x509 \
       -nodes -days 365 -newkey rsa:4096 \
       -keyout self.key.pem \
       -out self-x509.crt \
       -subj "/C=US/ST=WA/L=Seattle/CN=example.com/emailAddress=someEmail@gmail.com"
      

      该单个命令包含您通常会为证书详细信息提供的所有答案。通过这种方式,您可以设置参数并运行命令,获取输出 - 然后去喝咖啡。

      >> More here <<

      【讨论】:

      • 除 SAN 之外的所有参数......@vog 的答案也涵盖了这一点(并且早于这个)(虽然这有一个更完整的“主题”字段......)(不是很大一年到期的粉丝)
      【解决方案15】:

      正如已经详细讨论过的,self-signed certificatesare not trusted for the Internet。你可以add your self-signed certificate to many but not all browsers。或者你可以become your own certificate authority

      不想从证书颁发机构获得签名证书的主要原因是成本 -- Symantec charges between $995 - $1,999 per year for certificates -- just for a certificate intended for internal network, Symantec charges $399 per year。如果您正在处理信用卡付款或为高利润公司的利润中心工作,那么这笔费用很容易证明是合理的。对于一个在互联网上创建的个人项目,或者对于一个以最低预算运行的非营利组织,或者如果一个人在组织的成本中心工作——成本中心总是试图做更多的事情,这超出了许多人所能承受的范围。更少。

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

      如果您设置了 certbot,您可以启用它来为您创建和维护由Let’s Encrypt 证书颁发机构颁发的证书。

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

      请注意,有些说明并不完全正确,需要花一点时间在 Google 上摸索和弄清楚。第一次这花了我相当多的时间,但现在我想我可以在几分钟内完成。

      对于 DigitalOcean,当我被提示输入 DigitalOcean 凭据 INI 文件的路径时,我遇到了困难。脚本所指的是Applications & API 页面和该页面上的令牌/密钥选项卡。您需要为 DigitalOcean 的 API 拥有或生成个人访问令牌(读取和写入)——这是一个 65 个字符的十六进制字符串。然后需要将此字符串放入运行 certbot 的网络服务器上的文件中。该文件的第一行可以有注释(cmets 以# 开头)。第二行是:

      dns_digitalocean_token = 0000111122223333444455556666777788889999aaaabbbbccccddddeeeeffff
      

      一旦我弄清楚如何为 DigitalOcean 的 API 设置读写令牌,使用 certbot 设置wildcard certificate 就非常容易了。请注意,不必设置通配符证书,而是可以指定希望证书应用到的每个域和子域。通配符证书需要包含来自 DigitalOcean 的个人访问令牌的凭据 INI 文件。

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

      我的计划是编写一个脚本来使用 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
      

      【讨论】:

        【解决方案16】:

        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 传递不同的属性。我不喜欢弄乱配置文件((

        【讨论】:

          【解决方案17】:

          这对我有用

          openssl req -x509 -nodes -subj '/CN=localhost'  -newkey rsa:4096 -keyout ./sslcert/key.pem -out ./sslcert/cert.pem -days 365
          

          server.js

          var fs = require('fs');
          var path = require('path');
          var http = require('http');
          var https = require('https');
          var compression = require('compression');
          var express = require('express');
          var app = express();
          
          app.use(compression());
          app.use(express.static(__dirname + '/www'));    
          
          app.get('/*', function(req,res) {
            res.sendFile(path.join(__dirname+'/www/index.html'));
          });
          
          // your express configuration here
          
          var httpServer = http.createServer(app);
          var credentials = {
              key: fs.readFileSync('./sslcert/key.pem', 'utf8'),
              cert: fs.readFileSync('./sslcert/cert.pem', 'utf8')
          };
          var httpsServer = https.createServer(credentials, app);
          
          httpServer.listen(8080);
          httpsServer.listen(8443);
          
          console.log(`RUNNING ON  http://127.0.0.1:8080`);
          console.log(`RUNNING ON  http://127.0.0.1:8443`);
          

          【讨论】:

            【解决方案18】:

            生成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"
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2014-02-24
              相关资源
              最近更新 更多