在学习openssl官方的/test/certs的脚本实现, 做到第30个脚本实验时, 发现根CA证书和key不匹配.
估计做实验时, 遇到脚本需要的文件, 就随便拷贝一个同名的文件过来, 导致证书和key不是一个脚本产生的, 所以不匹配
就想从前面的实验中, 找出匹配的证书和key来做实验, 肯定有啊.
这事应该openssl编程就能做到. 不过时间紧, 先不用openssl编程来做.
看有没有简单暴力的方法.
在网上找到如下2个openssl命令, 可以输出证书和key的模块信息.
// 打印X509证书的模块信息
openssl x509 -inform PEM -modulus -in ca-cert.pem -noout
// 打印rsa私钥的模块信息
openssl rsa -inform PEM -modulus -in ca-key.pem -noout
这2个命令输出的都是文本信息, 如果这2个文本信息相同, 则说明这2个证书和私钥是匹配的
按照这个原理, 手工输入命令, 然后人肉比对. 原始操作就是这样的, 就可以知道一对证书和key是否匹配.
可以重定向为文件(e.g. openssl x509 -inform PEM -modulus -in ca-cert.pem -noout > cert_info.txt)
然后用win10自带的FC.exe 就可以比较2个文件是否相同(e.g. fc file_a file_b), 如果fc返回0, 这2个文件就是相同的.
但是作为研发, 能用程序做的, 绝对不可能用人工操作来折磨自己, 那肯定不能够啊.
用了一个小时, 手搓了一个原始工具来干活.
vs2019 vc++ MFC + Dialog模板
对话框设置为可以接收文件.
2个编辑框(证书和key), 也设置为可以接收文件.
从CEdit继承一个类CDropEdit(用类向导很方便), 加入拖拽消息处理(用类向导加入消息处理), 框架代码不用自己写.
#pragma once
#include <afxwin.h>
class CDropEdit :
public CEdit
{
public:
DECLARE_MESSAGE_MAP()
afx_msg void OnDropFiles(HDROP hDropInfo);
};
#include "pch.h"
#include "CDropEdit.h"
BEGIN_MESSAGE_MAP(CDropEdit, CEdit)
ON_WM_DROPFILES()
END_MESSAGE_MAP()
void CDropEdit::OnDropFiles(HDROP hDropInfo)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
// 拖拽实现很多, 可以参考msdn, 我就直接用下面这位同学分享的片段
// https://blog.csdn.net/wrl112/article/details/120335725
TCHAR szTmp[MAX_PATH * 2];
CString strVal;
unsigned int nRet = 0, nInx = 0;
do {
nRet = ::DragQueryFile(hDropInfo, nInx++, szTmp, sizeof(szTmp));
if (nRet > 0) {
strVal += szTmp;
}
else {
}
break;
} while (1);
::DragFinish(hDropInfo);
SetWindowText(strVal);
CEdit::OnDropFiles(hDropInfo);
}
#include <string>
std::string my_W2A(std::wstring str)
{
USES_CONVERSION;
std::string str_rc = W2A(str.c_str());
return str_rc;
}
void CCertKeyMatchDlg::OnBnClickedButtonMatch()
{
// TODO: 在此添加控件通知处理程序代码
CString csFileA;
CString csFileB;
CString csTmp;
std::string strFileA;
std::string strFileB;
char szBuf[10 * 1024];
int i_rc = 0;
do {
this->SetWindowTextW(_T(""));
m_EditFileA.GetWindowText(csFileA);
m_EditFileB.GetWindowText(csFileB);
if ((csFileA.GetLength() <= 0) || (csFileB.GetLength() <= 0))
{
TRACE(_T("文件名必须都不为空"));
break;
}
strFileA = my_W2A((LPCWSTR)csFileA);
strFileB = my_W2A((LPCWSTR)csFileB);
/*
// 打印X509证书的模块信息
openssl x509 -inform PEM -modulus -in ca-cert.pem -noout
// 打印rsa私钥的模块信息
openssl rsa -inform PEM -modulus -in ca-key.pem -noout
*/
sprintf(szBuf, "openssl x509 -inform PEM -modulus -in %s -noout > cert_info.txt", strFileA.c_str());
i_rc = system(szBuf);
_ASSERT(0 == i_rc);
sprintf(szBuf, "openssl rsa -inform PEM -modulus -in %s -noout > key_info.txt", strFileB.c_str());
i_rc = system(szBuf);
_ASSERT(0 == i_rc);
i_rc = system("fc cert_info.txt key_info.txt");
csTmp.Format(_T("证书和私钥%s"), (0 == i_rc) ? _T("匹配") : _T("不匹配"));
this->SetWindowTextW(csTmp);
} while (false);
}
void CCertKeyMatchDlg::OnDropFiles(HDROP hDropInfo)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
CDialogEx::OnDropFiles(hDropInfo);
}
在对话框初始化时, 加入了openssl的path, 除非必要, 不污染环境变量.
BOOL CCertKeyMatchDlg::OnInitDialog()
{
// ...
// TODO: 在此添加额外的初始化代码
char* psz_env_buf = new char[1024 * 1024];
if (NULL != psz_env_buf)
{
char* psz_env = getenv("path");
// ::SetEnvironmentStrings()
sprintf(psz_env_buf, "path=%s;%s", "C:\\openssl_3d2\\bin", psz_env);
int i_rc = _putenv(psz_env_buf);
_ASSERT(0 == i_rc);
delete[]psz_env_buf;
}
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
加入环境变量后, 调用程序时, 就不用写全路径, 就可以调用openssl.exe了.
感觉自己还挺机智的:P
因为cert和key的pem是成对的, 用everything看一下, 将cert.pem列出来, 然后进入对应目录找一下是否有对应的key.pem, 最好从生成时间早的目录找, 然后用这个工具比对.
很快就找出来了.