生成弱 X509 证书

Generate weak X509 certificate

我需要使用 RSA 和小模数为 CTF 挑战生成弱证书,因此它是可分解的。应该是64位左右。

我试过像使用普通证书一样使用 OpenSSL 生成证书,但出于安全原因,它禁止创建模数低于 512 位的证书。所以我已经更改了 OpenSSL 的源文件,因此它不会检查位长度并再次重新编译。然后我能够使用较小的模数创建私钥,但尝试使用该密钥(或新的小密钥)创建证书会引发我不完全理解的新错误。我什至想使用 Python 完全避免 OpenSSL 问题,但这只是表明它也使用 OpenSSL 并且有完全相同的问题。

正在生成小私钥:

$ openssl genrsa -out acn.pem 64
-----BEGIN PRIVATE KEY-----
MFQCAQAwDQYJKoZIhvcNAQEBBQAEQDA+AgEAAgkAlz0xJ3uUx5UCAwEAAQIIR1Zs
1Wo4EQECBQDHPJNVAgUAwlPjQQIFAKqNunkCBClt4QECBHlHx1Q=
-----END PRIVATE KEY-----

正在生成证书:

$ openssl req -key acn.pem -new -out domain.csr
...
140561480598848:error:04075070:rsa routines:RSA_sign:digest too big for rsa key:../crypto/rsa/rsa_sign.c:100:
140561480598848:error:0D0DC006:asn1 encoding routines:ASN1_item_sign_ctx:EVP lib:../crypto/asn1/a_sign.c:224:

我发现这个话题很有用,因为我什至可以选择自己的号码,但这个方法对我不起作用:

How to Generate rsa keys using specific input numbers in openssl?

这是来自 PicoCTF 的示例证书。我想创建一个类似的。

-----BEGIN CERTIFICATE-----
MIIB6zCB1AICMDkwDQYJKoZIhvcNAQECBQAwEjEQMA4GA1UEAxMHUGljb0NURjAe
Fw0xOTA3MDgwNzIxMThaFw0xOTA2MjYxNzM0MzhaMGcxEDAOBgNVBAsTB1BpY29D
VEYxEDAOBgNVBAoTB1BpY29DVEYxEDAOBgNVBAcTB1BpY29DVEYxEDAOBgNVBAgT
B1BpY29DVEYxCzAJBgNVBAYTAlVTMRAwDgYDVQQDEwdQaWNvQ1RGMCIwDQYJKoZI
hvcNAQEBBQADEQAwDgIHEaTUUhKxfwIDAQABMA0GCSqGSIb3DQEBAgUAA4IBAQAH
al1hMsGeBb3rd/Oq+7uDguueopOvDC864hrpdGubgtjv/hrIsph7FtxM2B4rkkyA
eIV708y31HIplCLruxFdspqvfGvLsCynkYfsY70i6I/dOA6l4Qq/NdmkPDx7edqO
T/zK4jhnRafebqJucXFH8Ak+G6ASNRWhKfFZJTWj5CoyTMIutLU9lDiTXng3rDU1
BhXg04ei1jvAf0UrtpeOA6jUyeCLaKDFRbrOm35xI79r28yO8ng1UAzTRclvkORt
b8LMxw7e+vdIntBGqf7T25PLn/MycGPPvNXyIsTzvvY/MXXJHnAqpI5DlqwzbRHz
q16/S1WLvzg4PsElmv1f
-----END CERTIFICATE-----

I need to generate weak certificate for CTF challenge using RSA and small modulus so it's factorable. It should be about 64 bits.

作为 self-signed 证书不可能做到这一点,因为正确的 RSA 签名无法使用这么小的密钥。

RSA-SSA-PKCS1_v1.5 是最短的结构化 RSA 签名填充,其结构为(根据 https://datatracker.ietf.org/doc/html/rfc8017#section-9.2):

DigestInfo ::= SEQUENCE {
    digestAlgorithm AlgorithmIdentifier,
    digest OCTET STRING
}

该结构的最短可能编码是 9 字节...并且使用 0 字节散列算法:

30 07 // SEQUENCE (7 content bytes)
   30 03 // digestAlgorithm: SEQUENCE (3 bytes)
      06 01 00 // OBJECT IDENTIFIER 0.0 ({itu-t(0) recommendation(0)})
      // omit implicit NULL
   04 00 // digest (empty)

因此,对于我们的空哈希,我们需要继续:

  1. If emLen < tLen + 11, output "intended encoded message length too short" and stop.

emLen 是模数的长度(以字节为单位),tLen 是我们编码结构的长度(9 个字节+)。

这使得 20 字节(160 位)成为最短的 RSA 密钥,可以执行任何可能被视为 RSA 签名的事情...这会产生毫无意义的签名(因为所有内容都在 0 位下发生冲突哈希)。

如果您愿意为 CTF 使用 1 字节的 OID,则您的 RSA 密钥模数需要为 20 字节 + 预期散列的长度(以字节为单位)。由于没有标识现有哈希算法的 1 字节 OID(也没有定义用于那么小的证书的哈希算法),因此现有的 easy-to-use 工具无法为您完成此操作。


当然,您可以发明一种新形式的 RSA 签名填充,例如使用 64 位密钥直接处理的 60 位散列。这将需要您做更多的工作。

您基本上只能使用 DER 编写器(或自己编写)来 hand-craft 证书。

Here's sample certificate from PicoCTF. I would like to create similar one.

那个证书不是self-signed。它有一个 2048 位的证书签名,来自 RSA-SSA-PKCS1_v1.5 和 MD2。因此,虽然它描述了一个短的 RSA 密钥(53 位模数),但它是用“适当”的东西签名的。如果这就是您所追求的,一般流程将类似于

  • 创建普通 self-signed 证书
  • 创建您的小型 RSA 密钥
  • 构建一个新证书,由第一个证书签名,包含小型 RSA 密钥。

很难为小密钥编码 CSR(因为它不能 self-sign 请求),但也许有办法让 openssl req -new 做一些不同于 [=60] 的事情=],这将允许跳过中间 CSR。 (或者使用 .NET 之类的库工具 CertificateRequest,或 OpenSSL 的 API 而不是它们的 CLI 工具,等等。)