使用 OpenSSL 以编程方式创建 X509 证书
我有一个 C/C++ 应用程序,需要创建一个包含公钥和私钥的 X509 pem 证书。 证书可以是自签名的,也可以是未签名的,都没关系。
我想在应用程序内执行此操作,而不是从命令行执行此操作。
哪些 OpenSSL 函数可以为我执行此操作? 任何示例代码都是奖励!
I have a C/C++ application and I need to create a X509 pem certificate containing both a public and private key. The certificate can be self signed, or unsigned, doesn't matter.
I want to do this inside an app, not from command line.
What OpenSSL functions will do this for me? Any sample code is a bonus!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
我意识到这是一个很晚(而且很长)的答案。 但考虑到这个问题在搜索引擎结果中的排名,我认为可能值得写一个像样的答案。
您将在下面读到的很多内容都借自 此演示 和 OpenSSL 文档。 下面的代码适用于 C 和 C++。
在我们实际创建证书之前,我们需要创建一个私钥。 OpenSSL 提供了
EVP_PKEY
结构,用于在内存中存储与算法无关的私钥。 该结构在openssl/evp.h
中声明,但由openssl/x509.h
包含(我们稍后将需要),因此您实际上不需要显式包含标题。为了分配
EVP_PKEY
结构,我们使用EVP_PKEY_new
:还有一个用于释放结构的相应函数 -
EVP_PKEY_free
- 它接受单个参数:上面初始化的EVP_PKEY
结构。现在我们需要生成一个密钥。 对于我们的示例,我们将生成 RSA 密钥。 这是通过声明的
RSA_generate_key
函数完成的在openssl/rsa.h 中。 此函数返回一个指向 RSA 结构的指针。该函数的简单调用可能如下所示:
如果
RSA_generate_key
的返回值为NULL
,则表示出现了问题。 如果没有,那么我们现在有了一个 RSA 密钥,我们可以将其分配给之前的EVP_PKEY
结构:当
EVP_PKEY 被调用时,
结构被释放。RSA
结构将自动释放。现在来说说证书本身。
OpenSSL 使用
X509
结构来表示内存中的 x509 证书。 该结构的定义位于openssl/x509.h
中。 我们需要的第一个函数是X509_new
。 它的使用相对简单:与
EVP_PKEY
的情况一样,有一个相应的函数用于释放结构 -X509_free
。现在我们需要使用一些
X509_*
函数设置证书的一些属性:这会将证书的序列号设置为“1”。 一些开源 HTTP 服务器拒绝接受序列号为“0”(默认值)的证书。 下一步是指定证书实际有效的时间范围。 我们通过以下两个函数调用来完成此操作:
第一行将证书的
notBefore
属性设置为当前时间。 (X509_gmtime_adj
函数将指定的秒数添加到当前时间 - 在本例中没有。)第二行将证书的notAfter
属性设置为从现在起 365 天(60秒 * 60 分钟 * 24 小时 * 365 天)。现在我们需要使用之前生成的密钥设置证书的公钥:
由于这是自签名证书,因此我们将颁发者的名称设置为主题的名称。 该过程的第一步是获取主题名称:
如果您以前曾在命令行上创建过自签名证书,您可能记得被要求输入国家/地区代码。 我们在这里提供它以及组织(“O”)和通用名称(“CN”):(
我在这里使用值“CA”,因为我是加拿大人,这是我们的国家/地区代码。另请注意参数 # 4 需要显式转换为
unsigned char *
。)现在我们可以实际设置发行者名称:
最后我们准备好执行签名过程。 我们使用之前生成的密钥调用
X509_sign
。 其代码非常简单:请注意,我们使用的是 SHA-1 哈希算法签署密钥。 这与我在本答案开头提到的使用 MD5 的
mkcert.c
演示不同。我们现在有了一个自签名证书! 但我们还没有完成 - 我们需要将这些文件写入磁盘。 值得庆幸的是,OpenSSL 也为我们提供了在
openssl/pem.h
中声明的PEM_*
函数。 我们需要的第一个是PEM_write_PrivateKey
来保存我们的私钥。如果您不想加密私钥,则只需为上面的第三个和第四个参数传递
NULL
即可。 无论哪种方式,您肯定希望确保该文件不是世界可读的。 (对于 Unix 用户,这意味着chmod 600 key.pem
。)唷! 现在我们只剩下一个功能 - 我们需要将证书写入磁盘。 我们需要的函数是
PEM_write_X509
:我们就完成了! 希望这个答案中的信息足以让您大致了解一切是如何工作的,尽管我们仅仅触及了 OpenSSL 的表面。
对于那些有兴趣了解上面所有代码在实际应用程序中的样子的人,我整理了一个 Gist(用 C++ 编写),您可以查看 此处。
I realize that this is a very late (and long) answer. But considering how well this question seems to rank in search engine results, I figured it might be worth writing a decent answer for.
A lot of what you will read below is borrowed from this demo and the OpenSSL docs. The code below applies to both C and C++.
Before we can actually create a certificate, we need to create a private key. OpenSSL provides the
EVP_PKEY
structure for storing an algorithm-independent private key in memory. This structure is declared inopenssl/evp.h
but is included byopenssl/x509.h
(which we will need later) so you don't really need to explicitly include the header.In order to allocate an
EVP_PKEY
structure, we useEVP_PKEY_new
:There is also a corresponding function for freeing the structure -
EVP_PKEY_free
- which accepts a single argument: theEVP_PKEY
structure initialized above.Now we need to generate a key. For our example, we will generate an RSA key. This is done with the
RSA_generate_key
function which is declared inopenssl/rsa.h
. This function returns a pointer to anRSA
structure.A simple invocation of the function might look like this:
If the return value of
RSA_generate_key
isNULL
, then something went wrong. If not, then we now have an RSA key, and we can assign it to ourEVP_PKEY
structure from earlier:The
RSA
structure will be automatically freed when theEVP_PKEY
structure is freed.Now for the certificate itself.
OpenSSL uses the
X509
structure to represent an x509 certificate in memory. The definition for this struct is inopenssl/x509.h
. The first function we are going to need isX509_new
. Its use is relatively straightforward:As was the case with
EVP_PKEY
, there is a corresponding function for freeing the structure -X509_free
.Now we need to set a few properties of the certificate using some
X509_*
functions:This sets the serial number of our certificate to '1'. Some open-source HTTP servers refuse to accept a certificate with a serial number of '0', which is the default. The next step is to specify the span of time during which the certificate is actually valid. We do that with the following two function calls:
The first line sets the certificate's
notBefore
property to the current time. (TheX509_gmtime_adj
function adds the specified number of seconds to the current time - in this case none.) The second line sets the certificate'snotAfter
property to 365 days from now (60 seconds * 60 minutes * 24 hours * 365 days).Now we need to set the public key for our certificate using the key we generated earlier:
Since this is a self-signed certificate, we set the name of the issuer to the name of the subject. The first step in that process is to get the subject name:
If you've ever created a self-signed certificate on the command line before, you probably remember being asked for a country code. Here's where we provide it along with the organization ('O') and common name ('CN'):
(I'm using the value 'CA' here because I'm Canadian and that's our country code. Also note that parameter #4 needs to be explicitly cast to an
unsigned char *
.)Now we can actually set the issuer name:
And finally we are ready to perform the signing process. We call
X509_sign
with the key we generated earlier. The code for this is painfully simple:Note that we are using the SHA-1 hashing algorithm to sign the key. This differs from the
mkcert.c
demo I mentioned at the beginning of this answer, which uses MD5.We now have a self-signed certificate! But we're not done yet - we need to write these files out to disk. Thankfully OpenSSL has us covered there too with the
PEM_*
functions which are declared inopenssl/pem.h
. The first one we will need isPEM_write_PrivateKey
for saving our private key.If you don't want to encrypt the private key, then simply pass
NULL
for the third and fourth parameter above. Either way, you will definitely want to ensure that the file is not world-readable. (For Unix users, this meanschmod 600 key.pem
.)Whew! Now we are down to one function - we need to write the certificate out to disk. The function we need for this is
PEM_write_X509
:And we're done! Hopefully the information in this answer is enough to give you a rough idea of how everything works, although we've barely scratched the surface of OpenSSL.
For those interested in seeing what all of the code above looks like in a real application, I've thrown together a Gist (written in C++) that you can view here.
您需要首先熟悉术语和机制。
根据定义,X.509证书不包含私钥。 相反,它是公钥的 CA 签名版本(以及 CA 放入签名中的任何属性)。 PEM 格式实际上只支持密钥和证书的单独存储 - 尽管您可以将两者连接起来。
无论如何,您都需要调用 OpenSSL API 的 20 多个不同函数来创建密钥和自签名证书。 OpenSSL 源本身中有一个示例,位于 demos/x509/mkcert.c
有关更详细的答案,请参阅下面的 Nathan Osman 的解释。
You'll need to familiarize yourself with the terminology and mechanisms first.
An X.509 certificate, by definition, does not include a private key. Instead, it is a CA-signed version of the public key (along with any attributes the CA puts into the signature). The PEM format really only supports separate storage of the key and the certificate - although you can then concatenate the two.
In any case, you'll need to invoke 20+ different functions of the OpenSSL API to create a key and a self-signed certificate. An example is in the OpenSSL source itself, in demos/x509/mkcert.c
For a more detailed answer, please see Nathan Osman's explanation below.
Nathan Osman 详细而全面地解释了它,在 C++ 中需要解决同样的问题,所以这里是我的小补充,cpp-样式重写概念,并考虑到一些注意事项:
当然,应该对函数的返回值进行更多检查,实际上应该检查所有它们,但这会使样本太“分支”并且很容易改进。
Nathan Osman explained it greatly and fully, had the same problem to be solved in C++ so here is my small addition, cpp-style rewritten concept with a couple of caveats taken into account:
Of course, there should be more checks of function's return values, actually all of them should be checked but that would make a sample too "branchy" and is pretty easy to improve anyway.
有机会通过应用程序内的
系统
调用来执行此操作吗? 这样做有几个很好的理由:许可:调用
openssl
可执行文件可以将其与您的应用程序分开,并可能提供某些优势。 免责声明:就此咨询律师。文档:OpenSSL 附带出色的命令行文档,极大地简化了可能复杂的工具。
可测试性:您可以从命令行运行 OpenSSL,直到您确切了解如何创建证书。 有很多选择; 预计要花大约一天的时间,直到您弄清所有细节为止。 之后,将该命令合并到您的应用程序中就很简单了。
如果您选择使用 API,请查看 www.openssl.org 上的
openssl-dev
开发人员列表。祝你好运!
Any chance of doing this via a
system
call from within your app? Several good reasons for doing this:Licensing: Calling the
openssl
executable arguably separates it from your application and may provide certain advantages. Disclaimer: consult a lawyer on this.Documentation: OpenSSL comes with phenomenal command-line documentation that greatly simplifies a potentially complicated tool.
Testability: you can exercise OpenSSL from the command line until you understand exactly how to create your certs. There are a lot of options; expect to spend about a day on this until you get all the details right. After that, it's trivial to incorporate the command into your app.
If you choose to use the API, check the
openssl-dev
developers' list on www.openssl.org.Good luck!