Ayuda para firmar cadena original con cer, key y contraseña. para COVE (VUCEM)

224 views
Skip to first unread message

olga lidia arias guzman

unread,
Dec 29, 2021, 6:44:37 PM12/29/21
to vfp-factura-electronica-mexico

Buenas tardes, felices fiestas para todos.  Soy de reciente ingreso a este grupo, para solicitar de su valiosa ayuda.

Programé un código muy simple para poder solicitar COVE a Ventanilla Única de Comercio Exterior Mexicana (VUCEM) generando el XML necesario para el consumo del webservice de esta plataforma, utilizando el active X de Chilkat_9_5_0.Http

Actualmente ya hice pruebas satisfactorias utilizando unos certificados, cadena original y cadena firmada de una “factura de prueba” que ya había obtenido COVE previamente (de otra aplicación en Delphi) y sin problema obtengo nuevos COVE de VUCEM al correr mi programita.

La cuestión que me falta es precisamente poder “firmar” una cadena de texto (la famosa “cadena original”, que es un string (o un TXT) con muchos datos delimitados por el carácter | ) y es por eso que publico esta entrada en el foro, pues la intención es hacerlo desde VFP, utilizando las librerías que fuesen necesarias, obteniendo un astringe (o un TXT) ya “firmado”.

 ¿Tendrá ya alguien del grupo, alguna rutina en VFP, para firmar con CER, KEY y contraseña, la cadena original?  De nueva cuenta, me estoy refiriendo al VUCEM, pero pienso que quizás sea lo mismo que aplica para los CFDI aquí en México.

 Una disculpa también por unirme al grupo nada más por buscar ayuda para esto, pero les prometo que publicaré mi código completo una vez que ya pueda firmar la cadena original, claro, con su ayuda.

¡De antemano muchas gracias y mis mejores deseos para todos en este 2022!

 

olga lidia arias guzman

unread,
Dec 30, 2021, 8:04:59 AM12/30/21
to vfp-factura-electronica-mexico
Actualmente cuento con la licencia de ActiveX para VFP de Chilkat, pero no he logrado encontrar la forma de firmar una cadena original de acuerdo a los requisitos de sha256  with RSA Encryption, Base 64 y utf-8

Gracias de antemano.

Mayra Moreno

unread,
Dec 30, 2021, 10:11:21 PM12/30/21
to vfp-factura-ele...@googlegroups.com
Yo lo hago con Open ssl, dame oportunidad y te paso un ejemplo.

Pásame tu correo 

--
Has recibido este mensaje porque estás suscrito al grupo "vfp-factura-electronica-mexico" de Grupos de Google.
Para cancelar la suscripción a este grupo y dejar de recibir sus mensajes, envía un correo electrónico a vfp-factura-electroni...@googlegroups.com.
Para ver esta conversación en el sitio web, visita https://groups.google.com/d/msgid/vfp-factura-electronica-mexico/dc1a3010-dd90-4453-a68a-de59ca917af8n%40googlegroups.com.

olga lidia arias guzman

unread,
Dec 31, 2021, 3:29:48 PM12/31/21
to vfp-factura-electronica-mexico
Muchas gracias, ya logré hacer todo con Chilkat, en el siguiente post compartiré mi código, que como es de prueba, es batante elementarl.

Felices Fiestas!

olga lidia arias guzman

unread,
Dec 31, 2021, 3:31:04 PM12/31/21
to vfp-factura-electronica-mexico

Muchas gracias al usuario del grupo António Tavares Lopes, con un comentario que me hizo en otro foro, pude hacer la prueba con Chilkat para completar este programita de prueba, aquí les comparto el código básico sin validaciones ni nada por el estilo, está muy elemental, pero lo pueden adaptar ustedes a sus necesidades y estilo. 

Tienen que tener una licencia de chilkat activex comprada.  Pero vale la pena pues queda demasiado sencillo todo el proceso para general el XML y consumir el webservice.  Felices fiestas!!!


LicGlo    = CreateObject('Chilkat_9_5_0.Global')
Resultado = LicGlo.UnlockBundle("la llave de activación de Chilkat")

*************************************************************
* Sección para general el XML que se enviará a VUCEM
*************************************************************
m.archivo         = &&Para grabar el XML
m.cadena_original = &&La cadena original a firmar, esta la pueden armar a mano que es bastante sencillo siguiendo el orden del XML, pues es texto separado por |
m.elcer           = &&La ruta del archivo .CER
m.elkey           = &&La ruta del archivo .KEY
m.elpwd           = &&La contraseña de la FIEL

m.cadena_original_FIRMADA = ''
m.CERTIFICADO             = ''

loPkey    = CreateObject('Chilkat_9_5_0.PrivateKey')        &&Creamos el objeto private key
Resultado = loPkey.LoadPkcs8EncryptedFile(m.elkey, m.elpwd) &&Se carga el .KEY y la contraseña de la FIEL
lcPkeyXml = loPkey.GetXml()                                 &&Se convierte a XML para aplicar el algoritmo RonaldShamirAdleman (RSA)
loRsa     = CreateObject('Chilkat_9_5_0.Rsa')               &&Creamos el objeto RSA
Resultado = loRsa.ImportPrivateKey(lcPkeyXml)               &&Se importa al RSA la llave privada en XML

loRsa.EncodingMode        = "base64"                             &&Definimos la codificación de salida (de acuerdo a VUCEM)
loRsa.Charset             = "utf-8"                              &&Y el charset
m.cadena_original_FIRMADA = loRsa.SignStringENC(cadena,"sha256") &&Obtenemos la cadea CADENA_FIRMADAda aplicando el HASH sha256 (de acuerdo a VUCEM)


loCert        = CreateObject('Chilkat_9_5_0.Cert') &&Creamos el objeto certificado
Resultado     = loCert.LoadFromFile(m.elcer)       &&Cargamos el .CER
m.CERTIFICADO = loCert.ExportCertPem()             &&Obtenemos el texto del certificado

* El texto del certificado viene con los siguientes datos que hay que eliminar
m.CERTIFICADO = strtran(m.CERTIFICADO, '-----BEGIN CERTIFICATE-----', '')
m.CERTIFICADO = strtran(m.CERTIFICADO, '-----END CERTIFICATE-----', '')
m.CERTIFICADO = strtran(m.CERTIFICADO, chr(13), '')
m.CERTIFICADO = strtran(m.CERTIFICADO, chr(10), '')

* Las siguientes variables las use para llenar la información que requiere el XML
* para COVE de VUCEM y así solo referenciarlas al armarlo, ya depende de cada
* programador como implementarlo

m.correo        = &&Correo para recibir cove
m.observaciones = &&Por lo general van los datos del transporte

m.tidenimp  = &&Indentificador registro emisor "1" para RFC "0" TAX-ID "3" sin TAX-ID
m.nombreimp = &&Nombre emisor
m.rfcimp    = &&RFC o TAX-ID emisor
m.calleimp  = &&Calle emisor
m.numeximp  = &&Numero exterior emisor
m.colimp    = &&Colonia emisor
m.munimp    = &&Municio emisor
m.edoimp    = &&Estado emisor
m.paisimp   = &&Pais emisor
m.cpimp     = &&CP emisor

m.tidendes  = &&Indentificador registro destino "1" para RFC "0" TAX-ID "3" sin TAX-ID
m.nombredes = &&Nombre destino
m.rfcdes    = &&RFC o TAX-ID del destino
m.calledes  = &&Calle destino
m.numexdes  = &&Numero exterior destino
m.coldes    = &&Colonia destino
m.mundes    = &&Municio destino
m.edodes    = &&Estado destino
m.paisdes   = &&Pais destino
m.cpdes     = &&CP destino

m.fecfactura = &&Fecha de factura
m.fechafvu   = substr(dtoc(m.fecfactura), 7, 4) + '-' + substr(dtoc(m.fecfactura), 4, 2) + '-' + substr(dtoc(m.fecfactura), 1, 2) &&Se convierte la fecha al formato de VUCEM
m.factura    = &&Numero de factura

m.userwebs  = &&Usuario webservices VUCEM
m.pwdwebs   = &&Contraseña webservices VUCEM

m.topercove   = &&Tipo de operación para COVE "TOCE.EXP" para exportación, "TOCE.IMP" para importación
m.aapatente   = &&Patente Agente Aduanal
m.RFCConsulta = &&RFC adicional para consultar COVE en VUCEM

m.soltipofig  = &&Tipo figura de quien envía la solicitud de COVE a VUCEM "1" Agente Aduanal "2" Apoderado Aduanal "3" Mandatario "4" Exportador "5" Importador

m.corigen     = &&Se cuenta con certificado de origen "0" NO "1" SI
m.subdivision = &&Se subdivide el valor "0" NO "1" SI

loXml     = CreateObject('Chilkat_9_5_0.Xml')  &&Creamos el objeto XML
* Se definen todos los parametros para el consumo del webservice (probados al 100%)
loXml.Tag = "soapenv:Envelope"
loXml.AddAttribute("xmlns:soapenv","http://schemas.xmlsoap.org/soap/envelope/")
loXml.UpdateAttrAt("soapenv:Header|wsse:Security",1,"xmlns:wsse","http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd")
loXml.UpdateChildContent("soapenv:Header|wsse:Security|wsse:UsernameToken|wsse:Username", m.userwebs)
loXml.UpdateAttrAt("soapenv:Header|wsse:Security|wsse:UsernameToken|wsse:Password",1,"Type","http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText")
loXml.UpdateChildContent("soapenv:Header|wsse:Security|wsse:UsernameToken|wsse:Password", m.pwdwebs)
loXml.UpdateAttrAt("soapenv:Body|oxml:solicitarRecibirCoveServicio",1,"xmlns:oxml","http://www.ventanillaunica.gob.mx/cove/ws/oxml/")
* Se agregan los datos generales para la solicitud
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:tipoOperacion",                  m.topercove)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:patenteAduanal",                 m.aapatente)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:fechafvuedicion",                m.fechafvu)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:observaciones",                  m.observaciones)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:tipoFigura",                     m.soltipofig)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:rfcConsulta",                    m.RFCConsulta)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:correoElectronico",              m.correo)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:numeroFacturaOriginal",          m.factura)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:factura|oxml:CERTIFICADOOrigen", m.corigen)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:factura|oxml:subdivision",       m.subdivision)
* Se agregan los datos del emisor
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:emisor|oxml:tipoIdentificador",                m.tidenimp)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:emisor|oxml:identificacion",                   m.rfcimp)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:emisor|oxml:nombre",                           m.nombreimp)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:emisor|oxml:domicilio|oxml:calle",             m.calleimp)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:emisor|oxml:domicilio|oxml:numeroExterior",    m.numeximp)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:emisor|oxml:domicilio|oxml:colonia",           m.colimp)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:emisor|oxml:domicilio|oxml:municipio",         m.munimp)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:emisor|oxml:domicilio|oxml:entidadFederativa", m.edoimp)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:emisor|oxml:domicilio|oxml:pais",              m.paisimp)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:emisor|oxml:domicilio|oxml:codigoPostal",      m.cpimp)
* Se agregan los datos del destino
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:destinatario|oxml:tipoIdentificador",                m.tidendes)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:destinatario|oxml:identificacion",                   m.rfcdes)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:destinatario|oxml:nombre",                           m.nombredes)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:destinatario|oxml:domicilio|oxml:calle",             m.calledes)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:destinatario|oxml:domicilio|oxml:numeroExterior",    m.numexdes)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:destinatario|oxml:domicilio|oxml:municipio",         m.mundes)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:destinatario|oxml:domicilio|oxml:entidadFederativa", m.edodes)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:destinatario|oxml:domicilio|oxml:pais",              m.paisdes)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:destinatario|oxml:domicilio|oxml:codigoPostal",      m.cpdes)

* A continuación se obtiene la informacion de las partidas y se comienza con los tags primero sin numero
* y a partir de la segunda partida se va armando un TAG distinto en el XML
select partidas &&La información que obtengan de su base de datos
m.renglonp = 0
go top
if reccount() != 0
   scan
      m.claveUnidadMedida   = alltrim(claveUnidadMedida)
      m.tipoMoneda          = alltrim(tipoMoneda)
      m.cantidad            = cantidad
      m.valorUnitario       = valorUnitario
      m.valorTotal          = valorTotal
      m.valorDolares        = valorDolares
      m.descripcionGenerica = alltrim(descripcionGenerica)
      m.marca               = alltrim(marca)
      m.modelo              = alltrim(modelo)
      m.submodelo           = alltrim(submodelo)
      m.numeroserie         = alltrim(numeroserie)
     
      if m.renglonp = 0
         loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:mercancias|oxml:claveUnidadMedida",   m.claveUnidadMedida)
         loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:mercancias|oxml:tipoMoneda",          m.tipoMoneda)
         loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:mercancias|oxml:cantidad",            m.cantidad)
         loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:mercancias|oxml:valorUnitario",       m.valorUnitario)
         loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:mercancias|oxml:valorTotal",          m.valorTotal)
         loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:mercancias|oxml:valorDolares",        m.valorDolares)
         loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:mercancias|oxml:descripcionGenerica", m.descripcionGenerica)
         if len(alltrim(m.marca)) != 0
            loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:mercancias|oxml:marca",            m.marca)
         endif
         if len(alltrim(m.modelo)) != 0
            loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:mercancias|oxml:modelo",           m.modelo)
         endif
         if len(alltrim(m.submodelo)) != 0
            loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:mercancias|oxml:submodelo",        m.submodelo)
         endif
         if len(alltrim(m.numeroserie)) != 0
            loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:mercancias|oxml:numeroserie",      m.numeroserie)
         endif
      else
         loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:mercancias[" + alltrim(str(m.renglonp, 5, 0)) + "]|oxml:claveUnidadMedida",   m.claveUnidadMedida)
         loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:mercancias[" + alltrim(str(m.renglonp, 5, 0)) + "]|oxml:tipoMoneda",          m.tipoMoneda)
         loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:mercancias[" + alltrim(str(m.renglonp, 5, 0)) + "]|oxml:cantidad",            m.cantidad)
         loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:mercancias[" + alltrim(str(m.renglonp, 5, 0)) + "]|oxml:valorUnitario",       m.valorUnitario)
         loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:mercancias[" + alltrim(str(m.renglonp, 5, 0)) + "]|oxml:valorTotal",          m.valorTotal)
         loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:mercancias[" + alltrim(str(m.renglonp, 5, 0)) + "]|oxml:valorDolares",        m.valorDolares)
         loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:mercancias[" + alltrim(str(m.renglonp, 5, 0)) + "]|oxml:descripcionGenerica", m.descripcionGenerica)
         if len(alltrim(m.marca)) != 0
            loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:mercancias[" + alltrim(str(m.renglonp, 5, 0)) + "]|oxml:marca",               m.marca)
         endif
         if len(alltrim(m.modelo)) != 0
            loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:mercancias[" + alltrim(str(m.renglonp, 5, 0)) + "]|oxml:modelo",              m.modelo)
         endif
         if len(alltrim(m.submodelo)) != 0
            loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:mercancias[" + alltrim(str(m.renglonp, 5, 0)) + "]|oxml:submodelo",           m.submodelo)
         endif
         if len(alltrim(m.numeroserie)) != 0
            loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:mercancias[" + alltrim(str(m.renglonp, 5, 0)) + "]|oxml:numeroserie",         m.numeroserie)
         endif
      endif
      m.renglonp = m.renglonp + 1
   endscan
endif
* Los datos para "FIRMAR" la solicitud de servicio de COVE
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:firmaElectronica|oxml:cadenaOriginal", m.cadena_original)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:firmaElectronica|oxml:certificado",    m.CERTIFICADO)
loXml.UpdateChildContent("soapenv:Body|oxml:solicitarRecibirCoveServicio|oxml:comprobantes|oxml:firmaElectronica|oxml:firma",          m.cadena_original_FIRMADA)

loXml.SaveXml(m.archivo) && Grabamos el XML
RELEASE loXml            && Soltamos el objeto XML

*************************************************************
* Sección para consumir el webservice de VUCEM
*************************************************************
loHttp    = CreateObject('Chilkat_9_5_0.Http') &&Creamos un objeto HTTP
loXml     = CreateObject('Chilkat_9_5_0.Xml')  &&Creamos un objeto XML
Resultado = loXml.LoadXmlFile(m.archivo)       &&Cargamos el archivo XML que generamos en la sección anterior
lcStrXml  = loXml.GetXml()                     &&Cargamos al XML a una variable de texto

* Definición de URL para consumir los webservices de VUCEM
loHttp.SetRequestHeader("SOAPAction","https://www.ventanillaunica.gob.mx/ventanilla/RecibirCoveService?wsdl")
loHttp.SetRequestHeader("Content-Type","text/xml; charset=utf-8")
lcEndPoint = "https://www.ventanillaunica.gob.mx/ventanilla/RecibirCoveService?wsdl"

loResp   = loHttp.PostXml(lcEndPoint,lcStrXml,"utf-8") &&Se entrega el XML al webservice
m.laresp = loHttp.LastMethodSuccess                    &&Si el valor es diferente de 1 hubo error y lo podemos obtener con la función = loHttp.LastErrorText

m.StatusSolicitud = loResp.StatusCode &&Datos de respuesta
m.UltimoMensaje   = loHttp.LastHeader &&Datos de respuesta

loXmlResp = CreateObject('Chilkat_9_5_0.Xml') &&Creamos otro ojeto XML
loXmlResp.LoadXml(loResp.BodyStr)             &&Cargamos el XML de respuesta
m.TXTRESP = loXmlResp.GetXml()                &&Convertimos a txte el XML de respuesta

* Soltamos todos los objetos
RELEASE loHttp
RELEASE loXml
RELEASE loXmlResp
RELEASE LicGlo

Antonio Meza

unread,
Jun 19, 2025, 2:00:59 PMJun 19
to vfp-factura-electronica-mexico
Hola buenas tardes, para consultarte si tienes la rutina para recuperar los acuses de coves o e-documents?

saludos
Antonio Meza
Reply all
Reply to author
Forward
0 new messages