/*============================================================================
 * OpcUa_P_OpenSSL_AES_CBC_Encrypt
 *===========================================================================*/
OpcUa_StatusCode OpcUa_P_OpenSSL_AES_CBC_Encrypt(
    OpcUa_CryptoProvider*   a_pProvider,
    OpcUa_Byte*             a_pPlainText,
    OpcUa_UInt32            a_plainTextLen,
    OpcUa_Key*              a_key,
    OpcUa_Byte*             a_pInitalVector,
    OpcUa_Byte*             a_pCipherText,
    OpcUa_UInt32*           a_pCipherTextLen)
{
    AES_KEY         key;

    OpcUa_Byte      pInitalVector[16];

    OpcUa_InitializeStatus(OpcUa_Module_P_OpenSSL, "AES_CBC_Encrypt");

    OpcUa_ReferenceParameter(a_pProvider);

    OpcUa_ReturnErrorIfArgumentNull(a_pPlainText);
    OpcUa_ReturnErrorIfArgumentNull(a_key);
    OpcUa_ReturnErrorIfArgumentNull(a_key->Key.Data);
    OpcUa_ReturnErrorIfArgumentNull(a_pInitalVector);
    OpcUa_ReturnErrorIfArgumentNull(a_pCipherTextLen);

    if(a_plainTextLen % 16 != 0)
    {
        uStatus = OpcUa_BadInvalidArgument;
        OpcUa_GotoErrorIfBad(uStatus);
    }

    *a_pCipherTextLen = a_plainTextLen;

    /* if just the output length is needed for the caller of this function */
    if(a_pCipherText == OpcUa_Null)
    {
        OpcUa_ReturnStatusCode;
    }

    /* we have to pass the key length in bits instead of bytes */
    if(AES_set_encrypt_key(a_key->Key.Data, a_key->Key.Length * 8, &key) < 0)
    {
        uStatus = OpcUa_Bad;
        OpcUa_GotoErrorIfBad(uStatus);
    }

    /* copy the IV because the AES_cbc_encrypt function overwrites it. */
    OpcUa_P_Memory_MemCpy(pInitalVector, 16, a_pInitalVector, 16);

    /* encrypt data */
    AES_cbc_encrypt(    a_pPlainText,
                        a_pCipherText,
                        a_plainTextLen,
                        &key,
                        pInitalVector,
                        AES_ENCRYPT);

OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;

OpcUa_FinishErrorHandling;
}
/*============================================================================
 * OpcUa_P_OpenSSL_X509_GetSignature
 *===========================================================================*/
OpcUa_StatusCode OpcUa_P_OpenSSL_X509_GetSignature(
    OpcUa_CryptoProvider*       a_pProvider,
    OpcUa_ByteString*           a_pCertificate,
    OpcUa_Signature*            a_pSignature)
{
    X509*                   pX509Certificate    = OpcUa_Null;
    const unsigned char*    pTemp               = OpcUa_Null;

OpcUa_InitializeStatus(OpcUa_Module_P_OpenSSL, "X509_GetSignature");

    OpcUa_ReferenceParameter(a_pProvider);

    OpcUa_ReturnErrorIfArgumentNull(a_pProvider);
    OpcUa_ReturnErrorIfArgumentNull(a_pCertificate);
    OpcUa_ReturnErrorIfArgumentNull(a_pSignature);

    /* d2i_X509 modifies the given pointer -> use local replacement */
    pTemp = a_pCertificate->Data;

    d2i_X509(&pX509Certificate, &pTemp, a_pCertificate->Length);

    if(pX509Certificate == OpcUa_Null)
    {
        uStatus = OpcUa_Bad;
        OpcUa_GotoErrorIfBad(uStatus);
    }

    a_pSignature->Signature.Length = pX509Certificate->signature->length;

    a_pSignature->Algorithm = OBJ_obj2nid(pX509Certificate->sig_alg->algorithm);

    if(a_pSignature->Algorithm == NID_undef)
    {
        uStatus = OpcUa_Bad;
        OpcUa_GotoErrorIfBad(uStatus);
    }

    if(a_pSignature->Signature.Data != OpcUa_Null)
    {
        uStatus = OpcUa_P_Memory_MemCpy(a_pSignature->Signature.Data,
                                        a_pSignature->Signature.Length,
                                        pX509Certificate->signature->data,
                                        pX509Certificate->signature->length);

    }

    X509_free(pX509Certificate);

OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;

    if(pX509Certificate != OpcUa_Null)
    {
        X509_free(pX509Certificate);
    }

OpcUa_FinishErrorHandling;
}
/* internal function */
OpcUa_P_OpenSSL_PSHA256_Ctx* OpcUa_P_OpenSSL_PSHA256_Context_Create(
    OpcUa_Byte*     a_pSecret,
    OpcUa_UInt32    a_secretLen,
    OpcUa_Byte*     a_pSeed,
    OpcUa_Int32     a_seedLen)
{
    OpcUa_P_OpenSSL_PSHA256_Ctx*  pCtx;
    OpcUa_Int                   size;

    if(a_pSecret == OpcUa_Null || a_pSeed   == OpcUa_Null)
        return OpcUa_Null;

    pCtx = OpcUa_Null;
    size = sizeof(OpcUa_P_OpenSSL_PSHA256_Ctx) + a_secretLen + a_seedLen;

    pCtx = (OpcUa_P_OpenSSL_PSHA256_Ctx*) OpcUa_P_Memory_Alloc(size);

    if (pCtx == OpcUa_Null)
        return OpcUa_Null;

    pCtx->secret_len = a_secretLen;
    pCtx->seed_len = a_seedLen;

    OpcUa_P_Memory_MemCpy(OpcUa_P_OpenSSL_PSHA256_SECRET(pCtx), a_secretLen, a_pSecret, a_secretLen);
    OpcUa_P_Memory_MemCpy(OpcUa_P_OpenSSL_PSHA256_SEED(pCtx), a_seedLen, a_pSeed, a_seedLen);

    /* A(0) = seed */
    /* A(i) = HMAC_SHA256(secret, A(i-1)) */
    /* Calculate A(1) = HMAC_SHA256(secret, seed) */
    HMAC(EVP_sha256(), a_pSecret, a_secretLen, a_pSeed, a_seedLen, (unsigned char*)pCtx->A, (unsigned int*)OpcUa_Null);

    if(pCtx == OpcUa_Null)
        return OpcUa_Null;

    return pCtx;
}
/*============================================================================
 * OpcUa_P_OpenSSL_X509_GetCertificateThumbprint
 *===========================================================================*/
OpcUa_StatusCode OpcUa_P_OpenSSL_X509_GetCertificateThumbprint(
    OpcUa_CryptoProvider*       a_pProvider,
    OpcUa_ByteString*           a_pCertificate,
    OpcUa_ByteString*           a_pCertificateThumbprint)
{
    X509*                   pX509Certificate    = OpcUa_Null;
    const unsigned char*    pTemp               = OpcUa_Null;

OpcUa_InitializeStatus(OpcUa_Module_P_OpenSSL, "X509_GetCertificateThumbprint");

    OpcUa_ReturnErrorIfArgumentNull(a_pProvider);
    OpcUa_ReturnErrorIfArgumentNull(a_pCertificate);
    OpcUa_ReturnErrorIfArgumentNull(a_pCertificate->Data);
    OpcUa_ReturnErrorIfArgumentNull(a_pCertificateThumbprint);

    /* SHA-1 produces 20 bytes */
    a_pCertificateThumbprint->Length = sizeof(pX509Certificate->sha1_hash);

    if(a_pCertificateThumbprint->Data == OpcUa_Null)
    {
        OpcUa_ReturnStatusCode;
    }

    /* d2i_X509 modifies the given pointer -> use local replacement */
    pTemp = a_pCertificate->Data;

    d2i_X509(&pX509Certificate, &pTemp, a_pCertificate->Length);

    if(pX509Certificate == OpcUa_Null)
    {
        uStatus = OpcUa_Bad;
        OpcUa_GotoErrorIfBad(uStatus);
    }

    /* update pX509Certificate->sha1_hash */
    X509_check_purpose(pX509Certificate, -1, 0);
    uStatus = OpcUa_P_Memory_MemCpy(a_pCertificateThumbprint->Data, a_pCertificateThumbprint->Length,
                                    pX509Certificate->sha1_hash, sizeof(pX509Certificate->sha1_hash));
    X509_free(pX509Certificate);

OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;
OpcUa_FinishErrorHandling;
}
/*============================================================================
 * OpcUa_P_ParseUrl
 *===========================================================================*/
OpcUa_StatusCode OpcUa_P_ParseUrl(  OpcUa_StringA   a_psUrl,
                                    OpcUa_StringA*  a_psIpAdress,
                                    OpcUa_UInt16*   a_puPort)
{
    OpcUa_UInt32    uUrlLength        = 0;

    OpcUa_StringA   sHostName         = OpcUa_Null;
    OpcUa_UInt32    uHostNameLength   = 0;

    OpcUa_CharA*    pcCursor          = OpcUa_Null;

    OpcUa_Int       nIndex1           = 0;
    OpcUa_Int       nIpStart          = 0;

    struct hostent* pHostEnt          = OpcUa_Null;

OpcUa_InitializeStatus(OpcUa_Module_Utilities, "P_ParseUrl");

    OpcUa_ReturnErrorIfArgumentNull(a_psUrl);
    OpcUa_ReturnErrorIfArgumentNull(a_psIpAdress);
    OpcUa_ReturnErrorIfArgumentNull(a_puPort);

    *a_psIpAdress = OpcUa_Null;

    uUrlLength = (OpcUa_UInt32)strlen(a_psUrl);

    /* check for // (end of protocol header) */
    pcCursor = strstr(a_psUrl, "//");

    if(pcCursor != OpcUa_Null)
    {
        /* begin of host address */
        pcCursor += 2;
        nIndex1 = (OpcUa_Int)(pcCursor - a_psUrl);
    }
    else
    {
        uStatus = OpcUa_BadSyntaxError;
        OpcUa_ReturnStatusCode;
    }

    /* skip protocol prefix and store beginning of ip adress */
    nIpStart = nIndex1;

    /* skip host address */
    while(      a_psUrl[nIndex1] != ':'
            &&  a_psUrl[nIndex1] != '/'
            &&  a_psUrl[nIndex1] != 0
            &&  nIndex1          <  (OpcUa_Int32)uUrlLength)
    {
        nIndex1++;
    }

    uHostNameLength = nIndex1 - nIpStart;
    sHostName       = (OpcUa_StringA)malloc(uHostNameLength + 1);
    if(sHostName == NULL)
    {
        return OpcUa_BadOutOfMemory;
    }

    memcpy(sHostName, &a_psUrl[nIpStart], uHostNameLength);
    sHostName[uHostNameLength] = '\0';

    pHostEnt = gethostbyname(sHostName);

    free(sHostName);

    if(pHostEnt == NULL)
    {
        /* hostname could not be resolved */
        return OpcUa_BadHostUnknown;
    }

    nIpStart = 0;
    *a_psIpAdress = (OpcUa_StringA)OpcUa_P_Memory_Alloc(16);
    memset(*a_psIpAdress, 0, 16);

#if OPCUA_USE_SAFE_FUNCTIONS
    nIpStart += sprintf_s(&(*a_psIpAdress)[0],         16,"%u", (unsigned char)(*((*pHostEnt).h_addr_list))[0]);
    (*a_psIpAdress)[nIpStart++] = '.';
    nIpStart += sprintf_s(&(*a_psIpAdress)[nIpStart],  12,"%u", (unsigned char)(*((*pHostEnt).h_addr_list))[1]);
    (*a_psIpAdress)[nIpStart++] = '.';
    nIpStart += sprintf_s(&(*a_psIpAdress)[nIpStart],   8,"%u", (unsigned char)(*((*pHostEnt).h_addr_list))[2]);
    (*a_psIpAdress)[nIpStart++] = '.';
    nIpStart += sprintf_s(&(*a_psIpAdress)[nIpStart],   4,"%u", (unsigned char)(*((*pHostEnt).h_addr_list))[3]);
#else /* OPCUA_USE_SAFE_FUNCTIONS */

    nIpStart += sprintf(&(*a_psIpAdress)[0], "%u", (unsigned char)(*((*pHostEnt).h_addr_list))[0]);
    (*a_psIpAdress)[nIpStart++] = '.';
    nIpStart += sprintf(&(*a_psIpAdress)[nIpStart], "%u", (unsigned char)(*((*pHostEnt).h_addr_list))[1]);
    (*a_psIpAdress)[nIpStart++] = '.';
    nIpStart += sprintf(&(*a_psIpAdress)[nIpStart], "%u", (unsigned char)(*((*pHostEnt).h_addr_list))[2]);
    (*a_psIpAdress)[nIpStart++] = '.';
    nIpStart += sprintf(&(*a_psIpAdress)[nIpStart], "%u", (unsigned char)(*((*pHostEnt).h_addr_list))[3]);
#endif /* OPCUA_USE_SAFE_FUNCTIONS */

    /* scan port */
    if(a_psUrl[nIndex1] == ':')
    {
        OpcUa_Int       nIndex2 = 0;
        OpcUa_CharA*    sPort   = OpcUa_Null;
        OpcUa_CharA sBuffer[MAX_PORT_LENGTH];

        /* skip delimiter */
        nIndex1++;

        /* store beginning of port */
        sPort = &a_psUrl[nIndex1];

        /* search for end of port */
        while(      a_psUrl[nIndex1] != '/'
                &&  a_psUrl[nIndex1] != 0
                &&  nIndex2          <  6)
        {
            nIndex1++;
            nIndex2++;
        }

        /* convert port */
        OpcUa_P_Memory_MemCpy(sBuffer, MAX_PORT_LENGTH-1, sPort, nIndex2);
        sBuffer[nIndex2] = 0;
        *a_puPort = (OpcUa_UInt16)OpcUa_P_CharAToInt(sBuffer);
    }
    else
    {
        /* return default port */
        if (strncmp(a_psUrl, "http:", 5) != 0)
        {
            *a_puPort = OPCUA_TCP_DEFAULT_PORT;
        }
        else
        {
            *a_puPort = OPCUA_HTTP_DEFAULT_PORT;
        }
    }

OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;
OpcUa_FinishErrorHandling;
}
/**
  @brief Extracts data from a certificate store object.

  @param pCertificate          [in] The certificate to examine.
  @param pIssuer               [out, optional] The issuer name of the certificate.
  @param pSubject              [out, optional] The subject name of the certificate.
  @param pSubjectUri           [out, optional] The subject's URI of the certificate.
  @param pSubjectIP            [out, optional] The subject's IP of the certificate.
  @param pSubjectDNS           [out, optional] The subject's DNS name of the certificate.
  @param pCertThumbprint       [out, optional] The thumbprint of the certificate.
  @param pSubjectHash          [out, optional] The hash code of the certificate.
  @param pCertRawLength        [out, optional] The length of the DER encoded data.
                               can be smaller than the total length of pCertificate in case of chain certificate or garbage follow.
*/
OpcUa_StatusCode OpcUa_P_OpenSSL_PKI_ExtractCertificateData(
    OpcUa_ByteString*           a_pCertificate,
    OpcUa_ByteString*           a_pIssuer,
    OpcUa_ByteString*           a_pSubject,
    OpcUa_ByteString*           a_pSubjectUri,
    OpcUa_ByteString*           a_pSubjectIP,
    OpcUa_ByteString*           a_pSubjectDNS,
    OpcUa_ByteString*           a_pCertThumbprint,
    OpcUa_UInt32*               a_pSubjectHash,
    OpcUa_UInt32*               a_pCertRawLength)
{
    X509*                       pX509Cert = OpcUa_Null;
    char*                       pName = OpcUa_Null;
    GENERAL_NAMES*              pNames = OpcUa_Null;
    const unsigned char*        p;

OpcUa_InitializeStatus(OpcUa_Module_P_OpenSSL, "PKI_ExtractCertificateData");

    OpcUa_ReturnErrorIfArgumentNull(a_pCertificate);

    if(a_pIssuer != OpcUa_Null)
    {
        a_pIssuer->Data = OpcUa_Null;
        a_pIssuer->Length = 0;
    }

    if(a_pSubject != OpcUa_Null)
    {
        a_pSubject->Data = OpcUa_Null;
        a_pSubject->Length = 0;
    }

    if(a_pSubjectUri != OpcUa_Null)
    {
        a_pSubjectUri->Data = OpcUa_Null;
        a_pSubjectUri->Length = 0;
    }

    if(a_pSubjectIP != OpcUa_Null)
    {
        a_pSubjectIP->Data = OpcUa_Null;
        a_pSubjectIP->Length = 0;
    }

    if(a_pSubjectDNS != OpcUa_Null)
    {
        a_pSubjectDNS->Data = OpcUa_Null;
        a_pSubjectDNS->Length = 0;
    }

    if(a_pCertThumbprint != OpcUa_Null)
    {
        a_pCertThumbprint->Data = OpcUa_Null;
        a_pCertThumbprint->Length = 0;
    }

    if(a_pSubjectHash != OpcUa_Null)
    {
        *a_pSubjectHash = 0;
    }

    if(a_pCertRawLength != OpcUa_Null)
    {
        *a_pCertRawLength = 0;
    }

    /* convert openssl X509 certificate to DER encoded bytestring certificate */
    p = a_pCertificate->Data;
    if(!(pX509Cert = d2i_X509((X509**)OpcUa_Null, &p, a_pCertificate->Length)))
    {
        uStatus = OpcUa_Bad;
        OpcUa_GotoErrorIfBad(uStatus);
    }

    if(a_pIssuer != OpcUa_Null)
    {
        pName = X509_NAME_oneline(X509_get_issuer_name(pX509Cert), NULL, 0);
        OpcUa_GotoErrorIfAllocFailed(pName);
        a_pIssuer->Length = strlen(pName)+1;
        a_pIssuer->Data = (OpcUa_Byte*)OpcUa_P_Memory_Alloc(a_pIssuer->Length*sizeof(OpcUa_Byte));
        OpcUa_GotoErrorIfAllocFailed(a_pIssuer->Data);
        uStatus = OpcUa_P_Memory_MemCpy(a_pIssuer->Data, a_pIssuer->Length, pName, a_pIssuer->Length);
        OpcUa_GotoErrorIfBad(uStatus);
        OPENSSL_free(pName);
        pName = OpcUa_Null;
    }

    if(a_pSubject != OpcUa_Null)
    {
        pName = X509_NAME_oneline(X509_get_subject_name(pX509Cert), NULL, 0);
        OpcUa_GotoErrorIfAllocFailed(pName);
        a_pSubject->Length = strlen(pName)+1;
        a_pSubject->Data = (OpcUa_Byte*)OpcUa_P_Memory_Alloc(a_pSubject->Length*sizeof(OpcUa_Byte));
        OpcUa_GotoErrorIfAllocFailed(a_pSubject->Data);
        uStatus = OpcUa_P_Memory_MemCpy(a_pSubject->Data, a_pSubject->Length, pName, a_pSubject->Length);
        OpcUa_GotoErrorIfBad(uStatus);
        OPENSSL_free(pName);
        pName = OpcUa_Null;
    }

    if(a_pSubjectUri != OpcUa_Null || a_pSubjectIP != OpcUa_Null || a_pSubjectDNS != OpcUa_Null)
    {
        pNames = X509_get_ext_d2i(pX509Cert, NID_subject_alt_name, OpcUa_Null, OpcUa_Null);
        if (pNames != OpcUa_Null)
        {
            int num;
            for (num = 0; num < sk_GENERAL_NAME_num(pNames); num++)
            {
                GENERAL_NAME *value = sk_GENERAL_NAME_value(pNames, num);
                switch (value->type)
                {
                case GEN_URI:
                    if (a_pSubjectUri != OpcUa_Null && a_pSubjectUri->Data == OpcUa_Null)
                    {
                        a_pSubjectUri->Length = value->d.ia5->length+1;
                        a_pSubjectUri->Data = (OpcUa_Byte*)OpcUa_P_Memory_Alloc(a_pSubjectUri->Length*sizeof(OpcUa_Byte));
                        OpcUa_GotoErrorIfAllocFailed(a_pSubjectUri->Data);
                        uStatus = OpcUa_P_Memory_MemCpy(a_pSubjectUri->Data, a_pSubjectUri->Length, value->d.ia5->data, a_pSubjectUri->Length);
                        OpcUa_GotoErrorIfBad(uStatus);
                    }
                break;

                case GEN_IPADD:
                    if (a_pSubjectIP != OpcUa_Null && a_pSubjectIP->Data == OpcUa_Null)
                    {
                        a_pSubjectIP->Length = value->d.ip->length;
                        a_pSubjectIP->Data = (OpcUa_Byte*)OpcUa_P_Memory_Alloc(a_pSubjectIP->Length*sizeof(OpcUa_Byte));
                        OpcUa_GotoErrorIfAllocFailed(a_pSubjectIP->Data);
                        uStatus = OpcUa_P_Memory_MemCpy(a_pSubjectIP->Data, a_pSubjectIP->Length, value->d.ip->data, a_pSubjectIP->Length);
                        OpcUa_GotoErrorIfBad(uStatus);
                    }
                break;

                case GEN_DNS:
                    if (a_pSubjectDNS != OpcUa_Null && a_pSubjectDNS->Data == OpcUa_Null)
                    {
                        a_pSubjectDNS->Length = value->d.ia5->length+1;
                        a_pSubjectDNS->Data = (OpcUa_Byte*)OpcUa_P_Memory_Alloc(a_pSubjectDNS->Length*sizeof(OpcUa_Byte));
                        OpcUa_GotoErrorIfAllocFailed(a_pSubjectDNS->Data);
                        uStatus = OpcUa_P_Memory_MemCpy(a_pSubjectDNS->Data, a_pSubjectDNS->Length, value->d.ia5->data, a_pSubjectDNS->Length);
                        OpcUa_GotoErrorIfBad(uStatus);
                    }
                break;
                }
            }
            sk_GENERAL_NAME_pop_free(pNames, GENERAL_NAME_free);
            pNames = OpcUa_Null;
        }
    }

    if(a_pCertThumbprint != OpcUa_Null)
    {
        /* update pX509Cert->sha1_hash */
        X509_check_purpose(pX509Cert, -1, 0);
        a_pCertThumbprint->Length = sizeof(pX509Cert->sha1_hash);
        a_pCertThumbprint->Data = (OpcUa_Byte*)OpcUa_P_Memory_Alloc(a_pCertThumbprint->Length*sizeof(OpcUa_Byte));
        OpcUa_GotoErrorIfAllocFailed(a_pCertThumbprint->Data);
        uStatus = OpcUa_P_Memory_MemCpy(a_pCertThumbprint->Data, a_pCertThumbprint->Length, pX509Cert->sha1_hash, a_pCertThumbprint->Length);
        OpcUa_GotoErrorIfBad(uStatus);
    }

    if(a_pSubjectHash != OpcUa_Null)
    {
        *a_pSubjectHash = X509_NAME_hash(X509_get_subject_name(pX509Cert));
    }

    if(a_pCertRawLength != OpcUa_Null)
    {
        *a_pCertRawLength = (OpcUa_UInt32)(p - a_pCertificate->Data);
    }

    X509_free(pX509Cert);

OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;

    if(a_pIssuer != OpcUa_Null && a_pIssuer->Data != OpcUa_Null)
    {
        OpcUa_P_Memory_Free(a_pIssuer->Data);
        a_pIssuer->Data = OpcUa_Null;
        a_pIssuer->Length = 0;
    }

    if(a_pSubject != OpcUa_Null && a_pSubject->Data != OpcUa_Null)
    {
        OpcUa_P_Memory_Free(a_pSubject->Data);
        a_pSubject->Data = OpcUa_Null;
        a_pSubject->Length = 0;
    }

    if(a_pSubjectUri != OpcUa_Null && a_pSubjectUri->Data != OpcUa_Null)
    {
        OpcUa_P_Memory_Free(a_pSubjectUri->Data);
        a_pSubjectUri->Data = OpcUa_Null;
        a_pSubjectUri->Length = 0;
    }

    if(a_pSubjectIP != OpcUa_Null && a_pSubjectIP->Data != OpcUa_Null)
    {
        OpcUa_P_Memory_Free(a_pSubjectIP->Data);
        a_pSubjectIP->Data = OpcUa_Null;
        a_pSubjectIP->Length = 0;
    }

    if(a_pSubjectDNS != OpcUa_Null && a_pSubjectDNS->Data != OpcUa_Null)
    {
        OpcUa_P_Memory_Free(a_pSubjectDNS->Data);
        a_pSubjectDNS->Data = OpcUa_Null;
        a_pSubjectDNS->Length = 0;
    }

    if(a_pCertThumbprint != OpcUa_Null && a_pCertThumbprint->Data != OpcUa_Null)
    {
        OpcUa_P_Memory_Free(a_pCertThumbprint->Data);
        a_pCertThumbprint->Data = OpcUa_Null;
        a_pCertThumbprint->Length = 0;
    }

    if (pName != OpcUa_Null)
    {
        OPENSSL_free(pName);
    }

    if (pNames != OpcUa_Null)
    {
        sk_GENERAL_NAME_pop_free(pNames, GENERAL_NAME_free);
    }

    if(pX509Cert != OpcUa_Null)
    {
        X509_free(pX509Cert);
    }

OpcUa_FinishErrorHandling;
}
/*============================================================================
 * OpcUa_P_OpenSSL_Random_Key_PSHA256_Derive
 *===========================================================================*/
OpcUa_StatusCode OpcUa_P_OpenSSL_Random_Key_PSHA256_Derive(
    OpcUa_CryptoProvider* a_pProvider,
    OpcUa_ByteString      a_secret, /* clientnonce | servernonce, servernonce | clientnonce */
    OpcUa_ByteString      a_seed,
    OpcUa_Int32           a_keyLen, /* output len */
    OpcUa_Key*            a_pKey)
{
    OpcUa_P_OpenSSL_PSHA256_Ctx*pCtx            = OpcUa_Null;
    OpcUa_Byte*                 pBuffer         = OpcUa_Null;

    OpcUa_Int                   bufferlength;
    OpcUa_Int                   i;
    OpcUa_Int                   iterations;

    OpcUa_CryptoProviderConfig* pConfig         = OpcUa_Null;
    OpcUa_Int32                 keyLen          = 0;

OpcUa_InitializeStatus(OpcUa_Module_P_OpenSSL, "Random_Key_PSHA256_Derive");

    OpcUa_ReferenceParameter(a_pProvider);

    OpcUa_ReturnErrorIfArgumentNull(a_pProvider);
    OpcUa_ReturnErrorIfArgumentNull(a_secret.Data);
    OpcUa_ReturnErrorIfArgumentNull(a_seed.Data);
    OpcUa_ReturnErrorIfArgumentNull(a_pKey);

    keyLen = a_keyLen;

    if(keyLen < 0)
    {
        if(a_pProvider->Handle != OpcUa_Null)
        {
            /* get default configuration */
            pConfig = (OpcUa_CryptoProviderConfig*)a_pProvider->Handle;
            keyLen = pConfig->SymmetricKeyLength;
        }
        else
        {
            uStatus = OpcUa_BadInvalidArgument;
            OpcUa_GotoErrorIfBad(uStatus);
        }
    }
    else if(keyLen > MAX_DERIVED_OUTPUT_LEN)
    {
            uStatus = OpcUa_BadInvalidArgument;
            OpcUa_GotoErrorIfBad(uStatus);
    }

    if(a_pKey->Key.Data == OpcUa_Null)
    {
        a_pKey->Key.Length = keyLen;
        OpcUa_ReturnStatusCode;
    }

    a_pKey->Type = OpcUa_Crypto_KeyType_Random;

    /** start creating key **/

    pCtx = (OpcUa_P_OpenSSL_PSHA256_Ctx*)OpcUa_Null;

    iterations = keyLen/32 + (keyLen%32?1:0);
    bufferlength = iterations*32;

    pBuffer = (OpcUa_Byte *)OpcUa_P_Memory_Alloc(bufferlength * sizeof(OpcUa_Byte));

    pCtx = OpcUa_P_OpenSSL_PSHA256_Context_Create(a_secret.Data, a_secret.Length, a_seed.Data, a_seed.Length);

    for(i=0; i<iterations; i++)
    {
        /* SHA256 produces 32 Bytes of output for every iteration */
        uStatus = OpcUa_P_OpenSSL_PSHA256_Hash_Generate(pCtx, pBuffer + (i*32));
        OpcUa_GotoErrorIfBad(uStatus);
    }

    OpcUa_P_Memory_MemCpy(a_pKey->Key.Data, a_pKey->Key.Length, pBuffer, keyLen);

    if(pCtx != OpcUa_Null)
    {
        OpcUa_P_Memory_Free(pCtx);
        pCtx = OpcUa_Null;
    }

    if(pBuffer != OpcUa_Null)
    {
        OpcUa_P_Memory_Free(pBuffer);
        pBuffer = OpcUa_Null;
    }

OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;

    if(pCtx != OpcUa_Null)
    {
        OpcUa_P_Memory_Free(pCtx);
        pCtx = OpcUa_Null;
    }

    if(pBuffer != OpcUa_Null)
    {
        OpcUa_P_Memory_Free(pBuffer);
        pBuffer = OpcUa_Null;
    }

OpcUa_FinishErrorHandling;
}