我想動態建立具有任意開始日期和結束日期的自簽名證書,包括過去的。我更喜歡使用標準工具,例如 OpenSSL,但任何能完成工作的工具都很棒。
堆疊溢位問題如何產生有效期限小於一天的openssl憑證?問類似的問題,但我希望我的憑證是自簽署的。
如果您想知道,自動化測試需要證書。
答案1
過去有兩種建立證書的方法。偽造時間 (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 年的日期,然後建立有效期為 2 年至 2010 年的憑證。
faketime '2008-12-24 08:15:42' openssl ...
附帶說明一下,這個實用程式可以在多個 Unix 版本中使用,包括 MacOS,作為任何類型程式的包裝器(不限於命令列)。
需要澄清的是,只有使用此方法載入的二進位檔案(及其子檔案)的時間才會更改,並且偽造時間不會影響系統其餘部分的當前時間。
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." */
來自變更日誌(2038 bug?) - 此變更日誌只是一個附加腳註,因為它只涉及那些直接使用 API 的人。
1.1.0e 和 1.1.1 之間的變更 [xx XXX xxxx]
*) 新增 ASN.1 類型 INT32、UINT32、INT64、UINT64 以及以 Z 為前綴的變體。不鼓勵使用 LONG 和 ZLONG,並計劃在 OpenSSL 1.2.0 中棄用。
因此,創建從 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 日]
*)修正「ca」程式的 -startdate 和 -enddate (缺少)參數。
PS至於所選的答案問題您從 StackExchange 引用:它通常是餿主意更改系統時間,尤其是在生產系統中;透過此答案中的方法,您在使用它們時不需要 root 權限。
答案2
我幾乎驚訝地發現顯而易見的事情有效:雖然openssl
將證書有效的天數作為參數,但只需提供一個負數!
openssl req -x509 -newkey rsa:4096 \
-keyout key.pem -out cert.pem -days -365
請注意,這實際上會導致一些非常奇怪的事情:一個證書的到期時間戳先於它的開始有效時間戳。我實際上不建議您將其用於自動化測試,因為它很奇怪。您可能還需要一種回溯有效期開始時間戳記的方法。
答案3
或者你可以使用類似這個簡短的 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))