我希望 OpenSSL 有一个可以做的工具
我不知道有什么 OpenSSL 功能或 OpenSSL 工具可以做到这一点。查看源代码,PEM_bytes_read_bio 可能是执行此操作的功能。但它没有记录,所以我不确定。 (函数名称以大写字母开头 - PEM_*。各种小写字母 - pem_* 是私有的,不应使用)。
如果你有OpenSSL sources 方便,那么解析例程的源代码在<openssl src>/crypto/pem/pem_lib.c 中。这就是实现PEM_bytes_read_bio 的地方。
但是这似乎有点“hacky”。
好吧,它并没有那么老套——你必须卷起袖子来编写代码。您也许可以使用 Bison 和 Flex 来创建解析器和词法分析器。你如何从 shell 中调用它是另一回事。使用词法分析器,我认为您可以在 O(n) 中解析 PEM 对象。
我需要找到一种方法来自动化将 PEM 文件拆分为多个 PEM 文件的过程...最好的方法是什么?
我在PEM Pack 为 Crypto++ 写过类似的文章。它增加了对 PEM 编码密钥的支持,包括加密密钥。 Crypto++ 是一个 C++ 库,但相同的通用算法应该适用于您选择的语言。
Crypto++ 中感兴趣的例程称为PEM_NextObject,它位于源文件pem-rd.cpp 中。您可以在页面底部的 ZIP 文件中找到源文件。 PEM_NextObject 找了四个项目:
- 领先
-----BEGIN
- 以下
-----
- 尾随
-----END
- 以下
-----
我使用了四个索引 - 每个标记一个。我会一次读取 64+1 个字节,因为 OpenSSL 以 64 个字符输出中断。我会将一行读入string 并将字符串连接到一个累加器中。然后我会使用find 在累加器中定位令牌(有些人放弃,因为它们是安全字符串)。如果我没有找到特定的索引,我会阅读另一行。
搜索token时,第一个token的搜索从位置0开始。下一次搜索是在找到上一个索引之后开始的。例如,对索引 2 的搜索从索引 1 加上令牌的大小开始;并且对索引 3 的搜索从索引 2 加上令牌的大小开始。如果找不到标记,我只搜索当前行和 10 个字符,以防标记跨越先前读取和当前读取。
我使用索引而不是迭代器,因为如果容器的大小增加,迭代器就会失效。串联会导致这种情况。幸运的是,索引总是有效的,因为它只是从字符串开头的偏移量。在 bash(或任何你选择的)中你可能没有这个问题。
如果我读到流的末尾但没有找到所有四个索引,那么我会抛出错误。
如果我找到了所有四个索引,那么我就有了一些声称是 PEM 编码的东西。我丢弃了所有前导字符,并修剪了尾随空格。所以 PEM 对象位于 (Index1) 到 (Index4 + 5) (+5 表示尾随 -----)。
因为我可能解析了无效的 PEM 对象(即 -----BEGIN FOO----- 和 -----END BAR-----),所以我需要另一个例程来对解析的 PEM 对象的类型进行分类。该函数称为PEM_GetType。
该算法应该运行良好,因为从算法分析的角度来看它并不令人震惊,而且 PEM 对象通常很小(小于 2K 或 4K)。我认为分析是O(n + m*10),其中m是文件中的行数。 m*10 是基于扫描 64 个字符的行以查找具有 10 个字符“倒带”的令牌,读取另一行,然后再次扫描令牌。回想一下,如果令牌跨行,我会“倒带”一点。
如果没有 PEM 对象且文件很大,则此算法执行正常。我很确定它在最坏的情况下也以 O(n + m*10) 运行。 如果n >>> m,那么它本质上是一个O(n)函数,因为m*10只是一个很大的边界c。
您可能还对 Server Fault 上的 How to split a PEM file 和 Stack Overflow 上的 Where is the PEM file format specified? 感兴趣。
-----BEGIN CERTIFICATE----- 到 -----END CERTIFICATE-----
当您显示证书时,还有其他类型的对象。例如,公钥和加密私钥。 如果您需要解密一个加密的密钥,那么您需要解除/借用/使用 OpenSSL 的EVP_BytesToKey。
EVP_BytesToKey 是一种非标准,因此它变成了复制/粘贴操作以确保互操作性。我似乎记得 EVP_BytesToKey 相当于 PKCS#5 派生 if EVP_BytesToKey 产生的字节数为 16 或更少。如果生成了 17 个或更多,则 OpenSSL 使用“非标准”扩展。
如果您对测试感兴趣,请查看pem-create-keys.sh。它会创建格式错误的 PEM 编码密钥(不是证书)。例如,它将不带换行符连接多个键,它会删除一个尾随破折号,它会删除一个尾随破折号,然后连接另一个键。