diff options
Diffstat (limited to 'libdigidoc/DigiDocOCSP.c')
-rw-r--r-- | libdigidoc/DigiDocOCSP.c | 1670 |
1 files changed, 1670 insertions, 0 deletions
diff --git a/libdigidoc/DigiDocOCSP.c b/libdigidoc/DigiDocOCSP.c new file mode 100644 index 0000000..48973e7 --- /dev/null +++ b/libdigidoc/DigiDocOCSP.c @@ -0,0 +1,1670 @@ +//================================================== +// FILE: DigiDocOCSP.c +// PROJECT: Digi Doc +// DESCRIPTION: DigiDoc OCSP handling routines +// AUTHOR: Veiko Sinivee, S|E|B IT Partner Estonia +//================================================== +// Copyright (C) AS Sertifitseerimiskeskus +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// This library 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 +// Lesser General Public License for more details. +// GNU Lesser General Public Licence is available at +// http://www.gnu.org/copyleft/lesser.html +//==========< HISTORY >============================= +// 26.04.2006 Veiko Sinivee +// Creation +//================================================== + +#include <libdigidoc/DigiDocConfig.h> +#include <libdigidoc/DigiDocOCSP.h> +#include <libdigidoc/DigiDocError.h> +#include <libdigidoc/DigiDocDebug.h> +#include <libdigidoc/DigiDocConvert.h> +#include <libdigidoc/DigiDocLib.h> +#include <libdigidoc/DigiDocCert.h> +#include <libdigidoc/DigiDocVerify.h> +#include <libdigidoc/DigiDocHTTP.h> +#include <openssl/sha.h> +#include <openssl/rsa.h> +#include <openssl/evp.h> +#include <openssl/objects.h> +#include <openssl/x509.h> +#include <openssl/x509v3.h> +#include <openssl/err.h> +#include <openssl/pem.h> +#include <openssl/ssl.h> +#include <openssl/ocsp.h> +#include <openssl/pkcs12.h> +#include <openssl/rand.h> +#include <ctype.h> + +#ifdef FRAMEWORK +#ifdef __APPLE__ +#include <Security/Security.h> +#endif + +static int password_callback(char *buf, int bufsiz, int verify, void *cb_data) +{ + static const char password[] = "pass"; + int res = strlen(password); + if (res > bufsiz) + res = bufsiz; + memcpy(buf, password, res); + return res; +} +#endif + +//================< OCSP functions> ================================= + +static int ddocOcspProxyAuthInfo(char *authinfo, const char *user, const char *pass) +{ + BIO *b64 = 0, *hash = 0; + char *data = 0; + + RETURN_IF_NULL_PARAM(authinfo); + authinfo[0] = 0; + + if(!user && !pass) + return ERR_OK; + + b64 = BIO_new(BIO_f_base64()); + RETURN_IF_NOT(b64, ERR_NULL_POINTER); + BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); + + hash = BIO_push(b64, BIO_new(BIO_s_mem())); + RETURN_IF_NOT(hash, ERR_NULL_POINTER); + + BIO_printf(hash, "%s:%s", user, pass); + (void)BIO_flush(hash); + + BIO_get_mem_data(hash, &data); + sprintf(authinfo, "Proxy-Authorization: Basic %s\r\n", data); + + BIO_free_all(hash); + return ERR_OK; +} + + +//============================================================ +// Decodes binary (DER) OCSP_RESPONSE data and returns a OCSP_RESPONSE object +// ppResp - pointer to a buffer to receive newly allocated OCSP_RESPONSE pointer +// data - (DER) OCSP_RESPONSE data +// len - length of data in bytes +//============================================================ +EXP_OPTION int ddocDecodeOCSPResponseData(OCSP_RESPONSE **ppResp, const byte* data, int len) +{ + BIO* b1 = 0; + + // check input params + RETURN_IF_NULL_PARAM(data); + RETURN_IF_NULL_PARAM(ppResp); + // mark as not read yet + *ppResp = 0; + // create BIO + b1 = BIO_new_mem_buf((void*)data, len); + RETURN_IF_NOT(b1, ERR_NULL_POINTER); + // decode OCSP + *ppResp = d2i_OCSP_RESPONSE_bio(b1, NULL); + BIO_free(b1); + ddocDebug(3, "ddocDecodeOCSPResponseData", "Decoding %d bytes DER data - OCSP_RESPONSE %s", len, (*ppResp ? "OK" : "ERROR")); + RETURN_IF_NOT(*ppResp, ERR_OCSP_UNKNOWN_TYPE); + return ERR_OK; +} + +//============================================================ +// Decodes base64 (PEM) OCSP_RESPONSE data and returns a OCSP_RESPONSE object +// ppResp - pointer to a buffer to receive newly allocated OCSP_RESPONSE pointer +// data - (PEM) OCSP_RESPONSE data +// len - length of data in bytes +//============================================================ +EXP_OPTION int ddocDecodeOCSPResponsePEMData(OCSP_RESPONSE **ppResp, const byte* data, int len) +{ + byte* p1 = 0; + int l1 = 0, err = ERR_OK; + + // check input params + RETURN_IF_NULL_PARAM(data); + RETURN_IF_NULL_PARAM(ppResp); + // mark as not read yet + *ppResp = 0; + // allocate memory for decoding + l1 = len; // should be enough as it shrinks + p1 = (byte*)malloc(l1); + RETURN_IF_BAD_ALLOC(p1); + memset(p1, 0, l1); + // decode base64 data + decode((const byte*)data, len, p1, &l1); + // decode OCSP + err = ddocDecodeOCSPResponseData(ppResp, p1, l1); + // cleanup + if(p1) + free(p1); + ddocDebug(3, "ddocDecodeOCSPResponsePEMData", "Decoding %d bytes PEM data - OSCP_RESPONSE %s", len, (*ppResp ? "OK" : "ERROR")); + return err; +} + +//============================================================ +// Reads in an OCSP Response file in DER format +// szFileName - OCSP response file name +//============================================================ +int ReadOCSPResponse(OCSP_RESPONSE **newOCSP_RESPONSE, const char* szFileName) +{ + BIO *bin = NULL; + OCSP_RESPONSE *resp = NULL; + int err = ERR_OK; + + ddocDebug(4, "ReadOCSPResponse", "File: %s", szFileName); + RETURN_IF_NULL_PARAM(newOCSP_RESPONSE); + RETURN_IF_NULL_PARAM(szFileName); + + if((bin = BIO_new_file(szFileName, "rb")) != NULL) { + ddocDebug(4, "ReadOCSPResponse", "File opened"); + resp = d2i_OCSP_RESPONSE_bio(bin, NULL); + BIO_free(bin); + if (resp == NULL) { + err = ERR_OCSP_WRONG_VERSION; + } + } else { + ddocDebug(4, "ReadOCSPResponse", "Cannot read file:%s", szFileName); + err =ERR_FILE_READ; + } + if (err != ERR_OK) SET_LAST_ERROR(err); + *newOCSP_RESPONSE = resp; + return err; +} + +//============================================================ +// Writes an OCSP Response to a file in DER format +// szFileName - OCSP response file name +// resp - OCSP response object +//============================================================ +int WriteOCSPResponse(const char* szFileName, const OCSP_RESPONSE* resp) +{ + BIO* bout = 0; + + RETURN_IF_NULL_PARAM(szFileName); + RETURN_IF_NULL_PARAM(resp); + if((bout = BIO_new_file(szFileName, "wb")) != NULL) { +#if OPENSSL_VERSION_NUMBER > 0x00908000 + ASN1_i2d_bio((int (*)(void*, unsigned char**))i2d_OCSP_RESPONSE, bout, (unsigned char*)resp); +#else + ASN1_i2d_bio((int (*)())i2d_OCSP_RESPONSE, bout, (unsigned char*)resp); +#endif + //i2d_OCSP_RESPONSE_bio((unsigned char*)bout, resp); + BIO_free(bout); + } else + SET_LAST_ERROR_RETURN_CODE(ERR_FILE_WRITE); + return ERR_OK; +} + +//============================================================ +// Reads in an OCSP Request file in DER format +// szFileName - OCSP Request file name +//============================================================ +int ReadOCSPRequest(OCSP_REQUEST **newOCSP_REQUEST, const char* szFileName) +{ + BIO *bin = NULL; + OCSP_REQUEST *req = NULL; + int err = ERR_OK; + + RETURN_IF_NULL_PARAM(*newOCSP_REQUEST); + RETURN_IF_NULL_PARAM(szFileName); + + if((bin = BIO_new_file(szFileName, "rb")) != NULL) { + req = d2i_OCSP_REQUEST_bio(bin, NULL); + BIO_free(bin); + if (req == NULL) { + err = ERR_OCSP_WRONG_VERSION; + } + } else + err =ERR_FILE_READ; + if (err != ERR_OK) SET_LAST_ERROR(err); + *newOCSP_REQUEST = req; + return err; +} + +//============================================================ +// Writes an OCSP Request to a file in DER format +// szFileName - OCSP Request file name +// resp - OCSP Request object +//============================================================ +int WriteOCSPRequest(const char* szFileName, const OCSP_REQUEST* req) +{ + BIO* bout = 0; + + if((bout = BIO_new_file(szFileName, "wb")) != NULL) { +#if OPENSSL_VERSION_NUMBER > 0x00908000 + ASN1_i2d_bio((int (*)(void*, unsigned char**))i2d_OCSP_RESPONSE, bout, (unsigned char*)req); +#else + ASN1_i2d_bio((int (*)())i2d_OCSP_RESPONSE, bout, (unsigned char*)req); +#endif + //i2d_OCSP_REQUEST_bio(bout, req); + BIO_free(bout); + } else + SET_LAST_ERROR_RETURN_CODE(ERR_FILE_WRITE); + return ERR_OK; +} + + + +//============================================================ +// Checks OCSP certificate status and handles errors +// status - status code +// return error code +//============================================================ +int handleOCSPCertStatus(int status) +{ + int err = ERR_OK; + switch(status) { + case V_OCSP_CERTSTATUS_GOOD: // cert is ok, do nothing + break; + case V_OCSP_CERTSTATUS_REVOKED: // cert has been revoked + err = ERR_OCSP_CERT_REVOKED; + break; + case V_OCSP_CERTSTATUS_UNKNOWN: // cert status unknown + err = ERR_OCSP_CERT_UNKNOWN; + break; + default: // should never happen? + err = ERR_OCSP_RESP_STATUS; + } + return err; +} + +//============================================================ +// Calculates NotaryInfo digest if possible +// pSigDoc - digidoc main object pointer +// pNotary - NotaryInfo object to be initialized +// return error code +//============================================================ +int calcNotaryDigest(SignedDoc* pSigDoc, NotaryInfo* pNotary) +{ + int err = ERR_OK, l1; + //AM 24.04.08 increased buffer size for sha256 + char buf1[DIGEST_LEN256+2]; + + RETURN_IF_NULL_PARAM(pNotary); + RETURN_IF_NULL_PARAM(pSigDoc); + l1 = sizeof(buf1); + err = calculateNotaryInfoDigest(pSigDoc, pNotary, (byte*)buf1, &l1); + //err = calculateOcspBasicResponseDigest(br, buf1, &l1); + if(!err) { + err = ddocNotInfo_SetOcspDigest(pNotary, buf1, l1); + } + return err; +} + +//============================================================ +// Initializes NotaryInfo object with data from OCSP object +// pSigDoc - digidoc main object pointer +// pNotary - NotaryInfo object to be initialized +// resp - OCSP response object +// notCert - Notary cert object +// return error code +//============================================================ +int initializeNotaryInfoWithOCSP(SignedDoc *pSigDoc, NotaryInfo *pNotary, + OCSP_RESPONSE *resp, X509 *notCert, int initDigest) +{ + int n, err = ERR_OK; + char buf[500]; + OCSP_RESPBYTES *rb = NULL; + OCSP_BASICRESP *br = NULL; + OCSP_RESPDATA *rd = NULL; + OCSP_RESPID *rid = NULL; + // OCSP_CERTSTATUS *cst = NULL; + OCSP_SINGLERESP *single = NULL; + OCSP_CERTID *cid = NULL; + X509_EXTENSION *nonce; + //AM 26.09.08 + DigiDocMemBuf mbuf1; + mbuf1.pMem = 0; + mbuf1.nLen = 0; + + + RETURN_IF_NULL_PARAM(pNotary); + RETURN_IF_NULL_PARAM(resp); + ddocDebug(3, "initializeNotaryInfoWithOCSP", "OCSP status: %d", OCSP_response_status(resp)); + // save the response in memory + err = ddocNotInfo_SetOCSPResponse_Value(pNotary, resp); + + // check the OCSP Response validity + switch(OCSP_response_status(resp)) { + case OCSP_RESPONSE_STATUS_SUCCESSFUL: // OK + break; + case OCSP_RESPONSE_STATUS_MALFORMEDREQUEST: + SET_LAST_ERROR_RETURN_CODE(ERR_OCSP_MALFORMED); + case OCSP_RESPONSE_STATUS_INTERNALERROR: + SET_LAST_ERROR_RETURN_CODE(ERR_OCSP_INTERNALERR); + case OCSP_RESPONSE_STATUS_TRYLATER: + SET_LAST_ERROR_RETURN_CODE(ERR_OCSP_TRYLATER); + case OCSP_RESPONSE_STATUS_SIGREQUIRED: + SET_LAST_ERROR_RETURN_CODE(ERR_OCSP_SIGREQUIRED); + case OCSP_RESPONSE_STATUS_UNAUTHORIZED: + SET_LAST_ERROR_RETURN_CODE(ERR_OCSP_UNAUTHORIZED); + default: + SET_LAST_ERROR_RETURN_CODE(ERR_OCSP_UNSUCCESSFUL); + } + RETURN_IF_NULL_PARAM(resp->responseBytes); + rb = resp->responseBytes; + if(OBJ_obj2nid(rb->responseType) != NID_id_pkix_OCSP_basic) + SET_LAST_ERROR_RETURN_CODE(ERR_OCSP_UNKNOWN_TYPE); + if((br = OCSP_response_get1_basic(resp)) == NULL) + SET_LAST_ERROR_RETURN_CODE(ERR_OCSP_NO_BASIC_RESP); + ddocDebug(4, "initializeNotaryInfoWithOCSP", "test2"); + rd = br->tbsResponseData; + if(ASN1_INTEGER_get(rd->version) != 0) + SET_LAST_ERROR_RETURN_CODE(ERR_OCSP_WRONG_VERSION); + n = sk_OCSP_SINGLERESP_num(rd->responses); + if(n != 1) + SET_LAST_ERROR_RETURN_CODE(ERR_OCSP_ONE_RESPONSE); + single = sk_OCSP_SINGLERESP_value(rd->responses, 0); + RETURN_IF_NULL(single); + cid = single->certId; + RETURN_IF_NULL(cid); + ddocDebug(4, "initializeNotaryInfoWithOCSP", "CertStatus-type: %d", single->certStatus->type); + //printf("TYPE: %d\n", single->certStatus->type); + if(single->certStatus->type != 0) { + ddocDebug(4, "initializeNotaryInfoWithOCSP", "errcode: %d", handleOCSPCertStatus(single->certStatus->type)); + SET_LAST_ERROR_RETURN_CODE(handleOCSPCertStatus(single->certStatus->type)); + } + //Removed 31.10.2003 + //if(single->singleExtensions) + // SET_LAST_ERROR_RETURN_CODE(ERR_OCSP_NO_SINGLE_EXT); + if(!rd->responseExtensions || + (sk_X509_EXTENSION_num(rd->responseExtensions) != 1) || + ((nonce = sk_X509_EXTENSION_value(rd->responseExtensions, 0)) == NULL)) + SET_LAST_ERROR_RETURN_CODE(ERR_OCSP_NO_NONCE); + i2t_ASN1_OBJECT(buf,sizeof(buf),nonce->object); + if(strcmp(buf, OCSP_NONCE_NAME)) + SET_LAST_ERROR_RETURN_CODE(ERR_OCSP_NO_NONCE); + rid = rd->responderId; + if(rid->type == V_OCSP_RESPID_NAME) { + pNotary->nRespIdType = RESPID_NAME_TYPE; + } else if(rid->type == V_OCSP_RESPID_KEY) { + pNotary->nRespIdType = RESPID_KEY_TYPE; + } else { + SET_LAST_ERROR_RETURN_CODE(ERR_OCSP_WRONG_RESPID); + } + // producedAt + err = asn1time2str(pSigDoc, rd->producedAt, buf, sizeof(buf)); + setString(&(pNotary->timeProduced), buf, -1); + n = sizeof(buf); + if(rid->type == V_OCSP_RESPID_NAME){ + //X509_NAME_oneline(rid->value.byName,buf,n); + err = ddocCertGetDNFromName(rid->value.byName, &mbuf1); + err = ddocNotInfo_SetResponderId(pNotary, (char*)mbuf1.pMem, -1); + ddocMemBuf_free(&mbuf1); + } + if(rid->type == V_OCSP_RESPID_KEY) { + err = ddocNotInfo_SetResponderId(pNotary, (const char*)rid->value.byKey->data, rid->value.byKey->length); + } + // digest type + i2t_ASN1_OBJECT(buf,sizeof(buf),cid->hashAlgorithm->algorithm); + //AM 24.11.09 why its needed? added if. 08.12.09 used for gen + if(!pNotary->szDigestType){ + setString(&(pNotary->szDigestType), buf, -1); + } + // signature algorithm + i2t_ASN1_OBJECT(buf,sizeof(buf),br->signatureAlgorithm->algorithm); + setString(&(pNotary->szSigType), buf, -1); + // notary cert + if(notCert && !err) + err = addNotaryInfoCert(pSigDoc, pNotary, notCert); + // get the digest from original OCSP data + if(initDigest && notCert) { + err = calcNotaryDigest(pSigDoc, pNotary); + } + if(br != NULL) + OCSP_BASICRESP_free(br); + if (err != ERR_OK) SET_LAST_ERROR(err); + return err; +} + +int initializeNotaryInfoWithOCSP2(SignedDoc *pSigDoc, NotaryInfo *pNotary, + OCSP_RESPONSE *resp, X509 *notCert, int initDigest) +{ + int n, err = ERR_OK; + char buf[500]; + OCSP_RESPBYTES *rb = NULL; + OCSP_BASICRESP *br = NULL; + OCSP_RESPDATA *rd = NULL; + OCSP_RESPID *rid = NULL; + // OCSP_CERTSTATUS *cst = NULL; + OCSP_SINGLERESP *single = NULL; + OCSP_CERTID *cid = NULL; + X509_EXTENSION *nonce; + //AM 26.09.08 + DigiDocMemBuf mbuf1; + mbuf1.pMem = 0; + mbuf1.nLen = 0; + + + RETURN_IF_NULL_PARAM(pNotary); + RETURN_IF_NULL_PARAM(resp); + // check the OCSP Response validity + switch(OCSP_response_status(resp)) { + case OCSP_RESPONSE_STATUS_SUCCESSFUL: // OK + break; + case OCSP_RESPONSE_STATUS_MALFORMEDREQUEST: + SET_LAST_ERROR_RETURN_CODE(ERR_OCSP_MALFORMED); + case OCSP_RESPONSE_STATUS_INTERNALERROR: + SET_LAST_ERROR_RETURN_CODE(ERR_OCSP_INTERNALERR); + case OCSP_RESPONSE_STATUS_TRYLATER: + SET_LAST_ERROR_RETURN_CODE(ERR_OCSP_TRYLATER); + case OCSP_RESPONSE_STATUS_SIGREQUIRED: + SET_LAST_ERROR_RETURN_CODE(ERR_OCSP_SIGREQUIRED); + case OCSP_RESPONSE_STATUS_UNAUTHORIZED: + SET_LAST_ERROR_RETURN_CODE(ERR_OCSP_UNAUTHORIZED); + default: + SET_LAST_ERROR_RETURN_CODE(ERR_OCSP_UNSUCCESSFUL); + } + RETURN_IF_NULL_PARAM(resp->responseBytes);; + rb = resp->responseBytes; + if(OBJ_obj2nid(rb->responseType) != NID_id_pkix_OCSP_basic) + SET_LAST_ERROR_RETURN_CODE(ERR_OCSP_UNKNOWN_TYPE); + if((br = OCSP_response_get1_basic(resp)) == NULL) + SET_LAST_ERROR_RETURN_CODE(ERR_OCSP_NO_BASIC_RESP); + rd = br->tbsResponseData; + if(ASN1_INTEGER_get(rd->version) != 0) + SET_LAST_ERROR_RETURN_CODE(ERR_OCSP_WRONG_VERSION); + n = sk_OCSP_SINGLERESP_num(rd->responses); + if(n != 1) + SET_LAST_ERROR_RETURN_CODE(ERR_OCSP_ONE_RESPONSE); + single = sk_OCSP_SINGLERESP_value(rd->responses, 0); + RETURN_IF_NULL(single); + cid = single->certId; + RETURN_IF_NULL(cid); + ddocDebug(4, "initializeNotaryInfoWithOCSP", "CertStatus-type: %d", single->certStatus->type); + //printf("TYPE: %d\n", single->certStatus->type); + //Am test + /*if(single->certStatus->type != 0) { + ddocDebug(4, "initializeNotaryInfoWithOCSP", "errcode: %d", handleOCSPCertStatus(single->certStatus->type)); + SET_LAST_ERROR_RETURN_CODE(handleOCSPCertStatus(single->certStatus->type)); + }*/ + //Removed 31.10.2003 + //if(single->singleExtensions) + // SET_LAST_ERROR_RETURN_CODE(ERR_OCSP_NO_SINGLE_EXT); + if(!rd->responseExtensions || + (sk_X509_EXTENSION_num(rd->responseExtensions) != 1) || + ((nonce = sk_X509_EXTENSION_value(rd->responseExtensions, 0)) == NULL)) + SET_LAST_ERROR_RETURN_CODE(ERR_OCSP_NO_NONCE); + i2t_ASN1_OBJECT(buf,sizeof(buf),nonce->object); + if(strcmp(buf, OCSP_NONCE_NAME)) + SET_LAST_ERROR_RETURN_CODE(ERR_OCSP_NO_NONCE); + rid = rd->responderId; + if(rid->type == V_OCSP_RESPID_NAME) { + pNotary->nRespIdType = RESPID_NAME_TYPE; + } else if(rid->type == V_OCSP_RESPID_KEY) { + pNotary->nRespIdType = RESPID_KEY_TYPE; + } else { + SET_LAST_ERROR_RETURN_CODE(ERR_OCSP_WRONG_RESPID); + } + // producedAt + err = asn1time2str(pSigDoc, rd->producedAt, buf, sizeof(buf)); + setString(&(pNotary->timeProduced), buf, -1); + n = sizeof(buf); + if(rid->type == V_OCSP_RESPID_NAME){ + err = ddocCertGetDNFromName(rid->value.byName, &mbuf1); + RETURN_IF_NOT(err == ERR_OK, err); + err = ddocNotInfo_SetResponderId(pNotary, (char*)mbuf1.pMem, -1); + ddocMemBuf_free(&mbuf1); + } + if(rid->type == V_OCSP_RESPID_KEY) { + err = ddocNotInfo_SetResponderId(pNotary, (const char*)rid->value.byKey->data, rid->value.byKey->length); + } + // digest type + i2t_ASN1_OBJECT(buf,sizeof(buf),cid->hashAlgorithm->algorithm); + setString(&(pNotary->szDigestType), buf, -1); + // signature algorithm + i2t_ASN1_OBJECT(buf,sizeof(buf),br->signatureAlgorithm->algorithm); + setString(&(pNotary->szSigType), buf, -1); + // notary cert + if(notCert && !err) + err = addNotaryInfoCert(pSigDoc, pNotary, notCert); + // save the response in memory + err = ddocNotInfo_SetOCSPResponse_Value(pNotary, resp); + // get the digest from original OCSP data + if(initDigest && notCert) { + err = calcNotaryDigest(pSigDoc, pNotary); + } + if(br != NULL) + OCSP_BASICRESP_free(br); + if (err != ERR_OK) SET_LAST_ERROR(err); + return err; +} + +//-------------------------------------------------- +// Helper function to read OCSP_RESPONSE from binary input data +// ppResp - address of newly allocated OCSP_RESPONSE object +// pMBufInData - input data +// returns error code or ERR_OK +//-------------------------------------------------- +int ddocOcspReadOcspResp(OCSP_RESPONSE** ppResp, DigiDocMemBuf* pMBufInData) +{ + int err = ERR_OK; + unsigned char* p1; + + RETURN_IF_NULL_PARAM(ppResp); + RETURN_IF_NULL_PARAM(pMBufInData); + RETURN_IF_NULL_PARAM(pMBufInData->pMem); + *ppResp = 0; + ddocDebug(4, "ddocOcspReadOcspResp", "converting: %d bytes to OCSP_RESPONSE", pMBufInData->nLen); + p1 = (unsigned char*)pMBufInData->pMem; + d2i_OCSP_RESPONSE(ppResp, (const unsigned char**)&p1, pMBufInData->nLen); + ddocDebug(4, "ddocOcspReadOcspResp", "OCSP_RESPONSE: %s", (*ppResp ? "OK" : "ERR")); + if(!(*ppResp)) err = ERR_OCSP_UNSUCCESSFUL; + return err; +} + +//-------------------------------------------------- +// Helper function to write OCSP_RESPONSE to binary output data +// pResp - address of OCSP_RESPONSE object +// pMBufOutData - output data +// returns error code or ERR_OK +//-------------------------------------------------- +int ddocOcspWriteOcspResp(OCSP_RESPONSE* pResp, DigiDocMemBuf* pMBufOutData) +{ + int err = ERR_OK, l1; + unsigned char* p1; + + RETURN_IF_NULL_PARAM(pResp); + RETURN_IF_NULL_PARAM(pMBufOutData); + pMBufOutData->pMem = NULL; + pMBufOutData->nLen = 0; + // find out how big a buffer we need + l1 = i2d_OCSP_RESPONSE(pResp, NULL); + ddocDebug(4, "ddocOcspReadOcspResp", "converting: %d bytes from OCSP_RESPONSE", l1); + // alloc mem + err = ddocMemSetLength(pMBufOutData, l1 + 50); + p1 = (unsigned char*)pMBufOutData->pMem; + l1 = i2d_OCSP_RESPONSE(pResp, &p1); + pMBufOutData->nLen = l1; + ddocDebug(4, "ddocOcspReadOcspResp", "Converted data: %d", l1); + return err; +} + +//============================================================ +// Converts OCSP_RESPONSE to PEM form with or without the headers +// pResp - OCSP_RESPONSE +// bHeaders - 1= with headers, 0=no headers +// buf - output buffer newly allocated +// returns error code +//============================================================ +EXP_OPTION int getOcspPEM(OCSP_RESPONSE* pResp, int bHeaders, char** buf) +{ + int l1, l2; + char *p1, *p2; + + RETURN_IF_NULL_PARAM(buf); + RETURN_IF_NULL_PARAM(pResp); + l1 = i2d_OCSP_RESPONSE(pResp, NULL); + p1 = (char*)malloc(l1+10); + RETURN_IF_BAD_ALLOC(p1); + p2 = p1; + i2d_OCSP_RESPONSE(pResp, (unsigned char**)&p2); + l2 = l1 * 2 + 200; + *buf = (char*)malloc(l2); + if(*buf == NULL) { + free(p1); + RETURN_IF_BAD_ALLOC(*buf); + } + memset(*buf, 0, l2); + if(bHeaders) + strncpy(*buf, "-----BEGIN OCSP RESPONSE-----\n", l2); + encode((const byte*)p1, l1, (byte*)strchr(*buf, 0), &l2); + if(bHeaders) + strncat(*buf, "\n-----END OCSP RESPONSE-----", l2 - strlen(*buf)); + free(p1); + return ERR_OK; +} + +//============================================================ +// Converts OCSP_REQUEST to DER form +// pResp - OCSP_REQUEST +// pMBuf - output buffer for OCSP req +// returns error code +//============================================================ +EXP_OPTION int ddocWriteOcspDER(OCSP_REQUEST* pReq, DigiDocMemBuf* pMBuf) +{ + int l1; + char *p1, *p2; + + RETURN_IF_NULL_PARAM(pMBuf); + RETURN_IF_NULL_PARAM(pReq); + l1 = i2d_OCSP_REQUEST(pReq, NULL); + p1 = (char*)malloc(l1+10); + RETURN_IF_BAD_ALLOC(p1); + p2 = p1; + i2d_OCSP_REQUEST(pReq, (unsigned char**)&p2); + ddocMemAppendData(pMBuf, p1, l1); + free(p1); + return ERR_OK; +} + + +//-------------------------------------------------- +// Helper function to return OCSP_RESPONSE in base64 form +// Memory buffer will be resized as necessary. +// Caller must release output buffer. +// pNotary - Notary object +// bHeaders - 1= with headers, 0=no headers +// pMBufOutData - output data +// returns error code or ERR_OK +//-------------------------------------------------- +EXP_OPTION int ddocGetOcspBase64(NotaryInfo *pNotary, int bHeaders, DigiDocMemBuf* pMBufOutData) +{ + const DigiDocMemBuf *pMBuf = 0; + DigiDocMemBuf mbuf1; + + RETURN_IF_NULL_PARAM(pNotary); + RETURN_IF_NULL_PARAM(pMBufOutData); + pMBufOutData->pMem = 0; + pMBufOutData->nLen = 0; + mbuf1.pMem = 0; + mbuf1.nLen = 0; + pMBuf = ddocNotInfo_GetOCSPResponse(pNotary); + RETURN_IF_NULL(pMBuf); + if(bHeaders) { + ddocMemAppendData(pMBufOutData, "-----BEGIN OCSP RESPONSE-----\n", -1); + ddocEncodeBase64(pMBuf, &mbuf1); + ddocMemAppendData(pMBufOutData, (const char*)mbuf1.pMem, mbuf1.nLen); + ddocMemAppendData(pMBufOutData, "\n-----END OCSP RESPONSE-----", -1); + ddocMemBuf_free(&mbuf1); + } + else + ddocEncodeBase64(pMBuf, pMBufOutData); + return ERR_OK; +} + + +//-------------------------------------------------- +// teeb 00:0a:df stiilis hexprinditud stringist tagasi tavalise +//-------------------------------------------------- +// Tanel - ver 1.66 +unsigned char *decodeHex(unsigned char *str) +{ + unsigned int i, j, k, len; + unsigned char *ret; + static unsigned char hex[] = { '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + + len = (int)(strlen((const char*)str) / 3) + 2; + if((ret=(unsigned char*)malloc(len)) == NULL) + return NULL; + memset(ret, 0, len); + for(i=0, j=0; i<strlen((const char*)str); i+=3) { + for(k=0; k<16; k++) + if(str[i] == hex[k]) + ret[j] = (unsigned char)(k<<4); + for(k=0; k<16; k++) + if(str[i+1] == hex[k]) + ret[j++] += (unsigned char)k; + } + return(ret); +} + +//-------------------------------------------------- +// otsib X.509v3 laienduste seest Authority Key Identifieri välja +//-------------------------------------------------- +// Tanel - ver 1.66 +unsigned char *get_authority_key(STACK_OF(X509_EXTENSION) *exts) +{ + int i, found=0; + X509_EXTENSION *ex=0; + ASN1_OBJECT *obj; + X509V3_EXT_METHOD *met; + void *st = NULL; + unsigned char *p; + STACK_OF(CONF_VALUE) *vals=NULL; + CONF_VALUE *val; + unsigned char *ret = 0; + + for(i=0; i<sk_X509_EXTENSION_num(exts); i++) { + ex = sk_X509_EXTENSION_value(exts, i); + obj = X509_EXTENSION_get_object(ex); + if(OBJ_obj2nid(obj) == NID_authority_key_identifier) { + found++; + break; + } + } + + if(!found) { + ddocDebug(4, "get_authority_key", "Extension not found"); + return(NULL); + } + + met = (X509V3_EXT_METHOD*)X509V3_EXT_get(ex); + p = ex->value->data; +#if OPENSSL_VERSION_NUMBER > 0x00908000 + // crashes here! + st = ASN1_item_d2i(NULL, (const unsigned char**)&p, ex->value->length, ASN1_ITEM_ptr(met->it)); +#else + st = ASN1_item_d2i(NULL, &p, ex->value->length, ASN1_ITEM_ptr(met->it)); +#endif + vals = met->i2v(met, st, NULL); + + /* P.R */ + ASN1_item_free((ASN1_VALUE *)st, ASN1_ITEM_ptr(met->it)); + /* P.R */ + + for(i=0; i<sk_CONF_VALUE_num(vals); i++) { + val = sk_CONF_VALUE_value(vals, i); + ddocDebug(4, "get_authority_key", "Extension %s - %s", val->name, val->value); + if(val->name && (strcmp(val->name, "keyid") == 0)) + ret = decodeHex((unsigned char*)val->value); + } + /* P.R */ + sk_CONF_VALUE_pop_free(vals, X509V3_conf_free); + /* P.R */ + + return ret; + +} + + + +//-------------------------------------------------- +// otsib X.509 seest Authority Key Identifieri välja +//-------------------------------------------------- +unsigned char *get_authority_key_from_cert(X509 *x) +{ + unsigned char *ret = 0; + AUTHORITY_KEYID *val = (AUTHORITY_KEYID*)X509_get_ext_d2i( x, NID_authority_key_identifier, NULL, NULL ); + if(!val) { + ddocDebug(4, "get_authority_key_from_cert", "Extension not found"); + return(NULL); + } + + //ret = ASN1_STRING_data(val->keyid); + // workaround encode/decode bugs + ret = decodeHex((unsigned char*)hex_to_string(ASN1_STRING_data(val->keyid), ASN1_STRING_length(val->keyid))); + AUTHORITY_KEYID_free(val); + + return ret; +} + + + +//-------------------------------------------------- +// creates OCSP_CERTID without using the issuer cert +// cert - certificate for which we need certid +// returns OCSP_CERTID pointer +//-------------------------------------------------- +// Tanel - ver 1.66 +OCSP_CERTID* createOCSPCertid(X509 *cert, X509* pCACert) +{ + OCSP_CERTID *pId = NULL; + X509_NAME *iname; + unsigned char *ikey = NULL; + ASN1_INTEGER *sno; + const EVP_MD *dgst; + X509_ALGOR *alg; + unsigned char md[EVP_MAX_MD_SIZE], buf1[100]; + unsigned int len; + int l1; + DigiDocMemBuf mbuf1, mbuf2; + + mbuf1.pMem = mbuf2.pMem = NULL; + mbuf1.nLen = mbuf2.nLen = 0; + l1 = (int)sizeof(buf1); + memset(buf1, 0, l1); + if(cert != NULL) { + ddocCertGetSubjectDN(cert, &mbuf1); + // standard variant would be + //pId = OCSP_cert_to_id(EVP_sha1(), cert, issuer); + if(pCACert) { + ddocCertGetSubjectDN(pCACert, &mbuf2); + ddocDebug(3, "createOCSPCertid", "Create ocsp id for cert: %s by CA: %s", (char*)mbuf1.pMem, (char*)mbuf2.pMem); + pId = OCSP_cert_to_id(EVP_sha1(), cert, pCACert); + + } else { // CA unknown + ddocDebug(3, "createOCSPCertid", "Create ocsp id for cert: %s unknown CA", (char*)mbuf1.pMem); + // issuer name hashi arvutamine + iname = X509_get_issuer_name(cert); + dgst = EVP_sha1(); + len = sizeof(md); + if(X509_NAME_digest(iname, dgst, md, &len)) { + // issuer key hashi lugemine + //ikey = get_authority_key(cert->cert_info->extensions); + ikey = get_authority_key_from_cert(cert); + if(ikey != NULL) { + // serial numbri lugemine + sno = X509_get_serialNumber(cert); + // OCSP certid koostamine + if((pId = OCSP_CERTID_new()) != NULL) { + // replace default algorithm ??? + alg = pId->hashAlgorithm; + if(alg->algorithm != NULL) + ASN1_OBJECT_free(alg->algorithm); + alg->algorithm = OBJ_nid2obj(EVP_MD_type(dgst)); + if((alg->parameter = ASN1_TYPE_new()) != NULL) { + alg->parameter->type = V_ASN1_NULL; + ASN1_INTEGER_free(pId->serialNumber); + pId->serialNumber = ASN1_INTEGER_dup(sno); + if(!ASN1_OCTET_STRING_set(pId->issuerNameHash, md, len) || + !ASN1_OCTET_STRING_set(pId->issuerKeyHash, ikey, strlen((const char*)ikey)) || + !pId->serialNumber) + { + fprintf(stderr, "Unable to fill in CID\n"); + OCSP_CERTID_free(pId); + pId = NULL; + } + } // else - failed to create algorithm + } + // cleanup ikey + free(ikey); + } + } // else - SHA1 failed + } + } + ddocMemBuf_free(&mbuf1); + ddocMemBuf_free(&mbuf2); + if(pId) + bin2hex((const byte*)pId->issuerKeyHash->data, pId->issuerKeyHash->length, (byte*)buf1, &l1); + ddocDebug(3, "createOCSPCertid", "Created ocsp id %s issuer-key-hash: %s", (pId ? "OK" : "ERR"), buf1); + return pId; +} + + +//-------------------------------------------------- +// Creates an OCSP_REQUEST object +// pSigDoc - address of signed document. If not NULL then +// used to check if older openssl 0.9.6 style request must +// be constructed. +// req - buffer for storing the pointer of new object +// cert - client certificate to verify +// nonce - nonce value (e.g. client signature value RSA-128 bytes) +// nlen - nonce value length +// pkey - public key used to signe th request (not used yet) +//-------------------------------------------------- +// VS - ver 1.66 +int createOCSPRequest(SignedDoc* pSigDoc, OCSP_REQUEST **req, + X509 *cert, X509* pCACert, byte* nonce, int nlen) +{ + int err = ERR_OK, l2; + OCSP_CERTID *id = 0; + byte buf2[DIGEST_LEN256 * 2 + 2]; + + RETURN_IF_NULL_PARAM(req); + RETURN_IF_NULL_PARAM(cert); + RETURN_IF_NULL_PARAM(nonce); + //RETURN_IF_NULL_PARAM(pCACert); + if((*req = OCSP_REQUEST_new()) != 0) { + if((id = createOCSPCertid(cert, pCACert)) != 0) { + if(OCSP_request_add0_id(*req, id)) { + if((err = OCSP_request_add1_nonce(*req, nonce, nlen)) != 0) + err = ERR_OK; + // debug + l2 = sizeof(buf2); + memset(buf2, 0, l2); + if(nlen <= DIGEST_LEN256) { + bin2hex((const char*)nonce, nlen, (byte*)buf2, &l2); + ddocDebug(3, "createOCSPRequest", "Sending nonce: %s len: %d err: %d", buf2, nlen, err); + } + } + } + } + return err; +} + +/* Quick and dirty HTTP OCSP request handler. + * Could make this a bit cleverer by adding + * support for non blocking BIOs and a few + * other refinements. + * Qick and dirty adaption of openssl -s + * OCSP_sendreq_bio() to add UserAgent HTTP header + */ + +OCSP_RESPONSE *OCSP_sendreq_bio_withParams(BIO *b, char *path, + OCSP_REQUEST *req, unsigned long ip_addr ) +{ + BIO *mem = NULL; + char tmpbuf[1024], adrhdr[100]; + OCSP_RESPONSE *resp = NULL; + char *p, *q, *r; + int len, retcode; + static char req_txt[] = +"POST %s HTTP/1.0\r\n\ +Content-Type: application/ocsp-request\r\n\ +User-Agent: LIB %s/%s APP %s\r\n%s\ +Content-Length: %d\r\n\r\n"; + + adrhdr[0] = 0; + if(ip_addr > 0) + snprintf(adrhdr, sizeof(adrhdr), "From: %d.%d.%d.%d\r\n", + (int)(ip_addr>>24)&0xFF, (int)(ip_addr>>16)&0xFF, (int)(ip_addr>>8)&0xFF, (int)(ip_addr&0xFF)); + len = i2d_OCSP_REQUEST(req, NULL); + if(BIO_printf(b, req_txt, path, getLibName(), getLibVersion(), + getGUIVersion(), (ip_addr > 0 ? adrhdr : ""), len) < 0) { + OCSPerr(OCSP_F_OCSP_SENDREQ_BIO,OCSP_R_SERVER_WRITE_ERROR); + goto err; + } +#if OPENSSL_VERSION_NUMBER > 0x00908000 + retcode = ASN1_i2d_bio((int (*)(void*, unsigned char**))i2d_OCSP_REQUEST, b, (unsigned char*)req); +#else + retcode = ASN1_i2d_bio((int (*)())i2d_OCSP_REQUEST, b, (unsigned char*)req); +#endif + if(retcode <= 0) { + OCSPerr(OCSP_F_OCSP_SENDREQ_BIO,OCSP_R_SERVER_WRITE_ERROR); + goto err; + } + mem = BIO_new(BIO_s_mem()); + if(!mem) goto err; + /* Copy response to a memory BIO: socket bios can't do gets! */ + do { + len = BIO_read(b, tmpbuf, sizeof tmpbuf); + if(len < 0) { + OCSPerr(OCSP_F_OCSP_SENDREQ_BIO,OCSP_R_SERVER_READ_ERROR); + goto err; + } + BIO_write(mem, tmpbuf, len); + } while(len > 0); + if(BIO_gets(mem, tmpbuf, 512) <= 0) { + OCSPerr(OCSP_F_OCSP_SENDREQ_BIO,OCSP_R_SERVER_RESPONSE_PARSE_ERROR); + goto err; + } + /* Parse the HTTP response. This will look like this: + * "HTTP/1.0 200 OK". We need to obtain the numeric code and + * (optional) informational message. + */ + + /* Skip to first white space (passed protocol info) */ + for(p = tmpbuf; *p && !isspace((unsigned char)*p); p++) continue; + if(!*p) { + OCSPerr(OCSP_F_OCSP_SENDREQ_BIO,OCSP_R_SERVER_RESPONSE_PARSE_ERROR); + goto err; + } + /* Skip past white space to start of response code */ + while(*p && isspace((unsigned char)*p)) p++; + if(!*p) { + OCSPerr(OCSP_F_OCSP_SENDREQ_BIO,OCSP_R_SERVER_RESPONSE_PARSE_ERROR); + goto err; + } + /* Find end of response code: first whitespace after start of code */ + for(q = p; *q && !isspace((unsigned char)*q); q++) continue; + if(!*q) { + OCSPerr(OCSP_F_OCSP_SENDREQ_BIO,OCSP_R_SERVER_RESPONSE_PARSE_ERROR); + goto err; + } + /* Set end of response code and start of message */ + *q++ = 0; + /* Attempt to parse numeric code */ + retcode = strtoul(p, &r, 10); + if(*r) goto err; + /* Skip over any leading white space in message */ + while(*q && isspace((unsigned char)*q)) q++; + if(*q) { + /* Finally zap any trailing white space in message (include CRLF) */ + /* We know q has a non white space character so this is OK */ + for(r = q + strlen(q) - 1; isspace((unsigned char)*r); r--) *r = 0; + } + if(retcode != 200) { + OCSPerr(OCSP_F_OCSP_SENDREQ_BIO,OCSP_R_SERVER_RESPONSE_ERROR); + if(!*q) { + ERR_add_error_data(2, "Code=", p); + } + else { + ERR_add_error_data(4, "Code=", p, ",Reason=", q); + } + goto err; + } + /* Find blank line marking beginning of content */ + while(BIO_gets(mem, tmpbuf, 512) > 0) + { + for(p = tmpbuf; *p && isspace((unsigned char)*p); p++) continue; + if(!*p) break; + } + if(*p) { + OCSPerr(OCSP_F_OCSP_SENDREQ_BIO,OCSP_R_NO_CONTENT); + goto err; + } + resp = d2i_OCSP_RESPONSE_bio(mem, NULL); + if(!resp) { + OCSPerr(OCSP_F_OCSP_SENDREQ_BIO,ERR_R_NESTED_ASN1_ERROR); + goto err; + } + err: + BIO_free(mem); + return resp; +} + + +//-------------------------------------------------- +// sends an OCSP_REQUES object to remore server and +// retrieves the OCSP_RESPONSE object +// resp - buffer to store the new responses pointer +// req - request objects pointer +// url - OCSP responder URL +// ip_addr - senders ip address if known or 0 +//-------------------------------------------------- +int sendOCSPRequest(OCSP_RESPONSE** resp, OCSP_REQUEST *req, + char* url, char* proxyHost, char* proxyPort, + unsigned long ip_addr) +{ + BIO* cbio = 0, *sbio = 0; + SSL_CTX *ctx = NULL; + char *host = NULL, *port = NULL, *path = "/"; + int err = ERR_OK, use_ssl = -1; + int connResult = 0; + long e = 0; + + RETURN_IF_NULL_PARAM(resp); + RETURN_IF_NULL_PARAM(req); + RETURN_IF_NULL_PARAM(url); + + //there is an HTTP proxy - connect to that instead of the target host + ddocDebug(3, "sendOCSPRequest", "Send OCSP to: %s over: %s:%s", url, + (proxyHost ? proxyHost : ""), (proxyPort ? proxyPort : "")); + if (proxyHost != 0 && *proxyHost != '\0') { + host = proxyHost; + if(proxyPort != 0 && *proxyPort != '\0') + port = proxyPort; + path = url; + } else { + if((err = OCSP_parse_url(url, &host, &port, &path, &use_ssl)) == 0) { + //printf("BIO_parse_url failed\n"); + ddocDebug(1, "sendOCSPRequest", "BIO_parse_url failed: %d - %s", err, url); + return ERR_WRONG_URL_OR_PROXY; + } + } + if((cbio = BIO_new_connect(host)) != 0) { + if(port != NULL) + BIO_set_conn_port(cbio, port); + if (use_ssl == 1) { + ctx = SSL_CTX_new(SSLv23_client_method()); + SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); + sbio = BIO_new_ssl(ctx, 1); + cbio = BIO_push(sbio, cbio); + } + if ((connResult = BIO_do_connect(cbio)) > 0) { + e = checkErrors(); + //printf("BIO_do_connect returned %d\n", connResult); + *resp = OCSP_sendreq_bio_withParams(cbio, path, req, ip_addr); + //printf("OCSP_sendreq_bio answered %lX\n", *resp); + e = checkErrors(); + if(ERR_GET_REASON(e) == BIO_R_BAD_HOSTNAME_LOOKUP || + ERR_GET_REASON(e) == OCSP_R_SERVER_WRITE_ERROR) + err = ERR_CONNECTION_FAILURE; + //if(ERR_GET_REASON(e) == BIO_R_BAD_HOSTNAME_LOOKUP) + // err = ERR_CONNECTION_FAILURE; + else + err = (*resp == 0) ? ERR_OCSP_WRONG_URL : ERR_OK; + //if (*resp == 0) + // printErrors(); + } else { + ddocDebug(1, "sendOCSPRequest", "BIO-Connection error: %d - %ld", err, e); + //printf("BIO_do_connect failed, rc = %d, shouldRetry = %d\n", connResult, BIO_should_retry(cbio)); + //printErrors(); + //if no connection + if (host != NULL) + err = ERR_WRONG_URL_OR_PROXY; + else + err = ERR_CONNECTION_FAILURE; + } + BIO_free_all(cbio); + if (use_ssl != -1) { + OPENSSL_free(host); + OPENSSL_free(port); + OPENSSL_free(path); + SSL_CTX_free(ctx); + } + } + else { + err = ERR_CONNECTION_FAILURE; + ddocDebug(1, "sendOCSPRequest", "Connection error: %d", err); + } + return(err); +} + + +//-------------------------------------------------- +// sends an OCSP_REQUES object to remore server and +// retrieves the OCSP_RESPONSE object +// resp - buffer to store the new responses pointer +// req - request objects pointer +// url - OCSP responder URL +// ip_addr - senders ip address if known or 0 +//-------------------------------------------------- +int sendOCSPRequest2(OCSP_RESPONSE** resp, OCSP_REQUEST *req, + char* url, char* proxyHost, char* proxyPort, char *proxyUser, char *proxyPass, + unsigned long ip_addr) +{ + int err = ERR_OK, l1 = 0; + DigiDocMemBuf mbuf1, mbuf2, mbuf3; + char buf1[30], buf2[200], buf3[100], *p1; + + mbuf1.pMem = 0; + mbuf1.nLen = 0; + mbuf2.pMem = 0; + mbuf2.nLen = 0; + mbuf3.pMem = 0; + mbuf3.nLen = 0; + ddocMemAssignData(&mbuf1, "POST ", -1); + if(proxyHost || (proxyPort && atoi(proxyPort) > 0)) { + ddocMemAppendData(&mbuf1, url, -1); + } else { + p1 = strstr(url, "://"); + if(p1) p1 += 3; + if(p1) p1 = strchr(p1, '/'); + if(p1) + ddocMemAppendData(&mbuf1, p1, -1); + else + ddocMemAppendData(&mbuf1, "/", -1); + } + ddocMemAppendData(&mbuf1, " HTTP/1.0\r\n", -1); + buf1[0] = buf2[0] = 0; + if(ip_addr > 0) + snprintf(buf1, sizeof(buf1), "From: %d.%d.%d.%d\r\n", + (int)(ip_addr>>24)&0xFF, (int)(ip_addr>>16)&0xFF, (int)(ip_addr>>8)&0xFF, (int)ip_addr&0xFF); + snprintf(buf2, sizeof(buf2), "User-Agent: LIB %s/%s APP %s\r\n%s", + getLibName(), getLibVersion(), getGUIVersion(), (ip_addr > 0 ? buf1 : "")); + ddocMemAppendData(&mbuf1, "Content-Type: application/ocsp-request\r\n", -1); + ddocMemAppendData(&mbuf1, buf2, -1); + //ddocMemAppendData(&mbuf1, "Host: www.sk.ee\r\n", -1); + //ddocMemAppendData(&mbuf1, "Accept: */*\r\n", -1); + // convert OCSP req + err = ddocWriteOcspDER(req, &mbuf3); + if(!err) { + snprintf(buf1, sizeof(buf1), "Content-Length: %d\r\n", (int)mbuf3.nLen); + ddocMemAppendData(&mbuf1, buf1, -1); + ddocMemAppendData(&mbuf1, "Connection: Close\r\n", -1); + if(proxyUser || proxyPass) { + err = ddocOcspProxyAuthInfo(buf3, proxyUser, proxyPass); + ddocMemAppendData(&mbuf1, buf3, -1); + } + if(proxyHost || (proxyPort && atoi(proxyPort) > 0)) // if we use proxy then send also Proxy-Connection + ddocMemAppendData(&mbuf1, "Proxy-Connection: Close\r\n", -1); + ddocMemAppendData(&mbuf1, "\r\n", -1); + ddocMemAppendData(&mbuf1, mbuf3.pMem, mbuf3.nLen); + ddocMemBuf_free(&mbuf3); + ddocDebug(3, "sendOCSPRequest2", "Send to host: %s request len: %d", url, mbuf1.nLen); + err = ddocPullUrl(url, &mbuf1, &mbuf2, proxyHost, proxyPort); + ddocDebug(3, "sendOCSPRequest2", "Recevied len: %d RC: %d", mbuf2.nLen, err); + if(!err && ((l1 = ddocGetHttpResponseCode(&mbuf2)) == 200)) { + ddocDebug(4, "sendOCSPRequest2", "HTTP response\n-----\n%s\n-----\n", mbuf2.pMem); + err = ddocGetHttpPayload(&mbuf2, &mbuf3); + if(!err) + err = ddocOcspReadOcspResp(resp, &mbuf3); + } else { + ddocDebug(1, "sendOCSPRequest2", "Ocsp request failed with http code: %d rc: %d", l1, err); + err = ERR_OCSP_UNSUCCESSFUL; + ddocDebug(1, "sendOCSPRequest2", "HTTP error message\n-----\n%s\n-----\n", mbuf2.pMem); + } + } + // cleanup + ddocMemBuf_free(&mbuf1); + ddocMemBuf_free(&mbuf2); + ddocMemBuf_free(&mbuf3); + return err; +} + + +//-------------------------------------------------- +// Creates and writes an OCSP_REQUEST object +// to disk +// pSigDoc - signedDoc address +// signerCertFile - cert file to verify +// issuertCertFile - this certs direct CA cert +// nonce - nonce (signature value) +// nlen - nonce length +// szOutputFile - output filename +//-------------------------------------------------- + EXP_OPTION int writeOCSPRequest(SignedDoc* pSigDoc, + const char* signerCertFile, const char* issuertCertFile, + byte* nonce, int nlen, const char* szOutputFile) + +{ + OCSP_REQUEST *req = 0; + X509 *cert = 0, *issuer = 0; + int err = ERR_OK, l1; + //EVP_PKEY* pkey; + byte buf1[DIGEST_LEN+2]; + + RETURN_IF_NULL_PARAM(signerCertFile); + RETURN_IF_NULL_PARAM(issuertCertFile); + RETURN_IF_NULL_PARAM(nonce); + RETURN_IF_NULL_PARAM(szOutputFile); + + if((err = ReadCertificate(&cert, signerCertFile)) == ERR_OK) { + //pkey = ReadPublicKey(signerCertFile); + if((err = ReadCertificate(&issuer, issuertCertFile)) == ERR_OK) { + l1 = sizeof(buf1); + calculateDigest(nonce, nlen, DIGEST_SHA1, buf1, &l1); + err = createOCSPRequest(pSigDoc, &req, cert, issuer, buf1, l1); + //WriteOCSPRequest(szOutputFile, req); + X509_free(issuer); + //AM 22.04.08 + if(req) + OCSP_REQUEST_free(req); + } + X509_free(cert); + } + return err; +} + +//-------------------------------------------------- +// Signs an OCSP_REQUEST using PKCS#12 conteiner +// req - OCSP_REQUEST +// filename - PKCS#12 conteiner file +// passwd - key decryption passwd +//-------------------------------------------------- +EXP_OPTION int signOCSPRequestPKCS12(OCSP_REQUEST *req, const char* filename, const char* passwd) +{ + EVP_PKEY *pkey; + int err = ERR_OK; + + STACK_OF(X509)* certs = NULL; + X509* x509=0; +#ifdef FRAMEWORK + SecIdentityRef identity = 0; + err = SecIdentityCopyPreference(CFSTR("ocsp.sk.ee"), 0, 0, &identity); + if(identity) { + SecCertificateRef certref = 0; + SecKeyRef keyref = 0; + err = SecIdentityCopyCertificate(identity, &certref); + err = SecIdentityCopyPrivateKey(identity, &keyref); + CFRelease(identity); + RETURN_IF_NULL(certref); + RETURN_IF_NULL(keyref); + + CFDataRef certdata = SecCertificateCopyData(certref); + CFRelease(certref); + RETURN_IF_NULL(certdata); + const unsigned char *p = CFDataGetBytePtr(certdata); + x509 = d2i_X509(0, &p, CFDataGetLength(certdata)); + CFRelease(certdata); + RETURN_IF_NULL(x509); + + CFDataRef keydata = 0; + SecKeyImportExportParameters params; + memset( ¶ms, 0, sizeof(params) ); + params.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION; + params.passphrase = CFSTR("pass"); + err = SecKeychainItemExport(keyref, kSecFormatWrappedPKCS8, 0, ¶ms, &keydata); + CFRelease(keyref); + RETURN_IF_NULL(keydata); + BIO *bio = BIO_new_mem_buf((void*)CFDataGetBytePtr(keydata), CFDataGetLength(keydata)); + pkey = d2i_PKCS8PrivateKey_bio(bio, 0, &password_callback, 0); + CFRelease(keydata); + BIO_free(bio); + + RETURN_IF_NULL(pkey); + } else { +#endif + RETURN_IF_NULL_PARAM(filename); + if(strlen(filename) == 0) + return ERR_OK; + + err = ReadCertificateByPKCS12(&x509, filename, passwd, &pkey); + RETURN_IF_NOT(err == ERR_OK, err); +#ifdef FRAMEWORK + } +#endif + +#if 0 // miscalulates on mac time zone + // VS: ver 1.66 + time(&tNow); + err = isCertValid(x509, tNow); +#else + if( X509_cmp_current_time(x509->cert_info->validity->notBefore) >= 0 && + X509_cmp_current_time(x509->cert_info->validity->notAfter) <= 0) + err = ERR_CERT_INVALID; +#endif + if (err != ERR_OK) + X509_free(x509); + RETURN_IF_NOT(err == ERR_OK, ERR_PKCS12_EXPIRED); + certs = sk_X509_new_null(); + RETURN_IF_NULL(certs); + + //sk_X509_push(certs, x509); + if (! OCSP_request_sign(req,x509,pkey,EVP_sha1(),certs,0)) { + EVP_PKEY_free(pkey); + err = ERR_OCSP_SIGN; + SET_LAST_ERROR(err); + } + X509_free(x509); + EVP_PKEY_free(pkey); + //AM 22.04.08 + sk_X509_free(certs); + return err; +} + +//-------------------------------------------------- +// Signs an OCSP_REQUEST using X509 cert and separate keyfile +// req - OCSP_REQUEST +// certFile - signers certificate file +// keyfile - signer's key file +// passwd - key decryption passwd +//-------------------------------------------------- +EXP_OPTION int signOCSPRequest(OCSP_REQUEST *req,const char* certFile,const char* keyfile,const char* passwd){ + + EVP_PKEY *pkey; + int err = ERR_OK; + STACK_OF(X509)* certs = NULL; + X509* x509 = NULL; + + certs = sk_X509_new_null(); + RETURN_IF_NULL_PARAM(certs); + + if((err = ReadCertificate(&x509, certFile)) != ERR_OK) { + SET_LAST_ERROR_RETURN_CODE(ERR_PKCS_CERT_LOC); + } + sk_X509_push(certs, x509); + if((err = ReadPrivateKey(&pkey, keyfile, passwd, FILE_FORMAT_PEM)) == ERR_OK) { + //ASN1_item_sign(ASN1_ITEM_rptr(OCSP_REQINFO),req->optionalSignature->signatureAlgorithm,NULL,req->optionalSignature->signature,req->tbsRequest,pkey,setSignAlgorithm(EVP_sha1())); + //OCSP_request_sign_internal(req, x509,pkey, NULL); + if(! OCSP_request_sign(req,x509,pkey,EVP_sha1(),certs,0)){ + EVP_PKEY_free(pkey); + SET_LAST_ERROR_RETURN_CODE(ERR_OCSP_SIGN); + } + //printf("OCSP_request_sign()=%d \n",r); + EVP_PKEY_free(pkey); + }else{ + SET_LAST_ERROR_RETURN_CODE(ERR_PRIVKEY_READ); + } + return err; +} + +//-------------------------------------------------- +// Creates and sends an OCSP_REQUEST object +// to the notary server, receives the response +// and uses it to create a confirmation object. +// pSigDoc - signed doc info +// pSigInfo - signature info +// caCerts - responder CA certs chain +// notaryCert - notarys cert search +// pkcs12FileName - +// pkcs12Password - +// notaryURL - notarys URL +// proxyHost - proxy host if needed +// proxyPort - proxy port if needed +//-------------------------------------------------- +EXP_OPTION int getConfirmation(SignedDoc* pSigDoc, SignatureInfo* pSigInfo, + const X509** caCerts, const X509* pNotCert, + char* pkcs12FileName, char* pkcs12Password, + char* notaryURL, char* proxyHost, char* proxyPort) + +{ + // default way to invoke it is without callers ip. + return getConfirmationWithIp(pSigDoc, pSigInfo, caCerts, pNotCert, + pkcs12FileName, pkcs12Password, + notaryURL, proxyHost, proxyPort, 0); +} + +//-------------------------------------------------- +// Creates and sends an OCSP_REQUEST object +// to the notary server, receives the response +// and uses it to create a confirmation object. +// pSigDoc - signed doc info +// pSigInfo - signature info +// caCerts - responder CA certs chain +// notaryCert - notarys cert search +// pkcs12FileName - +// pkcs12Password - +// notaryURL - notarys URL +// proxyHost - proxy host if needed +// proxyPort - proxy port if needed +// ip - callers ip address if known +//-------------------------------------------------- +EXP_OPTION int getConfirmationWithIp(SignedDoc* pSigDoc, SignatureInfo* pSigInfo, + const X509** caCerts, const X509* pNotCert, + char* pkcs12FileName, char* pkcs12Password, + char* notaryURL, char* proxyHost, char* proxyPort, + unsigned long ip) +{ + return getConfirmationWithIpEx(pSigDoc, pSigInfo, caCerts, pNotCert, + pkcs12FileName, pkcs12Password, notaryURL, proxyHost, proxyPort, 0, 0, ip); +} + +//-------------------------------------------------- +// Creates and sends an OCSP_REQUEST object +// to the notary server, receives the response +// and uses it to create a confirmation object. +// pSigDoc - signed doc info +// pSigInfo - signature info +// caCerts - responder CA certs chain +// notaryCert - notarys cert search +// pkcs12FileName - +// pkcs12Password - +// notaryURL - notarys URL +// proxyHost - proxy host if needed +// proxyPort - proxy port if needed +// proxyUser - proxy user if needed +// proxyPass - proxy pass if needed +// ip - callers ip address if known +//-------------------------------------------------- +EXP_OPTION int getConfirmationWithIpEx(SignedDoc* pSigDoc, SignatureInfo* pSigInfo, + const X509** caCerts, const X509* pNotCert, + char* pkcs12FileName, char* pkcs12Password, + char* notaryURL, char* proxyHost, char* proxyPort, + char* proxyUser, char* proxyPass, unsigned long ip) +{ + OCSP_REQUEST *req = 0; + OCSP_RESPONSE *resp = 0; + X509 *cert = 0, *pCA = 0; + int err = ERR_OK, l1, i; + byte buf1[DIGEST_LEN256+2]; + NotaryInfo* pNotInf = NULL; + DigiDocMemBuf* pMBuf; + + RETURN_IF_NULL_PARAM(pSigDoc); + RETURN_IF_NULL_PARAM(pSigInfo); + cert = ddocSigInfo_GetSignersCert(pSigInfo); + RETURN_IF_NULL(cert); + RETURN_IF_NULL_PARAM(notaryURL); + + clearErrors(); + + l1 = sizeof(buf1); + pMBuf = ddocSigInfo_GetSignatureValue_Value(pSigInfo); + RETURN_IF_NOT(pMBuf, err); +#ifdef WIN32 + RAND_screen(); + RAND_bytes((unsigned char*)buf1, DIGEST_LEN); +#else + if((l1 = RAND_load_file("/dev/urandom", 1024)) > 0) { + RAND_bytes((unsigned char*)buf1, DIGEST_LEN); + l1 = DIGEST_LEN; + } +#endif + err = calculateDigest(pMBuf->pMem, pMBuf->nLen, DIGEST_SHA1, buf1, &l1); + RETURN_IF_NOT(err == ERR_OK, err); + + // find lowest CA cert + for(i = 0; (caCerts != NULL) && (caCerts[i] != NULL); i++) + pCA = (X509*)caCerts[i]; + err = createOCSPRequest(pSigDoc, &req, cert, pCA, buf1, l1); + + // if both are NULL then this means don't sign OCSP requests + if(!err && ConfigItem_lookup_bool("SIGN_OCSP", 1) /*pkcs12FileName && pkcs12Password*/) { + ddocDebug(3, "getConfirmationWithIp", "Sign OCSP request with: %s", pkcs12FileName); + err=signOCSPRequestPKCS12(req, pkcs12FileName, pkcs12Password); + ddocDebug(3, "getConfirmationWithIp", "Signing ocsp rc: %d", err); + } + + if(!err) { + ddocDebug(3, "getConfirmationWithIp", "Send OCSP to: %s over: %s:%s", notaryURL, + (proxyHost ? proxyHost : ""), (proxyPort ? proxyPort : "")); + err = sendOCSPRequest2(&resp, req, notaryURL, proxyHost, proxyPort, proxyUser, proxyPass, ip); + } + if(!err) + err = NotaryInfo_new(&pNotInf, pSigDoc, pSigInfo); + //AM initializeNotaryInfoWithOCSP2? + if(!err) + err = initializeNotaryInfoWithOCSP(pSigDoc, pNotInf, resp, NULL, 1); + + if(!err && caCerts && pNotCert) { + err = finalizeAndVerifyNotary(pSigDoc, pSigInfo, pNotInf, caCerts, pNotCert); + } + // VS - if finalizing notary fails then remove it - #8602 + if(err) { + if(pNotInf) + ddocDebug(3, "getConfirmationWithIp", "Delete notary: %s because of error: %d", pNotInf->szId, err); + NotaryInfo_delete(pSigInfo); + } + if(resp) + OCSP_RESPONSE_free(resp); + if(req) + OCSP_REQUEST_free(req); + return err; +} + +//-------------------------------------------------- +// Adds responder certificate to notary, searches it's +// CA chain and then verifies notary +// pSigDoc - signed doc info +// pSigInfo - signature info +// caCertSearches - responder CA certs chain +// notaryCert - notarys cert search +// returns error code +//-------------------------------------------------- +int EXP_OPTION finalizeAndVerifyNotary2(SignedDoc* pSigDoc, SignatureInfo* pSigInfo, + NotaryInfo* pNotInf, + const X509** caCerts, const X509* pNotCert, const X509* pSigCa) +{ + int err = ERR_OK; + + RETURN_IF_NULL_PARAM(pNotCert); + RETURN_IF_NULL_PARAM(caCerts); + ddocDebug(3, "finalizeAndVerifyNotary2", "Notary: %s cert: %s", pNotInf->szId, (pNotCert ? "OK" : "NULL")); + err = addNotaryInfoCert(pSigDoc, pNotInf, (X509*)pNotCert); + RETURN_IF_NOT(err == ERR_OK, err); + err = calcNotaryDigest(pSigDoc, pNotInf); + RETURN_IF_NOT(err == ERR_OK, err); + err = verifyNotaryInfoCERT2(pSigDoc, pSigInfo, pNotInf, caCerts, NULL, pNotCert, pSigCa); + RETURN_IF_NOT(err == ERR_OK, err); + ddocDebug(3, "finalizeAndVerifyNotary2", "rc: %d cert: %s cert2: %s", err, (pNotCert ? "OK" : "NULL"), (ddocSigInfo_GetOCSPRespondersCert(pSigInfo) ? "OK" : "NULL")); + return ERR_OK; +} + +//-------------------------------------------------- +// Adds responder certificate to notary, searches it's +// CA chain and then verifies notary +// pSigDoc - signed doc info +// pSigInfo - signature info +// caCertSearches - responder CA certs chain +// notaryCert - notarys cert search +// returns error code +//-------------------------------------------------- +int EXP_OPTION finalizeAndVerifyNotary(SignedDoc* pSigDoc, SignatureInfo* pSigInfo, + NotaryInfo* pNotInf, + const X509** caCerts, const X509* pNotCert) +{ + return finalizeAndVerifyNotary2(pSigDoc, pSigInfo, pNotInf, caCerts, pNotCert, NULL); +} + + +//-------------------------------------------------- +// Verfies OCSP_RESPONSE signature +// pResp - signed OCSP response +// caCerts - CA certificate pointer array terminated with NULL +// CApath - path to (directory) all certs +// notCertFile - Notary (e.g. OCSP responder) cert file +//-------------------------------------------------- +int verifyOCSPResponse(OCSP_RESPONSE* pResp, + const X509** caCerts, const char *CApath, + const X509* notCert) +{ + X509_STORE *store; + OCSP_BASICRESP* bs = NULL; + STACK_OF(X509)* ver_certs = NULL; + int err = ERR_OK; + + RETURN_IF_NULL_PARAM(pResp); + RETURN_IF_NOT(ASN1_ENUMERATED_get(pResp->responseStatus) == 0, ERR_OCSP_UNSUCCESSFUL); + RETURN_IF_NOT(OBJ_obj2nid(pResp->responseBytes->responseType) == NID_id_pkix_OCSP_basic, ERR_OCSP_UNKNOWN_TYPE); + RETURN_IF_NOT(caCerts != NULL, ERR_OCSP_RESP_NOT_TRUSTED); + RETURN_IF_NOT(notCert != NULL, ERR_OCSP_CERT_NOTFOUND); + RETURN_IF_NOT((bs = OCSP_response_get1_basic(pResp)) != NULL, ERR_OCSP_NO_BASIC_RESP); + // now create an OCSP object and check its validity + if((setup_verifyCERT(&store, CApath, caCerts)) == ERR_OK) { + // new basic response + // create OCSP basic response + ver_certs = sk_X509_new_null(); + if(ver_certs) { + sk_X509_push(ver_certs, notCert); + err = OCSP_basic_verify(bs, ver_certs, store, OCSP_TRUSTOTHER); + if(err == ERR_LIB_NONE) { + err = ERR_OK; + } else { + //checkErrors(); + SET_LAST_ERROR(ERR_OCSP_WRONG_RESPID); + err = ERR_OCSP_WRONG_RESPID; + } + // cleanup + sk_X509_free(ver_certs); + } + X509_STORE_free(store); + } + if(bs) + OCSP_BASICRESP_free(bs); + return err; +} + +int checkNonceAndCertbyOCSP(OCSP_RESPONSE* resp, X509* cert, byte* nonce1, int nonceLen) +{ + int err = ERR_OK, n; + char buf[100]; + OCSP_BASICRESP *br = NULL; + OCSP_RESPDATA *rd = NULL; + OCSP_SINGLERESP *single = NULL; + OCSP_CERTID *cid = NULL; + X509_EXTENSION *nonce; + X509_NAME *iname; + unsigned char *ikey; + + RETURN_IF_NULL_PARAM(resp); + RETURN_IF_NULL_PARAM(cert); + if((br = OCSP_response_get1_basic(resp)) == NULL) + SET_LAST_ERROR_RETURN_CODE(ERR_OCSP_NO_BASIC_RESP); + rd = br->tbsResponseData; + n = sk_OCSP_SINGLERESP_num(rd->responses); + RETURN_IF_NOT(n == 1, ERR_OCSP_ONE_RESPONSE); + single = sk_OCSP_SINGLERESP_value(rd->responses, 0); + RETURN_IF_NOT(single, ERR_OCSP_ONE_RESPONSE); + cid = single->certId; + RETURN_IF_NULL(cid); + err = handleOCSPCertStatus(single->certStatus->type); + if(err) + SET_LAST_ERROR_RETURN_CODE(err); + if(single->singleExtensions) + SET_LAST_ERROR_RETURN_CODE(ERR_OCSP_NO_SINGLE_EXT); + if(!rd->responseExtensions || + (sk_X509_EXTENSION_num(rd->responseExtensions) != 1) || + ((nonce = sk_X509_EXTENSION_value(rd->responseExtensions, 0)) == NULL)) + SET_LAST_ERROR_RETURN_CODE(ERR_OCSP_NO_NONCE); + i2t_ASN1_OBJECT(buf, sizeof(buf), nonce->object); + if(strcmp(buf, OCSP_NONCE_NAME)) + SET_LAST_ERROR_RETURN_CODE(ERR_OCSP_NO_NONCE); + // check serial number + if(ASN1_INTEGER_cmp(X509_get_serialNumber(cert), cid->serialNumber) != 0) + SET_LAST_ERROR_RETURN_CODE(ERR_WRONG_CERT); + // check issuer name hash + iname = X509_get_issuer_name(cert); + n = sizeof(buf); + X509_NAME_digest(iname, EVP_sha1(), (byte*)buf, (unsigned int*)&n); + err = compareByteArrays((byte*)buf, (unsigned int)n, cid->issuerNameHash->data, cid->issuerNameHash->length); + RETURN_IF_NOT(err == ERR_OK, err); + // check issuer key hash + if((ikey = get_authority_key(cert->cert_info->extensions)) != NULL) { + err = compareByteArrays(ikey, strlen((const char*)ikey), + cid->issuerKeyHash->data, cid->issuerKeyHash->length); + // cleanup ikey + free(ikey); + } + // verify nonce value + if(nonce->value->length == DIGEST_LEN) + err = compareByteArrays(nonce->value->data, nonce->value->length, nonce1, nonceLen); + else + err = compareByteArrays(nonce->value->data + 2, nonce->value->length - 2, nonce1, nonceLen); + ddocDebug(3, "checkNonceAndCertbyOCSP", "nonce1-len: %d nonce2-len: %d err: %d", nonce->value->length, nonceLen, err); + if (err != ERR_OK) SET_LAST_ERROR(err); + if(br) + OCSP_BASICRESP_free(br); + return err; +} + |