欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

使用OpenSSL库签署数字证书

程序员文章站 2022-03-06 18:57:58
...

在使用pgopenssltypes扩展时,我意识到我还没有讨论如何使用OpenSSL库对数字证书进行签名。 (至少我不记得这样做了–我可能在博客的早期就对此进行了讨论。我很确定我已经讨论过使用BouncyCastle(java)库对数字证书进行签名。)

我的pgopenssltypes扩展将具有签名数字证书以进行测试的功能,但实际工作将在可能的pgca扩展中完成。 成为CA不仅需要签署简单证书的能力。

签署证书的基本代码很简单。

BIGNUM *serialNumber;
X509_NAME *issuerName;
X509_NAME *subjectName;
time_t notBefore;
time_t notAfter;
EVP_PKEY *issuerKey; // private key
EVP_PKEY *subjectKey; // public key

// allocate memory for a cert.
X509 cert = X509_new();

// this is standard
X509_set_version(cert, 3);

// set the the mandatory fields.
X509_set_serialNumber(cert, BN_to_ASN1_INTEGER(serialNumber, NULL));
X509_set_issuer_name(cert, issuerName);
X509_set_subject_name(cert, subjectName);
X509_set_notBefore(cert, ASN1_TIME_set(NULL, notBefore));
X509_set_notAfter(cert, ASN1_TIME_set(NULL, notAfter));
X509_set_pubkey(cert, subjectKey);

// sign the certificate. In this case I'm using SHA256 for the hash.
if ((r = X509_sign(cert, issuerKey, EVP_sha256())) <= 0) {
    fprintf(STDERR, "%s", ERR_reason_error_string(ERR_get_error());
    X509_free(cert);
    return NULL;
}

// release the memory
PKCS8_PRIV_KEY_INFO_free(p8);
EVP_PKEY_free(pkey);

// write certificate to file
// FILE *fp = fopen(...)
// PEM_write_X509(fp, cert);

return cert;

有一个使用X509_REQ而不是X509对象的变体,但这是不重要的更改。 (我记得X509_REQ是特殊的自签名X509证书,主要由完整的证书颁发机构(CA)使用。)

发行方通常与自己的数字证书无限制地关联。 这形成了“证书链”,其中每个证书的颁发者是下一个证书的主题。 链以“受信任”证书(由用户决定什么是“受信任”)或自签名证书(由用户决定哪个主题/发布者是多少)结束“受信任”)。

证书本身不是跟踪公钥的便捷方法,没有任何价值。 证书链只能在每个中间签名者都可以信任的范围内用于身份验证。

最后,上面的代码将很乐意允许使用任何**来签署证书。 在行为良好的应用程序中,将检查颁发者证书的“基本约束” –该数字指定可以签名多少个附加级别,并且每一代应至少丢弃一个级别。

资料类型

该代码包含几种不熟悉的数据类型。

BIGNUM是任意长度整数的OpenSSL实现。 某些CA使用128或160位***,而不是同时使用MD5或SHA1哈希值的长度。 但是,这只是一个惯例,没有理由为什么攻击者无法使用10k字节的***来尝试在编写不良的代码尝试读取缓冲区时导致缓冲区溢出。

有一个宽松的约定,即***较高的证书比***较低的证书发行晚。 许多CA不遵循此约定。

一些CA担心简单的序列会泄漏敏感信息,即已颁发了多少证书。 常见的对策是使用基于日期的***,例如,十六进制值可以是0x20141230NNNNNN,其中NNNNNN是时间,伪随机数或某种组合。

最后,谨慎的CA可能会在***本身中编码信息。 例如,它可能会悄悄地确保***始终为3 mod 17,或者类似的值反映证书中其他位置存在的信息。 有权访问CA私钥的攻击者可能没有意识到这些检查,而在野外发现错误的***可能是破坏的第一个迹象。

EVP_PKEY是通用私钥或公钥。 它可以包含RSA,ECC,DSA或DH**。 我认为在创建数字证书(可能是带有低功耗设备的ECC)时,我们只关心RSA**。

我们可以使用以下方法将RSA**转换为EVP_PKEY:

RSA *rsa;
EVP_PKEY *pkey;

pkey = EVP_PKEY_new();
if (EVP_PKEY_set1_RSA(pkey, rsa) <= 0) {
    fprintf(STDERR, "%s", ERR_reason_error_string(ERR_get_error());
    EVP_PKEY_free(pkey);
    return NULL;
}

return pkey;

将.p8(PKCS8)**转换为EVP_PKEY也很容易:

PKCS8_PRIV_KEY_INFO *p8;
EVP_PKEY *pkey;

pkey = EVP_PKCS82PKEY(p8);
if (pkey == null) {
    fprintf(STDERR, "%s", ERR_reason_error_string(ERR_get_error());
    return NULL;
}

return pkey;

X509_NAME与LDAP专有名称相同。 从字面上看-它们来自相同的X.500标准。 (因此X509。)许多体系结构都利用了这一点–这是将用户的数字证书与企业目录服务绑定的好方法。

X509_NAME是X509_NAME_ENTRY值的堆栈。 每个条目都是一个键值对。 例如,“ CN = Bear Giles,C = US,ST = Colorado”将具有三个X509_NAME_ENTRY值-(“ CN”,“ Bear Giles”),(“ C”,“ US”),(“ ST”,“科罗拉多”。 有几十个标准键,其中一些可以重复。 (例如,域组件的“ DC”。invariantproperties.com上的服务器将为“ DC = invariantproperties,DC = com”。)有关更多详细信息,请参见RFC 4519属性类型。

(旁注:LDAP通常具有上面显示的格式。OpenSSL倾向于用斜杠代替逗号,例如“ CN = Bear Giles / C = US / ST = Colorado”。)

您可以使用以下命令打印X509_NAME:

X509_NAME *name;
char buf[BUF_LEN];

X509_NAME_oneline(name, buf, BUF_LEN);

要么:

BIO *bio;  // can point to memory buffer, file, etc.
X509_NAME *name;
int obase; // indentation on multi-line output.

X509_NAME_print(bio, name, obase);

创建X509_NAME对象要复杂一些。

char *name;              // e.g., "C" for Country
unsigned char *value;    // e.g., "US"
int type = MBSTRING_ASC; // or MBSTRING_UTF8
X509_NAME *name;

// value is null-terminated string.
int len = -1;

// these values add entry to end of X509_NAME.
int loc = -1;
int set = 0;

name = X509_NAME_new();
if (X509_NAME_add_entry_by_txt(name, key, type, value, len, loc, set) <= 0) {
    fprintf(STDERR, "%s", ERR_reason_error_string(ERR_get_error());
    return NULL;
}

扩展名

X509v3证书的真正功能是可以使用任意信息进行扩展。 有十几种广泛使用的扩展名,但是任何人都可以获取新的ASN1 OID并将自己的信息添加到数字证书中。 (必须明确地请求并注册一个新的OID –如果将相同的OID用于不同的用途,则会引起问题。)仅必须注册根OID,才能*创建子OID。

有关标准扩展的详细讨论超出了本文的范围。 需要满足的条件是它们可以提供有关**用法的信息(例如,应使用证书对电子邮件进行加密,但不能在服务器上使用),证书的其他别名,甚至是对此证书签名的证书的限制。 (例如,它必须用于“ example.com”域下的服务器)。

非标准扩展名可能包括内部用户ID,进一步验证用户身份的生物特征,照片等。

翻译自: https://www.javacodegeeks.com/2015/01/signing-digital-certificates-with-openssl-library.html