2 * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 * @file Certificate.cpp
18 * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com)
20 * @brief Certificate class implementation
23 #include <sys/types.h>
31 #include <openssl/pem.h>
32 #include <openssl/x509.h>
33 #include <openssl/x509v3.h>
34 #include <openssl/objects.h>
36 #include <dpl/log/log.h>
38 #include "vcore/Base64.h"
39 #include "vcore/TimeConversion.h"
41 #include "vcore/Certificate.h"
45 typedef std::unique_ptr<X509, std::function<void(X509 *)>> ScopedX509;
46 typedef std::unique_ptr<FILE, std::function<int(FILE *)>> ScopedFile;
48 } // namespace anonymous
50 namespace ValidationCore {
52 Certificate::Certificate(X509 *cert)
55 VcoreThrowMsg(Certificate::Exception::WrongParamError,
56 "Input X509 shouldn't be NULL.");
58 m_x509 = X509_dup(cert);
61 VcoreThrowMsg(Certificate::Exception::OpensslInternalError,
62 "Internal Openssl error in d2i_X509 function.");
65 Certificate::Certificate(const std::string &data,
66 Certificate::FormType form)
69 VcoreThrowMsg(Certificate::Exception::WrongParamError,
70 "Input data shouldn't be empty");
73 const unsigned char *ptr;
74 std::string tmp = data;
76 // transform to DER format
77 if (FORM_BASE64 == form) {
83 if (!base64.finalize()) {
84 LogWarning("Error during decoding");
88 } catch (const Base64Decoder::Exception::Base &e) {
89 LogError("Exception in Certificate constructor : " << e.DumpToString());
90 VcoreThrowMsg(Certificate::Exception::Base64Error, "Failed to Base64Decoder");
94 ptr = reinterpret_cast<const unsigned char *>(tmp.c_str());
95 size = static_cast<int>(tmp.size());
96 m_x509 = d2i_X509(NULL, &ptr, size);
99 VcoreThrowMsg(Certificate::Exception::OpensslInternalError,
100 "Internal Openssl error in d2i_X509 function.");
103 static off_t getFileSize(const std::string &location)
106 stat(location.c_str(), &status);
107 return status.st_size;
110 CertificatePtr Certificate::createFromFile(const std::string &location)
114 fp = fopen(location.c_str(), "rb");
117 VcoreThrowMsg(Certificate::Exception::WrongParamError,
118 "File cannot be opened : " << location);
120 ScopedFile filePtr(fp, fclose);
121 x509 = PEM_read_X509(fp, NULL, NULL, NULL);
125 x509 = PEM_read_X509_AUX(fp, NULL, NULL, NULL);
129 ScopedX509 x509Ptr(x509, X509_free);
130 return CertificatePtr(new Certificate(x509));
133 off_t filesize = getFileSize(location);
136 VcoreThrowMsg(Certificate::Exception::WrongParamError,
137 "File content is empty : " << location);
139 unsigned char *content = new unsigned char[filesize + 1];
141 VcoreThrowMsg(Certificate::Exception::InternalError,
142 "Fail to allocate memory.");
144 memset(content, 0x00, filesize + 1);
147 if (fread(content, sizeof(unsigned char), filesize, fp) != static_cast<size_t>(filesize))
148 VcoreThrowMsg(Certificate::Exception::InternalError,
149 "file read failed. wrong size : " << location);
151 content[filesize] = '\0';
152 const unsigned char *ptr = reinterpret_cast<const unsigned char *>(content);
153 x509 = d2i_X509(NULL, &ptr, filesize);
156 VcoreThrowMsg(Certificate::Exception::OpensslInternalError,
157 "Internal Openssl error in d2i_X509 function.");
159 return CertificatePtr(new Certificate(x509));
162 Certificate::~Certificate()
167 X509 *Certificate::getX509(void) const
172 std::string Certificate::getDER(void) const
174 unsigned char *rawDer = NULL;
175 int size = i2d_X509(m_x509, &rawDer);
177 if (!rawDer || size <= 0)
178 VcoreThrowMsg(Certificate::Exception::OpensslInternalError,
181 std::string output(reinterpret_cast<char *>(rawDer), size);
182 OPENSSL_free(rawDer);
186 std::string Certificate::getBase64(void) const
188 Base64Encoder base64;
192 base64.append(getDER());
194 } catch (const Base64Encoder::Exception::Base &e) {
195 LogError("Exception in Certificate getBase64 : " << e.DumpToString());
196 VcoreThrowMsg(Certificate::Exception::Base64Error, "Failed to Base64Encoder");
202 bool Certificate::isSignedBy(const CertificatePtr &parent) const
205 LogDebug("Invalid certificate parameter.");
209 return 0 == X509_NAME_cmp(X509_get_subject_name(parent->m_x509),
210 X509_get_issuer_name(m_x509));
213 Certificate::Fingerprint Certificate::getFingerprint(
214 Certificate::FingerprintType type) const
216 unsigned int fingerprintlength = EVP_MAX_MD_SIZE;
217 unsigned char fingerprint[EVP_MAX_MD_SIZE];
220 if (type == FINGERPRINT_MD5) {
221 if (!X509_digest(m_x509, EVP_md5(), fingerprint, &fingerprintlength))
222 VcoreThrowMsg(Certificate::Exception::OpensslInternalError,
223 "MD5 digest counting failed!");
226 if (type == FINGERPRINT_SHA1) {
227 if (!X509_digest(m_x509, EVP_sha1(), fingerprint, &fingerprintlength))
228 VcoreThrowMsg(Certificate::Exception::OpensslInternalError,
229 "SHA1 digest counting failed");
232 raw.resize(fingerprintlength); // improve performance
233 std::copy(fingerprint, fingerprint + fingerprintlength, raw.begin());
237 X509_NAME *Certificate::getX509Name(FieldType type) const
239 X509_NAME *name = NULL;
243 name = X509_get_issuer_name(m_x509);
247 name = X509_get_subject_name(m_x509);
251 VcoreThrowMsg(Certificate::Exception::WrongParamError,
252 "Invalid field type param. type : " << type);
256 VcoreThrowMsg(Certificate::Exception::OpensslInternalError,
257 "Error during x509 name extraction.");
262 std::string Certificate::getOneLine(FieldType type) const
264 X509_NAME *name = getX509Name(type);
265 static const int MAXB = 1024;
266 char buffer[MAXB] = {0, };
267 X509_NAME_oneline(name, buffer, MAXB);
268 return std::string(buffer);
271 std::string Certificate::getField(FieldType type, int fieldNid) const
273 X509_NAME *subjectName = getX509Name(type);
274 X509_NAME_ENTRY *subjectEntry = NULL;
276 int entryCount = X509_NAME_entry_count(subjectName);
278 for (int i = 0; i < entryCount; ++i) {
279 subjectEntry = X509_NAME_get_entry(subjectName,
286 int nid = OBJ_obj2nid(
287 static_cast<ASN1_OBJECT *>(
288 X509_NAME_ENTRY_get_object(subjectEntry)));
290 if (nid != fieldNid) {
294 ASN1_STRING *pASN1Str = subjectEntry->value;
295 unsigned char *pData = NULL;
296 int nLength = ASN1_STRING_to_UTF8(&pData,
300 VcoreThrowMsg(Certificate::Exception::OpensslInternalError,
301 "Reading field error.");
304 output = std::string();
306 output = std::string(reinterpret_cast<char *>(pData), nLength);
314 std::string Certificate::getCommonName(FieldType type) const
316 return getField(type, NID_commonName);
319 std::string Certificate::getCountryName(FieldType type) const
321 return getField(type, NID_countryName);
324 std::string Certificate::getStateOrProvinceName(FieldType type) const
326 return getField(type, NID_stateOrProvinceName);
329 std::string Certificate::getLocalityName(FieldType type) const
331 return getField(type, NID_localityName);
334 std::string Certificate::getOrganizationName(FieldType type) const
336 return getField(type, NID_organizationName);
339 std::string Certificate::getOrganizationalUnitName(FieldType type) const
341 return getField(type, NID_organizationalUnitName);
344 std::string Certificate::getEmailAddres(FieldType type) const
346 return getField(type, NID_pkcs9_emailAddress);
349 std::string Certificate::getNameHash(FieldType type) const
351 unsigned long ulNameHash;
354 if (type == FIELD_SUBJECT)
355 ulNameHash = X509_subject_name_hash(m_x509);
357 ulNameHash = X509_issuer_name_hash(m_x509);
359 snprintf(buf, 9, "%08lx", ulNameHash);
360 return std::string(buf);
363 std::string Certificate::getUID(FieldType type) const
365 ASN1_BIT_STRING *uid = NULL;
367 if (type == FIELD_SUBJECT)
368 uid = m_x509->cert_info->subjectUID;
370 uid = m_x509->cert_info->issuerUID;
372 if (uid->data == NULL)
373 return std::string();
375 char *temp = new char[uid->length + 1];
378 LogError("Fail to allocate memory.");
379 return std::string();
382 memcpy(temp, uid->data, uid->length);
383 temp[uid->length] = 0;
384 std::string uidStr(temp);
389 std::string Certificate::getOCSPURL() const
391 // TODO verify this code
392 std::string retValue;
393 AUTHORITY_INFO_ACCESS *aia = static_cast<AUTHORITY_INFO_ACCESS *>(
394 X509_get_ext_d2i(m_x509,
399 // no AIA extension in the cert
404 int count = sk_ACCESS_DESCRIPTION_num(aia);
406 for (int i = 0; i < count; ++i) {
407 ACCESS_DESCRIPTION *ad = sk_ACCESS_DESCRIPTION_value(aia, i);
409 if (OBJ_obj2nid(ad->method) == NID_ad_OCSP &&
410 ad->location->type == GEN_URI) {
411 void *data = ASN1_STRING_data(ad->location->d.ia5);
414 retValue = std::string();
416 retValue = std::string(static_cast<char *>(data));
422 sk_ACCESS_DESCRIPTION_free(aia);
426 Certificate::AltNameSet Certificate::getAlternativeNameDNS() const
429 GENERAL_NAME *namePart = NULL;
430 STACK_OF(GENERAL_NAME)* san =
431 static_cast<STACK_OF(GENERAL_NAME) *>(
432 X509_get_ext_d2i(m_x509, NID_subject_alt_name, NULL, NULL));
434 while (sk_GENERAL_NAME_num(san) > 0) {
435 if ((namePart = sk_GENERAL_NAME_pop(san)) == NULL)
436 VcoreThrowMsg(Certificate::Exception::OpensslInternalError,
437 "openssl sk_GENERAL_NAME_pop err.");
439 if (GEN_DNS == namePart->type) {
440 char *temp = reinterpret_cast<char *>(ASN1_STRING_data(namePart->d.dNSName));
443 set.insert(std::string());
445 set.insert(std::string(temp));
446 LogDebug("FOUND GEN_DNS: " << temp);
449 LogDebug("FOUND GEN TYPE ID: " << namePart->type);
456 ASN1_TIME *Certificate::getNotAfterTime() const
458 auto timeafter = X509_get_notAfter(m_x509);
461 VcoreThrowMsg(Certificate::Exception::OpensslInternalError,
462 "Reading Not After error.");
464 LogDebug("Get notAfter ASN1_TIME : " <<
465 reinterpret_cast<char *>(timeafter->data));
469 ASN1_TIME *Certificate::getNotBeforeTime() const
471 auto timebefore = X509_get_notBefore(m_x509);
474 VcoreThrowMsg(Certificate::Exception::OpensslInternalError,
475 "Reading Not Before error.");
477 LogDebug("Get notBefore ASN1_TIME : " <<
478 reinterpret_cast<char *>(timebefore->data));
482 time_t Certificate::getNotAfter() const
484 auto time = getNotAfterTime();
487 if (asn1TimeToTimeT(time, &output) == 0)
488 VcoreThrowMsg(Certificate::Exception::OpensslInternalError,
489 "Converting ASN1_time to time_t error.");
494 time_t Certificate::getNotBefore() const
496 auto time = getNotBeforeTime();
499 if (asn1TimeToTimeT(time, &output) == 0)
500 VcoreThrowMsg(Certificate::Exception::OpensslInternalError,
501 "Converting ASN1_time to time_t error.");
506 bool Certificate::isRootCert()
508 // based on that root certificate has the same subject as issuer name
509 return isSignedBy(this->shared_from_this());
512 long Certificate::getVersion() const
514 return X509_get_version(m_x509);
517 std::string Certificate::getSerialNumberString() const
519 ASN1_INTEGER *ai = X509_get_serialNumber(m_x509);
522 VcoreThrowMsg(Certificate::Exception::OpensslInternalError,
523 "Error in X509_get_serialNumber");
525 std::stringstream stream;
526 stream << std::hex << std::setfill('0');
528 if (ai->type == V_ASN1_NEG_INTEGER) {
529 stream << "(Negetive) ";
532 for (int i = 0; i < ai->length; ++i) {
533 stream << std::setw(2) << (int)ai->data[i] << ":";
536 std::string data = stream.str();
539 data.erase(--data.end());
545 std::string Certificate::getKeyUsageString() const
547 // Extensions were defined in RFC 3280
548 const char *usage[] = {
561 ASN1_BIT_STRING *keyUsage = (ASN1_BIT_STRING *)
562 X509_get_ext_d2i(m_x509, NID_key_usage, &crit, &idx);
563 std::stringstream stream;
565 for (int i = 0; i < 9; ++i) {
566 if (ASN1_BIT_STRING_get_bit(keyUsage, i)) {
567 stream << usage[i] << ",";
571 std::string result = stream.str();
573 if (!result.empty()) {
574 result.erase(--result.end());
580 std::string Certificate::getSignatureAlgorithmString() const
582 std::unique_ptr<BIO, std::function<int(BIO *)>>
583 b(BIO_new(BIO_s_mem()), BIO_free);
586 VcoreThrowMsg(Certificate::Exception::OpensslInternalError,
589 if (i2a_ASN1_OBJECT(b.get(), m_x509->cert_info->signature->algorithm) < 0)
590 VcoreThrowMsg(Certificate::Exception::OpensslInternalError,
591 "Error in i2a_ASN1_OBJECT");
594 BIO_get_mem_ptr(b.get(), &bptr);
597 VcoreThrowMsg(Certificate::Exception::OpensslInternalError,
598 "Error in BIO_get_mem_ptr");
600 std::string result(bptr->data, bptr->length);
604 std::string Certificate::getPublicKeyString() const
606 std::unique_ptr<BIO, std::function<int(BIO *)>>
607 b(BIO_new(BIO_s_mem()), BIO_free);
610 VcoreThrowMsg(Certificate::Exception::OpensslInternalError,
613 EVP_PKEY *pkey = X509_get_pubkey(m_x509);
616 VcoreThrowMsg(Certificate::Exception::OpensslInternalError,
617 "Error in X509_get_pubkey");
619 EVP_PKEY_print_public(b.get(), pkey, 16, NULL);
622 BIO_get_mem_ptr(b.get(), &bptr);
625 VcoreThrowMsg(Certificate::Exception::OpensslInternalError,
626 "Error in BIO_get_mem_ptr");
628 std::string result(bptr->data, bptr->length);
632 void Certificate::getPublicKeyDER(unsigned char **pubkey, size_t *len) const
634 if (pubkey == NULL || len == NULL)
635 VcoreThrowMsg(Certificate::Exception::WrongParamError, "Wrong parameter");
637 EVP_PKEY *pkey = X509_get_pubkey(m_x509);
638 unsigned char *_pubkey = NULL;
639 int _len = i2d_PUBKEY(pkey, &_pubkey);
642 if (_pubkey == NULL || _len == 0)
643 VcoreThrowMsg(Certificate::Exception::OpensslInternalError,
644 "Error in i2d_PUBKEY");
647 *len = static_cast<size_t>(_len);
650 std::string Certificate::getPublicKeyAlgoString() const
652 return std::string(static_cast<const char *>(
653 OBJ_nid2ln(OBJ_obj2nid(m_x509->cert_info->key->algor->algorithm))));
656 int Certificate::isCA() const
658 return X509_check_ca(m_x509);
661 std::string Certificate::FingerprintToColonHex(
662 const Certificate::Fingerprint &fingerprint)
664 std::string outString;
667 for (size_t i = 0; i < fingerprint.size(); ++i) {
671 static_cast<unsigned int>(fingerprint[i]));
675 // remove trailing ":"
676 outString.erase(outString.end() - 1);
680 } // namespace ValidationCore