summaryrefslogtreecommitdiffstats
path: root/src/VBox/Main/src-server/CertificateImpl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Main/src-server/CertificateImpl.cpp')
-rw-r--r--src/VBox/Main/src-server/CertificateImpl.cpp590
1 files changed, 590 insertions, 0 deletions
diff --git a/src/VBox/Main/src-server/CertificateImpl.cpp b/src/VBox/Main/src-server/CertificateImpl.cpp
new file mode 100644
index 00000000..e346a337
--- /dev/null
+++ b/src/VBox/Main/src-server/CertificateImpl.cpp
@@ -0,0 +1,590 @@
+/* $Id: CertificateImpl.cpp $ */
+/** @file
+ * ICertificate COM class implementations.
+ */
+
+/*
+ * Copyright (C) 2008-2022 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#define LOG_GROUP LOG_GROUP_MAIN_CERTIFICATE
+#include <iprt/err.h>
+#include <iprt/path.h>
+#include <iprt/cpp/utils.h>
+#include <VBox/com/array.h>
+#include <iprt/crypto/x509.h>
+
+#include "ProgressImpl.h"
+#include "CertificateImpl.h"
+#include "AutoCaller.h"
+#include "Global.h"
+#include "LoggingNew.h"
+
+using namespace std;
+
+
+/**
+ * Private instance data for the #Certificate class.
+ * @see Certificate::m
+ */
+struct Certificate::Data
+{
+ Data()
+ : fTrusted(false)
+ , fExpired(false)
+ , fValidX509(false)
+ {
+ RT_ZERO(X509);
+ }
+
+ ~Data()
+ {
+ if (fValidX509)
+ {
+ RTCrX509Certificate_Delete(&X509);
+ RT_ZERO(X509);
+ fValidX509 = false;
+ }
+ }
+
+ /** Whether the certificate is trusted. */
+ bool fTrusted;
+ /** Whether the certificate is trusted. */
+ bool fExpired;
+ /** Valid data in mX509. */
+ bool fValidX509;
+ /** Clone of the X.509 certificate. */
+ RTCRX509CERTIFICATE X509;
+
+private:
+ Data(const Certificate::Data &rTodo) { AssertFailed(); NOREF(rTodo); }
+ Data &operator=(const Certificate::Data &rTodo) { AssertFailed(); NOREF(rTodo); return *this; }
+};
+
+
+///////////////////////////////////////////////////////////////////////////////////
+//
+// Certificate constructor / destructor
+//
+// ////////////////////////////////////////////////////////////////////////////////
+
+DEFINE_EMPTY_CTOR_DTOR(Certificate)
+
+HRESULT Certificate::FinalConstruct()
+{
+ return BaseFinalConstruct();
+}
+
+void Certificate::FinalRelease()
+{
+ uninit();
+ BaseFinalRelease();
+}
+
+/**
+ * Initializes a certificate instance.
+ *
+ * @returns COM status code.
+ * @param a_pCert The certificate.
+ * @param a_fTrusted Whether the caller trusts the certificate or not.
+ * @param a_fExpired Whether the caller consideres the certificate to be
+ * expired.
+ */
+HRESULT Certificate::initCertificate(PCRTCRX509CERTIFICATE a_pCert, bool a_fTrusted, bool a_fExpired)
+{
+ HRESULT rc = S_OK;
+ LogFlowThisFuncEnter();
+
+ AutoInitSpan autoInitSpan(this);
+ AssertReturn(autoInitSpan.isOk(), E_FAIL);
+
+ m = new Data();
+
+ int vrc = RTCrX509Certificate_Clone(&m->X509, a_pCert, &g_RTAsn1DefaultAllocator);
+ if (RT_SUCCESS(vrc))
+ {
+ m->fValidX509 = true;
+ m->fTrusted = a_fTrusted;
+ m->fExpired = a_fExpired;
+ autoInitSpan.setSucceeded();
+ }
+ else
+ rc = Global::vboxStatusCodeToCOM(vrc);
+
+ LogFlowThisFunc(("returns rc=%Rhrc\n", rc));
+ return rc;
+}
+
+void Certificate::uninit()
+{
+ /* Enclose the state transition Ready->InUninit->NotReady */
+ AutoUninitSpan autoUninitSpan(this);
+ if (autoUninitSpan.uninitDone())
+ return;
+
+ delete m;
+ m = NULL;
+}
+
+
+/** @name Wrapped ICertificate properties
+ * @{
+ */
+
+HRESULT Certificate::getVersionNumber(CertificateVersion_T *aVersionNumber)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ Assert(m->fValidX509);
+ switch (m->X509.TbsCertificate.T0.Version.uValue.u)
+ {
+ case RTCRX509TBSCERTIFICATE_V1: *aVersionNumber = CertificateVersion_V1; break;
+ case RTCRX509TBSCERTIFICATE_V2: *aVersionNumber = CertificateVersion_V2; break;
+ case RTCRX509TBSCERTIFICATE_V3: *aVersionNumber = CertificateVersion_V3; break;
+ default: AssertFailed(); *aVersionNumber = CertificateVersion_Unknown; break;
+ }
+ return S_OK;
+}
+
+HRESULT Certificate::getSerialNumber(com::Utf8Str &aSerialNumber)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ Assert(m->fValidX509);
+
+ char szTmp[_2K];
+ int vrc = RTAsn1Integer_ToString(&m->X509.TbsCertificate.SerialNumber, szTmp, sizeof(szTmp), 0, NULL);
+ if (RT_SUCCESS(vrc))
+ aSerialNumber = szTmp;
+ else
+ return Global::vboxStatusCodeToCOM(vrc);
+
+ return S_OK;
+}
+
+HRESULT Certificate::getSignatureAlgorithmOID(com::Utf8Str &aSignatureAlgorithmOID)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ Assert(m->fValidX509);
+ aSignatureAlgorithmOID = m->X509.TbsCertificate.Signature.Algorithm.szObjId;
+
+ return S_OK;
+}
+
+HRESULT Certificate::getSignatureAlgorithmName(com::Utf8Str &aSignatureAlgorithmName)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ Assert(m->fValidX509);
+ return i_getAlgorithmName(&m->X509.TbsCertificate.Signature, aSignatureAlgorithmName);
+}
+
+HRESULT Certificate::getIssuerName(std::vector<com::Utf8Str> &aIssuerName)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ Assert(m->fValidX509);
+ return i_getX509Name(&m->X509.TbsCertificate.Issuer, aIssuerName);
+}
+
+HRESULT Certificate::getSubjectName(std::vector<com::Utf8Str> &aSubjectName)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ Assert(m->fValidX509);
+ return i_getX509Name(&m->X509.TbsCertificate.Subject, aSubjectName);
+}
+
+HRESULT Certificate::getFriendlyName(com::Utf8Str &aFriendlyName)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ Assert(m->fValidX509);
+
+ PCRTCRX509NAME pName = &m->X509.TbsCertificate.Subject;
+
+ /*
+ * Enumerate the subject name and pick interesting attributes we can use to
+ * form a name more friendly than the RTCrX509Name_FormatAsString output.
+ */
+ const char *pszOrg = NULL;
+ const char *pszOrgUnit = NULL;
+ const char *pszGivenName = NULL;
+ const char *pszSurname = NULL;
+ const char *pszEmail = NULL;
+ for (uint32_t i = 0; i < pName->cItems; i++)
+ {
+ PCRTCRX509RELATIVEDISTINGUISHEDNAME pRdn = pName->papItems[i];
+ for (uint32_t j = 0; j < pRdn->cItems; j++)
+ {
+ PCRTCRX509ATTRIBUTETYPEANDVALUE pComponent = pRdn->papItems[j];
+ AssertContinue(pComponent->Value.enmType == RTASN1TYPE_STRING);
+
+ /* Select interesting components based on the short RDN prefix
+ string (easier to read and write than OIDs, for now). */
+ const char *pszPrefix = RTCrX509Name_GetShortRdn(&pComponent->Type);
+ if (pszPrefix)
+ {
+ const char *pszUtf8;
+ int vrc = RTAsn1String_QueryUtf8(&pComponent->Value.u.String, &pszUtf8, NULL);
+ if (RT_SUCCESS(vrc) && *pszUtf8)
+ {
+ if (!strcmp(pszPrefix, "Email"))
+ pszEmail = pszUtf8;
+ else if (!strcmp(pszPrefix, "O"))
+ pszOrg = pszUtf8;
+ else if (!strcmp(pszPrefix, "OU"))
+ pszOrgUnit = pszUtf8;
+ else if (!strcmp(pszPrefix, "S"))
+ pszSurname = pszUtf8;
+ else if (!strcmp(pszPrefix, "G"))
+ pszGivenName = pszUtf8;
+ }
+ }
+ }
+ }
+
+ if (pszGivenName && pszSurname)
+ {
+ if (pszEmail)
+ aFriendlyName = Utf8StrFmt("%s, %s <%s>", pszSurname, pszGivenName, pszEmail);
+ else if (pszOrg)
+ aFriendlyName = Utf8StrFmt("%s, %s (%s)", pszSurname, pszGivenName, pszOrg);
+ else if (pszOrgUnit)
+ aFriendlyName = Utf8StrFmt("%s, %s (%s)", pszSurname, pszGivenName, pszOrgUnit);
+ else
+ aFriendlyName = Utf8StrFmt("%s, %s", pszSurname, pszGivenName);
+ }
+ else if (pszOrg && pszOrgUnit)
+ aFriendlyName = Utf8StrFmt("%s, %s", pszOrg, pszOrgUnit);
+ else if (pszOrg)
+ aFriendlyName = Utf8StrFmt("%s", pszOrg);
+ else if (pszOrgUnit)
+ aFriendlyName = Utf8StrFmt("%s", pszOrgUnit);
+ else
+ {
+ /*
+ * Fall back on unfriendly but accurate.
+ */
+ char szTmp[_8K];
+ RT_ZERO(szTmp);
+ RTCrX509Name_FormatAsString(pName, szTmp, sizeof(szTmp) - 1, NULL);
+ aFriendlyName = szTmp;
+ }
+
+ return S_OK;
+}
+
+HRESULT Certificate::getValidityPeriodNotBefore(com::Utf8Str &aValidityPeriodNotBefore)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ Assert(m->fValidX509);
+ return i_getTime(&m->X509.TbsCertificate.Validity.NotBefore, aValidityPeriodNotBefore);
+}
+
+HRESULT Certificate::getValidityPeriodNotAfter(com::Utf8Str &aValidityPeriodNotAfter)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ Assert(m->fValidX509);
+ return i_getTime(&m->X509.TbsCertificate.Validity.NotAfter, aValidityPeriodNotAfter);
+}
+
+HRESULT Certificate::getPublicKeyAlgorithmOID(com::Utf8Str &aPublicKeyAlgorithmOID)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ Assert(m->fValidX509);
+ aPublicKeyAlgorithmOID = m->X509.TbsCertificate.SubjectPublicKeyInfo.Algorithm.Algorithm.szObjId;
+ return S_OK;
+}
+
+HRESULT Certificate::getPublicKeyAlgorithm(com::Utf8Str &aPublicKeyAlgorithm)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ Assert(m->fValidX509);
+ return i_getAlgorithmName(&m->X509.TbsCertificate.SubjectPublicKeyInfo.Algorithm, aPublicKeyAlgorithm);
+}
+
+HRESULT Certificate::getSubjectPublicKey(std::vector<BYTE> &aSubjectPublicKey)
+{
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); /* Getting encoded ASN.1 bytes may make changes to X509. */
+ return i_getEncodedBytes(&m->X509.TbsCertificate.SubjectPublicKeyInfo.SubjectPublicKey.Asn1Core, aSubjectPublicKey);
+}
+
+HRESULT Certificate::getIssuerUniqueIdentifier(com::Utf8Str &aIssuerUniqueIdentifier)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ return i_getUniqueIdentifier(&m->X509.TbsCertificate.T1.IssuerUniqueId, aIssuerUniqueIdentifier);
+}
+
+HRESULT Certificate::getSubjectUniqueIdentifier(com::Utf8Str &aSubjectUniqueIdentifier)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ return i_getUniqueIdentifier(&m->X509.TbsCertificate.T2.SubjectUniqueId, aSubjectUniqueIdentifier);
+}
+
+HRESULT Certificate::getCertificateAuthority(BOOL *aCertificateAuthority)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ *aCertificateAuthority = m->X509.TbsCertificate.T3.pBasicConstraints
+ && m->X509.TbsCertificate.T3.pBasicConstraints->CA.fValue;
+
+ return S_OK;
+}
+
+HRESULT Certificate::getKeyUsage(ULONG *aKeyUsage)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ *aKeyUsage = m->X509.TbsCertificate.T3.fKeyUsage;
+ return S_OK;
+}
+
+HRESULT Certificate::getExtendedKeyUsage(std::vector<com::Utf8Str> &aExtendedKeyUsage)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ NOREF(aExtendedKeyUsage);
+ return E_NOTIMPL;
+}
+
+HRESULT Certificate::getRawCertData(std::vector<BYTE> &aRawCertData)
+{
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); /* Getting encoded ASN.1 bytes may make changes to X509. */
+ return i_getEncodedBytes(&m->X509.SeqCore.Asn1Core, aRawCertData);
+}
+
+HRESULT Certificate::getSelfSigned(BOOL *aSelfSigned)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ Assert(m->fValidX509);
+ *aSelfSigned = RTCrX509Certificate_IsSelfSigned(&m->X509);
+
+ return S_OK;
+}
+
+HRESULT Certificate::getTrusted(BOOL *aTrusted)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ Assert(m->fValidX509);
+ *aTrusted = m->fTrusted;
+
+ return S_OK;
+}
+
+HRESULT Certificate::getExpired(BOOL *aExpired)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ Assert(m->fValidX509);
+ *aExpired = m->fExpired;
+ return S_OK;
+}
+
+/** @} */
+
+/** @name Wrapped ICertificate methods
+ * @{
+ */
+
+HRESULT Certificate::isCurrentlyExpired(BOOL *aResult)
+{
+ AssertReturnStmt(m->fValidX509, *aResult = TRUE, E_UNEXPECTED);
+ RTTIMESPEC Now;
+ *aResult = RTCrX509Validity_IsValidAtTimeSpec(&m->X509.TbsCertificate.Validity, RTTimeNow(&Now)) ? FALSE : TRUE;
+ return S_OK;
+}
+
+HRESULT Certificate::queryInfo(LONG aWhat, com::Utf8Str &aResult)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ /* Insurance. */
+ NOREF(aResult);
+ return setError(E_FAIL, tr("Unknown item %u"), aWhat);
+}
+
+/** @} */
+
+
+/** @name Methods extracting COM data from the certificate object
+ * @{
+ */
+
+/**
+ * Translates an algorithm OID into a human readable string, if possible.
+ *
+ * @returns S_OK.
+ * @param a_pAlgId The algorithm.
+ * @param a_rReturn The return string value.
+ * @throws std::bad_alloc
+ */
+HRESULT Certificate::i_getAlgorithmName(PCRTCRX509ALGORITHMIDENTIFIER a_pAlgId, com::Utf8Str &a_rReturn)
+{
+ const char *pszOid = a_pAlgId->Algorithm.szObjId;
+ const char *pszName;
+ if (!pszOid) pszName = "";
+ else if (strcmp(pszOid, RTCRX509ALGORITHMIDENTIFIERID_RSA)) pszName = "rsaEncryption";
+ else if (strcmp(pszOid, RTCRX509ALGORITHMIDENTIFIERID_MD2_WITH_RSA)) pszName = "md2WithRSAEncryption";
+ else if (strcmp(pszOid, RTCRX509ALGORITHMIDENTIFIERID_MD4_WITH_RSA)) pszName = "md4WithRSAEncryption";
+ else if (strcmp(pszOid, RTCRX509ALGORITHMIDENTIFIERID_MD5_WITH_RSA)) pszName = "md5WithRSAEncryption";
+ else if (strcmp(pszOid, RTCRX509ALGORITHMIDENTIFIERID_SHA1_WITH_RSA)) pszName = "sha1WithRSAEncryption";
+ else if (strcmp(pszOid, RTCRX509ALGORITHMIDENTIFIERID_SHA224_WITH_RSA)) pszName = "sha224WithRSAEncryption";
+ else if (strcmp(pszOid, RTCRX509ALGORITHMIDENTIFIERID_SHA256_WITH_RSA)) pszName = "sha256WithRSAEncryption";
+ else if (strcmp(pszOid, RTCRX509ALGORITHMIDENTIFIERID_SHA384_WITH_RSA)) pszName = "sha384WithRSAEncryption";
+ else if (strcmp(pszOid, RTCRX509ALGORITHMIDENTIFIERID_SHA512_WITH_RSA)) pszName = "sha512WithRSAEncryption";
+ else if (strcmp(pszOid, RTCRX509ALGORITHMIDENTIFIERID_SHA512T224_WITH_RSA)) pszName = "sha512-224WithRSAEncryption";
+ else if (strcmp(pszOid, RTCRX509ALGORITHMIDENTIFIERID_SHA512T256_WITH_RSA)) pszName = "sha512-256WithRSAEncryption";
+ else
+ pszName = pszOid;
+ a_rReturn = pszName;
+ return S_OK;
+}
+
+/**
+ * Formats a X.509 name into a string array.
+ *
+ * The name is prefix with a short hand of the relative distinguished name
+ * type followed by an equal sign.
+ *
+ * @returns S_OK.
+ * @param a_pName The X.509 name.
+ * @param a_rReturn The return string array.
+ * @throws std::bad_alloc
+ */
+HRESULT Certificate::i_getX509Name(PCRTCRX509NAME a_pName, std::vector<com::Utf8Str> &a_rReturn)
+{
+ if (RTCrX509Name_IsPresent(a_pName))
+ {
+ for (uint32_t i = 0; i < a_pName->cItems; i++)
+ {
+ PCRTCRX509RELATIVEDISTINGUISHEDNAME pRdn = a_pName->papItems[i];
+ for (uint32_t j = 0; j < pRdn->cItems; j++)
+ {
+ PCRTCRX509ATTRIBUTETYPEANDVALUE pComponent = pRdn->papItems[j];
+
+ AssertReturn(pComponent->Value.enmType == RTASN1TYPE_STRING,
+ setErrorVrc(VERR_CR_X509_NAME_NOT_STRING, "VERR_CR_X509_NAME_NOT_STRING"));
+
+ /* Get the prefix for this name component. */
+ const char *pszPrefix = RTCrX509Name_GetShortRdn(&pComponent->Type);
+ AssertStmt(pszPrefix, pszPrefix = pComponent->Type.szObjId);
+
+ /* Get the string. */
+ const char *pszUtf8;
+ int vrc = RTAsn1String_QueryUtf8(&pComponent->Value.u.String, &pszUtf8, NULL /*pcch*/);
+ AssertRCReturn(vrc, setErrorVrc(vrc, "RTAsn1String_QueryUtf8(%u/%u,,) -> %Rrc", i, j, vrc));
+
+ a_rReturn.push_back(Utf8StrFmt("%s=%s", pszPrefix, pszUtf8));
+ }
+ }
+ }
+ return S_OK;
+}
+
+/**
+ * Translates an ASN.1 timestamp into an ISO timestamp string.
+ *
+ * @returns S_OK.
+ * @param a_pTime The timestamp
+ * @param a_rReturn The return string value.
+ * @throws std::bad_alloc
+ */
+HRESULT Certificate::i_getTime(PCRTASN1TIME a_pTime, com::Utf8Str &a_rReturn)
+{
+ char szTmp[128];
+ if (RTTimeToString(&a_pTime->Time, szTmp, sizeof(szTmp)))
+ {
+ a_rReturn = szTmp;
+ return S_OK;
+ }
+ AssertFailed();
+ return E_FAIL;
+}
+
+/**
+ * Translates a X.509 unique identifier to a string.
+ *
+ * @returns S_OK.
+ * @param a_pUniqueId The unique identifier.
+ * @param a_rReturn The return string value.
+ * @throws std::bad_alloc
+ */
+HRESULT Certificate::i_getUniqueIdentifier(PCRTCRX509UNIQUEIDENTIFIER a_pUniqueId, com::Utf8Str &a_rReturn)
+{
+ /* The a_pUniqueId may not be present! */
+ if (RTCrX509UniqueIdentifier_IsPresent(a_pUniqueId))
+ {
+ void const *pvData = RTASN1BITSTRING_GET_BIT0_PTR(a_pUniqueId);
+ size_t const cbData = RTASN1BITSTRING_GET_BYTE_SIZE(a_pUniqueId);
+ size_t const cbFormatted = cbData * 3 - 1 + 1;
+ a_rReturn.reserve(cbFormatted); /* throws */
+ int vrc = RTStrPrintHexBytes(a_rReturn.mutableRaw(), cbFormatted, pvData, cbData, RTSTRPRINTHEXBYTES_F_SEP_COLON);
+ a_rReturn.jolt();
+ AssertRCReturn(vrc, Global::vboxStatusCodeToCOM(vrc));
+ }
+ else
+ Assert(a_rReturn.isEmpty());
+ return S_OK;
+}
+
+/**
+ * Translates any ASN.1 object into a (DER encoded) byte array.
+ *
+ * @returns S_OK.
+ * @param a_pAsn1Obj The ASN.1 object to get the DER encoded bytes for.
+ * @param a_rReturn The return byte vector.
+ * @throws std::bad_alloc
+ */
+HRESULT Certificate::i_getEncodedBytes(PRTASN1CORE a_pAsn1Obj, std::vector<BYTE> &a_rReturn)
+{
+ HRESULT hrc = S_OK;
+ Assert(a_rReturn.size() == 0);
+ if (RTAsn1Core_IsPresent(a_pAsn1Obj))
+ {
+ uint32_t cbEncoded;
+ int vrc = RTAsn1EncodePrepare(a_pAsn1Obj, 0, &cbEncoded, NULL);
+ if (RT_SUCCESS(vrc))
+ {
+ a_rReturn.resize(cbEncoded);
+ Assert(a_rReturn.size() == cbEncoded);
+ if (cbEncoded)
+ {
+ vrc = RTAsn1EncodeToBuffer(a_pAsn1Obj, 0, &a_rReturn.front(), a_rReturn.size(), NULL);
+ if (RT_FAILURE(vrc))
+ hrc = setErrorVrc(vrc, tr("RTAsn1EncodeToBuffer failed with %Rrc"), vrc);
+ }
+ }
+ else
+ hrc = setErrorVrc(vrc, tr("RTAsn1EncodePrepare failed with %Rrc"), vrc);
+ }
+ return hrc;
+}
+
+/** @} */
+