前言
网上搜索出来的OpenSSL解析证书很多都是命令行工具的操作,还有一些C++实现的,用的版本的比较老,很多接口都变了。整理了基于OpenSSL1.11版本解析X509证书的实现。
接口
参照QSslCertificate接口,基于标准C++实现类似的接口,不依赖Qt,支持RSA和SM2证书信息解析,解析PFX、P7B证书、解析证书链、验证书等
具体接口如下:
#ifndef X509CERTIFICATE_H
#define X509CERTIFICATE_H
#include <string>
#include <vector>
#include <map>
#include <memory>
#if defined(_MSC_VER) || defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
# define Q_DECL_EXPORT __declspec(dllexport)
# define Q_DECL_IMPORT __declspec(dllimport)
#else
# define Q_DECL_EXPORT __attribute__((visibility("default")))
# define Q_DECL_IMPORT __attribute__((visibility("default")))
#endif
#if defined(X509CERTIFICATE_LIBRARY)
# define X509CERTIFICATE_EXPORT Q_DECL_EXPORT
#else
# define X509CERTIFICATE_EXPORT Q_DECL_IMPORT
#endif
using namespace std;
class X509Certificate_p;
class X509CertificateExtension;
class X509CertificateExtension_p;
class X509CERTIFICATE_EXPORT X509Certificate
{
public:
X509Certificate();
X509Certificate(const char *cert, int len);
X509Certificate(const X509Certificate &other);
enum HashType {
Hash_Sha1,
Hash_Sha256,
Hash_SM3
};
enum SubjectInfo {
Organization,
CommonName,
LocalityName,
OrganizationalUnitName,
CountryName,
StateOrProvinceName,
DistinguishedNameQualifier,
SerialNumber,
EmailAddress
};
X509Certificate &operator=(const X509Certificate &other);
bool operator==(const X509Certificate &other) const;
inline bool operator!=(const X509Certificate &other) const { return !operator==(other); }
bool isNull() const;
void *handle() const;
int version() const;
string serialNumber() const;
string subject() const;
string subjectInfo(SubjectInfo subject) const;
string subjectDisplyName() const;
string issuer() const;
string issuerInfo(SubjectInfo issuer) const;
string issuerDisplyName() const;
string notBefor() const;
string notAfter() const;
string digest(HashType type) const;
string publicKeyValue() const;
string publicKeyType() const;
string signAlgType() const;
string signValue() const;
vector<X509CertificateExtension> extensions() const;
string toDer() const;
string toPem() const;
string toText() const;
static bool importPkcs12(const char *pfxFile, int len, char *priKey, int &priKeyLen,
X509Certificate *x509Cert, vector<X509Certificate> &caCerts,
const char *pass = "");
static bool importP7b(const char *p7b, int len, vector<X509Certificate> &caCerts);
static vector<X509Certificate *> splitCertChain(const string &chains);//only pem
static int verify(const X509Certificate &userCert, vector<X509Certificate> certificateChain);
static vector<X509Certificate> systemCaCertificates();
private:
shared_ptr<X509Certificate_p> p;
friend class X509Certificate_p;
};
class X509CERTIFICATE_EXPORT X509CertificateExtension
{
public:
X509CertificateExtension();
enum ExtMethod {
Ext_String,
Ext_Vector,
Ext_Map
};
bool isCritical() const;
bool isSupported() const;
string name() const;
string oid() const;
string value() const;
ExtMethod methodType() const;
string toString() const;
vector<string> toVector() const;
multimap<string, string> toMap() const;
private:
X509CertificateExtension_p *p = nullptr;
friend class X509Certificate;
};
#endif // X509CERTIFICATE_H
具体的代码实放在github上,这里,整个项目包含一个测试接口的工程和生成共享库的工程,都是Qt工程,当然实现的源码是不依赖Qt,构建其他工程也是可以的。
OpenSSL依赖版本Windows下已提供,默认使用的是静态链接,因为OpenSSL实在是太庞大了,X509只是其中很小的一部分,静态链接的方式会好些。Linux下的需要安装libssl-dev包,采用动态链接的方式,要用静态链接得自己编译了,Windows是因为有现成的库。测试了Windows和Linux下都是能够正常使用的,理论上只要OpenSSL能够使用,其他平台都能够使用,这就需要另外去测试了。
整个接口构造参照Qt的p指针和q指针的方式,即实现的类和接口类是分开的,希望能满足C++的二进制兼容,具体并没有去测试。
结语
最后,自己水平有限,不论是代码结构还是代码实现,其中或多或少都会有问题,欢迎各位大佬指正