//================================================== // 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef FRAMEWORK #ifdef __APPLE__ #include #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; ivalue->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; iname, 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; }