Firmar facturae

User avatar
Xevi
Posts: 168
Joined: Wed Nov 29, 2017 11:42 am
Location: Girona

Re: Firmar facturae

Post by Xevi »

Bien, poniendo a funcionar la firma electrónica de facturas, ahora me encuentro con un problema en algun certificado...

Si consulto los alias de certificados en mi almacén desde cmd...

AutofirmaCommandLine.exe listaliases -store windows
...
INFO: Solicitando los alias al KeyStore (SunMSCAPI version 9)
40123456X NOMBRE Y APELLIDOS (R: B12123123)
Disp. software de seguridad:50123456X NOMBRE2 Y APELLIDOS2 (R: B12123123)?s FNMT-RCM I
...

me devuelve los dos "alias" de los certificados que tengo instalados...
Bien, pues el segundo no se porqué narices me saca que es software de seguridad...
Por mas que he provado combinaciones (solo con el nombre, nombre y contenido entre parentesis), TODO el texto. Pues nada, que no reconoce ese "nombre" como certificado instalado.

Alguien le puede haber surgido este problema???

Lo he probado de hacer firmar con el programa Autofirma y si que lo firma correctamente, pero con el comando AutofirmaCommandLine, no hay manera de que me "pille" esa firma.

¿?¿?

Gracias por vuestro tiempo!!!
Un Saludo,
Xevi.

Aprendiz de la vida!!!
darioflores
Posts: 16
Joined: Tue Oct 06, 2015 7:06 am

Re: Firmar facturae

Post by darioflores »

Buenas, os pongo de nuevo el código con la corrección de pulsar cancelar en el diálogo.
Ahora devuelve una cadena vacía al pulsar cancelar:

Code: Select all

#include "fivewin.ch"


func main()


   MsgInfo("Seleccionado certificado: "+ToTxt(SELCERT()), "CryptoApi")



return nil

//*****************************************************************************
//*****************************************************************************

function ToTxt( uVal )

   local cType := ValType( uVal )

   do case
      case cType == "C" .or. cType == "M"
           return uVal

      case cType == "D"
           return DToC( uVal )

      case cType == "L"
           return If( uVal, ".T.", ".F." )

      case cType == "N"
           return AllTrim( Str( uVal ) )

      case cType == "B"
           return "{|| ... }"

      case cType == "A"
           return "{ ... }"

      case cType == "O"
           return If( __ObjHasData( uVal, "cClassName" ), uVal:cClassName, uVal:ClassName() )

      case cType == "H"
           return "{=>}"

      otherwise
           return ""
   endcase

return nil

//*****************************************************************************
//*****************************************************************************

#pragma BEGINDUMP

#include <windows.h>
//#include <psapi.h>
//#include <hbapi.h>
//#include <hbapierr.h>
//#include <hbapiitm.h>
//#include <wincrypt.h>

#define CRYPTUI_SELECT_LOCATION_COLUMN 0x000000010

//Definir el prototipo de las funciones:
typedef HCERTSTORE (WINAPI * PTYPECERTOPEN) (HCRYPTPROV, LPTSTR);
typedef PCCERT_CONTEXT (WINAPI * PTYPECERTSELECTDLG) (HCERTSTORE, HWND, LPCWSTR, LPCWSTR, DWORD, DWORD, void*);
typedef PCCERT_CONTEXT (WINAPI * PTYPECERTENUM) (HCERTSTORE, PCCERT_CONTEXT);
typedef DWORD (WINAPI * PTYPECERTGETNAME) (PCCERT_CONTEXT, DWORD, DWORD, VOID*, LPTSTR, DWORD);
typedef DWORD (WINAPI * PTYPECERTNAMETOSTR) (DWORD, PCERT_NAME_BLOB, DWORD, LPTSTR, DWORD);
typedef BOOL (WINAPI * PTYPECERTFREECC) (PCCERT_CONTEXT);
typedef BOOL (WINAPI * PTYPECERTCLOSESTORE) (HCERTSTORE, DWORD);

HB_FUNC(SELCERT)
{

   // Hay varios ejemplos en: https://msdn.microsoft.com/en-us/librar ... 61(v=vs.85).aspx

   HCERTSTORE hStore;
   PCCERT_CONTEXT PrevContext, CurContext;
   PCHAR sNombre;
   DWORD cbSize;
   PHB_ITEM pArray;
   PHB_ITEM pItem;
   PCCERT_CONTEXT   pCertContext;
   // Cargamos las librerías de las que queremos la dirección de las funciones.
   HMODULE HCrypt = LoadLibrary("Crypt32.dll");
   HMODULE HCrypt2 = LoadLibrary("Cryptui.dll");

   // Declaramos el tipo de puntero a la función, tenemos la definición arriba.
   PTYPECERTOPEN    pCertOpen;
   PTYPECERTSELECTDLG    pCertSelectDlg;
   PTYPECERTGETNAME pCertGetName;
   PTYPECERTNAMETOSTR pCertNameToStr;
   PTYPECERTFREECC pCertFreeCC;
   PTYPECERTCLOSESTORE pCertCloseStore;


   if (HCrypt != NULL && HCrypt2 != NULL){
      //Sacamos el puntero todas las funciones que vamos a usar mediante GetProcAddress:
      #ifdef UNICODE
         pCertOpen    = (PTYPECERTOPEN) GetProcAddress(HCrypt, "CertOpenSystemStoreW");
         pCertGetName = (PTYPECERTGETNAME) GetProcAddress(HCrypt, "CertGetNameStringW");
      #else
         pCertOpen    = (PTYPECERTOPEN) GetProcAddress(HCrypt, "CertOpenSystemStoreA");
         pCertGetName = (PTYPECERTGETNAME) GetProcAddress(HCrypt, "CertGetNameStringA");
      #endif
      pCertSelectDlg = (PTYPECERTSELECTDLG) GetProcAddress(HCrypt2, "CryptUIDlgSelectCertificateFromStore");
      pCertFreeCC  = (PTYPECERTFREECC) GetProcAddress(HCrypt, "CertFreeCertificateContext");
      pCertCloseStore  = (PTYPECERTCLOSESTORE) GetProcAddress(HCrypt, "CertCloseStore");
   }

   if (pCertOpen){
      // Llamada a CertOpenSystemStore:
      hStore = pCertOpen(NULL, TEXT("MY"));
   }

   if (hStore){
      pCertContext = pCertSelectDlg(hStore, NULL, NULL, NULL, CRYPTUI_SELECT_LOCATION_COLUMN, 0, NULL);

      if (pCertContext!=NULL){
         cbSize = pCertGetName(pCertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, NULL, 0);
         if (cbSize>0) {
            //Reservamos la memoria que necesitamos para el texto que recibiremos
            sNombre = (LPTSTR)malloc(cbSize * sizeof(TCHAR));

            pCertGetName(pCertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, sNombre, cbSize);


            // Llamada a CertFreeCertificateContext:
            pCertFreeCC(pCertContext);
         }
      } else {
         sNombre = "";
      }

      // Cerrar el almacen de certificados:
      // Llamada a CertCloseStore:
      pCertCloseStore(hStore, 0);
    }

    FreeLibrary(HCrypt);
    FreeLibrary(HCrypt2);

    hb_retc(sNombre);

}

#pragma ENDDUMP
 
Aunque funciona, el código dista mucho de ser perfecto, hay muchas comprobaciones que no se hacen o que se hacen y no terminan la ejecución. Además, el diálogo muestra todos los certificados, no solo los que tengan una clave privada, etc.
Pero vamos, para el uso que le vais a dar, funciona.


Por cierto, ya que estamos con el tema de facturae, si alguien tiene alguna clase que encapsule la generación del XML de la factura, se lo agradecería.

Un saludo.
User avatar
Xevi
Posts: 168
Joined: Wed Nov 29, 2017 11:42 am
Location: Girona

Re: Firmar facturae

Post by Xevi »

Siguiendo con el tema de selección de certificado para procesar la firma seguidamente con AutofirmaCommandLine...

He localizado el error (o el porqué) que no me deja proceder a la firma con ese certificado.
Por lo visto, ese certificado tiene el campo "Nombre descriptivo" relleno, y es precisamente por ese motivo que no me deja utilizarlo luego.
O sea, SI UN CERTIFICADO tiene el campo "Nombre descriptivo" relleno (no vacio), no deja utilizar el certificado.

Igual es por algun parámetro que no lo estoy utilizando correctamente en el AutofirmaCommandLine,
Pero...
Yo lo que he hecho es eliminar el contenido de ese campo en ese certificado... y ahora ya SI me funciona correctamente.

No se si a alguien le puede suceder lo mismo que a mi, pero aquí lo dejo, por si acaso!!!
Es una solución.

Gracias por vuestro tiempo.
Un Saludo,
Xevi.

Aprendiz de la vida!!!
User avatar
AngelSalom
Posts: 664
Joined: Fri Oct 07, 2005 7:38 am
Location: Vinaros (Castellón ) - España
Contact:

Re: Firmar facturae

Post by AngelSalom »

Gracias Xevi, no se me ha dado el caso ... aún ;-) Es bueno saberlo.
Angel Salom
http://www.visionwin.com
---------------------------------------------
fwh 19.05 - harbour 3.2 - bcc 7.0
Post Reply