1//
2// X509Certificate.cpp
3//
4// Library: Crypto
5// Package: Certificate
6// Module: X509Certificate
7//
8// Copyright (c) 2006-2009, Applied Informatics Software Engineering GmbH.
9// and Contributors.
10//
11// SPDX-License-Identifier: BSL-1.0
12//
13
14
15#include "Poco/Crypto/X509Certificate.h"
16#include "Poco/Crypto/CryptoException.h"
17#include "Poco/StreamCopier.h"
18#include "Poco/String.h"
19#include "Poco/DateTimeParser.h"
20#include "Poco/Format.h"
21#include <sstream>
22#include <openssl/pem.h>
23#ifdef _WIN32
24// fix for WIN32 header conflict
25#undef X509_NAME
26#endif
27#include <openssl/x509v3.h>
28#include <openssl/err.h>
29#include <openssl/evp.h>
30
31
32namespace Poco {
33namespace Crypto {
34
35
36X509Certificate::X509Certificate(std::istream& istr):
37 _pCert(0)
38{
39 load(istr);
40}
41
42
43X509Certificate::X509Certificate(const std::string& path):
44 _pCert(0)
45{
46 load(path);
47}
48
49
50X509Certificate::X509Certificate(X509* pCert):
51 _pCert(pCert)
52{
53 poco_check_ptr(_pCert);
54
55 init();
56}
57
58
59X509Certificate::X509Certificate(X509* pCert, bool shared):
60 _pCert(pCert)
61{
62 poco_check_ptr(_pCert);
63
64 if (shared)
65 {
66#if OPENSSL_VERSION_NUMBER >= 0x10100000L
67 X509_up_ref(_pCert);
68#else
69 _pCert->references++;
70#endif
71 }
72
73 init();
74}
75
76
77X509Certificate::X509Certificate(const X509Certificate& cert):
78 _issuerName(cert._issuerName),
79 _subjectName(cert._subjectName),
80 _serialNumber(cert._serialNumber),
81 _pCert(cert._pCert)
82{
83 _pCert = X509_dup(_pCert);
84}
85
86
87X509Certificate& X509Certificate::operator = (const X509Certificate& cert)
88{
89 X509Certificate tmp(cert);
90 swap(tmp);
91 return *this;
92}
93
94
95void X509Certificate::swap(X509Certificate& cert)
96{
97 using std::swap;
98 swap(cert._issuerName, _issuerName);
99 swap(cert._subjectName, _subjectName);
100 swap(cert._serialNumber, _serialNumber);
101 swap(cert._pCert, _pCert);
102}
103
104
105X509Certificate::~X509Certificate()
106{
107 X509_free(_pCert);
108}
109
110
111void X509Certificate::load(std::istream& istr)
112{
113 poco_assert (!_pCert);
114
115 std::stringstream certStream;
116 Poco::StreamCopier::copyStream(istr, certStream);
117 std::string cert = certStream.str();
118
119 BIO *pBIO = BIO_new_mem_buf(const_cast<char*>(cert.data()), static_cast<int>(cert.size()));
120 if (!pBIO) throw Poco::IOException("Cannot create BIO for reading certificate");
121 _pCert = PEM_read_bio_X509(pBIO, 0, 0, 0);
122 BIO_free(pBIO);
123
124 if (!_pCert) throw Poco::IOException("Failed to load certificate from stream");
125
126 init();
127}
128
129
130void X509Certificate::load(const std::string& path)
131{
132 poco_assert (!_pCert);
133
134 BIO *pBIO = BIO_new(BIO_s_file());
135 if (!pBIO) throw Poco::IOException("Cannot create BIO for reading certificate file", path);
136 if (!BIO_read_filename(pBIO, path.c_str()))
137 {
138 BIO_free(pBIO);
139 throw Poco::OpenFileException("Cannot open certificate file for reading", path);
140 }
141
142 _pCert = PEM_read_bio_X509(pBIO, 0, 0, 0);
143 BIO_free(pBIO);
144
145 if (!_pCert) throw Poco::ReadFileException("Faild to load certificate from", path);
146
147 init();
148}
149
150
151void X509Certificate::save(std::ostream& stream) const
152{
153 BIO *pBIO = BIO_new(BIO_s_mem());
154 if (!pBIO) throw Poco::IOException("Cannot create BIO for writing certificate");
155 try
156 {
157 if (!PEM_write_bio_X509(pBIO, _pCert))
158 throw Poco::IOException("Failed to write certificate to stream");
159
160 char *pData;
161 long size;
162 size = BIO_get_mem_data(pBIO, &pData);
163 stream.write(pData, size);
164 }
165 catch (...)
166 {
167 BIO_free(pBIO);
168 throw;
169 }
170 BIO_free(pBIO);
171}
172
173
174void X509Certificate::save(const std::string& path) const
175{
176 BIO *pBIO = BIO_new(BIO_s_file());
177 if (!pBIO) throw Poco::IOException("Cannot create BIO for reading certificate file", path);
178 if (!BIO_write_filename(pBIO, const_cast<char*>(path.c_str())))
179 {
180 BIO_free(pBIO);
181 throw Poco::CreateFileException("Cannot create certificate file", path);
182 }
183 try
184 {
185 if (!PEM_write_bio_X509(pBIO, _pCert))
186 throw Poco::WriteFileException("Failed to write certificate to file", path);
187 }
188 catch (...)
189 {
190 BIO_free(pBIO);
191 throw;
192 }
193 BIO_free(pBIO);
194}
195
196
197void X509Certificate::init()
198{
199 char buffer[NAME_BUFFER_SIZE];
200 X509_NAME_oneline(X509_get_issuer_name(_pCert), buffer, sizeof(buffer));
201 _issuerName = buffer;
202 X509_NAME_oneline(X509_get_subject_name(_pCert), buffer, sizeof(buffer));
203 _subjectName = buffer;
204 BIGNUM* pBN = ASN1_INTEGER_to_BN(X509_get_serialNumber(const_cast<X509*>(_pCert)), 0);
205 if (pBN)
206 {
207 char* pSN = BN_bn2hex(pBN);
208 if (pSN)
209 {
210 _serialNumber = pSN;
211 OPENSSL_free(pSN);
212 }
213 BN_free(pBN);
214 }
215}
216
217
218std::string X509Certificate::commonName() const
219{
220 return subjectName(NID_COMMON_NAME);
221}
222
223
224std::string X509Certificate::issuerName(NID nid) const
225{
226 if (X509_NAME* issuer = X509_get_issuer_name(_pCert))
227 {
228 char buffer[NAME_BUFFER_SIZE];
229 if (X509_NAME_get_text_by_NID(issuer, nid, buffer, sizeof(buffer)) >= 0)
230 return std::string(buffer);
231 }
232 return std::string();
233}
234
235
236std::string X509Certificate::subjectName(NID nid) const
237{
238 if (X509_NAME* subj = X509_get_subject_name(_pCert))
239 {
240 char buffer[NAME_BUFFER_SIZE];
241 if (X509_NAME_get_text_by_NID(subj, nid, buffer, sizeof(buffer)) >= 0)
242 return std::string(buffer);
243 }
244 return std::string();
245}
246
247
248void X509Certificate::extractNames(std::string& cmnName, std::set<std::string>& domainNames) const
249{
250 domainNames.clear();
251 if (STACK_OF(GENERAL_NAME)* names = static_cast<STACK_OF(GENERAL_NAME)*>(X509_get_ext_d2i(_pCert, NID_subject_alt_name, 0, 0)))
252 {
253 for (int i = 0; i < sk_GENERAL_NAME_num(names); ++i)
254 {
255 const GENERAL_NAME* name = sk_GENERAL_NAME_value(names, i);
256 if (name->type == GEN_DNS)
257 {
258 const char* data = reinterpret_cast<char*>(ASN1_STRING_data(name->d.ia5));
259 std::size_t len = ASN1_STRING_length(name->d.ia5);
260 domainNames.insert(std::string(data, len));
261 }
262 }
263 GENERAL_NAMES_free(names);
264 }
265
266 cmnName = commonName();
267 if (!cmnName.empty() && domainNames.empty())
268 {
269 domainNames.insert(cmnName);
270 }
271}
272
273
274Poco::DateTime X509Certificate::validFrom() const
275{
276 ASN1_TIME* certTime = X509_get_notBefore(_pCert);
277 std::string dateTime(reinterpret_cast<char*>(certTime->data));
278 int tzd;
279 return DateTimeParser::parse("%y%m%d%H%M%S", dateTime, tzd);
280}
281
282
283Poco::DateTime X509Certificate::expiresOn() const
284{
285 ASN1_TIME* certTime = X509_get_notAfter(_pCert);
286 std::string dateTime(reinterpret_cast<char*>(certTime->data));
287 int tzd;
288 return DateTimeParser::parse("%y%m%d%H%M%S", dateTime, tzd);
289}
290
291
292bool X509Certificate::issuedBy(const X509Certificate& issuerCertificate) const
293{
294 X509* pCert = const_cast<X509*>(_pCert);
295 X509* pIssuerCert = const_cast<X509*>(issuerCertificate.certificate());
296 EVP_PKEY* pIssuerPublicKey = X509_get_pubkey(pIssuerCert);
297 if (!pIssuerPublicKey) throw Poco::InvalidArgumentException("Issuer certificate has no public key");
298 int rc = X509_verify(pCert, pIssuerPublicKey);
299 EVP_PKEY_free(pIssuerPublicKey);
300 return rc == 1;
301}
302
303
304bool X509Certificate::equals(const X509Certificate& otherCertificate) const
305{
306 X509* pCert = const_cast<X509*>(_pCert);
307 X509* pOtherCert = const_cast<X509*>(otherCertificate.certificate());
308 return X509_cmp(pCert, pOtherCert) == 0;
309}
310
311
312std::string X509Certificate::signatureAlgorithm() const
313{
314 int sigNID = NID_undef;
315
316#if (OPENSSL_VERSION_NUMBER >= 0x1010000fL) && !defined(LIBRESSL_VERSION_NUMBER)
317 sigNID = X509_get_signature_nid(_pCert);
318#else
319 poco_check_ptr(_pCert->sig_alg);
320 sigNID = OBJ_obj2nid(_pCert->sig_alg->algorithm);
321#endif
322
323 if (sigNID != NID_undef)
324 {
325 const char* pAlgName = OBJ_nid2ln(sigNID);
326 if (pAlgName) return std::string(pAlgName);
327 else throw OpenSSLException(Poco::format("X509Certificate::"
328 "signatureAlgorithm(): OBJ_nid2ln(%d)", sigNID));
329 }
330 else
331 throw NotFoundException("X509Certificate::signatureAlgorithm()");
332
333 return "";
334}
335
336
337X509Certificate::List X509Certificate::readPEM(const std::string& pemFileName)
338{
339 List caCertList;
340 BIO* pBIO = BIO_new_file(pemFileName.c_str(), "r");
341 if (pBIO == NULL) throw OpenFileException("X509Certificate::readPEM()");
342 X509* x = PEM_read_bio_X509(pBIO, NULL, 0, NULL);
343 if (!x) throw OpenSSLException(Poco::format("X509Certificate::readPEM(%s)", pemFileName));
344 while(x)
345 {
346 caCertList.push_back(X509Certificate(x));
347 x = PEM_read_bio_X509(pBIO, NULL, 0, NULL);
348 }
349 BIO_free(pBIO);
350 return caCertList;
351}
352
353
354void X509Certificate::writePEM(const std::string& pemFileName, const List& list)
355{
356 BIO* pBIO = BIO_new_file(pemFileName.c_str(), "a");
357 if (pBIO == NULL) throw OpenFileException("X509Certificate::writePEM()");
358 List::const_iterator it = list.begin();
359 List::const_iterator end = list.end();
360 for (; it != end; ++it)
361 {
362 if (!PEM_write_bio_X509(pBIO, const_cast<X509*>(it->certificate())))
363 {
364 throw OpenSSLException("X509Certificate::writePEM()");
365 }
366 }
367 BIO_free(pBIO);
368}
369
370
371void X509Certificate::print(std::ostream& out) const
372{
373 out << "subjectName: " << subjectName() << std::endl;
374 out << "issuerName: " << issuerName() << std::endl;
375 out << "commonName: " << commonName() << std::endl;
376 out << "country: " << subjectName(X509Certificate::NID_COUNTRY) << std::endl;
377 out << "localityName: " << subjectName(X509Certificate::NID_LOCALITY_NAME) << std::endl;
378 out << "stateOrProvince: " << subjectName(X509Certificate::NID_STATE_OR_PROVINCE) << std::endl;
379 out << "organizationName: " << subjectName(X509Certificate::NID_ORGANIZATION_NAME) << std::endl;
380 out << "organizationUnitName: " << subjectName(X509Certificate::NID_ORGANIZATION_UNIT_NAME) << std::endl;
381 out << "emailAddress: " << subjectName(X509Certificate::NID_PKCS9_EMAIL_ADDRESS) << std::endl;
382 out << "serialNumber: " << subjectName(X509Certificate::NID_SERIAL_NUMBER) << std::endl;
383}
384
385
386void X509Certificate::printAll(std::ostream& out) const
387{
388 X509_NAME *subj = X509_get_subject_name(_pCert);
389
390 for (int i = 0; i < X509_NAME_entry_count(subj); ++i)
391 {
392 X509_NAME_ENTRY* e = X509_NAME_get_entry(subj, i);
393 ASN1_STRING* d = X509_NAME_ENTRY_get_data(e);
394 unsigned char* str = ASN1_STRING_data(d);
395 out << (char*) str << std::endl;
396 }
397}
398
399
400} } // namespace Poco::Crypto
401