/* * * Copyright (c) 2021-2023 Project CHIP Authors * Copyright (c) 2013-2017 Nest Labs, Inc. * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @file * This file implements utility functions for reading, writing, * parsing, resigning, encoding, and decoding CHIP certificates. * */ #include "chip-cert.h" #include #include #include using namespace chip; using namespace chip::Credentials; using namespace chip::ASN1; using namespace chip::TLV; using namespace chip::Encoding; bool ToolChipDN::SetCertName(X509_NAME * name) const { bool res = true; uint8_t rdnCount = RDNCount(); for (uint8_t i = 0; i < rdnCount; i++) { int attrNID; switch (rdn[i].mAttrOID) { case kOID_AttributeType_CommonName: attrNID = NID_commonName; break; case kOID_AttributeType_Surname: attrNID = NID_surname; break; case kOID_AttributeType_SerialNumber: attrNID = NID_serialNumber; break; case kOID_AttributeType_CountryName: attrNID = NID_countryName; break; case kOID_AttributeType_LocalityName: attrNID = NID_localityName; break; case kOID_AttributeType_StateOrProvinceName: attrNID = NID_stateOrProvinceName; break; case kOID_AttributeType_OrganizationName: attrNID = NID_organizationName; break; case kOID_AttributeType_OrganizationalUnitName: attrNID = NID_organizationalUnitName; break; case kOID_AttributeType_Title: attrNID = NID_title; break; case kOID_AttributeType_Name: attrNID = NID_name; break; case kOID_AttributeType_GivenName: attrNID = NID_givenName; break; case kOID_AttributeType_Initials: attrNID = NID_initials; break; case kOID_AttributeType_GenerationQualifier: attrNID = NID_generationQualifier; break; case kOID_AttributeType_DNQualifier: attrNID = NID_dnQualifier; break; case kOID_AttributeType_Pseudonym: attrNID = NID_pseudonym; break; case kOID_AttributeType_DomainComponent: attrNID = NID_domainComponent; break; case kOID_AttributeType_MatterNodeId: attrNID = gNIDChipNodeId; break; case kOID_AttributeType_MatterFirmwareSigningId: attrNID = gNIDChipFirmwareSigningId; break; case kOID_AttributeType_MatterICACId: attrNID = gNIDChipICAId; break; case kOID_AttributeType_MatterRCACId: attrNID = gNIDChipRootId; break; case kOID_AttributeType_MatterFabricId: attrNID = gNIDChipFabricId; break; case kOID_AttributeType_MatterCASEAuthTag: attrNID = gNIDChipCASEAuthenticatedTag; break; default: ExitNow(res = false); } char chipAttrStr[std::max(kChip64bitAttrUTF8Length, kChip32bitAttrUTF8Length)] = { 0 }; int type = V_ASN1_UTF8STRING; uint8_t * attrStr = reinterpret_cast(chipAttrStr); int attrLen = 0; if (IsChip64bitDNAttr(rdn[i].mAttrOID)) { VerifyOrReturnError(Encoding::Uint64ToHex(rdn[i].mChipVal, chipAttrStr, kChip64bitAttrUTF8Length, Encoding::HexFlags::kUppercase) == CHIP_NO_ERROR, false); attrLen = kChip64bitAttrUTF8Length; } else if (IsChip32bitDNAttr(rdn[i].mAttrOID)) { VerifyOrReturnError(Encoding::Uint32ToHex(static_cast(rdn[i].mChipVal), chipAttrStr, kChip32bitAttrUTF8Length, Encoding::HexFlags::kUppercase) == CHIP_NO_ERROR, false); attrLen = kChip32bitAttrUTF8Length; } else { if (rdn[i].mAttrOID == kOID_AttributeType_DomainComponent) { type = V_ASN1_IA5STRING; } else if (rdn[i].mAttrIsPrintableString) { type = V_ASN1_PRINTABLESTRING; } attrStr = reinterpret_cast(const_cast(rdn[i].mString.data())); attrLen = static_cast(rdn[i].mString.size()); } if (!X509_NAME_add_entry_by_NID(name, attrNID, type, attrStr, attrLen, -1, 0)) { ReportOpenSSLErrorAndExit("X509_NAME_add_entry_by_NID", res = false); } } exit: return res; } bool ToolChipDN::HasAttr(chip::ASN1::OID oid) const { uint8_t rdnCount = RDNCount(); for (uint8_t i = 0; i < rdnCount; i++) { if (oid == rdn[i].mAttrOID) { return true; } } return false; } void ToolChipDN::PrintDN(FILE * file, const char * name) const { uint8_t rdnCount = RDNCount(); char valueStr[128]; const char * certDesc = nullptr; fprintf(file, "%s: ", name); for (uint8_t i = 0; i < rdnCount; i++) { if (IsChip64bitDNAttr(rdn[i].mAttrOID)) { Encoding::Uint64ToHex(rdn[i].mChipVal, valueStr, sizeof(valueStr), Encoding::HexFlags::kUppercaseAndNullTerminate); } else if (IsChip32bitDNAttr(rdn[i].mAttrOID)) { Encoding::Uint32ToHex(static_cast(rdn[i].mChipVal), valueStr, sizeof(valueStr), Encoding::HexFlags::kUppercaseAndNullTerminate); } else { size_t len = rdn[i].mString.size(); if (len > sizeof(valueStr) - 1) { len = sizeof(valueStr) - 1; } memcpy(valueStr, rdn[i].mString.data(), len); valueStr[len] = 0; } fprintf(file, "%s=%s", chip::ASN1::GetOIDName(rdn[i].mAttrOID), valueStr); if (certDesc != nullptr) { fprintf(file, " (%s)", certDesc); } } } namespace { template bool HasRawPrefix(const uint8_t * buffer, size_t len, const uint8_t (&prefix)[N]) { return len >= N && memcmp(buffer, prefix, N) == 0; } bool HasStringPrefix(const uint8_t * buffer, size_t len, const char * prefix) { size_t prefixLen = strlen(prefix); return len >= prefixLen && memcmp(buffer, prefix, prefixLen) == 0; } CertFormat DetectCertFormat(const uint8_t * cert, uint32_t certLen) { static const uint8_t chipRawPrefix[] = { 0x15, 0x30, 0x01 }; static const char chipHexPrefix[] = "153001"; static const char chipB64Prefix[] = "FTAB"; static const uint8_t chipCompactPdcRawPrefix[] = { 0x15, 0x30, 0x09 }; static const char chipCompactPdcHexPrefix[] = "153009"; static const char chipCompactPdcB64Prefix[] = "FTAJ"; static const uint8_t derRawPrefix[] = { 0x30, 0x82 }; static const char derHexPrefix[] = "30820"; static const char pemMarker[] = "-----BEGIN CERTIFICATE-----"; VerifyOrReturnError(cert != nullptr, kCertFormat_Unknown); if (HasRawPrefix(cert, certLen, chipRawPrefix) || HasRawPrefix(cert, certLen, chipCompactPdcRawPrefix)) { return kCertFormat_Chip_Raw; } if (HasStringPrefix(cert, certLen, chipHexPrefix) || HasStringPrefix(cert, certLen, chipCompactPdcHexPrefix)) { return kCertFormat_Chip_Hex; } if (HasStringPrefix(cert, certLen, chipB64Prefix) || HasStringPrefix(cert, certLen, chipCompactPdcB64Prefix)) { return kCertFormat_Chip_Base64; } if (HasRawPrefix(cert, certLen, derRawPrefix)) { return kCertFormat_X509_DER; } if (HasStringPrefix(cert, certLen, derHexPrefix)) { return kCertFormat_X509_Hex; } if (ContainsPEMMarker(pemMarker, cert, certLen)) { return kCertFormat_X509_PEM; } return kCertFormat_Unknown; } bool SetCertSerialNumber(X509 * cert, uint64_t value = kUseRandomSerialNumber) { bool res = true; uint64_t rnd; ASN1_INTEGER * snInt = X509_get_serialNumber(cert); if (value == kUseRandomSerialNumber) { // Generate a random value to be used as the serial number. if (!RAND_bytes(reinterpret_cast(&rnd), sizeof(rnd))) { ReportOpenSSLErrorAndExit("RAND_bytes", res = false); } // Avoid negative numbers. value = rnd & 0x7FFFFFFFFFFFFFFFULL; } // Store the serial number as an ASN1 integer value within the certificate. if (ASN1_INTEGER_set_uint64(snInt, value) == 0) { ReportOpenSSLErrorAndExit("ASN1_INTEGER_set_uint64", res = false); } exit: return res; } bool SetCertTimeField(ASN1_TIME * asn1Time, const struct tm & value) { char timeStr[ASN1UniversalTime::kASN1TimeStringMaxLength + 1]; MutableCharSpan timeSpan(timeStr); ASN1UniversalTime val = { .Year = static_cast((value.tm_year == kX509NoWellDefinedExpirationDateYear) ? kX509NoWellDefinedExpirationDateYear : (value.tm_year + 1900)), .Month = static_cast(value.tm_mon + 1), .Day = static_cast(value.tm_mday), .Hour = static_cast(value.tm_hour), .Minute = static_cast(value.tm_min), .Second = static_cast(value.tm_sec) }; if (val.ExportTo_ASN1_TIME_string(timeSpan) != CHIP_NO_ERROR) { fprintf(stderr, "ExportTo_ASN1_TIME_string() failed\n"); return false; } timeSpan.data()[timeSpan.size()] = '\0'; if (!ASN1_TIME_set_string(asn1Time, timeStr)) { fprintf(stderr, "OpenSSL ASN1_TIME_set_string() failed\n"); return false; } return true; } bool SetValidityTime(X509 * cert, const struct tm & validFrom, uint32_t validDays, CertStructConfig & certConfig) { bool res = true; struct tm validFromLocal; struct tm validTo; time_t validToTime; // Compute the validity end date. // Note that this computation is done in local time, despite the fact that the certificate validity times are // UTC. This is because the standard posix time functions do not make it easy to convert a struct tm containing // UTC to a time_t value without manipulating the TZ environment variable. if (validDays == kCertValidDays_NoWellDefinedExpiration) { validTo.tm_year = kX509NoWellDefinedExpirationDateYear; validTo.tm_mon = kMonthsPerYear - 1; validTo.tm_mday = kMaxDaysPerMonth; validTo.tm_hour = kHoursPerDay - 1; validTo.tm_min = kMinutesPerHour - 1; validTo.tm_sec = kSecondsPerMinute - 1; validTo.tm_isdst = -1; } else { validTo = validFrom; validTo.tm_mday += validDays; validTo.tm_sec -= 1; // Ensure validity period is exactly a multiple of a day. validTo.tm_isdst = -1; validToTime = mktime(&validTo); if (validToTime == static_cast(-1)) { fprintf(stderr, "mktime() failed\n"); ExitNow(res = false); } localtime_r(&validToTime, &validTo); } if (certConfig.IsValidityCorrect()) { validFromLocal = validFrom; } else { // Switch values if error flag is set. validFromLocal = validTo; validTo = validFrom; } // Set the certificate's notBefore date. if (certConfig.IsValidityNotBeforePresent()) { res = SetCertTimeField(X509_get_notBefore(cert), validFromLocal); VerifyTrueOrExit(res); } // Set the certificate's notAfter date. if (certConfig.IsValidityNotAfterPresent()) { res = SetCertTimeField(X509_get_notAfter(cert), validTo); VerifyTrueOrExit(res); } exit: return true; } bool AddExtension(X509 * cert, int extNID, const char * extStr) { bool res = true; std::unique_ptr ex( X509V3_EXT_nconf_nid(nullptr, nullptr, extNID, const_cast(extStr)), &X509_EXTENSION_free); if (!X509_add_ext(cert, ex.get(), -1)) { ReportOpenSSLErrorAndExit("X509_add_ext", res = false); } exit: return res; } bool SetBasicConstraintsExtension(X509 * cert, bool isCA, int pathLen, CertStructConfig & certConfig) { if (!certConfig.IsExtensionBasicPresent()) { return true; } std::string basicConstraintsExt; if (certConfig.IsExtensionBasicCriticalPresent() && certConfig.IsExtensionBasicCritical()) { basicConstraintsExt += "critical"; } if (certConfig.IsExtensionBasicCAPresent()) { if (!basicConstraintsExt.empty()) { basicConstraintsExt += ","; } if ((certConfig.IsExtensionBasicCACorrect() && !isCA) || (!certConfig.IsExtensionBasicCACorrect() && isCA)) { basicConstraintsExt += "CA:FALSE"; } else { basicConstraintsExt += "CA:TRUE"; } } if (pathLen != kPathLength_NotSpecified) { if (!basicConstraintsExt.empty()) { basicConstraintsExt += ","; } basicConstraintsExt.append("pathlen:" + std::to_string(pathLen)); } return AddExtension(cert, NID_basic_constraints, basicConstraintsExt.c_str()); } bool SetKeyUsageExtension(X509 * cert, bool isCA, CertStructConfig & certConfig) { if (!certConfig.IsExtensionKeyUsagePresent()) { return true; } std::string keyUsageExt; if (certConfig.IsExtensionKeyUsageCriticalPresent() && certConfig.IsExtensionKeyUsageCritical()) { keyUsageExt += "critical"; } if ((certConfig.IsExtensionKeyUsageDigitalSigCorrect() && !isCA) || (!certConfig.IsExtensionKeyUsageDigitalSigCorrect() && isCA)) { if (!keyUsageExt.empty()) { keyUsageExt += ","; } keyUsageExt += "digitalSignature"; } if ((certConfig.IsExtensionKeyUsageKeyCertSignCorrect() && isCA) || (!certConfig.IsExtensionKeyUsageKeyCertSignCorrect() && !isCA)) { if (!keyUsageExt.empty()) { keyUsageExt += ","; } keyUsageExt += "keyCertSign"; } if ((certConfig.IsExtensionKeyUsageCRLSignCorrect() && isCA) || (!certConfig.IsExtensionKeyUsageCRLSignCorrect() && !isCA)) { if (!keyUsageExt.empty()) { keyUsageExt += ","; } keyUsageExt += "cRLSign"; } // In test mode only: just add an extra extension flag to prevent empty extantion. if (certConfig.IsErrorTestCaseEnabled() && (keyUsageExt.empty() || (keyUsageExt.compare("critical") == 0))) { if (!keyUsageExt.empty()) { keyUsageExt += ","; } keyUsageExt += "keyEncipherment"; } return AddExtension(cert, NID_key_usage, keyUsageExt.c_str()); } /** The key identifier field is derived from the public key using method (1) per RFC5280 (section 4.2.1.2): * * (1) The keyIdentifier is composed of the 160-bit SHA-1 hash of the * value of the BIT STRING subjectPublicKey (excluding the tag, * length, and number of unused bits). */ bool AddSubjectKeyId(X509 * cert, bool isSKIDLengthValid) { bool res = true; ASN1_BIT_STRING * pk = X509_get0_pubkey_bitstr(cert); unsigned char pkHash[EVP_MAX_MD_SIZE]; unsigned int pkHashLen; std::unique_ptr pkHashOS(ASN1_STRING_type_new(V_ASN1_OCTET_STRING), &ASN1_STRING_free); if (!EVP_Digest(pk->data, static_cast(pk->length), pkHash, &pkHashLen, EVP_sha1(), nullptr)) { ReportOpenSSLErrorAndExit("EVP_Digest", res = false); } if (pkHashLen != kKeyIdentifierLength) { fprintf(stderr, "Unexpected hash length returned from EVP_Digest()\n"); ExitNow(res = false); } if (!isSKIDLengthValid) { pkHashLen--; } if (!ASN1_STRING_set(pkHashOS.get(), pkHash, static_cast(pkHashLen))) { ReportOpenSSLErrorAndExit("ASN1_STRING_set", res = false); } if (!X509_add1_ext_i2d(cert, NID_subject_key_identifier, pkHashOS.get(), 0, X509V3_ADD_APPEND)) { ReportOpenSSLErrorAndExit("X509_add1_ext_i2d", res = false); } exit: return res; } bool AddAuthorityKeyId(X509 * cert, X509 * caCert, bool isAKIDLengthValid) { bool res = true; int isCritical; int index = 0; std::unique_ptr akid(AUTHORITY_KEYID_new(), &AUTHORITY_KEYID_free); akid->keyid = reinterpret_cast(X509_get_ext_d2i(caCert, NID_subject_key_identifier, &isCritical, &index)); if (akid->keyid == nullptr) { ReportOpenSSLErrorAndExit("X509_get_ext_d2i", res = false); } if (!isAKIDLengthValid) { akid->keyid->length = 19; } if (!X509_add1_ext_i2d(cert, NID_authority_key_identifier, akid.get(), 0, X509V3_ADD_APPEND)) { ReportOpenSSLErrorAndExit("X509_add1_ext_i2d", res = false); } exit: return res; } } // namespace bool ReadCert(const char * fileNameOrStr, std::unique_ptr & cert) { CertFormat origCertFmt; return ReadCert(fileNameOrStr, cert, origCertFmt); } bool ReadCert(const char * fileNameOrStr, std::unique_ptr & cert, CertFormat & certFmt) { bool res = true; uint32_t certLen = 0; std::unique_ptr certBuf; // If fileNameOrStr is a file name if (access(fileNameOrStr, R_OK) == 0) { res = ReadFileIntoMem(fileNameOrStr, nullptr, certLen); VerifyTrueOrExit(res); certBuf = std::unique_ptr(new uint8_t[certLen]); res = ReadFileIntoMem(fileNameOrStr, certBuf.get(), certLen); VerifyTrueOrExit(res); certFmt = DetectCertFormat(certBuf.get(), certLen); if (certFmt == kCertFormat_Unknown) { fprintf(stderr, "Unrecognized Cert Format in File: %s\n", fileNameOrStr); return false; } } // Otherwise, treat fileNameOrStr as a pointer to the certificate string else { certLen = static_cast(strlen(fileNameOrStr)); certFmt = DetectCertFormat(reinterpret_cast(fileNameOrStr), certLen); if (certFmt == kCertFormat_Unknown) { fprintf(stderr, "Unrecognized Cert Format in the Input Argument: %s\n", fileNameOrStr); return false; } certBuf = std::unique_ptr(new uint8_t[certLen]); memcpy(certBuf.get(), fileNameOrStr, certLen); } if ((certFmt == kCertFormat_X509_Hex) || (certFmt == kCertFormat_Chip_Hex)) { size_t len = chip::Encoding::HexToBytes(Uint8::to_char(certBuf.get()), certLen, certBuf.get(), certLen); VerifyOrReturnError(CanCastTo(2 * len), false); VerifyOrReturnError(2 * len == certLen, false); certLen = static_cast(len); } if (certFmt == kCertFormat_X509_PEM) { VerifyOrReturnError(chip::CanCastTo(certLen), false); std::unique_ptr certBIO( BIO_new_mem_buf(static_cast(certBuf.get()), static_cast(certLen)), &BIO_free_all); cert.reset(PEM_read_bio_X509(certBIO.get(), nullptr, nullptr, nullptr)); if (cert.get() == nullptr) { ReportOpenSSLErrorAndExit("PEM_read_bio_X509", res = false); } } else if ((certFmt == kCertFormat_X509_DER) || (certFmt == kCertFormat_X509_Hex)) { VerifyOrReturnError(chip::CanCastTo(certLen), false); const uint8_t * outCert = certBuf.get(); cert.reset(d2i_X509(nullptr, &outCert, static_cast(certLen))); if (cert.get() == nullptr) { ReportOpenSSLErrorAndExit("d2i_X509", res = false); } } // Otherwise, it is either CHIP TLV in raw, Base64, or hex encoded format. else { if (certFmt == kCertFormat_Chip_Base64) { res = Base64Decode(certBuf.get(), certLen, certBuf.get(), certLen, certLen); VerifyTrueOrExit(res); } std::unique_ptr x509CertBuf(new uint8_t[kMaxDERCertLength]); MutableByteSpan x509Cert(x509CertBuf.get(), kMaxDERCertLength); CHIP_ERROR err = ConvertChipCertToX509Cert(ByteSpan(certBuf.get(), certLen), x509Cert); if (err != CHIP_NO_ERROR) { fprintf(stderr, "Error converting certificate: %s\n", chip::ErrorStr(err)); ExitNow(res = false); } const uint8_t * outCert = x509Cert.data(); VerifyOrReturnError(chip::CanCastTo(x509Cert.size()), false); cert.reset(d2i_X509(nullptr, &outCert, static_cast(x509Cert.size()))); if (cert.get() == nullptr) { ReportOpenSSLErrorAndExit("d2i_X509", res = false); } } exit: return res; } bool ReadCertDER(const char * fileNameOrStr, MutableByteSpan & cert) { bool res = true; std::unique_ptr certX509(nullptr, &X509_free); VerifyOrReturnError(ReadCert(fileNameOrStr, certX509), false); uint8_t * certPtr = cert.data(); int certLen = i2d_X509(certX509.get(), &certPtr); if (certLen < 0) { ReportOpenSSLErrorAndExit("i2d_X509", res = false); } VerifyOrReturnError(chip::CanCastTo(certLen), false); cert.reduce_size(static_cast(certLen)); exit: return res; } bool X509ToChipCert(X509 * cert, MutableByteSpan & chipCert) { bool res = true; CHIP_ERROR err; uint8_t * derCert = nullptr; int derCertLen; derCertLen = i2d_X509(cert, &derCert); if (derCertLen < 0) { ReportOpenSSLErrorAndExit("i2d_X509", res = false); } VerifyOrReturnError(chip::CanCastTo(derCertLen), false); err = ConvertX509CertToChipCert(ByteSpan(derCert, static_cast(derCertLen)), chipCert); if (err != CHIP_NO_ERROR) { fprintf(stderr, "ConvertX509CertToChipCert() failed\n%s\n", chip::ErrorStr(err)); ExitNow(res = false); } exit: OPENSSL_free(derCert); return res; } bool LoadChipCert(const char * fileNameOrStr, bool isTrused, ChipCertificateSet & certSet, MutableByteSpan & chipCert) { bool res = true; CHIP_ERROR err; BitFlags decodeFlags; std::unique_ptr cert(nullptr, &X509_free); res = ReadCert(fileNameOrStr, cert); VerifyTrueOrExit(res); res = X509ToChipCert(cert.get(), chipCert); VerifyTrueOrExit(res); if (isTrused) { decodeFlags.Set(CertDecodeFlags::kIsTrustAnchor); } else { decodeFlags.Set(CertDecodeFlags::kGenerateTBSHash); } err = certSet.LoadCert(chipCert, decodeFlags); if (err != CHIP_NO_ERROR) { fprintf(stderr, "Error reading %s\n%s\n", fileNameOrStr, chip::ErrorStr(err)); ExitNow(res = false); } exit: return res; } bool WriteCert(const char * fileName, X509 * cert, CertFormat certFmt) { bool res = true; FILE * file = nullptr; uint8_t * derCert = nullptr; VerifyOrReturnError(cert != nullptr, false); VerifyOrReturnError(certFmt != kCertFormat_Unknown, false); if (IsChipCertFormat(certFmt)) { uint8_t chipCertBuf[kMaxCHIPCertLength]; MutableByteSpan chipCert(chipCertBuf); VerifyOrReturnError(X509ToChipCert(cert, chipCert), false); return WriteChipCert(fileName, chipCert, certFmt); } if (certFmt == kCertFormat_X509_Hex) { int derCertLen = i2d_X509(cert, &derCert); if (derCertLen < 0) { ReportOpenSSLErrorAndExit("i2d_X509", res = false); } VerifyOrExit(CanCastTo(derCertLen), res = false); VerifyOrExit(WriteDataIntoFile(fileName, derCert, static_cast(derCertLen), kDataFormat_Hex), res = false); ExitNow(res = true); } VerifyOrExit(OpenFile(fileName, file, true), res = false); if (certFmt == kCertFormat_X509_PEM) { if (PEM_write_X509(file, cert) == 0) { ReportOpenSSLErrorAndExit("PEM_write_X509", res = false); } } else if (certFmt == kCertFormat_X509_DER) { if (i2d_X509_fp(file, cert) == 0) { ReportOpenSSLErrorAndExit("i2d_X509_fp", res = false); } } else { fprintf(stderr, "Unsupported certificate format\n"); ExitNow(res = false); } exit: OPENSSL_free(derCert); CloseFile(file); return res; } bool WriteChipCert(const char * fileName, const ByteSpan & chipCert, CertFormat certFmt) { DataFormat dataFormat = kDataFormat_Unknown; VerifyOrReturnError(IsChipCertFormat(certFmt), false); if (certFmt == kCertFormat_Chip_Raw) dataFormat = kDataFormat_Raw; else if (certFmt == kCertFormat_Chip_Base64) dataFormat = kDataFormat_Base64; else dataFormat = kDataFormat_Hex; return WriteDataIntoFile(fileName, chipCert.data(), static_cast(chipCert.size()), dataFormat); } bool MakeCert(CertType certType, const ToolChipDN * subjectDN, X509 * caCert, EVP_PKEY * caKey, const struct tm & validFrom, uint32_t validDays, int pathLen, const FutureExtensionWithNID * futureExts, uint8_t futureExtsCount, X509 * newCert, EVP_PKEY * newKey, CertStructConfig & certConfig) { bool res = true; bool isCA = (certType == CertType::kRoot || certType == CertType::kICA); VerifyOrExit(subjectDN != nullptr, res = false); VerifyOrExit(caCert != nullptr, res = false); VerifyOrExit(caKey != nullptr, res = false); VerifyOrExit(newCert != nullptr, res = false); VerifyOrExit(newKey != nullptr, res = false); // Set the certificate version (must be 2, a.k.a. v3). if (!X509_set_version(newCert, certConfig.GetCertVersion())) { ReportOpenSSLErrorAndExit("X509_set_version", res = false); } // Generate a serial number for the cert. if (certConfig.IsSerialNumberPresent()) { res = SetCertSerialNumber(newCert, (certType == CertType::kNetworkIdentity ? 1 : kUseRandomSerialNumber)); VerifyTrueOrExit(res); } // Set the issuer name for the certificate. In the case of a self-signed cert, this will be // the new cert's subject name. if (certConfig.IsIssuerPresent()) { if (certType == CertType::kRoot) { res = subjectDN->SetCertIssuerDN(newCert); VerifyTrueOrExit(res); } else { if (!X509_set_issuer_name(newCert, X509_get_subject_name(caCert))) { ReportOpenSSLErrorAndExit("X509_set_issuer_name", res = false); } } } // Set the certificate validity time. res = SetValidityTime(newCert, validFrom, validDays, certConfig); VerifyTrueOrExit(res); // Set the certificate's public key. if (!X509_set_pubkey(newCert, newKey)) { ReportOpenSSLErrorAndExit("X509_set_pubkey", res = false); } // Injuct error into public key value. if (certConfig.IsPublicKeyError()) { ASN1_BIT_STRING * pk = X509_get0_pubkey_bitstr(newCert); pk->data[CertStructConfig::kPublicKeyErrorByte] ^= 0xFF; } // Set certificate subject DN. if (certConfig.IsSubjectPresent()) { res = subjectDN->SetCertSubjectDN(newCert); VerifyTrueOrExit(res); } // Add basic constraints certificate extensions. if (certConfig.IsExtensionBasicPathLenPresent() || !certConfig.IsExtensionBasicCAPresent()) { pathLen = certConfig.GetExtensionBasicPathLenValue(certType); } res = SetBasicConstraintsExtension(newCert, isCA, pathLen, certConfig); VerifyTrueOrExit(res); // Add key usage certificate extensions. res = SetKeyUsageExtension(newCert, isCA, certConfig); VerifyTrueOrExit(res); // Add extended key usage certificate extensions. if (!certConfig.IsExtensionExtendedKeyUsageMissing()) { if (certType == CertType::kNode) { res = AddExtension(newCert, NID_ext_key_usage, "critical,clientAuth,serverAuth"); VerifyTrueOrExit(res); } else if (certType == CertType::kFirmwareSigning) { res = AddExtension(newCert, NID_ext_key_usage, "critical,codeSigning"); VerifyTrueOrExit(res); } else if (certType == CertType::kNetworkIdentity) { res = AddExtension(newCert, NID_ext_key_usage, "critical,clientAuth,serverAuth"); VerifyTrueOrExit(res); } } // Add a subject key id extension for the certificate. if (certType != CertType::kNetworkIdentity && certConfig.IsExtensionSKIDPresent()) { res = AddSubjectKeyId(newCert, certConfig.IsExtensionSKIDLengthValid()); VerifyTrueOrExit(res); } // Add the authority key id extension from the signing certificate. For self-signed cert's this will // be the same as new cert's subject key id extension. if (certType != CertType::kNetworkIdentity && certConfig.IsExtensionAKIDPresent()) { if ((certType == CertType::kRoot) && !certConfig.IsExtensionSKIDPresent()) { res = AddSubjectKeyId(newCert, certConfig.IsExtensionSKIDLengthValid()); VerifyTrueOrExit(res); res = AddAuthorityKeyId(newCert, newCert, certConfig.IsExtensionAKIDLengthValid()); VerifyTrueOrExit(res); // Remove that temporary added subject key id int authKeyIdExtLoc = X509_get_ext_by_NID(newCert, NID_subject_key_identifier, -1); if (authKeyIdExtLoc != -1) { if (X509_delete_ext(newCert, authKeyIdExtLoc) == nullptr) { ReportOpenSSLErrorAndExit("X509_delete_ext", res = false); } } } else { res = AddAuthorityKeyId(newCert, caCert, certConfig.IsExtensionAKIDLengthValid()); VerifyTrueOrExit(res); } } for (uint8_t i = 0; i < futureExtsCount; i++) { res = AddExtension(newCert, futureExts[i].nid, futureExts[i].info); VerifyTrueOrExit(res); } // Sign the new certificate. if (!X509_sign(newCert, caKey, certConfig.GetSignatureAlgorithmDER())) { ReportOpenSSLErrorAndExit("X509_sign", res = false); } // Injuct error into signature value. if (certConfig.IsSignatureError()) { const ASN1_BIT_STRING * sig = nullptr; X509_get0_signature(&sig, nullptr, newCert); sig->data[20] ^= 0xFF; } exit: return res; } CHIP_ERROR MakeCertTLV(CertType certType, const ToolChipDN * subjectDN, X509 * caCert, EVP_PKEY * caKey, const struct tm & validFrom, uint32_t validDays, int pathLen, const FutureExtensionWithNID * futureExts, uint8_t futureExtsCount, X509 * x509Cert, EVP_PKEY * newKey, CertStructConfig & certConfig, MutableByteSpan & chipCert) { TLVWriter writer; TLVType containerType; TLVType containerType2; TLVType containerType3; uint8_t subjectPubkey[chip::Crypto::CHIP_CRYPTO_PUBLIC_KEY_SIZE_BYTES] = { 0 }; uint8_t issuerPubkey[chip::Crypto::CHIP_CRYPTO_PUBLIC_KEY_SIZE_BYTES] = { 0 }; uint8_t keyid[chip::Crypto::kSHA1_Hash_Length] = { 0 }; bool isCA; VerifyOrReturnError(subjectDN != nullptr, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(caCert != nullptr, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(caKey != nullptr, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(x509Cert != nullptr, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(newKey != nullptr, CHIP_ERROR_INVALID_ARGUMENT); isCA = (certType == CertType::kICA || certType == CertType::kRoot); // If error testing is enabled, let field inclusion be controlled that way, // otherwise use compact identity format for Network (Client) Identities. bool useCompactIdentityFormat = (!certConfig.IsErrorTestCaseEnabled() && certType == CertType::kNetworkIdentity); uint8_t * p = subjectPubkey; VerifyOrReturnError(i2o_ECPublicKey(EVP_PKEY_get0_EC_KEY(newKey), &p) == chip::Crypto::CHIP_CRYPTO_PUBLIC_KEY_SIZE_BYTES, CHIP_ERROR_INVALID_ARGUMENT); p = issuerPubkey; VerifyOrReturnError(i2o_ECPublicKey(EVP_PKEY_get0_EC_KEY(caKey), &p) == chip::Crypto::CHIP_CRYPTO_PUBLIC_KEY_SIZE_BYTES, CHIP_ERROR_INVALID_ARGUMENT); writer.Init(chipCert); ReturnErrorOnFailure(writer.StartContainer(AnonymousTag(), kTLVType_Structure, containerType)); if (!useCompactIdentityFormat) { // serial number if (certConfig.IsSerialNumberPresent()) { ASN1_INTEGER * asn1Integer = X509_get_serialNumber(x509Cert); uint64_t serialNumber; uint8_t serialNumberArray[sizeof(uint64_t)]; VerifyOrReturnError(1 == ASN1_INTEGER_get_uint64(&serialNumber, asn1Integer), CHIP_ERROR_INVALID_ARGUMENT); Encoding::BigEndian::Put64(serialNumberArray, serialNumber); ReturnErrorOnFailure(writer.PutBytes(ContextTag(kTag_SerialNumber), serialNumberArray, sizeof(serialNumberArray))); } // signature algorithm ReturnErrorOnFailure(writer.Put(ContextTag(kTag_SignatureAlgorithm), certConfig.GetSignatureAlgorithmTLVEnum())); // issuer Name if (certConfig.IsIssuerPresent()) { if (certType == CertType::kRoot) { ReturnErrorOnFailure(subjectDN->EncodeToTLV(writer, ContextTag(kTag_Issuer))); } else { uint8_t caChipCertBuf[kMaxCHIPCertLength]; MutableByteSpan caChipCert(caChipCertBuf); VerifyOrReturnError(true == X509ToChipCert(caCert, caChipCert), CHIP_ERROR_INVALID_ARGUMENT); ChipDN issuerDN; ReturnErrorOnFailure(ExtractSubjectDNFromChipCert(caChipCert, issuerDN)); ReturnErrorOnFailure(issuerDN.EncodeToTLV(writer, ContextTag(kTag_Issuer))); } } // validity uint32_t validFromChipEpoch; uint32_t validToChipEpoch; VerifyOrReturnError( true == CalendarToChipEpochTime(static_cast(validFrom.tm_year + 1900), static_cast(validFrom.tm_mon + 1), static_cast(validFrom.tm_mday), static_cast(validFrom.tm_hour), static_cast(validFrom.tm_min), static_cast(validFrom.tm_sec), validFromChipEpoch), CHIP_ERROR_INVALID_ARGUMENT); if (validDays == kCertValidDays_NoWellDefinedExpiration) { validToChipEpoch = 0; } else { VerifyOrReturnError(CanCastTo(validFromChipEpoch + validDays * kSecondsPerDay - 1), CHIP_ERROR_INVALID_ARGUMENT); validToChipEpoch = validFromChipEpoch + validDays * kSecondsPerDay - 1; } if (!certConfig.IsValidityCorrect()) { uint32_t validTemp = validFromChipEpoch; validFromChipEpoch = validToChipEpoch; validToChipEpoch = validTemp; } if (certConfig.IsValidityNotBeforePresent()) { ReturnErrorOnFailure(writer.Put(ContextTag(kTag_NotBefore), validFromChipEpoch)); } if (certConfig.IsValidityNotAfterPresent()) { ReturnErrorOnFailure(writer.Put(ContextTag(kTag_NotAfter), validToChipEpoch)); } // subject Name if (certConfig.IsSubjectPresent()) { ReturnErrorOnFailure(subjectDN->EncodeToTLV(writer, ContextTag(kTag_Subject))); } // public key algorithm ReturnErrorOnFailure(writer.Put(ContextTag(kTag_PublicKeyAlgorithm), GetOIDEnum(kOID_PubKeyAlgo_ECPublicKey))); // public key curve Id uint8_t ecCurveEnum = certConfig.IsSigCurveWrong() ? 0x02 : GetOIDEnum(kOID_EllipticCurve_prime256v1); ReturnErrorOnFailure(writer.Put(ContextTag(kTag_EllipticCurveIdentifier), ecCurveEnum)); } // public key if (certConfig.IsPublicKeyError()) { subjectPubkey[CertStructConfig::kPublicKeyErrorByte] ^= 0xFF; } ReturnErrorOnFailure( writer.PutBytes(ContextTag(kTag_EllipticCurvePublicKey), subjectPubkey, chip::Crypto::CHIP_CRYPTO_PUBLIC_KEY_SIZE_BYTES)); // extensions if (!useCompactIdentityFormat) { ReturnErrorOnFailure(writer.StartContainer(ContextTag(kTag_Extensions), kTLVType_List, containerType2)); { if (isCA) { // basic constraints if (certConfig.IsExtensionBasicPresent()) { ReturnErrorOnFailure( writer.StartContainer(ContextTag(kTag_BasicConstraints), kTLVType_Structure, containerType3)); if (certConfig.IsExtensionBasicCAPresent()) { ReturnErrorOnFailure(writer.PutBoolean(ContextTag(kTag_BasicConstraints_IsCA), certConfig.IsExtensionBasicCACorrect() ? isCA : !isCA)); } if (pathLen != kPathLength_NotSpecified) { ReturnErrorOnFailure( writer.Put(ContextTag(kTag_BasicConstraints_PathLenConstraint), static_cast(pathLen))); } ReturnErrorOnFailure(writer.EndContainer(containerType3)); } // key usage if (certConfig.IsExtensionKeyUsagePresent()) { BitFlags keyUsage; if (!certConfig.IsExtensionKeyUsageDigitalSigCorrect()) { keyUsage.Set(KeyUsageFlags::kDigitalSignature); } if (certConfig.IsExtensionKeyUsageKeyCertSignCorrect()) { keyUsage.Set(KeyUsageFlags::kKeyCertSign); } if (certConfig.IsExtensionKeyUsageCRLSignCorrect()) { keyUsage.Set(KeyUsageFlags::kCRLSign); } ReturnErrorOnFailure(writer.Put(ContextTag(kTag_KeyUsage), keyUsage.Raw())); } } else { // basic constraints if (certConfig.IsExtensionBasicPresent()) { ReturnErrorOnFailure( writer.StartContainer(ContextTag(kTag_BasicConstraints), kTLVType_Structure, containerType3)); if (certConfig.IsExtensionBasicCAPresent()) { ReturnErrorOnFailure(writer.PutBoolean(ContextTag(kTag_BasicConstraints_IsCA), certConfig.IsExtensionBasicCACorrect() ? isCA : !isCA)); } ReturnErrorOnFailure(writer.EndContainer(containerType3)); } // key usage if (certConfig.IsExtensionKeyUsagePresent()) { BitFlags keyUsage; if (certConfig.IsExtensionKeyUsageDigitalSigCorrect()) { keyUsage.Set(KeyUsageFlags::kDigitalSignature); } if (!certConfig.IsExtensionKeyUsageKeyCertSignCorrect()) { keyUsage.Set(KeyUsageFlags::kKeyCertSign); } if (!certConfig.IsExtensionKeyUsageCRLSignCorrect()) { keyUsage.Set(KeyUsageFlags::kCRLSign); } ReturnErrorOnFailure(writer.Put(ContextTag(kTag_KeyUsage), keyUsage)); } // extended key usage if (!certConfig.IsExtensionExtendedKeyUsageMissing() && (certType == CertType::kNode)) { ReturnErrorOnFailure(writer.StartContainer(ContextTag(kTag_ExtendedKeyUsage), kTLVType_Array, containerType3)); if (certType == CertType::kNode) { ReturnErrorOnFailure(writer.Put(AnonymousTag(), GetOIDEnum(kOID_KeyPurpose_ClientAuth))); ReturnErrorOnFailure(writer.Put(AnonymousTag(), GetOIDEnum(kOID_KeyPurpose_ServerAuth))); } else if (certType == CertType::kFirmwareSigning) { ReturnErrorOnFailure(writer.Put(AnonymousTag(), GetOIDEnum(kOID_KeyPurpose_CodeSigning))); } ReturnErrorOnFailure(writer.EndContainer(containerType3)); } } // subject key identifier if (certConfig.IsExtensionSKIDPresent()) { ReturnErrorOnFailure(Crypto::Hash_SHA1(subjectPubkey, sizeof(subjectPubkey), keyid)); size_t keyIdLen = certConfig.IsExtensionSKIDLengthValid() ? sizeof(keyid) : sizeof(keyid) - 1; ReturnErrorOnFailure(writer.Put(ContextTag(kTag_SubjectKeyIdentifier), ByteSpan(keyid, keyIdLen))); } // authority key identifier if (certConfig.IsExtensionAKIDPresent()) { ReturnErrorOnFailure(Crypto::Hash_SHA1(issuerPubkey, sizeof(issuerPubkey), keyid)); size_t keyIdLen = certConfig.IsExtensionAKIDLengthValid() ? sizeof(keyid) : sizeof(keyid) - 1; ReturnErrorOnFailure(writer.Put(ContextTag(kTag_AuthorityKeyIdentifier), ByteSpan(keyid, keyIdLen))); } for (uint8_t i = 0; i < futureExtsCount; i++) { ReturnErrorOnFailure( writer.Put(ContextTag(kTag_FutureExtension), ByteSpan(reinterpret_cast(futureExts[i].info), strlen(futureExts[i].info)))); } } ReturnErrorOnFailure(writer.EndContainer(containerType2)); } // signature const ASN1_BIT_STRING * asn1Signature = nullptr; X509_get0_signature(&asn1Signature, nullptr, x509Cert); uint8_t signatureRawBuf[chip::Crypto::kP256_ECDSA_Signature_Length_Raw]; MutableByteSpan signatureRaw(signatureRawBuf); ReturnErrorOnFailure(chip::Crypto::EcdsaAsn1SignatureToRaw( chip::Crypto::kP256_FE_Length, ByteSpan(asn1Signature->data, static_cast(asn1Signature->length)), signatureRaw)); ReturnErrorOnFailure(writer.Put(ContextTag(kTag_ECDSASignature), signatureRaw)); ReturnErrorOnFailure(writer.EndContainer(containerType)); ReturnErrorOnFailure(writer.Finalize()); chipCert.reduce_size(writer.GetLengthWritten()); return CHIP_NO_ERROR; } bool ResignCert(X509 * cert, X509 * caCert, EVP_PKEY * caKey) { bool res = true; int authKeyIdExtLoc = -1; res = SetCertSerialNumber(cert); VerifyTrueOrExit(res); if (!X509_set_issuer_name(cert, X509_get_subject_name(caCert))) { ReportOpenSSLErrorAndExit("X509_set_issuer_name", res = false); } // Remove any existing authority key id authKeyIdExtLoc = X509_get_ext_by_NID(cert, NID_authority_key_identifier, -1); if (authKeyIdExtLoc != -1) { if (X509_delete_ext(cert, authKeyIdExtLoc) == nullptr) { ReportOpenSSLErrorAndExit("X509_delete_ext", res = false); } } res = AddAuthorityKeyId(cert, caCert, true); VerifyTrueOrExit(res); if (!X509_sign(cert, caKey, EVP_sha256())) { ReportOpenSSLErrorAndExit("X509_sign", res = false); } exit: return res; } bool MakeAttCert(AttCertType attCertType, const char * subjectCN, uint16_t subjectVID, uint16_t subjectPID, bool encodeVIDandPIDasCN, X509 * caCert, EVP_PKEY * caKey, const struct tm & validFrom, uint32_t validDays, X509 * newCert, EVP_PKEY * newKey, CertStructConfig & certConfig, X509_EXTENSION * cdpExt) { bool res = true; uint16_t vid = certConfig.IsSubjectVIDMismatch() ? static_cast(subjectVID + 1) : subjectVID; uint16_t pid = certConfig.IsSubjectPIDMismatch() ? static_cast(subjectPID + 1) : subjectPID; bool isCA = (attCertType != kAttCertType_DAC); VerifyOrReturnError(subjectCN != nullptr, false); VerifyOrReturnError(caCert != nullptr, false); VerifyOrReturnError(caKey != nullptr, false); VerifyOrReturnError(newCert != nullptr, false); VerifyOrReturnError(newKey != nullptr, false); if (!X509_set_version(newCert, certConfig.GetCertVersion())) { ReportOpenSSLErrorAndExit("X509_set_version", res = false); } // Generate a serial number for the cert. res = SetCertSerialNumber(newCert); VerifyTrueOrExit(res); // Set the certificate validity time. res = SetValidityTime(newCert, validFrom, validDays, certConfig); VerifyTrueOrExit(res); // Set the certificate's public key. if (!X509_set_pubkey(newCert, newKey)) { ReportOpenSSLErrorAndExit("X509_set_pubkey", res = false); } // Encode Common Name (CN) Attribute. { char cnAttrStr[chip::Crypto::kMax_CommonNameAttr_Length]; size_t cnAttrStrLen = 0; if (subjectCN != nullptr) { VerifyOrReturnError(strlen(subjectCN) <= sizeof(cnAttrStr), false); memcpy(cnAttrStr, subjectCN, strlen(subjectCN)); cnAttrStrLen += strlen(subjectCN); } if (encodeVIDandPIDasCN) { if (subjectVID != VendorId::NotSpecified) { // Add space to separate from the previous string. if (cnAttrStrLen > 0) { VerifyOrReturnError((cnAttrStrLen + 1) <= sizeof(cnAttrStr), false); cnAttrStr[cnAttrStrLen] = ' '; cnAttrStrLen++; } VerifyOrReturnError((cnAttrStrLen + strlen(chip::Crypto::kVIDPrefixForCNEncoding) + chip::Crypto::kVIDandPIDHexLength) <= sizeof(cnAttrStr), false); memcpy(&cnAttrStr[cnAttrStrLen], chip::Crypto::kVIDPrefixForCNEncoding, strlen(chip::Crypto::kVIDPrefixForCNEncoding)); cnAttrStrLen += strlen(chip::Crypto::kVIDPrefixForCNEncoding); VerifyOrReturnError(Encoding::Uint16ToHex(vid, &cnAttrStr[cnAttrStrLen], chip::Crypto::kVIDandPIDHexLength, Encoding::HexFlags::kUppercase) == CHIP_NO_ERROR, false); cnAttrStrLen += chip::Crypto::kVIDandPIDHexLength; } if (subjectPID != 0) { // Add space to separate from the previous string. if (cnAttrStrLen > 0) { VerifyOrReturnError((cnAttrStrLen + 1) <= sizeof(cnAttrStr), false); cnAttrStr[cnAttrStrLen++] = ' '; } VerifyOrReturnError((cnAttrStrLen + strlen(chip::Crypto::kPIDPrefixForCNEncoding) + chip::Crypto::kVIDandPIDHexLength) <= sizeof(cnAttrStr), false); memcpy(&cnAttrStr[cnAttrStrLen], chip::Crypto::kPIDPrefixForCNEncoding, strlen(chip::Crypto::kPIDPrefixForCNEncoding)); cnAttrStrLen += strlen(chip::Crypto::kPIDPrefixForCNEncoding); VerifyOrReturnError(Encoding::Uint16ToHex(pid, &cnAttrStr[cnAttrStrLen], chip::Crypto::kVIDandPIDHexLength, Encoding::HexFlags::kUppercase) == CHIP_NO_ERROR, false); cnAttrStrLen += chip::Crypto::kVIDandPIDHexLength; } } // Add common name attribute to the certificate subject DN. if (!X509_NAME_add_entry_by_NID(X509_get_subject_name(newCert), NID_commonName, MBSTRING_UTF8, reinterpret_cast(cnAttrStr), static_cast(cnAttrStrLen), -1, 0)) { ReportOpenSSLErrorAndExit("X509_NAME_add_entry_by_NID", res = false); } } if (!encodeVIDandPIDasCN) { // Add VID attribute to the certificate subject DN. if (subjectVID != VendorId::NotSpecified) { char chipAttrStr[chip::Crypto::kVIDandPIDHexLength]; VerifyOrReturnError(Encoding::Uint16ToHex(vid, chipAttrStr, chip::Crypto::kVIDandPIDHexLength, Encoding::HexFlags::kUppercase) == CHIP_NO_ERROR, false); if (!X509_NAME_add_entry_by_NID(X509_get_subject_name(newCert), gNIDChipAttAttrVID, MBSTRING_UTF8, reinterpret_cast(chipAttrStr), sizeof(chipAttrStr), -1, 0)) { ReportOpenSSLErrorAndExit("X509_NAME_add_entry_by_NID", res = false); } } // Add PID attribute to the certificate subject DN. if (subjectPID != 0) { char chipAttrStr[chip::Crypto::kVIDandPIDHexLength]; VerifyOrReturnError(Encoding::Uint16ToHex(pid, chipAttrStr, chip::Crypto::kVIDandPIDHexLength, Encoding::HexFlags::kUppercase) == CHIP_NO_ERROR, false); if (!X509_NAME_add_entry_by_NID(X509_get_subject_name(newCert), gNIDChipAttAttrPID, MBSTRING_UTF8, reinterpret_cast(chipAttrStr), sizeof(chipAttrStr), -1, 0)) { ReportOpenSSLErrorAndExit("X509_NAME_add_entry_by_NID", res = false); } } } // Set the issuer name for the certificate. In the case of a self-signed cert, this will be // the new cert's subject name. if (!X509_set_issuer_name(newCert, X509_get_subject_name(caCert))) { ReportOpenSSLErrorAndExit("X509_set_issuer_name", res = false); } // Add basic constraints certificate extensions. res = SetBasicConstraintsExtension(newCert, isCA, certConfig.GetExtensionBasicPathLenValue(attCertType), certConfig); VerifyTrueOrExit(res); // Add key usage certificate extensions. res = SetKeyUsageExtension(newCert, isCA, certConfig); VerifyTrueOrExit(res); if (certConfig.IsExtensionSKIDPresent()) { // Add a subject key id extension for the certificate. res = AddSubjectKeyId(newCert, certConfig.IsExtensionSKIDLengthValid()); VerifyTrueOrExit(res); } if (certConfig.IsExtensionAKIDPresent()) { // Add the authority key id extension from the signing certificate. res = AddAuthorityKeyId(newCert, caCert, certConfig.IsExtensionAKIDLengthValid()); VerifyTrueOrExit(res); } if (certConfig.IsExtensionExtendedKeyUsagePresent()) { // Add optional Extended Key Usage extentsion. res = AddExtension(newCert, NID_ext_key_usage, "critical,clientAuth,serverAuth"); VerifyTrueOrExit(res); } if (certConfig.IsExtensionAuthorityInfoAccessPresent()) { // Add optional Authority Informational Access extentsion. res = AddExtension(newCert, NID_info_access, "OCSP;URI:http://ocsp.example.com/"); VerifyTrueOrExit(res); } if (certConfig.IsExtensionSubjectAltNamePresent()) { // Add optional Subject Alternative Name extentsion. res = AddExtension(newCert, NID_subject_alt_name, "DNS:test.com"); VerifyTrueOrExit(res); } if (cdpExt != nullptr) { int result = X509_add_ext(newCert, cdpExt, -1); VerifyTrueOrExit(result == 1); } if (certConfig.IsExtensionCDPPresent()) { // Add second CDP extension. res = AddExtension(newCert, NID_crl_distribution_points, "URI:http://example.com/test_crl.pem"); VerifyTrueOrExit(res); } // Sign the new certificate. if (!X509_sign(newCert, caKey, certConfig.GetSignatureAlgorithmDER())) { ReportOpenSSLErrorAndExit("X509_sign", res = false); } exit: return res; }