我想即时创建带有任意开始日期和结束日期(包括过去的结束日期)的自签名证书。我更喜欢使用标准工具,例如OpenSSL,但是任何可以完成工作的工具都很棒。
堆栈溢出问题如何生成有效期少于一天的openssl证书?问类似的问题,但我希望我的证书可以自签名。
如果您想知道,证书是自动化测试所必需的。
我想即时创建带有任意开始日期和结束日期(包括过去的结束日期)的自签名证书。我更喜欢使用标准工具,例如OpenSSL,但是任何可以完成工作的工具都很棒。
堆栈溢出问题如何生成有效期少于一天的openssl证书?问类似的问题,但我希望我的证书可以自签名。
如果您想知道,证书是自动化测试所必需的。
Answers:
过去有两种创建证书的方法。伪造时间(1)(2),或在签署证书时定义时间间隔(3)。
1)首先,关于伪造的时间:做一个节目觉得是从系统中不同的日期,看看libfaketime
和faketime
要在Debian中安装它:
sudo apt-get install faketime
然后,您将faketime
在openssl
命令前使用。
有关使用示例:
$faketime 'last friday 5 pm' /bin/date
Fri Apr 14 17:00:00 WEST 2017
$faketime '2008-12-24 08:15:42' /bin/date
Wed Dec 24 08:15:42 WET 2008
来自man faketime
:
给定的命令将被欺骗,使您相信当前系统时间是时间戳中指定的时间。除非另有说明,否则壁钟将从该日期和时间开始继续运行(请参阅高级选项)。实际上,faketime是libfaketime的简单包装器,它使用LD_PRELOAD机制加载一个小的库,该库拦截对诸如time(2)和fstat(2)之类的系统调用。
因此,例如,根据您的情况,您可以很好地定义一个日期为2008年,然后创建一个有效期至2010年的2年证书。
faketime '2008-12-24 08:15:42' openssl ...
附带说明一下,该实用程序可以在包括MacOS在内的多个Unix版本中用作任何程序的包装(不是命令行专用的)。
需要说明的是,只有使用此方法加载的二进制文件(及其子进程)的时间才会更改,伪时间不会影响系统其余部分的当前时间。
2)如@Wyzard所述,您还拥有datefudge
与用法非常相似的软件包faketime
。
作为差异,datefudge
不影响fstat
(即不改变文件时间的创建)。它还具有自己的库datefudge.so,可以使用LD_PRELOAD进行加载。
它也有一个 -s
static time
位置,尽管过去了多少秒,但始终返回参考时间。
$ datefudge --static "2007-04-01 10:23" sh -c "sleep 3; date -R"
Sun, 01 Apr 2007 10:23:00 +0100
3)除了伪造时间以外,甚至更简单,您还可以在OpenSSL 中对证书签名时定义证书有效性的起点和终点。
您在问题中链接到的问题的误解是,证书有效性不是在请求时(在CSR请求中)定义的,而是在签名时定义的。
当使用openssl ca
创建自签名证书,添加的选项-startdate
和-enddate
。
根据openssl的资料,这两个选项中的日期格式openssl/crypto/x509/x509_vfy.c
为ASN1_TIME或ASN1UTCTime:格式必须为YYMMDDHHMMSSZ或YYYYMMDDHHMMSSZ。
报价openssl/crypto/x509/x509_vfy.c
:
int X509_cmp_time(const ASN1_TIME *ctm, time_t *cmp_time) { static const size_t utctime_length = sizeof("YYMMDDHHMMSSZ") - 1; static const size_t generalizedtime_length = sizeof("YYYYMMDDHHMMSSZ") - 1; ASN1_TIME *asn1_cmp_time = NULL; int i, day, sec, ret = 0; /* * Note that ASN.1 allows much more slack in the time format than RFC5280. * In RFC5280, the representation is fixed: * UTCTime: YYMMDDHHMMSSZ * GeneralizedTime: YYYYMMDDHHMMSSZ * * We do NOT currently enforce the following RFC 5280 requirement: * "CAs conforming to this profile MUST always encode certificate * validity dates through the year 2049 as UTCTime; certificate validity * dates in 2050 or later MUST be encoded as GeneralizedTime." */
并且来自CHANGE日志(错误2038吗?)-此更改日志仅作为一个附加脚注,因为它只涉及那些直接使用API的用户。
在1.1.0e和1.1.1之间更改[xx XXX xxxx]
*)添加ASN.1类型INT32,UINT32,INT64,UINT64以及带有Z前缀的变体。这是为了替换LONG和ZLONG并确保大小安全。不鼓励在OpenSSL 1.2.0中使用LONG和ZLONG并计划弃用。
因此,创建从2008年1月1日到2010年1月1日的证书的过程如下:
openssl ca -config /path/to/myca.conf -in req.csr -out ourdomain.pem \
-startdate 200801010000Z -enddate 201001010000Z
要么
openssl ca -config /path/to/myca.conf -in req.csr -out ourdomain.pem \
-startdate 0801010000Z -enddate 1001010000Z
-startdate
并-enddate
确实出现在openssl
源和“更改”日志中;正如@guntbert指出的那样,尽管它们没有出现在man openssl
主页上,但它们也出现在man ca
:
-startdate date this allows the start date to be explicitly set. The format of the date is YYMMDDHHMMSSZ (the same as an ASN1 UTCTime structure). -enddate date this allows the expiry date to be explicitly set. The format of the date is YYMMDDHHMMSSZ (the same as an ASN1 UTCTime structure).
报价openssl/CHANGE
:
0.9.3a和0.9.4之间的变化[1999年8月9日]
*)将-startdate和-enddate(缺少)参数固定为“ ca”程序。
PS对于您从StackExchange引用的问题的选定答案:更改系统时间通常是个坏主意,尤其是在生产系统中;并且使用此答案中的方法,使用它们时不需要root特权。
datefudge
。
faketime
和datefudge
做工精美,我的Debian杰西系统。
我发现这一显而易见的事情行之有效,我几乎感到惊讶:虽然openssl
以证书应有效的天数作为参数,但只需提供一个负数!
openssl req -x509 -newkey rsa:4096 \
-keyout key.pem -out cert.pem -days -365
请注意,这实际上会导致一些非常奇怪的事情:证书的过期时间戳早于其有效期开始时间戳。实际上,我不建议您将其用于自动化测试,因为这很奇怪。您可能还想要一种回溯有效期开始时间戳的方法。
或者,您可以使用类似此简短的python程序的内容...(使用小孔)
它创建一个密钥(test.key)和一个证书(test.crt),其起始时间为过去10年(-10 * 365 * 24 * 60 * 60秒为-10年),到期时间为过去5年(-5 * 365 * 24 * 60 * 60)。
请注意,这是一个最小的演示程序,因此它无需设置任何扩展名(例如basicConstraints)并使用固定的序列号。
#!/usr/bin/env python
from OpenSSL import crypto
key = crypto.PKey()
key.generate_key(crypto.TYPE_RSA, 2048)
cert = crypto.X509()
cert.get_subject().CN = "Test"
cert.set_serial_number(666)
cert.gmtime_adj_notBefore(-10*365*24*60*60)
cert.gmtime_adj_notAfter(-5*365*24*60*60)
cert.set_issuer(cert.get_subject())
cert.set_pubkey(key)
cert.sign(key, 'sha384')
open("test.crt", "wb").write(crypto.dump_certificate(crypto.FILETYPE_PEM, cert))
open("test.key", "wb").write(crypto.dump_privatekey(crypto.FILETYPE_PEM, key))