Decodificar Certificados digitales a Fox 100%, código para la comunidad

1,674 views
Skip to first unread message

Fernando Mora

unread,
Sep 17, 2019, 4:01:00 PM9/17/19
to Comunidad de Visual Foxpro en Español
Saludos Foxeros!

Comparto el código para decodificar certificados digitales, a Fox puro y duro. Extraer, Serial, Issuer, Exponete, Modulus, y el certificado codificado en formato base64, todo esta información es la que se carga en el nodo "Signature" de los Xml en facturación electrónica. En este ejemplo se decodifica certificados digitales desde el almacén de certificados del sistema. Si desean hacerlos desde archivos P12 o PFX usen la función GetImportP12PFX que publique acá:  https://groups.google.com/forum/#!topic/publicesvfoxpro/b5kJC0VlBY0

En este ejemplo se decodifica el certificado en sus campos versión 1, hay mas, se puede decodificar las extensiones y los campos de las propiedades. Tengan en cuenta que así como se decodifica un certificado, su codificación es un proceso inverso, es decir también podemos crear certificados digitales desde Fox y de una forma sencilla. De todos estos últimos meses que he venido investigando este tema, veo que hacer esto desde Fox es mucho mas fácil que de otros lenguajes. Nosotros desde Fox podemos manipular las apis y tener los datos directos en formato de cadena, casi no tenemos que andar haciendo malabares para extraer los datos desde punteros ni realizar operaciones complicadas como veo hacen en otros lenguajes. 

El ejemplo envía la información aun archivo de texto, usted podría direccionar a un cursor. Con la decodificación de la fecha de emisión y la fecha de caducidad podemos verificar mediante programación si el certificado esta dentro del periodo de valides. 

Saludos amigos, y larga vida a Fox!
Fernando Mora
Machala - Ecuador


*------ Constantes CertOpenStore para: lpszStoreProvider 
#DEFINE CERT_STORE_PROV_SYSTEM          10
#define CERT_STORE_PROV_MSG        1
#define CERT_STORE_PROV_MEMORY      2
#define CERT_STORE_PROV_FILE        3
#define CERT_STORE_PROV_REG        4
#define CERT_STORE_PROV_PKCS7      4
#define CERT_STORE_PROV_SERIALIZED 6
#define CERT_STORE_PROV_FILENAME_A 7
#define CERT_STORE_PROV_FILENAME_W 8
#define CERT_STORE_PROV_SYSTEM_A    9
#define CERT_STORE_PROV_SYSTEM_W    10
#DEFINE CERT_STORE_PROV_COLLECTION 11
#define CERT_STORE_PROV_SYSTEM_REGISTRY_A 12
#define CERT_STORE_PROV_SYSTEM_REGISTRY_W 13
#define CERT_STORE_PROV_SYSTEM_REGISTRY CERT_STORE_PROV_SYSTEM_REGISTRY_W
#define CERT_STORE_PROV_PHYSICAL_W 14
#define CERT_STORE_PROV_PHYSICAL CERT_STORE_PROV_PHYSICAL_W
#define CERT_STORE_PROV_SMART_CARD_W 15
#define CERT_STORE_PROV_SMART_CARD CERT_STORE_PROV_SMART_CARD_W
#define CERT_STORE_PROV_LDAP_W 16
#define CERT_STORE_PROV_LDAP CERT_STORE_PROV_LDAP_W
#define CERT_STORE_PROV_PKCS12 17
*------ Constantes CertOpenStore para:
#DEFINE PKCS_7_ASN_ENCODING 65536
#DEFINE X509_ASN_ENCODING 1
*------ Constantes CertOpenStore para: dwFlags
#DEFINE CERT_STORE_OPEN_EXISTING_FLAG 0x00004000
#DEFINE CERT_SYSTEM_STORE_LOCATION_MASK          0x00FF0000
#DEFINE CERT_SYSTEM_STORE_LOCATION_SHIFT        16
#DEFINE CERT_SYSTEM_STORE_CURRENT_USER_ID        1
#DEFINE CERT_SYSTEM_STORE_LOCAL_MACHINE_ID      2
#DEFINE CERT_SYSTEM_STORE_CURRENT_SERVICE_ID    4
#DEFINE CERT_SYSTEM_STORE_SERVICES_ID            5
#DEFINE CERT_SYSTEM_STORE_USERS_ID              6
#DEFINE CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY_ID 7
#DEFINE CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY_ID 8
#DEFINE CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE_ID 9
#DEFINE CERT_SYSTEM_STORE_CURRENT_USER BITLSHIFT(1,16)
#DEFINE CERT_SYSTEM_STORE_LOCAL_MACHINE BITLSHIFT(2,16)
#DEFINE CERT_SYSTEM_STORE_CURRENT_SERVICE BITLSHIFT(4,16)
#DEFINE CERT_SYSTEM_STORE_SERVICES BITLSHIFT(5,16)
#DEFINE CERT_SYSTEM_STORE_USERS BITLSHIFT(6,16)
#DEFINE CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY BITLSHIFT(7,16)
#DEFINE CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY BITLSHIFT(8,16)
#DEFINE CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE BITLSHIFT(9,16)
*------ Constantes CryptUIDlgSelectCertificateFromStore para: dwDontUseColumn
#DEFINE CRYPTUI_SELECT_ISSUEDTO_COLUMN 0x000000001
#DEFINE CRYPTUI_SELECT_ISSUEDBY_COLUMN 0x000000002
#DEFINE CRYPTUI_SELECT_INTENDEDUSE_COLUMN 0x000000004
#DEFINE CRYPTUI_SELECT_FRIENDLYNAME_COLUMN 0x000000008
#DEFINE CRYPTUI_SELECT_LOCATION_COLUMN 0x00000010
#DEFINE CRYPTUI_SELECT_EXPIRATION_COLUMN 0x000000020

=SetDeclarationsApis()
hStore = GetCertOpenStore("MY")
IF hStore>0
lnCertContext = GetCryptUIDlgSelectCertificateFromStore(hStore)
GetDecodeCertificateX509(lnCertContext)
nResp = CertFreeCertificateContext(lnCertContext)
nResp = CertCloseStore(hStore, 0)
ENDIF

PROCEDURE GetDecodeCertificateX509()
LPARAMETERS tpCertContext AS Long
cCertInfo = GetStructure_CertInfo(tpCertContext)
cVersion = GetVersion(cCertInfo)
cSerialNumber = STRCONV(GetSerialNumber(cCertInfo),15)
cSignatureAlg = GetSignatureAlgorithm(cCertInfo)
cIssuer = GetIssuer(tpCertContext)
tNotBefor = GetNotBefore(tpCertContext)
tNotAfter = GetNotAfter(tpCertContext)
cSubject = GetSubject(tpCertContext)
cPublicKeyInfo = GetSubjectPublicKeyInfo(cCertInfo)
bPublicKeyValue = GetSubjectPublicKeyValue(cCertInfo)
cPublicKeyValue = STRCONV(bPublicKeyValue,15)
cExponent = GetExponent(bPublicKeyValue)
cModulos = STRCONV(GetModulus(bPublicKeyValue, SUBSTR(cPublicKeyInfo, 1, AT(";", cPublicKeyInfo)-1)),13)
cCertificateType = GetCertificateType(tpCertContext)
cCertificateEncoded = STRCONV(GetCertificateEncoded(tpCertContext),13)
*------ Salida a Texto
TEXT TO lcFileText TEXTMERGE NOSHOW ADDITIVE 
Versión: <<cVersion>>
Número de serie: <<cSerialNumber>>
Algoritmo de Firma: <<cSignatureAlg>>
Emisor : <<cIssuer>>
Fecha Emision: <<tNotBefor>>
Fecha Caduca: <<tNotAfter>>
Sujeto : <<cSubject>>
Algoritmo de Clave Publica: <<cPublicKeyInfo>>
Clave Pública: <<cPublicKeyValue>>
Exponente: <<cExponent>>
Modulus: <<cModulos>>
Tipo de Certificado: <<cCertificateType>>
Certificado Codificado: <<cCertificateEncoded>>
ENDTEXT
*------ Grabamos en archivo de texto
STRTOFILE(lcFileText, ADDBS(FULLPATH(""))+"CertX509Decode.txt")
MODIFY FILE ADDBS(FULLPATH(""))+"CertX509Decode.txt"
ENDPROC


PROCEDURE GetCertOpenStore()
LPARAMETERS tcStoreName AS String
hStoreHandle = 0
lpszStoreProvider = CERT_STORE_PROV_SYSTEM_A
dwEncodingType = BITOR(PKCS_7_ASN_ENCODING, X509_ASN_ENCODING)
hCryptProv = 0
dwFlags = CERT_SYSTEM_STORE_CURRENT_USER
pvPara = "MY" + CHR(0)
hStoreHandle = CertOpenStore(lpszStoreProvider, dwEncodingType, hCryptProv, dwFlags, pvPara)
RETURN hStoreHandle
ENDPROC

PROCEDURE GetCryptUIDlgSelectCertificateFromStore()
LPARAMETERS thStore AS Long
pCertContext = 0
hCertStore = thStore
hWnd = _SCREEN.HWnd
pwszTitle = NULL
pwszDisplayString = NULL
dwDontUseColumn = 0
dwFlags = 0
pvReserved = NULL
pCertContext=CryptUIDlgSelectCertificateFromStore(hCertStore, hWnd, pwszTitle, pwszDisplayString, dwDontUseColumn, dwFlags, pvReserved)
RETURN pCertContext
ENDPROC

PROCEDURE GetStructure_CertInfo()
LPARAMETERS tpCertContext AS Long
lbCertInfo = SYS(2600, tpCertContext, 20)
lpCertInfo = CTOBIN(SUBSTR(lbCertInfo, 13, 4), "4RS")
lcCertInfo = SYS(2600, lpCertInfo, 112)
RETURN lcCertInfo
ENDPROC

PROCEDURE GetVersion()
LPARAMETERS tcCertInfo AS String
lpVersion1 = CTOBIN(SUBSTR(tcCertInfo,1,4),"4RS")
RETURN lpVersion1 + 1
ENDPROC

PROCEDURE GetSerialNumber()
LPARAMETERS tcCertInfo AS String
lbSerialNumber = SUBSTR(tcCertInfo, 5, 8)
lnSerialNumber = CTOBIN(SUBSTR(lbSerialNumber, 1, 4),"4RS")
lpSerialNumber = CTOBIN(SUBSTR(lbSerialNumber, 5, 4),"4RS")
lcSerialNumber = SYS(2600, lpSerialNumber, lnSerialNumber)
lcSerialRevers = GetReverseString(lcSerialNumber)
RETURN lcSerialRevers
ENDPROC

PROCEDURE GetSignatureAlgorithm()
LPARAMETERS tcCertInfo AS String
lbSignAlgorithm = SUBSTR(tcCertInfo, 13, 12)
lpAlgorithmOID = CTOBIN(SUBSTR(lbSignAlgorithm, 1, 4),"4RS")
lnAlgorithmPara = CTOBIN(SUBSTR(lbSignAlgorithm, 5, 4),"4RS")
lpAlgorithmPara = CTOBIN(SUBSTR(lbSignAlgorithm, 9, 4),"4RS")
lcAlgorithmOID = SYS(2600, lpAlgorithmOID, GetStrLenA(lpAlgorithmOID))
lcAlgorithmPara = STRCONV(SYS(2600, lpAlgorithmPara, lnAlgorithmPara),15)
RETURN lcAlgorithmOID +"; " + lcAlgorithmPara
ENDPROC

PROCEDURE GetIssuer()
LPARAMETERS tpCertContext AS Long
lcIssuer = ""
lpCertInfo  = CTOBIN(SYS(2600, tpCertContext + 12, 4), "4RS")
IF lpCertInfo > 0
lcIssuer = GetCertNameToString(lpCertInfo + 24)
ENDIF
RETURN lcIssuer
ENDPROC

PROCEDURE GetNotBefore()
LPARAMETERS tpCertContext AS Long
ltNotBefore = CTOT("")
lpCertInfo  = CTOBIN(SYS(2600, tpCertContext+ 12, 4), "4RS")
IF lpCertInfo > 0
lpNotBefore  = lpCertInfo + 32
lpSystemTime = SPACE(16)
IF FileTimeToSystemTime(lpNotBefore, @lpSystemTime)#0 
ltNotBefore = GetBinatyToDateTime(lpSystemTime)
ENDIF
ENDIF
RETURN ltNotBefore
ENDPROC

PROCEDURE GetNotAfter()
LPARAMETERS tpCertContext AS Long
ltNotAfter = CTOT("")
lpCertInfo = CTOBIN(SYS(2600, tpCertContext + 12, 4), "4RS")
IF lpCertInfo > 0
lpNotAfter = lpCertInfo + 40
lpSystemTime = SPACE(16)
IF FileTimeToSystemTime(lpNotAfter, @lpSystemTime)#0
ltNotAfter = GetBinatyToDateTime(lpSystemTime)
ENDIF
ENDIF
RETURN ltNotAfter 
ENDPROC

PROCEDURE GetSubject()
LPARAMETERS tpCertContext AS Long
lcSubjectStr = ""
IF tpCertContext>0
lpCertInfo = CTOBIN(SYS(2600, tpCertContext + 12, 4), "4RS")
IF lpCertInfo > 0
lcSubjectStr=GetCertNameToString(lpCertInfo + 48)
ENDIF
ENDIF
RETURN lcSubjectStr
ENDPROC

PROCEDURE GetSubjectPublicKeyInfo()
LPARAMETERS tcCertInfo AS String
lbSubjectPubKey = SUBSTR(tcCertInfo,57,12)
lpAlgorithmPKey = CTOBIN(SUBSTR(lbSubjectPubKey, 1, 4),"4RS")
lnAlgorithmPara = CTOBIN(SUBSTR(lbSubjectPubKey, 5, 4),"4RS")
lpAlgorithmPara = CTOBIN(SUBSTR(lbSubjectPubKey, 9, 4),"4RS")
lcAlgorithmPKey = SYS(2600, lpAlgorithmPKey, GetStrLenA(lpAlgorithmPKey))
lcAlgorithmPara = STRCONV(SYS(2600, lpAlgorithmPara, lnAlgorithmPara),15)
RETURN lcAlgorithmPKey +"; "+ lcAlgorithmPara
ENDPROC

PROCEDURE GetSubjectPublicKeyValue()
LPARAMETERS tcCertInfo AS String
lbPublicKey = SUBSTR(tcCertInfo,69,8)
lnPublicKey = CTOBIN(SUBSTR(lbPublicKey, 1, 4),"4RS")
lpPublicKey = CTOBIN(SUBSTR(lbPublicKey, 5, 4),"4RS")
lcPublicKey = SYS(2600, lpPublicKey, lnPublicKey)
RETURN lcPublicKey
ENDPROC

PROCEDURE GetExponent()
LPARAMETERS tbPublicKey AS String
cAsn1Exponent = RIGHT(tbPublicKey,3)
cB64Exponent = STRCONV(cAsn1Exponent,13)
RETURN cB64Exponent
ENDPROC

PROCEDURE GetModulus()
PARAMETERS tbPublicKey AS String, tcIdAlgorithm AS String
nLenPubK=LEN(tbPublicKey)
IF VARTYPE(tcIdAlgorithm)<>"C"
tcIdAlgorithm="1.2.840.113549.1.1.5"
ENDIF
cAsn1Modulus=tbPublicKey
DO CASE
CASE LEFT(tcIdAlgorithm,13)<>"1.2.840.10045" AND LEFT(tcIdAlgorithm,9)<>"1.3.132.0"
*----- Algorithm RSA Cryptography (Rivest, Shamir y Adleman)
nIniChr=AT(CHR(0),tbPublicKey)+1
nEndChr=AT(STRCONV("0203010001",16), tbPublicKey)
IF nEndChr==0
nEndChr=AT(STRCONV("020103",16), tbPublicKey)
ENDIF
IF nIniChr>nEndChr
bIni512=STRCONV("30470240",16)
nIniChr=AT(bIni512,tbPublicKey)+LEN(bIni512)
ENDIF
cAsn1Modulus=SUBSTR(tbPublicKey, nIniChr, nEndChr-nIniChr)
CASE LEFT(tcIdAlgorithm,13)=="1.2.840.10045" OR LEFT(tcIdAlgorithm,9)=="1.3.132.0"
*----- Algorithm ECC Cryptography (Elliptic Curve Cryptography)
cAsn1Modulus=tbPublicKey
ENDCASE
RETURN cAsn1Modulus
ENDPROC

PROCEDURE GetCertificateType()
LPARAMETERS tpCertContext AS Long
lcCertType = ""
IF tpCertContext>0
lcCertContext = SYS(2600, tpCertContext, 20)
lnCertType = CTOBIN(SUBSTR(lcCertContext, 1, 4), "4RS")
lcCertType = IIF(lnCertType=1, "X509Certificate", "PKCS7Certificate")
ENDIF
RETURN lcCertType
ENDPROC

PROCEDURE GetCertificateEncoded()
LPARAMETERS tpCertContext AS Long
lbCertEncoded = ""
IF tpCertContext>0
lcCertContext = SYS(2600, tpCertContext, 20)
lpCertEncoded = CTOBIN(SUBSTR(lcCertContext, 5, 4), "4RS")
lnCertEncoded = CTOBIN(SUBSTR(lcCertContext, 9, 4), "4RS")
lbCertEncoded = SYS(2600, lpCertEncoded, lnCertEncoded)
ENDIF
RETURN lbCertEncoded
ENDPROC

PROCEDURE GetReverseString()
LPARAMETERS tcStringToReverse AS String 
LOCAL lcReverse, nRev
lcReverse=""
FOR nRev = LEN(tcStringToReverse) TO 1 STEP -1
lcReverse = lcReverse + SUBSTR(tcStringToReverse, nRev,1)
NEXT
RETURN lcReverse
ENDPROC

PROCEDURE GetCertNameToString()
LPARAMETERS pbCertNameBlob AS Long
*----- Valores posibles para wDwStrType, pueden ser la suma de varios
SIMPLENAMESTR     = 1
OIDNAMESTR   = 2
X500NAMESTR   = 3
SEMICOLONFLAG     = 0x40000000
CRLFFLAG   = 0x08000000
NOPLUSFLAG   = 0x20000000
NOQUOTINGFLAG     = 0x10000000
NAMEREVERSE   = 0x02000000
DISABLEIE4UTF8    = 0x00010000
ENABLEPUNYCODE    = 0x00200000
*----- Asignamos valores a Parametros
nCertEncodTyp = BITOR(PKCS_7_ASN_ENCODING, X509_ASN_ENCODING)
cpName   = pbCertNameBlob
nDwStrType   = BITOR(X500NAMESTR, OIDNAMESTR, NAMEREVERSE, SEMICOLONFLAG)
lcNameDecoded = ""
lnNameLong    = 0
*----- Intento 1, obtenemos longitud de cadena
lnNameLong = CertNameToStr(nCertEncodTyp, cpName, nDwStrType, @lcNameDecoded, @lnNameLong)
IF lnNameLong <> 0
*----- Intento 2, conociendo longitud de cadena, Obtenemos el nombre decodificado
lcNameDecoded= REPLICATE(CHR(0), lnNameLong)
CertNameToStr(nCertEncodTyp, cpName, nDwStrType, @lcNameDecoded, @lnNameLong)
ENDIF
*----- La función devuelve una cadena terminada en nulo, quitamos el CHR(0) que represanta nulo
RETURN STRTRAN(lcNameDecoded, CHR(0), "")
ENDPROC

PROCEDURE GetBinatyToDateTime()
LPARAMETERS tbSystemTime AS String
ltDateTime = ""
IF !EMPTY(tbSystemTime)
wYear = CTOBIN(SUBSTR(tbSystemTime,1,2),"2RS")
wMonth = CTOBIN(SUBSTR(tbSystemTime,3,2),"2RS")
wDayWeek = CTOBIN(SUBSTR(tbSystemTime,5,2),"2RS")
wDay = CTOBIN(SUBSTR(tbSystemTime,7,2),"2RS")
wHour = CTOBIN(SUBSTR(tbSystemTime,9,2),"2RS")
wMinute  = CTOBIN(SUBSTR(tbSystemTime,11,2),"2RS")
wSecond  = CTOBIN(SUBSTR(tbSystemTime,13,2),"2RS")
wMilSec  = CTOBIN(SUBSTR(tbSystemTime,15,2),"2RS")
ltDateTime = DATETIME(wYear, wMonth, wDay, wHour, wMinute, wSecond)
ENDIF
RETURN ltDateTime
ENDPROC

PROCEDURE SetDeclarationsApis()
DECLARE LONG CertOpenStore IN Crypt32;
LONG lpszStoreProvider,;
LONG dwEncodingType,;
LONG hCryptProv,;
LONG dwFlags,;
STRING pvPara
DECLARE LONG CryptUIDlgSelectCertificateFromStore IN Cryptui;
LONG hCertStore,;
LONG hWnd, ;
STRING @pwszTitle, ;
STRING @pwszDisplayString, ;
LONG dwDontUseColumn, ;
LONG dwFlags,;
STRING pvReserved

DECLARE LONG CertNameToStr IN Crypt32;
LONG dwCertEncodingType, ;
LONG pName, ;
LONG dwStrType, ;
STRING @psz, ;
LONG csz

DECLARE LONG CertFreeCertificateContext IN Crypt32;
LONG pCertContext

DECLARE LONG CertCloseStore IN Crypt32;
LONG hCertStore,;
LONG dwFlags

*------ Kernel32
DECLARE LONG FileTimeToSystemTime IN Kernel32;
LONG lpFileTime,;
STRING @lpSystemTime
*------ Determina la longitud de un puntero, no devuelve en caracter null de finalización
DECLARE LONG lstrlenA IN Kernel32 AS GetStrLenA;
LONG lpString

DECLARE LONG lstrlenW IN Kernel32 AS GetStrLenW;
LONG lpString
ENDPROC





Fernando Mora

unread,
Sep 17, 2019, 4:09:16 PM9/17/19
to Comunidad de Visual Foxpro en Español

Aquí una captura del código, ejecutando y devolviendo datos, en este caso decodifico un certificado X509 creado en Fox a puro código. Con Fox y CryptoApi podemos crear certificados autofirmados, y certificados de entidad final, y firmar esos certificados con nuestro certificado de raíz también creado en Fox. 



decode_full.jpg

Leonardo N.

unread,
Sep 17, 2019, 6:35:42 PM9/17/19
to Comunidad de Visual Foxpro en Español
Fernando;

Felicitaciones por el trabajo y estoy sorprendido por la capacidad de manipulación de los certificados digitales desde VFP.  Tengo unas inquietudes:
1. "decodifico un certificado X509 creado en Fox". Significa que podemos crear nuestro propio certificado digital ??..y si es asi..este certificado tiene el  estándar X.509 v3.?. Actualmente compro los certificados digitales para la firma de las facturas electronicas.  En vez de comprar, puedo generar mi propio certificado digital ??..estoy en lo correcto ó estoy alucinando..

2. "Con la decodificación de la fecha de emisión y la fecha de caducidad podemos verificar mediante programación si el certificado esta dentro del periodo de valides". Puedo decodificar y extraer estos datos de cualquier certificado PFX, CER o p12??.  Seria un golazo dado que ayudaría para comunicar al cliente que su certificado esta a punto de vencer..

Atento a tu respuesta}

Saludos

Fernando Mora

unread,
Sep 17, 2019, 8:05:41 PM9/17/19
to Comunidad de Visual Foxpro en Español
Hola Leonardo.

Tu pregunta 1, SI, podemos crear certificados desde Fox con CryptoAPI y SI, esos certificados cumplen el estándar x509 versión 3, normalmente vemos esto en aplicaciones desarrolladas en CSharp o C++, lo que no sabíamos es que esos lenguajes usan el mismo CryptoAPI para esa tarea, es decir en lugar de usar librerías creadas en esos lenguajes, podemos desarrollar nuestras aplicaciones a puro Fox sin intermediarios. Puedes crear tus propios certificados, pero consulta el tema legal en tu país.

Pregunta 2. Así es, puedes decodificar cualquier certificado, pero ten en claro, un certificado es un archivo extensión CER o PEM, los archivos PFX y P12 son contenedores de certificados, es decir pueden contener varios certificados con sus respectivos pares de claves, (publico/privada) y desde luego, el propósito para decodificar la fecha de emisión y caducidad es para alertar al cliente que esta por caducar su certificado y que solicite uno nuevo. 

Diego F.

unread,
Sep 18, 2019, 10:00:14 AM9/18/19
to Comunidad de Visual Foxpro en Español
Excelente Fernando. Muy buen trabajo.

Diego.

Zarlu

unread,
Sep 18, 2019, 10:46:01 AM9/18/19
to Comunidad de Visual Foxpro en Español
Fernando Mora
Buenos días!

Muchas gracias por tu aporte.

Es de esta manera que se contribuye a que se resuelvan e
innoven los sistemas vfp dando como resultado "Larga vida al zorro"

Muchas gracias

zarlu
Chetumal, Quintana Roo, México


Cesar Luque

unread,
Sep 18, 2019, 1:00:35 PM9/18/19
to Comunidad de Visual Foxpro en Español
Enorme aporte!!
De que manera podrìa obtener la fecha de validez de un certificado .pfx?

Saludos.

Cèsar.-

HernanCano

unread,
Sep 18, 2019, 3:45:36 PM9/18/19
to Comunidad de Visual Foxpro en Español
César:
Luego de ejecutar el procedimiento, en el .TXT generado, mira el renglón

Fecha Caduca: <<tNotAfter>>



El miércoles, 18 de septiembre de 2019, 12:00:35 (UTC-5), Cesar Luque escribió:
Enorme aporte!!
De que manera podría obtener la fecha de validez de un certificado .pfx?

Saludos.

César.-

Fernando Mora

unread,
Sep 18, 2019, 6:14:59 PM9/18/19
to Comunidad de Visual Foxpro en Español
Hola Cesar!
Lo que te dice Hernan es lo correcto. 
Ahora aclaremos una vez mas, un PFX no es un certificado, sino un contenedor (almacén/tienda términos usados por MS) dentro del PFX o P12 vienen los certificados, usa la función GetImportP12PFX del enlace que publique en el primer mensaje de este hilo. Esa función te devuelve un puntero a CertStore (hStore), ese puntero se lo pasas a la función GetCryptUIDlgSelectCertificateFromStore que te devolverá el puntero al certificado. Ese puntero se lo puedes pasar directamente a la función GetNotAfter que te devolverá la fecha-hora de caducidad del certificado.

Saludos.


El miércoles, 18 de septiembre de 2019, 12:00:35 (UTC-5), Cesar Luque escribió:

Esteban H

unread,
Sep 18, 2019, 7:05:36 PM9/18/19
to publice...@googlegroups.com

Fernando, cómo puedo procesar un archivo extensión crt con esta Api?

 

Saludos

Esteban

Fernando Mora

unread,
Sep 18, 2019, 9:22:47 PM9/18/19
to Comunidad de Visual Foxpro en Español
Hola Esteban
Los archivos CRT son lo mismo que los archivos CER, PEM o DER, son certificados de intercambio o de información. Solo tienes que crearle un contexto al certificado. Aquí te arme un código para que puedas crearle un puntero al contexto de tu certificados, unifica esto con el código del primer mensaje de este hilo para que puedas obtener la decodificación de tu archivo CRT.


*------------ Constantes Certificate Type
#DEFINE X509_ASN_ENCODING 1  
#DEFINE PKCS_7_ASN_ENCODING 0x00010000
*------------ Constantes CryptStringToBinary, para: dwFlags 
#DEFINE CRYPT_STRING_BASE64HEADER 0x00000000
#DEFINE CRYPT_STRING_BASE64 0x00000001
#DEFINE CRYPT_STRING_BINARY 0x00000002
#DEFINE CRYPT_STRING_BASE64REQUESTHEADER 0x00000003
#DEFINE CRYPT_STRING_HEX 0x00000004
#DEFINE CRYPT_STRING_HEXASCII 0x00000005
#DEFINE CRYPT_STRING_BASE64_ANY 0x00000006
#DEFINE CRYPT_STRING_ANY 0x00000007
#DEFINE CRYPT_STRING_HEX_ANY 0x00000008
#DEFINE CRYPT_STRING_BASE64X509CRLHEADER 0x00000009
#DEFINE CRYPT_STRING_HEXADDR 0x0000000a
#DEFINE CRYPT_STRING_HEXASCIIADDR 0x0000000b
#DEFINE CRYPT_STRING_HEXRAW 0x0000000c
#DEFINE CRYPT_STRING_STRICT 0x20000000

=SetDeclarationsAPI()
cFileCer=GETFILE("CER,CRT,PEM")
IF EMPTY(cFileCer)
RETURN
ENDIF
lnCertContext=GetImportCerCrtPemDer(cFileCer)
? lnCertContext

PROCEDURE GetImportCerCrtPemDer()
PARAMETERS tcFilePemOrCer AS String
pCertContext = 0
cStringCert=FILETOSTR(tcFilePemOrCer)
IF !EMPTY(cStringCert)
*----- Decodificamos la cadena para ASN1
pbCertEncoded = GetCryptStringToBinary(cStringCert)
IF !EMPTY(pbCertEncoded)
dwCertEncodingType = BITOR(PKCS_7_ASN_ENCODING, X509_ASN_ENCODING)
cbCertEncoded = LEN(pbCertEncoded)
pCertContext = CertCreateCertificateContext(dwCertEncodingType, pbCertEncoded, cbCertEncoded)
ELSE
MESSAGEBOX("No se pudo decodicar el archivo", 16, _SCREEN.Caption)
ENDIF
ELSE
MESSAGEBOX("El archivo esta vacio", 16, _SCREEN.Caption)
ENDIF
RETURN pCertContext
ENDPROC

PROCEDURE GetCryptStringToBinary()
PARAMETERS tcStrToDecode AS String
*------- Convertimos binarios a Asn1
pszString = tcStrToDecode
cchString = LEN(pszString)
pbBinary=""
dwBufferLen=0
pdwSkip=0
pdwFlags=0
nResp = CryptStringToBinary(pszString, cchString, CRYPT_STRING_ANY, NULL, @dwBufferLen, @pdwSkip, @pdwFlags)
pbBinary = SPACE(dwBufferLen)
DO CASE
CASE pdwFlags=0
*------- Base64, with certificate beginning and ending headers.
CryptStringToBinary(pszString, cchString, CRYPT_STRING_BASE64HEADER, @pbBinary, @dwBufferLen, @pdwSkip, @pdwFlags)
CASE pdwFlags=1
*------- Base64, without headers.
CryptStringToBinary(pszString, cchString, CRYPT_STRING_BASE64, @pbBinary, @dwBufferLen, @pdwSkip, @pdwFlags)
CASE pdwFlags=2
*------- Pure binary copy.
pbCertEncoded=tcStrToDecode
ENDCASE
RETURN pbBinary
ENDPROC

PROCEDURE SetDeclarationsAPI()
DECLARE LONG CertCreateCertificateContext IN Crypt32;
LONG dwCertEncodingType,;
STRING pbCertEncoded,;
LONG cbCertEncoded

DECLARE LONG CryptStringToBinary IN Crypt32;
STRING @pszString, ;
LONG cchString, ;
LONG dwFlags,;
STRING @pbBinary, ;
LONG @pcbBinary,;
LONG @pdwSkip, ;
LONG @pdwFlags

ENDPROC




Jose Antonio Blasco

unread,
Sep 19, 2019, 4:45:03 AM9/19/19
to Comunidad de Visual Foxpro en Español
Buenos días Fernando,
En primer lugar gracias por tu esfuerzo y trabajo.
He seguido y descargado, todo lo que has publicado sobre los certificados.
Este último código que has facilitado, que genera un archivo de texto con información del certificado y de la firma creo que puede servirme, aunque reconozco que apenas se nada de la generación de certificados.
En mi caso, he desarrollado una aplicación que genera Facturas Electrónicas para España, que aquí se llama "FACTURAe" o "FACe".
Yo me limito a generar el archivo XML con la estructura debida, y luego utilizo una herramienta externa, facilitada por el Ministerio que se llama "Autofirma".
Se le pasa el archivo XML y otros parámetros, y te devuelve otro archivo con la información del XML y al final le ha añadido la firma.  
Este archivo tiene extensión XSIG y es el que se envía a la Administración.
Mi intención sería poder generar todo con VFP para dejar de usar la herramienta externa, pero aunque he comparado lo que yo veo de la firma en mi archivo XSIG con el archivo de texto que genera tu último código, y hay parecido, no veo como incluirlo.

No se si podrás o querrás ayudarme, pero si acaso, te adjunto un archivo ZIP (debe renombrarse la extensión), que incluye el archivo XML y el XSIG de una misma factura.  Los datos son ficticios.

En cualquier caso, gracias por todo lo que publicas.

Un saludo.
Jose A. Blasco
Zaragoza - España
Visual FoxPro 9 SP2



--
Has recibido este mensaje porque estás suscrito al grupo "Comunidad de Visual Foxpro en Español" 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 publicesvfoxp...@googlegroups.com.
Para ver esta conversación en el sitio web, visita https://groups.google.com/d/msgid/publicesvfoxpro/dd70e3a6-0cd2-4d8a-9503-89845f8ab06e%40googlegroups.com.
factura._ip

Fernando Mora

unread,
Sep 20, 2019, 12:20:50 AM9/20/19
to Comunidad de Visual Foxpro en Español

Hola Jose Antonio, acabo de llegar de Guayaquil y revise tu archivo. Veo que el archivo XSIG es el Xml firmado, ese armado se llama Xades-EPES, porque incluye la ruta de políticas de firmado. Yo tengo los arreglos para Xades-BES (probado y en producción) que es el armado de xml que usamos acá en Ecuador, y tengo Xades-EPES y Xades-T pero no se ha probado, porque acá no se usa. Tengo dudas del valor hash del nodo "SignaturePolicyIdentifier", no se si es un hash al archivo o a la cadena de la dirección del archivo, voy a revisar. Hace un tiempo publique las funciones para crear los hash y realizar el firmado del hash en este hilo:  https://groups.google.com/forum/#!topic/publicesvfoxpro/qU_hJZr87a0%5B101-125%5D. si tienes ese código?

Para cancelar la suscripción a este grupo y dejar de recibir sus mensajes, envía un correo electrónico a publice...@googlegroups.com.

Fernando Mora

unread,
Sep 20, 2019, 1:04:19 AM9/20/19
to Comunidad de Visual Foxpro en Español
Respondo la duda del hash, es un hash al archivo, no a la ruta, por lo tanto con FILETOSTR obtener el contenido del archivo y pasarlo a la función GetDigestValue con el parámetro "SHA1" al resultado del hash lo convertimos a base64 con STRCONV(cadena,13).

Ahora otra duda, veo que hay 3 certificados en el nodo KeyInfo... eso suele pasar porque se pasa la ruta del p12/pfx a la librería o programa externo, y esa librería coloca cuanto certificado encuentre en el contenedor p12, si encuentra 20 certificado colocara los 20 así de errático... porque normalmente solo va el certificado del firmante, dentro del certificado del firmante va la información de la ruta de la cadena de confianza, es por eso que no es necesario incluir los certificados de CA... eso hasta donde he leído es así, ahora no se si el formato o la norma allá sea esa, de incluir todos los certificado. Que piensas tu?  como son los comprobantes de FE que emiten los demás contribuyentes? también van con 3 certificados?

Jose Antonio Blasco

unread,
Sep 20, 2019, 4:02:54 AM9/20/19
to Comunidad de Visual Foxpro en Español
Buenos días Fernando y gracias por responder,
He comprobado que SI tengo el código que comentas, de abril de este año, pero ya te he comentado mis escasos conocimientos sobre el funcionamiento interno de los certificados.
Con respecto a lo que comentas de los 3 certificados en el nodo KeyInfo, sólo puedo decirte que en mi caso, y en el de mis clientes, tienen los certificados instalados en el almacén de certificados, bien a través de IE/Chrome o bien de Firefox.
Nadie trabaja sobre los p12/pfx directamente.  
No se si con ésto te contesto a lo que me preguntas.
Con todo esto, ¿ crees que puedo llegar a obtener el código correspondiente a la firma para incluirlo en el archivo XML?

Un saludo y gracias por todo.

Jose A. Blasco
Zaragoza - España
Visual FoxPro 9 SP2


El vie., 20 sept. 2019 a las 7:04, Fernando Mora (<servipcco...@gmail.com>) escribió:
Respondo la duda del hash, es un hash al archivo, no a la ruta, por lo tanto con FILETOSTR obtener el contenido del archivo y pasarlo a la función GetDigestValue con el parámetro "SHA1" al resultado del hash lo convertimos a base64 con STRCONV(cadena,13).

Ahora otra duda, veo que hay 3 certificados en el nodo KeyInfo... eso suele pasar porque se pasa la ruta del p12/pfx a la librería o programa externo, y esa librería coloca cuanto certificado encuentre en el contenedor p12, si encuentra 20 certificado colocara los 20 así de errático... porque normalmente solo va el certificado del firmante, dentro del certificado del firmante va la información de la ruta de la cadena de confianza, es por eso que no es necesario incluir los certificados de CA... eso hasta donde he leído es así, ahora no se si el formato o la norma allá sea esa, de incluir todos los certificado. Que piensas tu?  como son los comprobantes de FE que emiten los demás contribuyentes? también van con 3 certificados?

--
Has recibido este mensaje porque estás suscrito al grupo "Comunidad de Visual Foxpro en Español" 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 publicesvfoxp...@googlegroups.com.
Para ver esta conversación en el sitio web, visita https://groups.google.com/d/msgid/publicesvfoxpro/b1b47120-de77-4e8d-9482-3051731afb84%40googlegroups.com.

Roman Koltsov

unread,
Sep 21, 2019, 4:30:10 AM9/21/19
to Comunidad de Visual Foxpro en Español
Hola a todos

Un poco más tarde, si puedo formularlo en español, trataré de decir cómo se hace en Rusia. Por cierto, en nuestro país se utiliza el formato XAdES-T, pero generamos el código en Java, ya que personalmente no entiendo cómo interactuar con el servidor TSA en VFP, como escribí anteriormente.

Fernando Mora

unread,
Sep 21, 2019, 2:30:03 PM9/21/19
to Comunidad de Visual Foxpro en Español
Hola Jose Antonio.
Por lo que comentas, veo que el arreglo que exigen en tu país es incluir la cadena de confianza, por eso incluyen los 3 certificados, (entidad final, entidad CA  y entidad de Raíz). Acá no lo usamos así, para lograr eso veo en la documentación de MS que hay que utilizar la funciones para tomar la cadena de confianza del certificado y obtener los 3 contextos. Voy ha revisar como se usan las apis que hacen eso (CerficiateChain). Por ahora lo que se me ocurre es que podríamos utilizar un simple bucle en fox para escavar en los almacenes de CA y ROOT para obtener los certificados de la cadena de confianza, decodificar los 3 contextos e incluirlos en el nodo KeyInfo. Voy a revisar y probar para ver como te puedo ayudar a armar tu arreglo. 

Para cancelar la suscripción a este grupo y dejar de recibir sus mensajes, envía un correo electrónico a publice...@googlegroups.com.

Fernando Mora

unread,
Sep 21, 2019, 2:36:31 PM9/21/19
to Comunidad de Visual Foxpro en Español
Hola Roman.
Tengo un código para interactuar desde VFP con un servidor TSA, por lo menos desde el lado del cliente. Voy a abrir otro hilo para publicarlo, me gustaría que revises y confirmes si eso esta correcto, acá no tengo contra que probarlo, ya que acá no usamos ese arreglo. 

Roman Koltsov

unread,
Sep 21, 2019, 9:56:27 PM9/21/19
to Comunidad de Visual Foxpro en Español
Hola, Fernando

Le envío un ejemplo de contenedor P7B con cadena de certificado sin clave privada.

Roman Koltsov

unread,
Sep 21, 2019, 10:08:14 PM9/21/19
to Comunidad de Visual Foxpro en Español
¿Utiliza un contenedor P7B en su país?

Jose Antonio Blasco

unread,
Sep 23, 2019, 3:55:26 AM9/23/19
to Comunidad de Visual Foxpro en Español
Buenos días Fernando y gracias por responderme.  
Como este hilo se está complicando, no se si viste éste mensaje que te envié el viernes:

"He comprobado que SI tengo el código que comentas, de abril de este año, pero ya te he comentado mis escasos conocimientos sobre el funcionamiento interno de los certificados.
Con respecto a lo que comentas de los 3 certificados en el nodo KeyInfo, sólo puedo decirte que en mi caso, y en el de mis clientes, tienen los certificados instalados en el almacén de certificados, bien a través de IE/Chrome o bien de Firefox.
Nadie trabaja sobre los p12/pfx directamente.  
No se si con ésto te contesto a lo que me preguntas.
Con todo esto, ¿ crees que puedo llegar a obtener el código correspondiente a la firma para incluirlo en el archivo XML? ""

Un saludo y gracias por todo.
Jose A. Blasco
Zaragoza - España
Visual FoxPro 9 SP2


--
Has recibido este mensaje porque estás suscrito al grupo "Comunidad de Visual Foxpro en Español" 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 publicesvfoxp...@googlegroups.com.
Para ver esta conversación en el sitio web, visita https://groups.google.com/d/msgid/publicesvfoxpro/0ca87706-6018-44c2-b8d2-ea28f66c359a%40googlegroups.com.

Fernando Mora

unread,
Sep 24, 2019, 9:13:13 AM9/24/19
to Comunidad de Visual Foxpro en Español
Hola Jose Antonio, si lei tu mensajes, y te respondí que aun no se como obtener la cadena de confianza de un certificado porque no he topado las funciones CertificateChain, y lo que se me ocurre por ahora es con un bucle buscar dentro de los almacenes de CA el emisor del certificado Final, y en ROOT buscar el emisor del certificado de CA, de esa manera también se podría obtener la cadena de confianza del certificado. Tomar los 3 contexto y decodificarlos para armar el noto KeyInfo, aún no lo he hecho, en cuanto pueda lo intento y publico el código final. 

Saludos.
Fernando
Para cancelar la suscripción a este grupo y dejar de recibir sus mensajes, envía un correo electrónico a publice...@googlegroups.com.

Jose Antonio Blasco

unread,
Sep 25, 2019, 4:51:46 AM9/25/19
to Comunidad de Visual Foxpro en Español
Disculpa Fernando, Gmail me está dando problemas desde Chrome y no veo todos los mensajes.
Gracias por tu interés y un saludo.

Jose A. Blasco
Zaragoza - España
Visual FoxPro 9 SP2


Para cancelar la suscripción a este grupo y dejar de recibir sus mensajes, envía un correo electrónico a publicesvfoxp...@googlegroups.com.
Para ver esta conversación en el sitio web, visita https://groups.google.com/d/msgid/publicesvfoxpro/a614384b-3439-402f-a0b1-c23e6d968154%40googlegroups.com.

Fernando Mora

unread,
Sep 26, 2019, 12:38:01 AM9/26/19
to Comunidad de Visual Foxpro en Español
Hola Jose Anotnio.

Buenas noticias. Si se pudo obtener la cadena de certificado, se logro interpretar el armado de las estructuras que intervienen en este proceso.
Como esto es otro tema, abrí otro hilo para abordarlo. revisa este enlace: https://groups.google.com/forum/#!topic/publicesvfoxpro/mbhdF4YcbaY


Saludos!

Jose Antonio Blasco

unread,
Sep 26, 2019, 3:14:40 AM9/26/19
to Comunidad de Visual Foxpro en Español
!!! Muchísimas gracias Fernando !!!
Hoy mismo lo pruebo y te digo.

Un abrazo.

Jose A. Blasco
Zaragoza - España
Visual FoxPro 9 SP2


Para cancelar la suscripción a este grupo y dejar de recibir sus mensajes, envía un correo electrónico a publicesvfoxp...@googlegroups.com.
Para ver esta conversación en el sitio web, visita https://groups.google.com/d/msgid/publicesvfoxpro/eff55916-a197-4d1f-9093-95453e0031df%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages