//==================================================
// FILE:	DigiDocCert.c
// PROJECT:     Digi Doc
// DESCRIPTION: Digi Doc functions for certificate handling
// 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 >=============================
//      09.09.2004      Veiko Sinivee
//                      Creation
//==================================================

#include <libdigidoc/DigiDocDefs.h>
#include <libdigidoc/DigiDocCert.h>
#include <libdigidoc/DigiDocConvert.h>
#include <libdigidoc/DigiDocLib.h>
#include <libdigidoc/DigiDocError.h>
#include <libdigidoc/DigiDocDebug.h>
#include <libdigidoc/DigiDocMem.h>
#include <libdigidoc/DigiDocOCSP.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 <string.h>

//==========< forward declarations >====================

extern int createOCSPRequest(SignedDoc* pSigDoc, OCSP_REQUEST **req,
		      X509 *cert, X509 *pCA, byte* nonce, int nlen);
extern int signOCSPRequestPKCS12(OCSP_REQUEST *req, const char* filename, const char* passwd);

extern int verifyOCSPResponse(OCSP_RESPONSE* pResp, 
				    const X509** caCerts, const char *CApath,
				    const X509* notCert);
extern int hasUmlauts(const char* str);
extern int checkNonceAndCertbyOCSP(OCSP_RESPONSE* resp, X509* cert, byte* nonce1, int nonceLen);


//==========< utility functions >====================


//--------------------------------------------------
// Reads a certificate file
// certfile - name of the certificate file
//--------------------------------------------------
EXP_OPTION int ReadCertificate(X509 **x509, const char *szCertfile)
{
  BIO *in; 

  RETURN_IF_NULL_PARAM(szCertfile);
  if((in = BIO_new_file(szCertfile, "rb")) != NULL) {
    *x509 = PEM_read_bio_X509(in, NULL, NULL, 0);
    BIO_free(in);
    if(!*x509) SET_LAST_ERROR_RETURN_CODE(ERR_NULL_CERT_POINTER);
  } else 
    SET_LAST_ERROR_RETURN_CODE(ERR_FILE_READ);
  return ERR_OK;
}

//--------------------------------------------------
// Reads a certificate file
// certfile - name of the certificate file
//--------------------------------------------------
EXP_OPTION int ReadCertificateNoErr(X509 **x509, const char *szCertfile)
{
    BIO *in;
    
    RETURN_IF_NULL_PARAM(szCertfile);
    if((in = BIO_new_file(szCertfile, "rb")) != NULL) {
        *x509 = PEM_read_bio_X509(in, NULL, NULL, 0);
        BIO_free(in);
        if(!*x509) return ERR_NULL_CERT_POINTER;
    } else
        return ERR_FILE_READ;
    return ERR_OK;
}


//--------------------------------------------------
// Reads a certificate from pkcs12 conteiner
//--------------------------------------------------
EXP_OPTION int ReadCertificateByPKCS12(X509 **x509, const char *pkcs12file, const char *passwd, EVP_PKEY **pkey)
{
  BIO *bio;
  PKCS12 *p12;
	
  //memset(debugBuf,0,sizeof(debugBuf));
  RETURN_IF_NULL_PARAM(pkcs12file);
  bio=BIO_new_file(pkcs12file, "rb");
  RETURN_IF_NULL(bio);
  p12 = d2i_PKCS12_bio(bio, NULL);
  BIO_free(bio);
  RETURN_IF_NOT(p12, ERR_OCSP_PKCS12_CONTAINER);
  PKCS12_parse(p12, passwd, pkey, x509, NULL);
  // Hack: clear PKCS12_parse error "ERROR: 185073780 - error:0B080074:x509 certificate routines:X509_check_private_key:key values mismatch
  ERR_get_error();
  PKCS12_free(p12);
	
  RETURN_IF_NOT(*x509, ERR_OCSP_PKCS12_CONTAINER);
  return ERR_OK;
}

//--------------------------------------------------
// Reads certificates serial number
// szSerial - buffer for serial number
// nMaxLen - maximum serial number buffer length
// certfile - name of the certificate file
//--------------------------------------------------
EXP_OPTION int GetCertSerialNumber(char *szSerial, int nMaxLen, const char *szCertfile)
{
  BIO *in; 
  X509 *x509;

  RETURN_IF_NULL_PARAM(szSerial);
  RETURN_IF_NULL_PARAM(szCertfile);
  if((in = BIO_new_file(szCertfile, "rb")) != NULL) {
    x509 = PEM_read_bio_X509(in, NULL, NULL, 0);
    BIO_free(in);
    RETURN_IF_NOT(x509, ERR_NULL_CERT_POINTER);
    ReadCertSerialNumber(szSerial, nMaxLen, x509);
    X509_free(x509);
  }
  else 
    SET_LAST_ERROR_RETURN_CODE(ERR_FILE_READ);
  return ERR_OK;
}

//--------------------------------------------------
// Reads certificates serial number
// x509 - certificate object
//--------------------------------------------------
int ReadCertSerialNumber(char* szSerial, int nMaxLen, X509 *x509)
{
  ASN1_INTEGER *bs = NULL;
  BIGNUM* bn;
  char* str;
  int err = ERR_OK;
  
  RETURN_IF_NOT(x509, ERR_NULL_CERT_POINTER);
  RETURN_IF_NOT(szSerial, ERR_NULL_POINTER);
  bs = X509_get_serialNumber(x509);
  RETURN_IF_NOT(bs, ERR_NULL_SER_NUM_POINTER);
  bn = ASN1_INTEGER_to_BN(bs, NULL);
  RETURN_IF_NOT(bn, ERR_NULL_SER_NUM_POINTER);
  str = BN_bn2dec(bn);
  RETURN_IF_NOT(str, ERR_NULL_SER_NUM_POINTER);
  memset(szSerial, 0, nMaxLen);
  strncpy(szSerial, str, nMaxLen -1);
  if(strlen(str) > (unsigned int)nMaxLen) {
    err = ERR_BUF_LEN;
    SET_LAST_ERROR(ERR_BUF_LEN);
  }
  //AM 28.05.08 bn should be freed too
  if(bn)
	BN_free(bn);
  OPENSSL_free(str);
  checkErrors();
  return err;
}



//--------------------------------------------------
// Reads a public key file
// certfile - name of the certificate file
//--------------------------------------------------
int ReadPublicKey(EVP_PKEY **PublicKey, const char *szCertfile)
{
  EVP_PKEY *pkey = NULL;
  X509 *x509 = NULL;
  int err = ERR_OK;

  RETURN_IF_NULL_PARAM(szCertfile);
  RETURN_IF_NULL_PARAM(PublicKey);  // SVEN - ver 1.92 - crashbug fixed
  if((err = ReadCertificate(&x509, szCertfile)) == ERR_OK) {
    pkey = X509_extract_key(x509);
    X509_free(x509);
    RETURN_IF_NOT(pkey, ERR_NULL_KEY_POINTER);
    *PublicKey = pkey;
    return ERR_OK;
  } else 
    SET_LAST_ERROR_RETURN_CODE(err); 
}


//--------------------------------------------------
// Reads a public key file
// certfile - name of the certificate file
//--------------------------------------------------
int GetPublicKey(EVP_PKEY **pubKey, const X509* x509)
{
  EVP_PKEY *pkey = NULL;
  
  RETURN_IF_NULL_PARAM(x509);	
  // SET_LAST_ERROR_RETURN_IF_NOT(x509, ERR_NULL_CERT_POINTER, pkey);
  // pkey = X509_extract_key((X509*)x509);
  pkey = X509_get_pubkey((X509*)x509);
  RETURN_IF_NOT(pkey, ERR_NULL_KEY_POINTER);
  *pubKey = pkey;
  return ERR_OK; 
}


//--------------------------------------------------
// Callback routine for passing key password
//--------------------------------------------------
int pemkey_callback(char *buf, int size, int rwflag, void *userdata)
{
  RETURN_OBJ_IF_NULL(buf, 0);
  RETURN_OBJ_IF_NULL(userdata, 0);
  memset(buf, 0, size);
  strncpy(buf, (char*)userdata, size);
  return strlen(buf);
}


//--------------------------------------------------
// Reads a private key file
// keyfile - name of the private key file
// passwd - key password (problems with encrypted passwwords!)
// format - file format (PEM or DER)
//--------------------------------------------------
EXP_OPTION int ReadPrivateKey(EVP_PKEY **privKey, const char *keyfile, const char* passwd, int format)
{
  BIO *in; 
  EVP_PKEY *pkey = NULL;

  RETURN_IF_NULL_PARAM(keyfile);
  RETURN_IF_NULL_PARAM(passwd);
  if((in = BIO_new_file(keyfile, "rb")) != NULL) {
    switch(format) {
    case FILE_FORMAT_ASN1:
      pkey = d2i_PrivateKey_bio(in,NULL);
      break;
    case FILE_FORMAT_PEM:
      PEM_read_bio_PrivateKey(in, &pkey, pemkey_callback, (void*)passwd);
      break;
    }
    BIO_free(in);
    RETURN_IF_NOT(pkey, ERR_NULL_KEY_POINTER);
    *privKey = pkey;
  }
  else
    SET_LAST_ERROR_RETURN_CODE(ERR_FILE_READ);
  return ERR_OK;
}

//--------------------------------------------------
// Writes a private key file
// keyfile - name of the private key file
// passwd - key password (problems with encrypted passwwords!)
// format - file format (PEM or DER)
//--------------------------------------------------
EXP_OPTION int WritePrivateKey(EVP_PKEY *privKey, const char *keyfile, const char* passwd, int format)
{
  BIO *out = NULL; 
  EVP_PKEY *pkey = privKey;

  RETURN_IF_NULL_PARAM(privKey);
  RETURN_IF_NULL_PARAM(keyfile);
  if((out = BIO_new_file(keyfile, "wb")) != NULL) {
    switch(format) {
    case FILE_FORMAT_ASN1:
	  i2d_PUBKEY_bio(out, pkey);
      break;
    case FILE_FORMAT_PEM:
		PEM_write_bio_PrivateKey(out, pkey, (passwd ? EVP_des_ede3_cbc() : NULL), 
			(unsigned char*)passwd, strlen(passwd), pemkey_callback, (void*)passwd) ;
      break;
    }
    BIO_free(out);
  }
  else
    SET_LAST_ERROR_RETURN_CODE(ERR_FILE_WRITE);
  return ERR_OK;
}

//--------------------------------------------------
// Writes a private key and cert to a PEM file 
// privKey - private key
// pCert - certificate
// keyfile - name of the private key file
// passwd - key password (problems with encrypted passwwords!)
//--------------------------------------------------
EXP_OPTION int ddocWriteKeyAndCertPem(EVP_PKEY *privKey, X509* pCert, 
								  const char *keyfile, const char* passwd)
{
  BIO *out = NULL; 
  
  RETURN_IF_NULL_PARAM(pCert);
  RETURN_IF_NULL_PARAM(privKey);
  RETURN_IF_NULL_PARAM(keyfile);
  if((out = BIO_new_file(keyfile, "wb")) != NULL) {
	PEM_write_bio_X509(out, pCert);
    PEM_write_bio_PrivateKey(out, privKey, (passwd ? EVP_des_ede3_cbc() : NULL), 
		(unsigned char*)passwd, (passwd ? strlen(passwd) : 0), pemkey_callback, (void*)passwd);
    BIO_free(out);
  }
  else
    SET_LAST_ERROR_RETURN_CODE(ERR_FILE_WRITE);
  return ERR_OK;
}



//--------------------------------------------------
// Reads an RSA private key file
// keyfile - name of the private key file
// passwd - key password (problems with encrypted passwwords!)
// format - file format (PEM or DER)
//--------------------------------------------------
int ReadRSAPrivateKey(RSA **privKey, const char *keyfile, const char* passwd, int format)
{

  BIO *in = 0;
  RSA *pkey = 0;

  RETURN_IF_NULL_PARAM(keyfile);
  RETURN_IF_NULL_PARAM(passwd);
  if((in = BIO_new_file(keyfile, "rb")) != 0) {
    switch(format) {
    case FILE_FORMAT_ASN1:
      pkey = d2i_RSAPrivateKey_bio(in,NULL);
      break;
    case FILE_FORMAT_PEM:
      PEM_read_bio_RSAPrivateKey(in, &pkey, pemkey_callback, (void*)passwd);
      break;
    }
    BIO_free(in);
    RETURN_IF_NOT(pkey, ERR_NULL_KEY_POINTER);
    *privKey = pkey;
  }
  else
    SET_LAST_ERROR_RETURN_CODE(ERR_FILE_READ);
  return ERR_OK;
}

//--------------------------------------------------
// Verifys a certificate by sending an OCSP_REQUEST object
// to the notary server and checking the response.
// Uses servers timestamps hash code as nonce value.
// pCert - certificate to test
// caCerts - responder CA certs chain
// notaryCert - notarys cert search
// proxyHost - proxy servers name
// proxyPort - proxy servers port
// notaryURL - notarys URL
// ppResp - address to return OCSP response. Use NULL if
// you don't want OCSP response to be returned
// return 0 for OK, or error code
//--------------------------------------------------
EXP_OPTION int verifyCertificateByOCSP(X509* pCert, const X509** caCerts, 
				       const X509* notaryCert, char* notaryURL, 
				       char* proxyHost, char* proxyPort, 
				       const char* pkcs12file, const char* pkcs12paswd,
				       OCSP_RESPONSE **ppResp)
{
	return verifyCertificateByOCSPWithIp(pCert, caCerts, notaryCert, 
		notaryURL, proxyHost, proxyPort, pkcs12file, pkcs12paswd, ppResp, 0);
}

//--------------------------------------------------
// Verifys a certificate by sending an OCSP_REQUEST object
// to the notary server and checking the response.
// Uses servers timestamps hash code as nonce value.
// pCert - certificate to test
// caCerts - responder CA certs chain
// notaryCert - notarys cert search
// proxyHost - proxy servers name
// proxyPort - proxy servers port
// notaryURL - notarys URL
// ppResp - address to return OCSP response. Use NULL if
// you don't want OCSP response to be returned
// return 0 for OK, or error code
//--------------------------------------------------
EXP_OPTION int verifyCertificateByOCSPWithIp(X509* pCert, const X509** caCerts, 
				       const X509* notaryCert, char* notaryURL, 
				       char* proxyHost, char* proxyPort, 
				       const char* pkcs12file, const char* pkcs12paswd,
				       OCSP_RESPONSE **ppResp, unsigned long ip) 

{
  OCSP_REQUEST *req = 0;
  OCSP_RESPONSE *resp = 0;
  int err = ERR_OK, l1, i;
  byte nonce1[DIGEST_LEN+2];
  time_t tNow;
  X509* pCA = NULL;
  
  RETURN_IF_NULL(pCert);
  //RETURN_IF_NULL(notaryCert);
  //RETURN_IF_NULL(caCerts);
  RETURN_IF_NULL(notaryURL);
  
  // mark as not found yet
  if(ppResp)
    *ppResp = 0;
  time(&tNow);
  l1 = sizeof(nonce1);
  calculateDigest((const byte*)&tNow, sizeof(tNow), DIGEST_SHA1, nonce1, &l1);
  // find lowest CA
  for(i = 0; (caCerts != NULL) && (caCerts[i] != NULL); i++)
    pCA = (X509*)caCerts[i];
  err = createOCSPRequest(NULL, &req, pCert, pCA, nonce1, l1);		
  if(err == ERR_OK && pkcs12file)
    err = signOCSPRequestPKCS12(req, pkcs12file, pkcs12paswd);
  if(err == ERR_OK) {
    //WriteOCSPRequest("test1.req", req);
    err = sendOCSPRequest(&resp, req, notaryURL, proxyHost, proxyPort, ip);
    //WriteOCSPResponse("test1.resp", resp);
    //printf("sendOCSPRequest returned %d\n", err);
    if(err == ERR_OK) {
      if(caCerts && notaryCert) {
        ddocDebug(1, "verifyCertificateByOCSPWithIp", "Verify OCSP resp with known CA certs");
        // check the response signature
        err = verifyOCSPResponse(resp, (const X509**)caCerts, NULL, notaryCert);
      } 
      if(err == ERR_OK)
	err = checkNonceAndCertbyOCSP(resp, pCert, nonce1, l1);
      //WriteOCSPResponse("test1.resp", resp);
    }
  }
  // free OCSP request, if allocated;
  if (req)
    OCSP_REQUEST_free(req);
  // return OCSP response if required
  if(ppResp)
    *ppResp = resp;
  else
    OCSP_RESPONSE_free(resp);
  return err;
}

//============================================================
// Decodes binary (DER) cert data and returns a cert object
// certData - binary (DER) cert data
// certLen - cert data length
//============================================================
EXP_OPTION int ddocDecodeX509Data(X509 **ppX509, const byte* certData, int certLen)
{
  BIO* b1 = NULL;

  // check input params
  RETURN_IF_NULL_PARAM(certData);
  RETURN_IF_NULL_PARAM(ppX509);
  // mark as not read yet
  *ppX509 = 0;
  // create memory BIO on it
  b1 = BIO_new_mem_buf((void*)certData, certLen);
  RETURN_IF_NOT(b1, ERR_CERT_INVALID);
  *ppX509 = d2i_X509_bio(b1, NULL);
  ddocDebug(4, "ddocDecodeX509Data", "Decoding %d bytes DER data - cert %s", certLen, (*ppX509 ? "OK" : "ERROR"));
  // cleanup
  BIO_free(b1);
  RETURN_IF_NOT(*ppX509, ERR_CERT_INVALID);
  return ERR_OK;
}

//============================================================
// Decodes base64 (PEM) cert data and returns a cert object
// certData - base64 (PEM) cert data
// certLen - cert data length
//============================================================
EXP_OPTION int ddocDecodeX509PEMData(X509 **ppX509, const char* certData, int certLen)
{
  byte* p1 = 0;
  int l1 = 0, err = ERR_OK;

  // check input params
  RETURN_IF_NULL_PARAM(certData);
  RETURN_IF_NULL_PARAM(ppX509);
  // mark as not read yet
  *ppX509 = 0;
  // allocate memory for decoding
  l1 = certLen; // 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*)certData, certLen, p1, &l1);
  // decode cert
  err = ddocDecodeX509Data(ppX509, p1, l1);
  ddocDebug(4, "ddocDecodeX509PEMData", 
	    "Decoding %d bytes PEM data - cert %s", certLen, (*ppX509 ? "OK" : "ERROR"));
  // cleanup
  if(p1)
    free(p1);
  return err;
}

//============================================================
// Checks if the cert is valid (between begin and end date)
// cert - certificate object
// tDate - date to check the cert
//============================================================
EXP_OPTION int isCertValid(X509* cert, time_t tDate)
{
  int err = ERR_OK;
  ASN1_TIME *tm = 0;
  time_t tStart, tEnd;

  RETURN_IF_NULL_PARAM(cert);
  tm = X509_get_notBefore(cert);
  RETURN_IF_NULL(tm);
  asn1time2time_t_local(tm, &tStart);
  tm = X509_get_notAfter(cert);
  RETURN_IF_NULL(tm);
  asn1time2time_t_local(tm, &tEnd);
  if(tDate < tStart || tDate > tEnd) {
    err = ERR_CERT_INVALID;
    SET_LAST_ERROR(err);
  } 
  return err;
}

//============================================================
// Retrieves the certificates first validity time as tim_t in GMT zone
// pCert - certificate object
// returns certificates first validity time as tim_t in GMT zone
//============================================================
EXP_OPTION time_t getCertNotBeforeTimeT(X509* pCert)
{
  time_t t1 = 0;
  ASN1_TIME *tm = 0;

  if(pCert) {
    tm = X509_get_notBefore(pCert);
    if(tm) 
      asn1time2time_t_local(tm, &t1);
  } 
  return t1;
}

//============================================================
// Retrieves the certificates last validity time as tim_t in GMT zone
// pCert - certificate object
// returns certificates last validity time as tim_t in GMT zone
//============================================================
EXP_OPTION time_t getCertNotAfterTimeT(X509* pCert)
{
  time_t t1 = 0;
  ASN1_TIME *tm = 0;

  if(pCert) {
    tm = X509_get_notAfter(pCert);
    if(tm) 
      asn1time2time_t_local(tm, &t1);
  } 
  return t1;
}

//============================================================
// Checks if the cert has been signed by this CA-cert
// cert - certificate object
// cafile - CA cert file
//============================================================
EXP_OPTION int isCertSignedBy(X509* cert, const char* cafile)
{
  int err = ERR_OK;
  EVP_PKEY* pubkey = NULL; // SVEN - ver 1.92 - crashbug fixed
	
  (void)ReadPublicKey(&pubkey, cafile);
  RETURN_IF_NOT(pubkey, ERR_PUBKEY_READ);
  
  err = X509_verify(cert, pubkey);
  if(err == ERR_LIB_NONE)
    err = ERR_OK;
  else {
    err = ERR_CERT_ISSUER;
    SET_LAST_ERROR(err);
  }
  // checkErrors();
  //332:error:0D0890A1:lib(13):func(137):reason(161):.\crypto\asn1\a_verify.c:141:
  EVP_PKEY_free(pubkey);
  return err;
}

//============================================================
// Writes cert to file without the usual PEM headers
// bout - output file
// cert - certificate object
// returns error code
//============================================================
int writeCertToXMLFile(BIO* bout, X509* cert)
{
  int l1, l2;
  char *p1, *p2;

  RETURN_IF_NULL_PARAM(bout);
  RETURN_IF_NULL_PARAM(cert);
  l1 = i2d_X509(cert, NULL);
  p1 = (char*)malloc(l1+10);
  RETURN_IF_BAD_ALLOC(p1);
  p2 = p1;
  i2d_X509(cert, (unsigned char**)&p2);
  l2 = l1 * 2;
  p2 = (char*)malloc(l2);
  if (p2 == NULL) {
    free(p1);
    RETURN_IF_BAD_ALLOC(p2);
  }
  encode((const byte*)p1, l1, (byte*)p2, &l2);
  BIO_puts(bout, p2);
  free(p2);
  free(p1);
  return ERR_OK;
}

//============================================================
// Converts the certificate to PEM form with or without headers
// cert - certificate object
// bHeaders - 1= with headers, 0=no headers
// buf - output buffer newly allocated
// returns error code
//============================================================
EXP_OPTION int getCertPEM(X509* cert, int bHeaders, char** buf)
{
  int l1, l2;
  char *p1, *p2;

  RETURN_IF_NULL_PARAM(buf);
  RETURN_IF_NULL_PARAM(cert);
  l1 = i2d_X509(cert, NULL);
  p1 = (char*)malloc(l1+10);
  RETURN_IF_BAD_ALLOC(p1);
  p2 = p1;
  i2d_X509(cert, (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 CERTIFICATE-----\n", l2);
  encode((const byte*)p1, l1, (byte*)strchr(*buf, 0), &l2);
  l2 = l1 * 2 + 200 - strlen(*buf);
  if(bHeaders)
    strncat(*buf, "\n-----END CERTIFICATE-----", l2);
  free(p1);
  return ERR_OK;
}



//--------------------------------------------------
// Returns the certificates validity first date
// cert - certificate data
// timestamp - timestamp buffer
// returns error code or ERR_OK
//--------------------------------------------------
EXP_OPTION int getCertNotBefore(const SignedDoc* pSigDoc, X509* cert, char* timestamp, int len)
{
  int err = ERR_OK;
  ASN1_TIME *tm = 0;
  RETURN_IF_NULL_PARAM(cert);
  RETURN_IF_NULL_PARAM(timestamp);
  
  tm = X509_get_notBefore(cert);
  RETURN_IF_NULL(tm);
  err = asn1time2strYear(pSigDoc, tm, timestamp, 1900, len);
  
  if (err != ERR_OK) SET_LAST_ERROR(err);
  return err;
}


//--------------------------------------------------
// Returns the certificates validity last date
// cert - certificate data
// timestamp - timestamp buffer
// returns error code or ERR_OK
//--------------------------------------------------
EXP_OPTION int getCertNotAfter(const SignedDoc* pSigDoc, X509* cert, char* timestamp, int len)
{
  int err = ERR_OK;
  ASN1_TIME *tm = 0;

  RETURN_IF_NULL_PARAM(cert);
  RETURN_IF_NULL_PARAM(timestamp);
  
  tm = X509_get_notAfter(cert);
  RETURN_IF_NULL(tm);
  err = asn1time2strYear(pSigDoc, tm, timestamp, 1900, len);

  if (err != ERR_OK) SET_LAST_ERROR(err);
  return err;
}


//--------------------------------------------------
// Saves the certificate in a file
// cert - certificate data
// szFileName - destination filename
// nFormat - cert format
// returns error code or ERR_OK
//--------------------------------------------------
EXP_OPTION int saveCert(X509* cert, const char* szFileName, int nFormat)
{
  //	int err = ERR_OK;
  BIO* b;
  
  RETURN_IF_NULL_PARAM(cert);
  RETURN_IF_NULL_PARAM(szFileName);
  
  if((b = BIO_new_file(szFileName, "w")) != NULL) {
    if(nFormat == FILE_FORMAT_PEM)
      PEM_write_bio_X509(b, cert);
    if(nFormat == FILE_FORMAT_ASN1)
      i2d_X509_bio(b, cert);
    BIO_free(b);
  } else 
    SET_LAST_ERROR_RETURN_CODE(ERR_FILE_WRITE);
  return ERR_OK;
}

// new functions for Win client

EXP_OPTION void* decodeCert(const char* pemData)
{
  BIO* b1;
  X509* x509;
  int l1;

  RETURN_OBJ_IF_NULL(pemData, NULL);
  l1 = strlen(pemData);
  b1 = BIO_new_mem_buf((void*)pemData, l1);
  RETURN_OBJ_IF_NULL(b1, 0);
  x509 = PEM_read_bio_X509(b1, NULL, NULL, 0);
  if(!x509)
    SET_LAST_ERROR(ERR_CERT_READ);
  //		checkErrors();
  BIO_free(b1);
  return x509;
}




//===========================================================
// Encodes certificate from X509 to binary
//===========================================================
EXP_OPTION void encodeCert(const X509* x509, char * encodedCert, int* encodedCertLen)
{
  if(x509==NULL){
    *encodedCertLen = 0;
    return;
  }
  if(encodedCert==NULL){
    *encodedCertLen = 0;
    return;
  }
  *encodedCertLen = i2d_X509((X509*)x509, NULL);
  i2d_X509((X509*)x509, (unsigned char**)&encodedCert);
}

//============================================================
// Checks if the cert has been signed by this CA-cert
// cert - certificate object
// cafile - CA cert file
//============================================================
EXP_OPTION int isCertSignedByCERT(const X509* cert, const X509* caCert)
{
  int err = ERR_OK;
  EVP_PKEY* pubkey;
  DigiDocMemBuf mbuf1, mbuf2, mbuf3;

  mbuf1.pMem = 0;
  mbuf1.nLen = 0;
  mbuf2.pMem = 0;
  mbuf2.nLen = 0;
  mbuf3.pMem = 0;
  mbuf3.nLen = 0;
  RETURN_IF_NULL_PARAM(cert);
  RETURN_IF_NULL_PARAM(caCert);
  err = ddocCertGetSubjectCN((X509*)caCert, &mbuf1);
  err = ddocCertGetSubjectCN((X509*)cert, &mbuf2);
  err = ddocCertGetIssuerCN((X509*)cert, &mbuf3);
  ddocDebug(3, "isCertSignedByCERT", 
	    "Issuer: %s, Subject: %s, Subjects-issuer: %s", 
	    (const char*)mbuf1.pMem, (const char*)mbuf2.pMem,
	    (const char*)mbuf3.pMem);
  ddocMemBuf_free(&mbuf1);
  ddocMemBuf_free(&mbuf2);
  ddocMemBuf_free(&mbuf3);
  err = GetPublicKey(&pubkey, caCert);
  if(err == ERR_OK) {
    err = X509_verify((X509*)cert, pubkey);
    ddocDebug(3, "isCertSignedByCERT", "verify: %d", err);
    if(err == ERR_LIB_NONE)
      err = ERR_OK;
    else
      err = ERR_CERT_ISSUER;
    if(err != ERR_OK) {
      ddocDebug(3, "isCertSignedByCERT", "Cert not issued by ca verify: %d", err);
      checkErrors();
    }
    EVP_PKEY_free(pubkey);
  }
  else 
    err = ERR_PUBKEY_READ;
  if (err != ERR_OK) SET_LAST_ERROR(err);
  return err;
}


/*int utc2latin1(const char* data, int len, char* dest)
{
  int i, j;
  
  for(i = j = 0; i < len; i++) {
    if(data[i]) {
      dest[j] = data[i];
      j++;
    }
  }
  dest[j] = 0;
  return j;
  }*/

//--------------------------------------------------------
// Checks if the desired key-usage bit is set on a given cert
// pCert - certificate
// nBit - flag index
// return 1 if bit is set
//--------------------------------------------------------
EXP_OPTION int ddocCertCheckKeyUsage(X509 *pCert, int nBit)
{
  int crit = -1, extIdx = -1;
  ASN1_BIT_STRING *keyUsage;

  if(pCert && nBit >= 0 && nBit < 8) {
    keyUsage = (ASN1_BIT_STRING *)X509_get_ext_d2i(pCert, NID_key_usage, &crit, &extIdx);
    if(keyUsage) {
      ddocDebug(3, "ddocCertCheckKeyUsage", "Bit: %d set: %d", nBit, ASN1_BIT_STRING_get_bit(keyUsage, nBit));
      return ASN1_BIT_STRING_get_bit(keyUsage, nBit);
    }
  }
  return 0;
}


//--------------------------------------------------------
// Finds and copies a substring from source string that is
// prefixed by szLabel and terminated by szTerminator
// szSrc - source string
// szLabel - prefix of searched string
// szTerminator - terminator string
// szDest - found string. Caller must free this mem.
//--------------------------------------------------------
int ddocCertCopySubstring(const char* szSrc, const char* szLabel, const char* szTerminator, char** szDest)
{
	ptrdiff_t l;
	char *p1, *p2;

	RETURN_IF_NULL_PARAM(szSrc);
	RETURN_IF_NULL_PARAM(szLabel);
	RETURN_IF_NULL_PARAM(szTerminator);
	RETURN_IF_NULL_PARAM(szDest);
	*szDest = 0; // mark as empty
	p1 = strstr(szSrc, szLabel);
	if(p1) {
		p1 += strlen(szLabel);
		p2 = strstr(p1, szTerminator);
		if(!p2)
			p2 = strchr(p1, 0);
		if(p2 && p2 > p1) {
			l = p2 - p1 + 1;
			*szDest = (char*)malloc(l);
			if(*szDest) {
				memset(*szDest, 0, l);
				strncpy(*szDest, p1, p2 - p1);
			}
		}
	}
	return ERR_OK;
}

EXP_OPTION int readCertPoliciesFromOU(X509* pX509, PolicyIdentifier** pPolicies, int* nPols)
{
	DigiDocMemBuf mbuf1;
	int err = ERR_OK;
	char *pUri, *pDesc, *pOid;
	PolicyIdentifier *pTmp;

    RETURN_IF_NULL_PARAM(pX509);
    RETURN_IF_NULL_PARAM(pPolicies);
    RETURN_IF_NULL_PARAM(nPols);
	mbuf1.nLen = 0;
	mbuf1.pMem = 0;
	pUri = pDesc = pOid = 0;
	err = ddocCertGetSubjectOrganizationUnit(pX509, &mbuf1);
	if(!err && mbuf1.pMem) {
	  err = ddocCertCopySubstring((const char*)mbuf1.pMem, "SP Desc:", "\n", &pDesc);
	  if(!err) {
	    err = ddocCertCopySubstring((const char*)mbuf1.pMem, "SP URI:", "\n", &pUri);
		if(!err) {
		  err = ddocCertCopySubstring((const char*)mbuf1.pMem, "SP OID:", "\n", &pOid);
		  if(!err && (pDesc || pUri || pOid)) {
			pTmp = (PolicyIdentifier*)realloc(*pPolicies, sizeof(PolicyIdentifier) * ((*nPols) + 1));
			if(pTmp) {
				*pPolicies = pTmp;
				pTmp = (*pPolicies) + (*nPols);
				*nPols  = (*nPols) + 1;
				pTmp->szOID = (pOid ? strdup((const char*)pOid) : 0);
				pTmp->szUserNotice = (pDesc ? strdup((const char*)pDesc) : 0);
				pTmp->szCPS = (pUri ? strdup((const char*)pUri) : 0);
			}
		  }
		  free(pOid);
		}
		free(pUri);
	  }
	  free(pDesc);
	}
	ddocMemBuf_free(&mbuf1);
	return err;
}

//--------------------------------------------------
// Reads certificates PolicyIdentifiers and returns 
// them in a newly allocated structure. Caller must 
// free this, regardless of function return status.
// pX509 - certificate
// pPolicies - returned PolicyIdentifiers array
// nPols - number of returned policies
//--------------------------------------------------
EXP_OPTION int readCertPolicies(X509* pX509, PolicyIdentifier** pPolicies, int* nPols)
{
  char buf[512], *p;
  int err = 0, i, k, j, pos, l;
  PolicyIdentifier *pPol, *pTmp;
  
  RETURN_IF_NULL_PARAM(pX509);
  RETURN_IF_NULL_PARAM(pPolicies);
  RETURN_IF_NULL_PARAM(nPols);
  *pPolicies = NULL;
  *nPols = 0;
  // try read from certificates OU first
  if(!err)
    err = readCertPoliciesFromOU(pX509, pPolicies, nPols);
  // read from usual place
  pos = X509_get_ext_by_NID(pX509, NID_certificate_policies, -1);
  if(pos >= 0) {
    POLICYINFO *pol;
    STACK_OF(POLICYINFO)* pPols;
    STACK_OF(POLICYQUALINFO)* pQuals;
    POLICYQUALINFO* pQual;
    X509_EXTENSION* pExt = X509_get_ext(pX509, pos);
    // X509V3_EXT_METHOD *method = X509V3_EXT_get(pExt);
    //pPols = (STACK*)X509V3_EXT_d2i(pExt);
	pPols = X509V3_EXT_d2i(pExt);
    for(i = 0; i < sk_POLICYINFO_num(pPols); i++) {
      pol = sk_POLICYINFO_value(pPols, i);
      if(*pPolicies && *nPols) {
	pTmp = (PolicyIdentifier*)realloc(*pPolicies, sizeof(PolicyIdentifier) * ((*nPols) + 1));
	if (pTmp != NULL) {
	  *pPolicies = pTmp;
	  *nPols = (*nPols) + 1;
	  pPol = (*pPolicies) + ((*nPols) - 1);
	} else
	  SET_LAST_ERROR_RETURN_CODE(ERR_BAD_ALLOC);
      } else {
	pTmp = (PolicyIdentifier*)malloc(sizeof(PolicyIdentifier));
	if (pTmp != NULL) {
	  *pPolicies = pTmp;
	  pPol = *pPolicies;
	  *nPols = 1;
	} else
	  SET_LAST_ERROR_RETURN_CODE(ERR_BAD_ALLOC);
      }
      pPol->szCPS = pPol->szOID = pPol->szUserNotice = NULL;
      // check
      i2t_ASN1_OBJECT(buf, sizeof(buf), pol->policyid);
      pPol->szOID = strdup(buf);
      pQuals = pol->qualifiers;
      for(k = 0; k < sk_POLICYQUALINFO_num(pQuals); k++) {
	pQual = sk_POLICYQUALINFO_value(pQuals, k);
	switch(OBJ_obj2nid(pQual->pqualid)) {
	  case NID_id_qt_cps:
	    pPol->szCPS = strdup((const char*)pQual->d.cpsuri->data);
	    // if strdup fails, szCPS will be NULL
	    break;
	  case NID_id_qt_unotice:
	    ddocDebug(4, "readCertPolicies", "Exptext: %d - \'%s\'",
		      pQual->d.usernotice->exptext->length, pQual->d.usernotice->exptext->data);
	    if(pQual->d.usernotice->exptext) {
	      if(pQual->d.usernotice->exptext->type == V_ASN1_UTF8STRING) {
		pPol->szUserNotice = strdup((const char*)pQual->d.usernotice->exptext->data);
	      } else {
		p = 0;
		l = ASN1_STRING_to_UTF8((unsigned char**)&p, pQual->d.usernotice->exptext);
		pPol->szUserNotice = strdup(p);
		OPENSSL_free(p);
	      }
	      /*if(pQual->d.usernotice->exptext->data[0]) {
		pPol->szUserNotice = strdup((const char*)pQual->d.usernotice->exptext->data);
	      } else {
		utc2latin1((const char*)pQual->d.usernotice->exptext->data,
			   pQual->d.usernotice->exptext->length, buf);
		pPol->szUserNotice = strdup(buf);
		// if strdup fails, szUserNotice will be NULL
		}*/
	    }
	    if(pQual->d.usernotice->noticeref) {
	      NOTICEREF *ref = pQual->d.usernotice->noticeref;
	      p = 0; 
	      l = 0;
	      // calculate the memory required
	      l += strlen((const char*)ref->organization->data) + 1;
	      for(j = 0; j < sk_ASN1_INTEGER_num(ref->noticenos); j++) {
		ASN1_INTEGER *num;
		char *tmp;
		num = sk_ASN1_INTEGER_value(ref->noticenos, j);
		tmp = i2s_ASN1_INTEGER(NULL, num);
		l += strlen(tmp) + 2;
		OPENSSL_free(tmp);
	      }
	      // now alloc mem and copy data
	      l += 10; // alloc with a little extra
	      p = (char*)malloc(l); 
	      if(p) {
		memset(p, 0, l);
		strncpy(p, (const char*)ref->organization->data, l);
		strncat(p, " ", l - strlen(p));
		for(j = 0; j < sk_ASN1_INTEGER_num(ref->noticenos); j++) {
		  ASN1_INTEGER *num;
		  char *tmp;
		  num = sk_ASN1_INTEGER_value(ref->noticenos, j);
		  if (j) strncat(p, ", ", l - strlen(p));
		  tmp = i2s_ASN1_INTEGER(NULL, num);
		  strncat(buf, tmp, sizeof(buf) - strlen(buf));
		  OPENSSL_free(tmp);
		}
		pPol->szUserNotice = p;
	      }
	    }
	    break;
	default:
	  break;
	}
      }
    }
	sk_POLICYINFO_pop_free(pPols, POLICYINFO_free);
  }
  ddocDebug(4, "readCertPolicies", "End");
  return err;
}



//--------------------------------------------------
// Frees policy identifiers array
// pPolicies - PolicyIdentifiers array
// nPols - number of policies
//--------------------------------------------------
EXP_OPTION void PolicyIdentifiers_free(PolicyIdentifier* pPolicies, int nPols)
{
  int i;
  for(i = 0; i < nPols; i++) {
    free(pPolicies[i].szOID);
    free(pPolicies[i].szCPS);
    free(pPolicies[i].szUserNotice);
    //free(pPolicies[i]);
  }
  free(pPolicies);
}

//--------------------------------------------------
// Reads certificates extension value
// pCert - certificate
// pMemBuf - memory buffer to return data
// nExt - extension code
//--------------------------------------------------
EXP_OPTION int readCertExtData(X509* pCert, DigiDocMemBuf* pMemBuf, int nExt, int nOff)
{
  int err = ERR_OK, pos;
  X509_EXTENSION* pExt;
/*  char buf1[300];
  int l1;*/

  RETURN_IF_NULL_PARAM(pCert);
  RETURN_IF_NULL_PARAM(pMemBuf);  
  pMemBuf->pMem = 0;
  pMemBuf->nLen = 0;
  pos = X509_get_ext_by_NID(pCert, nExt, -1);
  if(pos >= 0) {
	pExt = X509_get_ext(pCert, pos);
	ASN1_OCTET_STRING *value = X509_EXTENSION_get_data(pExt);
	if(pExt && value && value->data) {
      /*    memset(buf1, 0, sizeof(buf1));
          l1 = sizeof(buf1);
	  bin2hex(pExt->value->data, pExt->value->length, buf1, &l1);
          ddocDebug(3, "readCertExtData", "Ext: %d len: %d data: %s", nExt, pExt->value->length, buf1);*/
		if(value->length > 20 && nOff)
			//ddocMemAssignData(pMemBuf, ((char*)pExt->value->data) + (pExt->value->length - 20), 20);
			ddocMemAssignData(pMemBuf, ((char*)value->data) + nOff, 20);
		else
			ddocMemAssignData(pMemBuf, ((char*)value->data), value->length);
	}
  }
  return err;
}

//--------------------------------------------------
// Reads certificates authority key identifier
// pCert - certificate
// pMemBuf - memory buffer to return data
//--------------------------------------------------
EXP_OPTION int readAuthorityKeyIdentifier(X509* pCert, DigiDocMemBuf* pMemBuf)
{
    int err = ERR_OK;
    int crit = 0, l1;
    char *p = 0;
    
    AUTHORITY_KEYID *pAkid = X509_get_ext_d2i(pCert, NID_authority_key_identifier, &crit, NULL); 
    if(pAkid) {
        l1 = i2d_ASN1_OCTET_STRING(pAkid->keyid, (unsigned char **)&p);
        if(l1 > 20)
          ddocMemAssignData(pMemBuf, p + (l1 - 20), 20);
        else
          ddocMemAssignData(pMemBuf, (char*)p, l1);
        OPENSSL_free(p);
        AUTHORITY_KEYID_free(pAkid);
    }
    return err;
  //return readCertExtData(pCert, pMemBuf, NID_authority_key_identifier, 5);
}

//--------------------------------------------------
// Reads certificates subject key identifier
// pCert - certificate
// pMemBuf - memory buffer to return data
//--------------------------------------------------
EXP_OPTION int readSubjectKeyIdentifier(X509* pCert, DigiDocMemBuf* pMemBuf)
{
  return readCertExtData(pCert, pMemBuf, NID_subject_key_identifier, 2);
}


//--------------------------------------------------
// Checks if this is a company CPS policy
// pPolicy - PolicyIdentifier to be checked
//--------------------------------------------------
EXP_OPTION int isCompanyCPSPolicy(PolicyIdentifier* pPolicy)
{
  //return (strstr(pPolicy->szCPS, "1.3.6.4.1.10015.7") != NULL);
  if(pPolicy && pPolicy->szCPS)
    return (strstr(pPolicy->szCPS, "1.3.6.1.4.1.10015.5") != NULL);
  else
    return 0;
}


//--------------------------------------------------
// Returns the certificates sha1 hash. 
// pCert - certificate data
// pMemBuf - memory buffer object for storing DN
// returns error code or ERR_OK
//--------------------------------------------------
int ddocCertGetDigest(X509* pCert, DigiDocMemBuf* pMemBuf)
{
  int err = ERR_OK;
  unsigned int l1;

  RETURN_IF_NULL_PARAM(pCert);
  RETURN_IF_NULL_PARAM(pMemBuf);  
  pMemBuf->pMem = 0;
  pMemBuf->nLen = 0;
  l1 = 30;
  err = ddocMemSetLength(pMemBuf, l1);
  X509_digest(pCert, EVP_sha1(), (unsigned char*)pMemBuf->pMem, &l1);
  pMemBuf->nLen = l1;
  return err;
}

//--------------------------------------------------
// Returns the certificates public key sha1 hash. 
// pCert - certificate data
// pMemBuf - memory buffer object for storing DN
// returns error code or ERR_OK
//--------------------------------------------------
int ddocCertGetPubkeyDigest(X509* pCert, DigiDocMemBuf* pMemBuf)
{
  int err = ERR_OK;
  unsigned int l1;

  RETURN_IF_NULL_PARAM(pCert);
  RETURN_IF_NULL_PARAM(pMemBuf);  
  pMemBuf->pMem = 0;
  pMemBuf->nLen = 0;
  l1 = 30;
  err = ddocMemSetLength(pMemBuf, l1);
  X509_pubkey_digest(pCert, EVP_sha1(), (unsigned char*)pMemBuf->pMem, &l1);
  pMemBuf->nLen = l1;
  return err;
}

//--------------------------------------------------
// Returns the certificates subject name sha1 hash. 
// pCert - certificate data
// pMemBuf - memory buffer object for storing DN
// returns error code or ERR_OK
//--------------------------------------------------
int ddocCertGetSubjectNameDigest(X509* pCert, DigiDocMemBuf* pMemBuf)
{
    int err = ERR_OK;
    unsigned int l1;
    X509_NAME *iname;
    
    RETURN_IF_NULL_PARAM(pCert);
    RETURN_IF_NULL_PARAM(pMemBuf);  
    pMemBuf->pMem = 0;
    pMemBuf->nLen = 0;
    l1 = 30;
    err = ddocMemSetLength(pMemBuf, l1);
    iname = X509_get_subject_name(pCert);
    X509_NAME_digest(iname, EVP_sha1(), (unsigned char*)pMemBuf->pMem, &l1);
    pMemBuf->nLen = l1;
    return err;
}

//--------------------------------------------------
// Returns the certificates issuer name sha1 hash. 
// pCert - certificate data
// pMemBuf - memory buffer object for storing DN
// returns error code or ERR_OK
//--------------------------------------------------
int ddocCertGetIssuerNameDigest(X509* pCert, DigiDocMemBuf* pMemBuf)
{
    int err = ERR_OK;
    unsigned int l1;
    X509_NAME *iname;
    
    RETURN_IF_NULL_PARAM(pCert);
    RETURN_IF_NULL_PARAM(pMemBuf);  
    pMemBuf->pMem = 0;
    pMemBuf->nLen = 0;
    l1 = 30;
    err = ddocMemSetLength(pMemBuf, l1);
    iname = X509_get_issuer_name(pCert);
    X509_NAME_digest(iname, EVP_sha1(), (unsigned char*)pMemBuf->pMem, &l1);
    pMemBuf->nLen = l1;
    return err;
}

//--------------------------------------------------
// Returns the certificates DN. Internal function.
// Do not call directly, subject to change
// pCert - certificate data
// pMemBuf - memory buffer object for storing DN
// bIssuer - 1=issuer, 0=subject
// returns error code or ERR_OK
//--------------------------------------------------
int ddocCertGetDN(X509* pCert, DigiDocMemBuf* pMemBuf, int bIssuer)
{
  int err = ERR_OK;
  X509_NAME *pName = 0;
  X509_NAME_ENTRY *pNe = 0;
  ASN1_STRING *data = 0;
  int i, n, l, t, b = 0;
  const char *s;
  unsigned char* p;
	
  RETURN_IF_NULL_PARAM(pCert);
  RETURN_IF_NULL_PARAM(pMemBuf);
  
  // initialize
  if(pMemBuf->pMem)
    err = ddocMemBuf_free(pMemBuf);
  pMemBuf->pMem = 0;
  pMemBuf->nLen = 0;

  // iterate over name entries
  if(bIssuer)
    pName = X509_get_issuer_name(pCert);
  else
    pName = X509_get_subject_name(pCert);
  RETURN_IF_NULL(pName)
  for(i = 0; (err == ERR_OK) && (i < X509_NAME_entry_count(pName)); i++) {
	pNe = X509_NAME_get_entry(pName, i);
	n = OBJ_obj2nid(X509_NAME_ENTRY_get_object(pNe));
    s = OBJ_nid2sn(n);
	data = X509_NAME_ENTRY_get_data(pNe);
	t = data->type;
    // mostly we find here:
    // V_ASN1_PRINTABLESTRING, V_ASN1_TELETEXSTRING or V_ASN1_BMPSTRING
    // that we convert to UTF, but V_ASN1_UTF8STRING allready is in UTF8
    // handle only the enry types we know
    if(n != NID_undef && s != NULL) {
      // convert to UTF8 only
      p = 0;
      if(t == V_ASN1_UTF8STRING) {
		p = data->data;
		l = data->length;
      } else
		l = ASN1_STRING_to_UTF8(&p, data);
      ddocDebug(5, "ddocCertGetDN", 
		"NameEntry nid: %d type: %d len: %d item: %s value: \'%s\'", 
		n, t, l, s, (p ? (const char*)p : "NULL"));
      // append separator if necessary
      if(b) 
	err = ddocMemAppendData(pMemBuf, ",", -1); // RFC2253 must use ,
      else 
	b = 1;
      // print the entry
      err = ddocMemAppendData(pMemBuf, s, -1);
      err = ddocMemAppendData(pMemBuf, "=", -1);
      err = ddocMemAppendData(pMemBuf, (const char*)p, l);
      // cleanup
      if(p && t != V_ASN1_UTF8STRING)
	OPENSSL_free(p);
    }
    ddocDebug(5, "ddocCertGetDN", "Subject: \'%s\' len: %d", 
	      (const char*)pMemBuf->pMem, pMemBuf->nLen);
  } // for

  return err;
}

//--------------------------------------------------
// Returns the certificates issuer name.
// pCert - certificate data
// pMemBuf - memory buffer object for storing DN
// returns error code or ERR_OK
//--------------------------------------------------
EXP_OPTION int ddocCertGetIssuerDN(X509* pCert, DigiDocMemBuf* pMemBuf)
{
  return ddocCertGetDN(pCert, pMemBuf, 1);
}


//--------------------------------------------------
// Returns the certificates subject name.
// pCert - certificate data
// pMemBuf - memory buffer object for storing DN
// returns error code or ERR_OK
//--------------------------------------------------
EXP_OPTION int ddocCertGetSubjectDN(X509* pCert, DigiDocMemBuf* pMemBuf)
{
  return ddocCertGetDN(pCert, pMemBuf, 0);
}

//--------------------------------------------------
// Returns the certificates DN.
// Do not call directly, subject to change
// pCert - certificate data
// pMemBuf - memory buffer object for storing DN
// bIssuer - 1=issuer, 0=subject
// returns error code or ERR_OK
//--------------------------------------------------
EXP_OPTION int ddocCertGetDNFromName(X509_NAME* pName, DigiDocMemBuf* pMemBuf)
{
  int err = ERR_OK;
  X509_NAME_ENTRY *pNe = 0;
  ASN1_STRING *data = 0;
  int i, n, l, t, b = 0;
  const char *s;
  unsigned char* p;
	
  RETURN_IF_NULL_PARAM(pMemBuf);
  
  // initialize
  if(pMemBuf->pMem)
    err = ddocMemBuf_free(pMemBuf);
  pMemBuf->pMem = 0;
  pMemBuf->nLen = 0;

  for(i = 0; (err == ERR_OK) && (i < X509_NAME_entry_count(pName)); i++) {
	pNe = X509_NAME_get_entry(pName, i);
	n = OBJ_obj2nid(X509_NAME_ENTRY_get_object(pNe));
    s = OBJ_nid2sn(n);
	data = X509_NAME_ENTRY_get_data(pNe);
	t = data->type;
    // mostly we find here:
    // V_ASN1_PRINTABLESTRING, V_ASN1_TELETEXSTRING or V_ASN1_BMPSTRING
    // that we convert to UTF, but V_ASN1_UTF8STRING allready is in UTF8
    // handle only the enry types we know
    if(n != NID_undef && s != NULL) {
      // convert to UTF8 only
      p = 0;
      if(t == V_ASN1_UTF8STRING) {
		p = data->data;
		l = data->length;
      } else
		l = ASN1_STRING_to_UTF8(&p, data);
      ddocDebug(5, "ddocCertGetDN", 
		"NameEntry nid: %d type: %d len: %d item: %s value: \'%s\'", 
		n, t, l, s, (p ? (const char*)p : "NULL"));
      // append separator if necessary
      if(b) 
	err = ddocMemAppendData(pMemBuf, "/", -1);
      else 
	b = 1;
      // print the entry
      err = ddocMemAppendData(pMemBuf, s, -1);
      err = ddocMemAppendData(pMemBuf, "=", -1);
      err = ddocMemAppendData(pMemBuf, (const char*)p, l);
      // cleanup
      if(p && t != V_ASN1_UTF8STRING)
	OPENSSL_free(p);
    }
    ddocDebug(5, "ddocCertGetDN", "Subject: \'%s\' len: %d", 
	      (const char*)pMemBuf->pMem, pMemBuf->nLen);
  } // for

  return err;
}

//--------------------------------------------------
// Returns the certificates subject name and returns 
// the desired item from it.
// pCert - certificate data
// pMemBuf - memory buffer object for storing result
// nNid - cert name part NID
// bIssuer - 1=from issuer DN, 0=from subject DN
// returns error code or ERR_OK
//--------------------------------------------------
int ddocCertGetDNPart(X509* pCert, DigiDocMemBuf* pMemBuf, int nNid, int bIssuer)
{
  int err = ERR_OK;
  X509_NAME *pName = 0;
  X509_NAME_ENTRY *pNe = 0;
  ASN1_STRING *data;
  int i, n, l, t, m, j;
  const char *s;
  unsigned char* p = 0;
	
  RETURN_IF_NULL_PARAM(pCert);
  RETURN_IF_NULL_PARAM(pMemBuf);
  
  // initialize
  if(pMemBuf->pMem)
    err = ddocMemBuf_free(pMemBuf);
  pMemBuf->pMem = 0;
  pMemBuf->nLen = 0;

  // iterate over name entries
  if(bIssuer)
    pName = X509_get_issuer_name(pCert);
  else
    pName = X509_get_subject_name(pCert);
  for(i = 0; (err == ERR_OK) && (i < X509_NAME_entry_count(pName)); i++) {
	pNe = X509_NAME_get_entry(pName, i);
	n = OBJ_obj2nid(X509_NAME_ENTRY_get_object(pNe));
    s = OBJ_nid2sn(n);
	data = X509_NAME_ENTRY_get_data(pNe);
	t = data->type;
    // mostly we find here:
    // V_ASN1_PRINTABLESTRING, V_ASN1_TELETEXSTRING or V_ASN1_BMPSTRING
    // that we convert to UTF, but V_ASN1_UTF8STRING allready is in UTF8
    // handle only the enry types we know
    if(n == nNid && s != NULL) {
      // convert to UTF8 only
      if(t == V_ASN1_UTF8STRING) {
		p = data->data;
		l = data->length;
      } else
		l = ASN1_STRING_to_UTF8(&p, data);
	// test for 0x0
        m = (p ? strlen(p) : 0);
	if(m < l && p) {
	 for(j = 0; j < l; j++)
	   if(p[j] == 0)
		p[j] = ' ';
	}
      ddocDebug(5, "ddocCertGetDNPart", 
		"NameEntry type: %d len: %d item: %s value: \'%s\'", 
		t, l, s, (p ? (const char*)p : "NULL"));
	  if(pMemBuf->pMem && strlen(pMemBuf->pMem))
		ddocMemAppendData(pMemBuf, "\n", -1);
      err = ddocMemAppendData(pMemBuf, (const char*)p, l);
      // cleanup
      if(p && t != V_ASN1_UTF8STRING)
	OPENSSL_free(p);
		// continue search
    }
  } // for

  return err;
}

//--------------------------------------------------
// Returns the certificates subject CN
// pCert - certificate data
// pMemBuf - memory buffer object for storing result
// returns error code or ERR_OK
//--------------------------------------------------
EXP_OPTION int ddocCertGetSubjectCN(X509* pCert, DigiDocMemBuf* pMemBuf)
{
  return ddocCertGetDNPart(pCert, pMemBuf, NID_commonName, 0);
}


//--------------------------------------------------
// Returns the certificates issuer CN
// pCert - certificate data
// pMemBuf - memory buffer object for storing result
// returns error code or ERR_OK
//--------------------------------------------------
EXP_OPTION int ddocCertGetIssuerCN(X509* pCert, DigiDocMemBuf* pMemBuf)
{
  return ddocCertGetDNPart(pCert, pMemBuf, NID_commonName, 1);
}


//--------------------------------------------------
// Returns the certificates subject first name
// pCert - certificate data
// pMemBuf - memory buffer object for storing result
// returns error code or ERR_OK
//--------------------------------------------------
EXP_OPTION int ddocCertGetSubjectFirstName(X509* pCert, DigiDocMemBuf* pMemBuf)
{
  return ddocCertGetDNPart(pCert, pMemBuf, NID_firstName, 0);
}


//--------------------------------------------------
// Returns the certificates subject last name
// pCert - certificate data
// pMemBuf - memory buffer object for storing result
// returns error code or ERR_OK
//--------------------------------------------------
EXP_OPTION int ddocCertGetSubjectLastName(X509* pCert, DigiDocMemBuf* pMemBuf)
{
  return ddocCertGetDNPart(pCert, pMemBuf, NID_lastName, 0);
}


//--------------------------------------------------
// Returns the certificates subject personal code
// pCert - certificate data
// pMemBuf - memory buffer object for storing result
// returns error code or ERR_OK
//--------------------------------------------------
EXP_OPTION int ddocCertGetSubjectPerCode(X509* pCert, DigiDocMemBuf* pMemBuf)
{
  return ddocCertGetDNPart(pCert, pMemBuf, NID_perCode, 0);
}

//--------------------------------------------------
// Returns the certificates subject country code
// pCert - certificate data
// pMemBuf - memory buffer object for storing result
// returns error code or ERR_OK
//--------------------------------------------------
EXP_OPTION int ddocCertGetSubjectCountryName(X509* pCert, DigiDocMemBuf* pMemBuf)
{
  return ddocCertGetDNPart(pCert, pMemBuf, NID_countryName, 0);
}

//--------------------------------------------------
// Returns the certificates subject organization
// pCert - certificate data
// pMemBuf - memory buffer object for storing result
// returns error code or ERR_OK
//--------------------------------------------------
EXP_OPTION int ddocCertGetSubjectOrganization(X509* pCert, DigiDocMemBuf* pMemBuf)
{
  return ddocCertGetDNPart(pCert, pMemBuf, NID_organization, 0);
}

//--------------------------------------------------
// Returns the certificates subject organization unit
// pCert - certificate data
// pMemBuf - memory buffer object for storing result
// returns error code or ERR_OK
//--------------------------------------------------
EXP_OPTION int ddocCertGetSubjectOrganizationUnit(X509* pCert, DigiDocMemBuf* pMemBuf)
{
  return ddocCertGetDNPart(pCert, pMemBuf, NID_organizationUnit, 0);
}

//--------------------------------------------------
// Returns the desired item from string rep of DN
// sDn - certificate DN
// sId - searched DN part
// pMBuf - memory buffer object for storing result
// returns error code or ERR_OK
//--------------------------------------------------
int ddocGetDNPartFromString(const char* sDn, const char* sId, DigiDocMemBuf* pMBuf)
{
    int err = ERR_OK, j, l;
    char *p0 = 0, *p1 = 0;
    char buf1[30];
    
    RETURN_IF_NULL_PARAM(sDn);
    RETURN_IF_NULL_PARAM(sId);
    RETURN_IF_NULL_PARAM(pMBuf);
    // initialize
    if(pMBuf->pMem)
        err = ddocMemBuf_free(pMBuf);
    pMBuf->pMem = 0;
    pMBuf->nLen = 0;
    memset(buf1, 0, sizeof(buf1));
    snprintf(buf1, sizeof(buf1)-1, "%s=", sId);
    p0 = strstr(sDn, buf1);
    if(p0) {
      p0 += strlen(buf1);
      p1 = strchr(p0, ',');
      if(p1) {
        while(p1 && *(p1-1) == '\\')
          p1 = strchr(p1, ',');
        if(p1 && *p1 == ',') p1--;
      } else {
        p1 = strchr(p0, '/');
        if(p1) {
          while(p1 && *(p1-1) == '\\') 
            p1 = strchr(p1, '/');
          if(p1 && *p1 == '/') p1--;
        } else 
            p1 = (char*)sDn + strlen(sDn);
      }
      //
      if(p0 && p1 && (int)(p1 - p0) > 0) {
        l = (int)(p1 - p0) + 1;
        ddocMemAppendData(pMBuf, (const char*)p0, l);
        ((char*)pMBuf->pMem)[l] = 0;
      }
    }
    return err;
}

//AM 06.03.08
//--------------------------------------------------
// Returns the certificates DN. Internal function.
// Do not call directly, subject to change
// pCert - certificate data
// pMemBuf - memory buffer object for storing DN
// bIssuer - 1=issuer, 0=subject
// returns error code or ERR_OK
//--------------------------------------------------
int bdocCertGetDN(X509* pCert, DigiDocMemBuf* pMemBuf, int bIssuer)
{
  int err = ERR_OK;
  X509_NAME *pName = 0;
  X509_NAME_ENTRY *pNe = 0;
  ASN1_STRING *data = NULL;
  int i, n, l, t, b = 0;
  const char *s;
  unsigned char* p;
	
  RETURN_IF_NULL_PARAM(pCert);
  RETURN_IF_NULL_PARAM(pMemBuf);
  
  // initialize
  if(pMemBuf->pMem)
    err = ddocMemBuf_free(pMemBuf);
  pMemBuf->pMem = 0;
  pMemBuf->nLen = 0;

  // iterate over name entries
  if(bIssuer)
    pName = X509_get_issuer_name(pCert);
  else
    pName = X509_get_subject_name(pCert);
  for(i = 0; (err == ERR_OK) && (i < X509_NAME_entry_count(pName)); i++) {
	pNe = X509_NAME_get_entry(pName, i);
	data = X509_NAME_ENTRY_get_data(pNe);
	n = OBJ_obj2nid(X509_NAME_ENTRY_get_object(pNe));
    s = OBJ_nid2sn(n);
	t = data->type;
    // mostly we find here:
    // V_ASN1_PRINTABLESTRING, V_ASN1_TELETEXSTRING or V_ASN1_BMPSTRING
    // that we convert to UTF, but V_ASN1_UTF8STRING allready is in UTF8
    // handle only the enry types we know
    if(n != NID_undef && s != NULL) {
      // convert to UTF8 only
      p = 0;
      if(t == V_ASN1_UTF8STRING) {
		p = data->data;
		l = data->length;
      } else
		l = ASN1_STRING_to_UTF8(&p, data);
      ddocDebug(5, "ddocCertGetDN", 
		"NameEntry nid: %d type: %d len: %d item: %s value: \'%s\'", 
		n, t, l, s, (p ? (const char*)p : "NULL"));
      // append separator if necessary
      if(b) 
	err = ddocMemAppendData(pMemBuf, ",", -1);
      else 
	b = 1;
      // print the entry
      err = ddocMemAppendData(pMemBuf, s, -1);
      err = ddocMemAppendData(pMemBuf, "=", -1);
      err = ddocMemAppendData(pMemBuf, (const char*)p, l);
      // cleanup
      if(p && t != V_ASN1_UTF8STRING)
	OPENSSL_free(p);
    }
    ddocDebug(5, "ddocCertGetDN", "Subject: \'%s\' len: %d", 
	      (const char*)pMemBuf->pMem, pMemBuf->nLen);
  } // for

  return err;
}

//================< deprecated functions> =================================

#ifdef WITH_DEPRECATED_FUNCTIONS

//============================================================
// Decodes base64 (PEM) cert data and returns a cert object
// certData - base64 (PEM) cert data
// certLen - cert data length
//============================================================
// FIXME : Ugly as hell...
#pragma message ("Function decodeCertificateData may be removed from future releases. Please use function ddocDecodeX509PEMData() instead.")
EXP_OPTION int decodeCertificateData(X509 **newX509, const byte* certData, int certLen)
{
        int l1, l2;
        char *buf1;
        BIO* b1 = NULL;
        X509* x509 = NULL;

        RETURN_IF_NULL_PARAM(certData);

        l2 = certLen * 2;
        buf1 = (char*)malloc(l2);
        RETURN_IF_BAD_ALLOC(buf1);
        memset(buf1, 0, l2);
        strncpy(buf1, "-----BEGIN CERTIFICATE-----\r\n", l2);
        l1 = l2 - strlen(buf1);
        encode(certData, certLen, (byte*)strchr(buf1,0), &l1);
	l1 = l2 - strlen(buf1);
        strncat(buf1, "\n-----END CERTIFICATE-----", l1);
        l1 = strlen(buf1);
        b1 = BIO_new_mem_buf(buf1, l1);
        if (!b1) {
                free(buf1);
                SET_LAST_ERROR_RETURN_CODE(ERR_NULL_POINTER);
        }
        x509 = PEM_read_bio_X509(b1, NULL, NULL, 0);
        BIO_free(b1);
        free(buf1);
        RETURN_IF_NOT(x509, ERR_CERT_INVALID);

        *newX509 = x509;
        return ERR_OK;
}

//--------------------------------------------------
// Returns the certificates issuer name
// cert - certificate data
// buf - usser name buffer
// buflen - pointer to buffer length (will be modified)
// returns error code or ERR_OK
//--------------------------------------------------
EXP_OPTION int getCertIssuerName(X509* cert, char* buf, int* buflen)
{
  int l1;
  char buf1[X509_NAME_BUF_LEN];

  RETURN_IF_NULL_PARAM(cert);
  RETURN_IF_NULL_PARAM(buf);
  RETURN_IF_NULL_PARAM(buflen);

  if(*buflen >= X509_NAME_LEN) {
    l1 = sizeof(buf1);
    memset(buf1,0,l1);
    memset(buf, 0, *buflen);
    X509_NAME_oneline(X509_get_issuer_name(cert), buf1, l1);
    unicode2ascii(buf1, buf);
    ddocDebug(5, "getCertIssuerName", "Issuer: \'%s\' -> \'%s\'", buf1, buf);
    *buflen = strlen(buf);
  } else
    SET_LAST_ERROR_RETURN_CODE(ERR_BUF_LEN);	
  return ERR_OK;
}

//--------------------------------------------------
// Returns the certificates subject name. This finction
// is also used in the COM library
// cert - certificate data
// buf - user name buffer
// buflen - pointer to buffer length (will be modified)
// returns error code or ERR_OK
//--------------------------------------------------
#pragma message ("Function getCertSubjectName may be removed from future releases. Please use function ddocCertGetSubjectDN() instead.")
EXP_OPTION int getCertSubjectName(X509* cert, char* buf, int* buflen)
{
  int err = ERR_OK;
  DigiDocMemBuf mbuf;

  mbuf.pMem = 0;
  mbuf.nLen = 0;
  err = ddocCertGetSubjectDN(cert, &mbuf);
  if(err == ERR_OK) {
    if(*buflen >= X509_NAME_LEN) {
#ifdef WIN32
      utf82oem((const char*)mbuf.pMem, buf, buflen);
#else
      utf82ascii((const char*)mbuf.pMem, buf, buflen);
#endif
      *buflen = strlen(buf);
    } else
      SET_LAST_ERROR(ERR_BUF_LEN);
    ddocMemBuf_free(&mbuf);
  }

  return err;
}

//--------------------------------------------------
// Returns the certificates subjects DN
// cert - certificate data
// buf - usser name buffer
// buflen - pointer to buffer length (will be modified)
// returns error code or ERR_OK
//--------------------------------------------------
EXP_OPTION int getCertSubjectDN(X509* cert, char* buf, int* buflen, int bUTF8)
{
  int  l1;
  char buf1[X509_NAME_BUF_LEN];

  RETURN_IF_NULL_PARAM(cert);
  RETURN_IF_NULL_PARAM(buf);
  RETURN_IF_NULL_PARAM(buflen);
  if(*buflen >= X509_NAME_LEN) {
    l1 = sizeof(buf1);
    memset(buf1, 0, l1);
    memset(buf, 0, *buflen);
    X509_NAME_oneline(X509_get_subject_name(cert), buf1, l1);
    if (bUTF8) {
      unicode2ascii(buf1, buf);
      *buflen = strlen(buf);
    } else {
      ascii2utf8(buf1, buf, buflen);
    }
  }
  else
    SET_LAST_ERROR_RETURN_CODE(ERR_BUF_LEN);	
  return ERR_OK;
}


//--------------------------------------------------
// Returns the certificates subjects name's CN part
// cert - certificate data
// buf - usser name buffer
// buflen - pointer to buffer length (will be modified)
// returns error code or ERR_OK
//--------------------------------------------------
EXP_OPTION int getCertSubjectCN(X509* cert, char* buf, int* buflen, int bUTF8)
{
  int  l1, err = ERR_OK;
  char buf1[X509_NAME_BUF_LEN], *p , *p2, buf2[X509_NAME_BUF_LEN];

  RETURN_IF_NULL_PARAM(cert);
  RETURN_IF_NULL_PARAM(buf);
  RETURN_IF_NULL_PARAM(buflen);

  if(*buflen >= X509_NAME_LEN) {
    l1 = sizeof(buf1);
    memset(buf1,0,l1);
    memset(buf, 0, *buflen);
    err = getCertSubjectName(cert, buf1, &l1);
    if(bUTF8 && hasUmlauts(buf1)) {
      //printf("Converting umlauts toi UTF8\n");
      l1 = sizeof(buf2);
      ascii2utf8(buf1, buf2, &l1);
      strncpy(buf1, buf2, sizeof(buf1));
      ddocDebug(4, "getCertSubjectCN", "Subject: \'%s\'", buf1);
    }    
    /*if (!bUTF8) {
      l1 = sizeof(buf2);
      utf82ascii(buf1, buf2, &l1);
      //strcpy(buf1, buf2);
      }*/
    //printf("SUBJECT: %s\n", buf1);
    p = strstr(buf1, "CN=");
    if(p) {
      p += 3;
      p2 = strchr(p, '/');
      if(!p2)
	p2 = p + strlen(p);
      if(p2) {
	strncpy(buf, p, p2-p);
	buf[p2-p] = 0;
      }
    }
    *buflen = strlen(buf);
  }
  else
    SET_LAST_ERROR_RETURN_CODE(ERR_BUF_LEN);	
  return ERR_OK;
}

//--------------------------------------------------
// Returns the certificates issuer name's CN part
// cert - certificate data
// buf - usser name buffer
// buflen - pointer to buffer length (will be modified)
// returns error code or ERR_OK
//--------------------------------------------------
EXP_OPTION int getCertIssuerCN(X509* cert, char* buf, int* buflen, int bUTF8)
{
  int  l1;
  char buf1[X509_NAME_BUF_LEN], *p , *p2, buf2[X509_NAME_BUF_LEN];

  RETURN_IF_NULL_PARAM(cert);
  RETURN_IF_NULL_PARAM(buf);
  RETURN_IF_NULL_PARAM(buflen);
  
  if(*buflen >= X509_NAME_LEN) {
    l1 = sizeof(buf1);
    memset(buf1,0,l1);
    memset(buf, 0, *buflen);
    X509_NAME_oneline(X509_get_issuer_name(cert), buf1, l1);
    if (bUTF8) {
      unicode2ascii(buf1, buf2);
    } else {
      l1 = sizeof(buf2);
      ascii2utf8(buf1, buf2, &l1);
    }
    //printf("SUBJECT: %s\n", buf2);
    p = strstr(buf2, "CN=");
    if(p) {
      p += 3;
      p2 = strchr(p, '/');
      if(!p2)
	p2 = p + strlen(p);
      if(p2) {
	strncpy(buf, p, p2-p);
	buf[p2-p] = 0;
      }
    }
    *buflen = strlen(buf);
  }
  else
    SET_LAST_ERROR_RETURN_CODE(ERR_BUF_LEN);	
  return ERR_OK;
}

// get cert owners id-code
EXP_OPTION int getCertOwnerCode(const X509* pCert, char* buf, int len)
{
  int err, l1;
  char buf1[500], *p;
  
  RETURN_IF_NULL_PARAM(pCert);
  RETURN_IF_NULL_PARAM(buf);
  l1 = sizeof(buf1);
  err = getCertSubjectName((void*)pCert, buf1, &l1);
  if (err != ERR_OK) SET_LAST_ERROR_RETURN_CODE(err);
  err = ERR_CERT_READ; 
  p = strstr(buf1, "CN=");
  if (p) {
    p = strchr(p, ',');
    if(p) {
      p = strchr(p+1, ',');
      if(p) {
	strncpy(buf, p+1, 1en);
	buf[1en] = 0;
	err = ERR_OK;
      } 
    } else { // no comma -> no id-code !
      buf[0] = 0; 
      // is this really an error ?
      err = ERR_WRONG_CERT;
    }  
  }
  if (err != ERR_OK) SET_LAST_ERROR(err);
  return err;
}


#endif // WITH_DEPRECATED_FUNCTIONS
