* Constants for use with CryptoAPI function CryptGetObjectUrl
* to programmatically extract CRL list distribution point from a digital certificate (CDP)
* AIA extension - Authority Information Access contains AIA records and possibly even the OCSP URL (OID '1.3.6.1.5.5.7.1.1')
#DEFINE URL_OID_CERTIFICATE_ISSUER                    1  && AIA: $urlType = URL_OID_CERTIFICATE_ISSUER
* CDP extension - CRL Distribution Points (OID '2.5.29.31')
#DEFINE URL_OID_CERTIFICATE_CRL_DIST_POINT            2  && CRL: $urlType = URL_OID_CERTIFICATE_CRL_DIST_POINT
#DEFINE URL_OID_CTL_ISSUER                            3
#DEFINE URL_OID_CTL_NEXT_UPDATE                       4
#DEFINE URL_OID_CRL_ISSUER                            5
#DEFINE URL_OID_CERTIFICATE_FRESHEST_CRL              6
#DEFINE URL_OID_CRL_FRESHEST_CRL                      7
#DEFINE URL_OID_CROSS_CERT_DIST_POINT                 8
#DEFINE URL_OID_CERTIFICATE_OCSP                      9
#DEFINE URL_OID_CERTIFICATE_OCSP_AND_CRL_DIST_POINT   10
#DEFINE URL_OID_CERTIFICATE_CRL_DIST_POINT_AND_OCSP   11
#DEFINE URL_OID_CROSS_CERT_SUBJECT_INFO_ACCESS        12
#DEFINE URL_OID_CERTIFICATE_ONLY_OCSP                 13 && OCSP: $urlType = URL_OID_CERTIFICATE_ONLY_OCSP
#DEFINE CRYPT_GET_URL_FROM_PROPERTY                   1
#DEFINE CRYPT_GET_URL_FROM_EXTENSION                  2  && 0x2
#DEFINE CRYPT_GET_URL_FROM_UNAUTH_ATTRIBUTE           3
#DEFINE CRYPT_GET_URL_FROM_AUTH_ATTRIBUTE             8
Declare Function CryptGetObjectUrl Lib "cryptnet.dll" ( _
                 ByVal pszUrlOid As String, _
                 pvPara As Any, _
                 ByVal dwFlags As Long, _
                 ByRef pUrlArray As PCRYPT_URL_ARRAY, _
                 ByRef pcbUrlArray As Long, _
                 ByRef pUrlInfo As PCRYPT_URL_INFO, _
                 ByRef pcbUrlInfo As Long, _
                 pvReserved As Any) As Long
$signature = @"
[DllImport("cryptnet.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool CryptGetObjectUrl(
    int pszUrlOid,
    IntPtr pvPara,
    int dwFlags,
    byte[] pUrlArray,
    ref int pcbUrlArray,
    IntPtr pUrlInfo,
    ref int pcbUrlInfo,
    int pvReserved
);
"@
DECLARE INTEGER CryptGetObjectUrl IN cryptnet;
    STRING  @ pszUrlOid,;     && _In_       STRING OR INTEGER??? 
    INTEGER   pvPara,;        && _In_
    INTEGER   dwFlags,;       && _In_
    LONG    @ pUrlArray,;     && _Out_      PCRYPT_URL_ARRAY  && OR STRING @ pUrlArray?   pUrlArray will contains a byte array that represents little-endian Unicode strings
    LONG    @ pcbUrlArray,;   && _Inout_    DWORD *,          pcbUrlArray will contain a number of bytes to use for pUrlArray byte array
    LONG    @ pUrlInfo,;      && _Out_      PCRYPT_URL_INFO,  not required for our task
    LONG    @ pcbUrlInfo,;    && _Inout_    DWORD *,          not required for our task
    INTEGER   pvReserved      && _reserved_ LPVOID,           not required for our taskpcbUrlArray = 0
pszUrlOid = BINTOC(2, "4RS") + Ñhr(0)
ret = CryptGetObjectUrl(@pszUrlOid, pCertContext, 0, NULL, @pcbUrlArray, NULL, NULL, NULL)
If ret = 0
    lnResultCode = GetLastError()
    lcErr = "Error by calling CryptGetObjectUrl"
    =MessageBox(lcErr,16,"Error: " + Transform(lnResultCode,"@0"))
Else
    pUrlArray = LocalAlloc(0x0040, m.pcbUrlArray)  && Pointer to structure CRYPT_URL_ARRAY
    * https://www.winehq.org/pipermail/wine-cvs/2009-December/062848.html
    If !CryptGetObjectUrl(URL_OID_CERTIFICATE_CRL_DIST_POINT, pCertContext, 0, pUrlArray, @pcbUrlArray, NULL, NULL, NULL)=0
          nBufSize=0  
          = CryptBinaryToString(@m.pUrlArray, Len(m.pUrlArray), CRYPT_STRING_BINARY, NULL, @nBufSize)  && CRYPT_STRING_BINARY = 0x00000002
          lcCDP_URL = REPLICATE(CHR(0), m.nBufSize)  
          If !CryptBinaryToString(@m.pUrlArray, Len(m.pUrlArray), CRYPT_STRING_BINARY, @lcCDP_URL, @nBufSize) = 0
            If !Empty(hFile)
                lcStr = "CRL List URL: " + AllT(lcCDP_URL)
                FPuts(hFile, lcStr)
            EndIf
        Else
            =MessageBox("Can't to retrieve CRL List URL from Certificate!",16,"Error")
        EndIf
    Else
        =MessageBox("Cant't to obtain CRL point address!",16,"Error")
    EndIf
    LocalFree(pUrlInfo)
    LocalFree(pUrlArray)
EndIf
* https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptbinarytostringa
* Constants for third parameter of CryptBinaryToStringA function
#DEFINE CRYPT_STRING_BASE64HEADER           0x00000000  && Base64, with certificate beginning and ending headers
#DEFINE CRYPT_STRING_BASE64                 0x00000001  && Base64, without headers
#DEFINE CRYPT_STRING_BINARY                 0x00000002  && Pure binary copy
#DEFINE CRYPT_STRING_BASE64REQUESTHEADER    0x00000003  && Base64, with request beginning and ending headers
#DEFINE CRYPT_STRING_HEX                    0x00000004  && Hexadecimal only
#DEFINE CRYPT_STRING_HEXASCII               0x00000005  && Hexadecimal, with ASCII character display
#DEFINE CRYPT_STRING_BASE64X509CRLHEADER    0x00000009  && Base64, with X.509 CRL beginning and ending headers
#DEFINE CRYPT_STRING_HEXADDR                0x0000000A  && Hexadecimal, with address display
#DEFINE CRYPT_STRING_HEXASCIIADDR           0x0000000B  && Hexadecimal, with ASCII character and address display
#DEFINE CRYPT_STRING_HEXRAW                 0x0000000C  && A raw hexadecimal string. Windows Server 2003 and Windows XP:  This value is not supported
#DEFINE CRYPT_STRING_STRICT                 0x20000000  && Enforce strict decoding of ASN.1 text formats. Some ASN.1 binary BLOBS can have the first few bytes of the BLOB incorrectly interpreted as Base64 text. In this case, the rest of the text is ignored. Use this flag to enforce complete decoding of the BLOB. Windows Server 2008, Windows Vista, Windows Server 2003 and Windows XP: This value is not supported
* In addition to the values above, one or more of the following values can be specified to modify the behavior of the function
#DEFINE CRYPT_STRING_NOCRLF                 0x40000000  && Do not append any new line characters to the encoded string. The default behavior is to use a carriage return/line feed (CR/LF) pair (0x0D/0x0A) to represent a new line. Windows Server 2003 and Windows XP:  This value is not supported
#DEFINE CRYPT_STRING_NOCR                   0x80000000  && Only use the line feed (LF) character (0x0A) for a new line. The default behavior is to use a CR/LF pair (0x0D/0x0A) to represent a new line
DECLARE INTEGER CryptBinaryToString IN Crypt32;  
      STRING @pbBinary,;
      LONG cbBinary,;
      LONG dwFlags,;
      STRING @pszString,;
      LONG @pcchString
DECLARE INTEGER GlobalAlloc IN kernel32;
    INTEGER wFlags,;
    INTEGER dwBytes
DECLARE INTEGER GlobalFree IN kernel32;
    INTEGER hMem
DECLARE INTEGER LocalAlloc IN kernel32;
    INTEGER uFlags,;
    INTEGER uBytes
DECLARE INTEGER LocalFree IN kernel32;
    INTEGER hMem
#DEFINE X509_ASN_ENCODING 		1  #DEFINE PKCS_7_ASN_ENCODING 		0x00010000
CLEAR=DeclarationsApi()lnHandleStore = 0lnHandleStore = CertOpenSystemStore(0, "MY")IF lnHandleStore=0	RETURN .F.ENDIFlpCertContext = 0lpCertContext=CryptUIDlgSelectCertificateFromStore(lnHandleStore, 0, null, null, 1, 0, null)IF lpCertContext=0	RETURN .F.ENDIF
lcOidValue = GetOidValues(lpCertContext, "2.5.29.31", "ARRAY")? "ARRAY: ", lcOidValue ??lcOidValue = GetOidValues(lpCertContext, "2.5.29.31", "URL")? "URL: ", STRTRAN(lcOidValue, "; ", CHR(10))???lcOidValue = GetOidValues(lpCertContext, "1.3.6.1.5.5.7.1.1", "ARRAY")? "ARRAY: ", lcOidValue ??lcOidValue = GetOidValues(lpCertContext, "1.3.6.1.5.5.7.1.1", "URL")? "URL: ", STRTRAN(lcOidValue, "; ", CHR(10))
PROCEDURE GetOidValues()	PARAMETERS tpCertContext AS Long, tcOidName AS String, tcRequest AS String	lcOidUrl = ""	nExtensions = 0	lcCertInfo = ""	IF VARTYPE(tpCertContext)="N"		lbCertInfo = SUBSTR(SYS(2600, tpCertContext, 20), 13, 4)		lcCertInfo = SYS(2600, CTOBIN(lbCertInfo, "4RS"), 112)	ENDIF	IF !EMPTY(lcCertInfo)		cBinaryOid  = SUBSTR(lcCertInfo ,105,8)		nExtensions = CTOBIN(SUBSTR(cBinaryOid,1,4),"4RS")		pRegExtens  = CTOBIN(SUBSTR(cBinaryOid,5,4),"4RS")		ENDIF	IF nExtensions>0		nLenReg=(nExtensions*16)		cBlobExtensions = SYS(2600, pRegExtens, nLenReg)		lpCertOid = CertFindExtension(tcOidName, nExtensions, cBlobExtensions)		IF lpCertOid>0			cOidBinary = SYS(2600, lpCertOid, 16)			cOidName   = SYS(2600, CTOBIN(SUBSTR(cOidBinary,1,4), "4RS"), 32)			nOidType   = CTOBIN(SUBSTR(cOidBinary,5,4), "4RS")			nOidLenght = CTOBIN(SUBSTR(cOidBinary,9,4), "4RS")			cOidValue  = SYS(2600, CTOBIN(SUBSTR(cOidBinary,13,4), "4RS"), nOidLenght)			lcStructInfo = GetCryptDecodeObjectExString(tcOidName, cOidValue)			IF !EMPTY(lcStructInfo)				lcOidUrl = GetCryptDecodeToString(tcOidName, cOidValue, lcStructInfo, tcRequest)			ENDIF		ENDIF	ENDIF	RETURN lcOidUrlENDPROC
PROCEDURE GetCryptDecodeObjectExString()	LPARAMETERS tc_Structure_Name AS String, tc_Structure_Binarys AS String	DECLARE LONG CryptDecodeObjectEx IN Crypt32;		LONG 	 dwCertEncodingType,;		STRING	 lpszStructType,;		STRING	 pbEncoded,;		LONG 	 cbEncoded,;		LONG 	 dwFlags,;		STRING	 pDecodePara,;		STRING	@pvStructInfo,;		LONG 	@pcbStructInfo
	dwCertEncodingType = BITOR(PKCS_7_ASN_ENCODING, X509_ASN_ENCODING)	lpszStructType = tc_Structure_Name	pbEncoded = tc_Structure_Binarys	cbEncoded = LEN(pbEncoded)	pbKeyBlob = ""	cbKeyBlob = 0	nResp=CryptDecodeObjectEx(dwCertEncodingType, @lpszStructType, pbEncoded, cbEncoded, 0, NULL, NULL, @cbKeyBlob)	IF cbKeyBlob>0		pbKeyBlob = SPACE(cbKeyBlob)		nResp=CryptDecodeObjectEx(dwCertEncodingType, @lpszStructType, pbEncoded, cbEncoded, 0, NULL, @pbKeyBlob, @cbKeyBlob)	ENDIF	RETURN pbKeyBlobENDPROC 
PROCEDURE GetCryptDecodeToString()	LPARAMETERS tcExtension AS String, tcExtValue AS String, pvStructInfo AS String, tcRequest AS String	lcReturnStr=""	DO CASE	CASE tcExtension="2.5.29.31"		*----- Puntos de distribución CRL, CRL_DIST_POINTS_INFO		lcReturnStr = GetStructure_CrlDistPoint(pvStructInfo, tcRequest)	CASE tcExtension=="1.3.6.1.5.5.7.1.1"		*----- Acceso a la infomración de entidad emisora (CERT_AUTHORITY_INFO_ACCESS)		lcReturnStr = GetStructure_CertAuthorityInfoAccess(pvStructInfo, tcRequest)	OTHERWISE	ENDCASE	RETURN lcReturnStrENDPROC
PROCEDURE GetStructure_CrlDistPoint()	LPARAMETERS tcCrlDistPoint AS String, tcRequest AS String	IF VARTYPE(tcCrlDistPoint)<>"C"		RETURN ""	ENDIF	IF EMPTY(tcCrlDistPoint)		RETURN ""	ENDIF	lcReturnURL = ""	lcReturnCRL = "CRL distribution points: " + "; "	*----- Número de elementos en la matriz de miembros rgDistPoint, CRL_DIST_POINTS_INFO(cDistPoint, rgDistPoint)	nDistPoint  = CTOBIN(SUBSTR(tcCrlDistPoint,1,4), "4RS")	rgDistPoint = CTOBIN(SUBSTR(tcCrlDistPoint,5,4), "4RS")	nIniChr = 5	nIniSub = (nDistPoint * 32)+5	FOR nDP = 1 TO nDistPoint		lcReturnCRL = lcReturnCRL + "[" + TRANSFORM(nDP) + "] CRL distribution point: " + "; "		*----- CRL_DIST_POINT structure - CERT_ALT_NAME_INFO structure - CRL_DIST_POINT_NAME 		nDistPointName = CTOBIN(SUBSTR(tcCrlDistPoint, nIniChr+4, 4), "4RS")		nAltEntry = CTOBIN(SUBSTR(tcCrlDistPoint, nIniChr+8, 4), "4RS")		rgAltEntry = STRCONV(SUBSTR(tcCrlDistPoint, nIniChr+12, 4), 15)		nReasonFlags = CTOBIN(SUBSTR(tcCrlDistPoint, nIniChr+16, 4), "4RS")		nCRLIssuer = CTOBIN(SUBSTR(tcCrlDistPoint, nIniChr+20, 4), "4RS")		nDistPointNameChoice = CTOBIN(SUBSTR(tcCrlDistPoint, nIniChr+24, 4), "4RS")		nFullName = CTOBIN(SUBSTR(tcCrlDistPoint, nIniChr+28, 4), "4RS")		cUnusedBits = CTOBIN(SUBSTR(tcCrlDistPoint, nIniChr+32, 4), "4RS")		DIMENSION aMatDistPoint(nAltEntry,2)		nIniChr = nIniChr + 32		FOR nAE = 1 TO nAltEntry			nAltNameChoice = CTOBIN(SUBSTR(tcCrlDistPoint, nIniSub+4, 4), "4RS")			nAltNameLenght = 0			IF nAltNameChoice=1 OR nAltNameChoice=5				nIniSub = nIniSub + 4				nAltNameLenght = CTOBIN(SUBSTR(tcCrlDistPoint, nIniSub+4, 4), "4RS")			ENDIF			cHexaAddres = STRCONV(SUBSTR(tcCrlDistPoint, nIniSub+8, 4),15)			cHexaSeparator = STRCONV(SUBSTR(tcCrlDistPoint, nIniSub+12, 4),15)			nIniSub = nIniSub + IIF(cHexaSeparator="20202020", 12, 8)			aMatDistPoint(nAE, 1)=nAltNameChoice			aMatDistPoint(nAE, 2)=nAltNameLenght		NEXT		IF nAltNameChoice=1 OR nAltNameChoice=5			nIniSub = nIniSub + 4		ENDIF		cHexaSeparator = STRCONV(SUBSTR(tcCrlDistPoint, nIniSub, 4),15)		DO WHILE cHexaSeparator="20202020"			nIniSub = nIniSub + 4			cHexaSeparator = STRCONV(SUBSTR(tcCrlDistPoint, nIniSub, 4),15)		ENDDO		cBlobCRL = SUBSTR(tcCrlDistPoint, nIniSub)		FOR nAE = 1 TO (ALEN(aMatDistPoint)/2)			DO CASE			CASE aMatDistPoint(nAE, 1)=7				cAltNameChoice = SUBSTR(cBlobCRL, 1, AT(CHR(0)+CHR(0), cBlobCRL)+2)				lcUrl = ALLTRIM(STRTRAN(cAltNameChoice, CHR(0), ""))				cNameCRL = "Name URL = ; " + lcUrl + "; "				lcReturnURL = lcReturnURL + IIF(".crl"$LOWER(lcUrl), lcUrl + "; ", "")			CASE aMatDistPoint(nAE, 1)=5				cAltNameChoice = SUBSTR(cBlobCRL, 1, aMatDistPoint(nAE, 2))				cNameCRL = "Directory Name: " + CHR(13) + GetStructure_CertNameBlob(null, cAltNameChoice) + "; "				ENDCASE			nLenght = LEN(cAltNameChoice) + IIF(4-MOD(LEN(cAltNameChoice),4)=4, 0, 4-MOD(LEN(cAltNameChoice),4)) + 1			cHexaSeparator = STRCONV(SUBSTR(cBlobCRL, nLenght, 4),15)			cBlobCRL = SUBSTR(cBlobCRL, nLenght + IIF(cHexaSeparator="20202020", 4, 0))			nIniSub = nIniSub + nLenght - IIF(cHexaSeparator="20202020", 1, 5)			lcReturnCRL = lcReturnCRL + cNameCRL + "; "		NEXT	NEXT	IF VARTYPE(tcRequest)=="C"		lcReturnCRL = IIF(tcRequest="URL", lcReturnURL, lcReturnCRL)	ENDIF	RETURN lcReturnCRLENDPROC
PROCEDURE GetStructure_CertAuthorityInfoAccess()	LPARAMETERS tcAuthorityInfo AS String, tcRequest AS String	*----- Acceso a la infomración de entidad emisora (CERT_AUTHORITY_INFO_ACCESS)	IF VARTYPE(tcAuthorityInfo)<>"C"		RETURN ""	ENDIF	IF EMPTY(tcAuthorityInfo)		RETURN ""	ENDIF	lcReturnAuthoInfo = ""	lcReturnURL = ""	*----- Numero de elementos en Array	nAccDescr = CTOBIN(SUBSTR(tcAuthorityInfo,1,4), "4RS")	*----- Puntero de Origen	nPoint00 = CTOBIN(SUBSTR(tcAuthorityInfo,5,4), "4RS")	nIniStr = 5	FOR nAIA = 1 TO nAccDescr		*----- Puntero de Origen		nPoint01 = CTOBIN(SUBSTR(tcAuthorityInfo, nIniStr+4, 4), "4RS")		*----- Tipo de Entrada		nPoint02 = CTOBIN(SUBSTR(tcAuthorityInfo, nIniStr+8, 4), "4RS")		*----- Puntero de Origen		nPoint03 = CTOBIN(SUBSTR(tcAuthorityInfo, nIniStr+12, 4), "4RS")		*----- Separador de región		nPoint04 = CTOBIN(SUBSTR(tcAuthorityInfo, nIniStr+16, 4), "4RS")		nIniStr = nIniStr + 16	NEXT	nIniStr = nIniStr + 4	cbAutInfo = SUBSTR(tcAuthorityInfo, nIniStr)	lcReturnAuthoInfo = "Access to the issuer entity information: ; "	FOR DPN = 1 TO nAccDescr		lcReturnAuthoInfo = lcReturnAuthoInfo + "[ " + TRANSFORM(DPN) + "] Access to authority information " + "; "		nEndBit = AT(REPLICATE(CHR(0),3), cbAutInfo)		IF nEndBit==0			lcAutInfo = ALLTRIM(SUBSTR(cbAutInfo, 1))			cbAutInfo = SUBSTR(cbAutInfo, LEN(lcAutInfo))		ELSE			lcAutInfo = ALLTRIM(SUBSTR(cbAutInfo, 1, nEndBit-1))					cbAutInfo = SUBSTR(cbAutInfo, AT(lcAutInfo, cbAutInfo) + LEN(lcAutInfo))		ENDIF		IF !EMPTY(lcAutInfo)					lcMetAcce = "Access method = " + SUBSTR(lcAutInfo, 1, AT(CHR(0), lcAutInfo)-1) + "; "			lcAltName = "Alternative name: ; "			lcUrlStr  = STRCONV(ALLTRIM(SUBSTR(lcAutInfo, AT(CHR(0), lcAutInfo)+1)) + CHR(0), 6)			lcAltName = lcAltName + + "URL = " + lcUrlStr			lcAutInfo = lcMetAcce + lcAltName +"; "			lcReturnURL = lcReturnURL + lcUrlStr + "; "		ENDIF		lcReturnAuthoInfo = lcReturnAuthoInfo + lcAutInfo + "; "		cbAutInfo = GetCleanStringInit(cbAutInfo)	NEXT	IF VARTYPE(tcRequest)=="C"		lcReturnAuthoInfo = IIF(tcRequest="URL", lcReturnURL, lcReturnAuthoInfo)	ENDIF	RETURN lcReturnAuthoInfoENDPROC
PROCEDURE GetStructure_CertNameBlob()	LPARAMETERS tcNameBlob AS String, tcStringBinary AS String	lcIssuerStr=""	IF VARTYPE(tcNameBlob)="C"		lcIssuerByte = 0h + tcNameBlob		pbCert_Name_Blob = HeapAlloc(GetProcessHeap(), 0, LEN(lcIssuerByte))		RtlMoveMemory(pbCert_Name_Blob, @lcIssuerByte, LEN(lcIssuerByte))		lcIssuerStr = GetCertNameToString(pbCERT_NAME_BLOB)		HeapFree(GetProcessHeap(), 0, pbCert_Name_Blob)	ENDIF	IF EMPTY(lcIssuerStr) AND !EMPTY(tcStringBinary)		cbData = LEN(tcStringBinary)	  	lbIssuer = SUBSTR(tcStringBinary, 1, cbData)		pbData = HeapAlloc(GetProcessHeap(), 0, cbData)		RtlMoveMemory(pbData, @lbIssuer, cbData)		lcIssuerByte = 0h + BINTOC(cbData,"4RS") + BINTOC(pbData, "4RS")		pbCert_Name_Blob = HeapAlloc(GetProcessHeap(), 0, LEN(lcIssuerByte))		RtlMoveMemory(pbCert_Name_Blob, @lcIssuerByte, LEN(lcIssuerByte))		lcIssuerStr = GetCertNameToString(pbCERT_NAME_BLOB)		HeapFree(GetProcessHeap(), 0, pbCert_Name_Blob)		HeapFree(GetProcessHeap(), 0, pbData)	ENDIF		RETURN lcIssuerStrENDPROC
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     = 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 GetCleanStringInit()	LPARAMETERS tcStringToCleanInit AS String	lnChrIni = 1	FOR nl = 1 TO LEN(tcStringToCleanInit)		cChr = STRCONV(SUBSTR(tcStringToCleanInit, nl, 1),15)		IF cChr<>"00" AND cChr<>"20" AND cChr<>"1A"			lnChrIni = nl			EXIT		ENDIF	NEXT	RETURN SUBSTR(tcStringToCleanInit, lnChrIni)ENDPROC
PROCEDURE DeclarationsApi()	DECLARE LONG CertOpenSystemStore IN Crypt32;		STRING	 hprov,;		STRING	 szSubsystemProtocol
	DECLARE LONG CertCloseStore IN Crypt32;		LONG 	 hCertStore,;		LONG	 dwFlags		DECLARE LONG CryptUIDlgSelectCertificateFromStore IN Cryptui;		LONG 	 hCertStore,;		LONG 	 hWnd, ;		STRING 	@pwszTitle, ;		STRING	@pwszDisplayString, ;		LONG	 dwDontUseColumn, ;		LONG 	 dwFlags,;		STRING	 pvReserved		DECLARE LONG CertFindExtension IN Crypt32;		STRING	 pszObjId,;		LONG 	 nExtensions,;		STRING	 rgExtensions		DECLARE LONG CertNameToStr IN Crypt32;		LONG	 dwCertEncodingType, ;		LONG	 pName, ;		LONG	 dwStrType, ;		STRING	@psz, ;		LONG	 csz		DECLARE LONG GetProcessHeap IN Kernel32
	DECLARE LONG HeapAlloc IN Kernel32;		LONG 	hHeap,;		LONG 	dwFlags,;		LONG 	dwBytes
	DECLARE LONG HeapFree IN Kernel32;		LONG 	hHeap,;		LONG 	dwFlags,;		LONG 	lpMem
	DECLARE RtlMoveMemory IN Kernel32;		LONG	Destination,;		STRING @Source,;		LONG	Length
ENDPROC
Aquí un par de capturas del código ejecutándose y devolviendo resultado. Use dos certificados, uno nacional del Banco Central del Ecuador emitido para usuario final (MY), y otro de Microsoft de tipo CA.
Saludos,
Fernando

 
#DEFINE URL_OID_CERTIFICATE_CRL_DIST_POINT		2#DEFINE URL_OID_CERTIFICATE_CRL_DIST_POINT_AND_OCSP 	11#DEFINE CRYPT_GET_URL_FROM_EXTENSION 			2
CLEAR=DeclarationsApi()lnHandleStore = 0lnHandleStore = CertOpenSystemStore(0, "MY")IF lnHandleStore=0	RETURN .F.ENDIFlpCertContext = 0lpCertContext=CryptUIDlgSelectCertificateFromStore(lnHandleStore, 0, null, null, 1, 0, null)IF lpCertContext=0	RETURN .F.ENDIF
pszUrlOid = URL_OID_CERTIFICATE_CRL_DIST_POINT_AND_OCSP pvPara = lpCertContextdwFlags = CRYPT_GET_URL_FROM_EXTENSIONpUrlArray = NULLpcbUrlArray = 0pUrlInfo = NULLpcbUrlInfo = 0pvReserved = 0nResp = CryptGetObjectUrl(@pszUrlOid, pvPara, dwFlags, pUrlArray, @pcbUrlArray, pUrlInfo, @pcbUrlInfo, pvReserved)IF nResp>0	pUrlArray = SPACE(pcbUrlArray)	pUrlInfo = SPACE(pcbUrlInfo)	CryptGetObjectUrl(@pszUrlOid, pvPara, dwFlags, @pUrlArray, @pcbUrlArray, @pUrlInfo, @pcbUrlInfo, pvReserved)	? "URL ARRAY: ", STRCONV(pUrlArray,6)	?	? "URL INFO: ", pUrlInfoENDIFCertFreeCertificateContext(lpCertContext)CertCloseStore(lnHandleStore, 0)
PROCEDURE DeclarationsApi()	DECLARE LONG CertOpenSystemStore IN Crypt32;		STRING	 hprov,;		STRING	 szSubsystemProtocol
	DECLARE LONG CertCloseStore IN Crypt32;		LONG 	 hCertStore,;		LONG	 dwFlags		DECLARE LONG CertFreeCertificateContext IN Crypt32;		LONG 	 pCertContext		DECLARE LONG CryptUIDlgSelectCertificateFromStore IN Cryptui;		LONG 	 hCertStore,;		LONG 	 hWnd, ;		STRING 	@pwszTitle, ;		STRING	@pwszDisplayString, ;		LONG	 dwDontUseColumn, ;		LONG 	 dwFlags,;		STRING	 pvReserved		DECLARE LONG CryptGetObjectUrl IN Cryptnet;		LONG	 pszUrlOid,;		LONG	 pvPara,;		LONG	 dwFlags,;		STRING	@pUrlArray,;		LONG	@pcbUrlArray,;		STRING	@pUrlInfo,;		LONG	@pcbUrlInfo,;		LONG	 pvReserved
ENDPROC
Hola Roman. Si, luego de pasar por java y csharp, decidí desarrollar una librería 100% fox, que al igual que csharp y C++, use al máximo CNG de Windows, para el tema criptográfico. Esto abarca todas las necesidades de firmado de Xml, cifrado y descifrado de datos (asimétrico, simétrico), codificación y decodificación de certificados, importación y exportación de certificados, pares de claves, en diversos formatos (cer,pem,key,pfx,p12,) etc. Uso Long (64 bits) en lugar de Integer (32 bits) por dos razones, mejor rendimiento, y simplificar la asignación de parámetros de las funciones. Usando Long y String no te haces lio y da buenos resultados. Mis aplicaciones las tengo instalada en clientes que usan desde Windows 7 32 bits SP1 hasta Windows 10 64 bits, todos trabajan sin problemas. En este momento estoy trabajando en cifrado RSA, AES, DES, 3DES, estoy en fase de pruebas. Has usado las funciones de BCrypt para hacer cifrados AES? necesito ayuda para cifrar archivos grandes. Mi código aún no es estable en esa parte.
DECLARE LONG CertCreateCertificateContext IN Crypt32;	LONG	 dwCertEncodingType,;	STRING	 pbCertEncoded,;	LONG	 cbCertEncoded
DECLARE LONG CertFreeCertificateContext IN Crypt32;	LONG 	 pCertContext
#DEFINE X509_ASN_ENCODING    1#DEFINE PKCS_7_ASN_ENCODING  0x00010000
TEXT TO lcCertB64 TEXTMERGE ADDITIVE NOSHOWMIICLDCCAZWgAwIBAgIQRjRrx4AAVrwR024uxBCzsDANBgkqhkiG9w0BAQUFADASMRAwDgYDVQQD EwdDYXJsUlNBMB4XDTk5MDkxOTAxMDg0N1oXDTM5MTIzMTIzNTk1OVowEzERMA8GA1UEAxMIQWxp Y2VSU0EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOCJczmN2PX16Id2OX9OsAW7U4PeD7er 3H3HdSkNBS5tEt+mhibU0m+qWCn8l+z6glEPMIC+sVCeRkTxLLvYMs/GaG8H2bBgrL7uNAlqE/X3 BQWT3166NVbZYf8Zf8mB5vhs6odAcO+sbSx0ny36VTq5mXcCpkhSjE7zVzhXdFdfAgMBAAGjgYEw fzAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIGwDAfBgNVHSMEGDAWgBTp4JAnrHggeprTTPJC N04irp44uzAdBgNVHQ4EFgQUd9K00bdMioqjzkWdzuw8oDrj/1AwHwYDVR0RBBgwFoEUQWxpY2VS U0FAZXhhbXBsZS5jb20wDQYJKoZIhvcNAQEFBQADgYEAPnBHqEjME1iPylFxa042GF0EfoCxjU3M yqOPzH1WyLzPbrMcWakgqgWBqE4lradwFHUv9ceb0Q7pY9Jkt8ZmbnMhVN/0uiVdfUnTlGsiNnRz uErsL2Tt0z3Sp0LF6DeKtNufZ+S9n/n+dO/q+e5jatg/SyUJtdgadq7rm9tJsCI=ENDTEXT
lnCertContext = GetCertCreateCertificateContext(lcCertB64)IF lnCertContext>0	? "Aquí esta su puntero al contexto del certificado: " + TRANSFORM(lnCertContext)	*----- No olvide liberar el puntero luego de utilizar el certificado	CertFreeCertificateContext(lnCertContext)ENDIF
*---- Función que devuelve un puntero al contexto de un certificado codificado, pasar la cadena en b64 please.PROCEDURE GetCertCreateCertificateContext()	PARAMETERS tcCertEncodedB64 AS String	pCertContext = 0	IF !EMPTY(tcCertEncodedB64)		dwCertEncodingType = BITOR(PKCS_7_ASN_ENCODING, X509_ASN_ENCODING)		pbCertEncoded = STRCONV(ALLTRIM(STRTRAN(tcCertEncodedB64, CHR(0), "")),14)		cbCertEncoded = LEN(pbCertEncoded)		pCertContext = CertCreateCertificateContext(dwCertEncodingType, pbCertEncoded, cbCertEncoded)	ENDIF	RETURN pCertContextENDPROC
#DEFINE URL_OID_CERTIFICATE_CRL_DIST_POINT        2
#DEFINE URL_OID_CERTIFICATE_CRL_DIST_POINT_AND_OCSP     11
#DEFINE CRYPT_GET_URL_FROM_EXTENSION             2
#DEFINE X509_ASN_ENCODING    1
#DEFINE PKCS_7_ASN_ENCODING  0x00010000
#DEFINE CRYPT_STRING_BASE64  0x00000001  && Base64, without headers.
#DEFINE CERT_SHA1_HASH_PROP_ID 3         && Returns the SHA1 hash.
#DEFINE CRYPT_STRING_HEX     0x00000004  && Hexadecimal only format.
* https://cpdn.cryptopro.ru/content/csp40/html/group___pro_c_s_p_ex_DP8.html
#DEFINE szOID_CP_GOST_R3411_R3410EL            "1.2.643.2.2.3"        && Algorythm GOST R 34.10-2001
#DEFINE szOID_CP_GOST_R3411_12_256_R3410    "1.2.643.7.1.1.3.2"    && Algorythm GOST R 34.10-2012, 256 bit
#DEFINE szOID_CP_GOST_R3411_12_512_R3410    "1.2.643.7.1.1.3.3"    && Algorythm GOST R 34.10-2012, 512 bit
DECLARE INTEGER CryptStringToBinary IN crypt32;
    STRING @pszString,;
    LONG    cchString,;
    LONG    dwFlags,;
    STRING @pbBinary,;
    LONG   @pcbBinary,;
    LONG    pdwSkip,;
    LONG    pdwFlags
DECLARE LONG CertCreateCertificateContext IN Crypt32;
    LONG    dwCertEncodingType,;
    STRING    pbCertEncoded,;
    LONG    cbCertEncoded
DECLARE LONG CertFreeCertificateContext IN Crypt32;
    LONG     pCertContext
DECLARE LONG CertOpenSystemStore IN Crypt32;
    STRING    hprov,;
    STRING    szSubsystemProtocol
DECLARE LONG CertCloseStore IN Crypt32;
    LONG     hCertStore,;
    LONG    dwFlags
    
DECLARE LONG CertFreeCertificateContext IN Crypt32;
    LONG     pCertContext
    
DECLARE LONG CryptUIDlgSelectCertificateFromStore IN Cryptui;
    LONG     hCertStore,;
    LONG     hWnd, ;
    STRING     @pwszTitle, ;
    STRING    @pwszDisplayString, ;
    LONG    dwDontUseColumn, ;
    LONG     dwFlags,;
    STRING    pvReserved
    
DECLARE LONG CryptGetObjectUrl IN Cryptnet;
    LONG    pszUrlOid,;
    LONG    pvPara,;
    LONG    dwFlags,;
    STRING    @pUrlArray,;
    LONG    @pcbUrlArray,;
    STRING    @pUrlInfo,;
    LONG    @pcbUrlInfo,;
    LONG    pvReserved
DECLARE INTEGER CryptBinaryToString IN Crypt32;  
      STRING @pbBinary,;
      LONG cbBinary,;
      LONG dwFlags,;
      STRING @pszString,;
      LONG @pcchString
DECLARE INTEGER CertGetCertificateContextProperty IN crypt32;
    INTEGER  pCertContext,;
    LONG     dwPropId,;
    INTEGER  pvPara,;
    LONG   @ pcbData
DECLARE INTEGER CertOIDToAlgId IN crypt32;
    STRING  @ pszObjId
DECLARE INTEGER LocalAlloc IN kernel32;
    INTEGER uFlags,;
    INTEGER uBytes
DECLARE INTEGER LocalFree IN kernel32;
    INTEGER hMem
DECLARE INTEGER GetLastError ;
    IN WIN32API AS GetLastError
    
DECLARE INTEGER lstrlen IN kernel32;
    INTEGER lpString
TEXT TO lcCertB64 TEXTMERGE ADDITIVE NOSHOW
MIIIjTCCCDygAwIBAgIRAZe4dS0WrMSA6BEUsUB9qOYwCAYGKoUDAgIDMIHuMR4wHAYJKoZIhvcN
AQkBFg91Y2ZvbXNAZmZvbXMucnUxGDAWBgUqhQNkARINMTAyNzczOTcxMjg1NzEaMBgGCCqFAwOB
AwEBEgwwMDc3MjcwMzIzODIxCzAJBgNVBAYTAlJVMRgwFgYDVQQIDA83NyDQnNC+0YHQutCy0LAx
FTATBgNVBAcMDNCc0L7RgdC60LLQsDEyMDAGA1UECQwp0J3QvtCy0L7RgdC70L7QsdC+0LTRgdC6
0LDRjyDRg9C7Liwg0LQuMzcxETAPBgNVBAoMCNCk0J7QnNChMREwDwYDVQQDDAjQpNCe0JzQoTAe
Fw0xODA5MDUxMzU2MzdaFw0xOTEyMDUxNDA2MzdaMIIBlzEaMBgGCCqFAwOBAwEBEgwwMDY4MzEw
MDAyNjUxGDAWBgUqhQNkARINMTAyNjgwMTE1ODgxMjEbMBkGCSqGSIb3DQEJARYMdGZvbXNAdG1i
LnJ1MQswCQYDVQQGEwJSVTEsMCoGA1UECAwj0KLQsNC80LHQvtCy0YHQutCw0Y8g0L7QsdC70LDR
gdGC0YwxFTATBgNVBAcMDNCi0LDQvNCx0L7QsjErMCkGA1UECQwi0YPQuy4g0JrQvtC80LzRg9C9
0LDQu9GM0L3QsNGPLCAxODE3MDUGA1UECgwu0KLQpNCe0JzQoSDQotCw0LzQsdC+0LLRgdC60L7Q
uSDQvtCx0LvQsNGB0YLQuDFJMEcGA1UECwxA0J7RgtC00LXQuyDQuNC90YTQvtGA0LzQsNGG0LjQ
vtC90L3QvtCz0L4g0L7QsdC10YHQv9C10YfQtdC90LjRjzE/MD0GA1UEAww20JDQmNChINCf0KMg
0J7QnNChINCi0LDQvNCx0L7QstGB0LrQvtC5INC+0LHQu9Cw0YHRgtC4MGMwHAYGKoUDAgITMBIG
ByqFAwICJAAGByqFAwICHgEDQwAEQJClKMdUgM0jjkBUJzW+mGmTF26Gmgcvk1LbPwllgCg5FxzN
HqF7k3CmIawgTzWyMhIsdCCQHSu9HTdGhZ5NtZejggUFMIIFATAOBgNVHQ8BAf8EBAMCA/gwHQYD
VR0OBBYEFM9X1n2LqtthzFr7YScF5TyaY5shMDMGCSsGAQQBgjcVBwQmMCQGHCqFAwICMgEJyZlh
lao6hJGWT4WgsXOD13KDtj4CAQECAQAwggGGBgNVHSMEggF9MIIBeYAUFbdV5IiKzBbjPrBbebba
8+cNX/ehggFSpIIBTjCCAUoxHjAcBgkqhkiG9w0BCQEWD2RpdEBtaW5zdnlhei5ydTELMAkGA1UE
BhMCUlUxHDAaBgNVBAgMEzc3INCzLiDQnNC+0YHQutCy0LAxFTATBgNVBAcMDNCc0L7RgdC60LLQ
sDE/MD0GA1UECQw2MTI1Mzc1INCzLiDQnNC+0YHQutCy0LAsINGD0LsuINCi0LLQtdGA0YHQutCw
0Y8sINC0LiA3MSwwKgYDVQQKDCPQnNC40L3QutC+0LzRgdCy0Y/Qt9GMINCg0L7RgdGB0LjQuDEY
MBYGBSqFA2QBEg0xMDQ3NzAyMDI2NzAxMRowGAYIKoUDA4EDAQESDDAwNzcxMDQ3NDM3NTFBMD8G
A1UEAww40JPQvtC70L7QstC90L7QuSDRg9C00L7RgdGC0L7QstC10YDRj9GO0YnQuNC5INGG0LXQ
vdGC0YCCCwD0Z+EyAAAAAAJqMCcGA1UdJQQgMB4GCCsGAQUFBwMCBggrBgEFBQcDBAYIKwYBBQUH
AwEwMwYJKwYBBAGCNxUKBCYwJDAKBggrBgEFBQcDAjAKBggrBgEFBQcDBDAKBggrBgEFBQcDATAd
BgNVHSAEFjAUMAgGBiqFA2RxATAIBgYqhQNkcQIwggFDBgUqhQNkcASCATgwggE0DDTQodCa0JfQ
mCAi0JrRgNC40L/RgtC+0J/RgNC+IENTUCIgKNCy0LXRgNGB0LjRjyA0LjApDFrQn9CQ0JogItCj
0LTQvtGB0YLQvtCy0LXRgNGP0Y7RidC40Lkg0YbQtdC90YLRgCAi0JrRgNC40L/RgtC+0J/RgNC+
INCj0KYiINCy0LXRgNGB0LjQuCAyLjAMT9Ch0LXRgNGC0LjRhNC40LrQsNGCINGB0L7QvtGC0LLQ
tdGC0YHRgtCy0LjRjyDihJYg0KHQpC8xMjQtMjg2NCDQvtGCIDIwLjAzLjIwMTYMT9Ch0LXRgNGC
0LjRhNC40LrQsNGCINGB0L7QvtGC0LLQtdGC0YHRgtCy0LjRjyDihJYg0KHQpC8xMjgtMjk4MyDQ
vtGCIDE4LjExLjIwMTYwLAYFKoUDZG8EIwwh0KHQmtCX0JggItCa0YDQuNC/0YLQvtCf0YDQviBD
U1AiMFgGA1UdHwRRME8wTaBLoEmGR2h0dHA6Ly91Y2ZvbXMuZmZvbXMucnUvY2RwLzE1Yjc1NWU0
ODg4YWNjMTZlMzNlYjA1Yjc5YjZkYWYzZTcwZDVmZjcuY3JsMIGXBggrBgEFBQcBAQSBijCBhzAw
BggrBgEFBQcwAYYkaHR0cDovL3VjZm9tcy5mZm9tcy5ydS9vY3NwL29jc3Auc3JmMFMGCCsGAQUF
BzAChkdodHRwOi8vdWNmb21zLmZmb21zLnJ1L2FpYS8xNWI3NTVlNDg4OGFjYzE2ZTMzZWIwNWI3
OWI2ZGFmM2U3MGQ1ZmY3LmNydDArBgNVHRAEJDAigA8yMDE4MDkwNTEzNTYzN1qBDzIwMTkwOTA1
MTM1NjM3WjAIBgYqhQMCAgMDQQB5goLR7QDXU6ZtYraFLA8KpEPBlDOXhgRxsZwRZNvyfoshn9+Z
KOYIaFyw5lc7/PwRgyq/XmAf3iZ7ObUdeN7i
ENDTEXT
m.lcCertB64 = StrTran(m.lcCertB64, '
', '')  && Very actual in some cases!
lnCertContext = 0
If !Empty(lcCertB64)
    nBufsize=0  
    = CryptStringToBinary(@lcCertB64, LEN(m.lcCertB64), CRYPT_STRING_BASE64, NULL, @nBufsize, 0,0)
    pbCertEncoded = REPLICATE(CHR(0), m.nBufsize)
    nResp = CryptStringToBinary(@lcCertB64, LEN(m.lcCertB64), CRYPT_STRING_BASE64, @pbCertEncoded, @nBufsize, 0,0)
    dwCertEncodingType = BITOR(PKCS_7_ASN_ENCODING, X509_ASN_ENCODING)
    cbCertEncoded = LEN(pbCertEncoded)
    lnCertContext = CertCreateCertificateContext(dwCertEncodingType, pbCertEncoded, cbCertEncoded)
EndIf
If lnCertContext=0
    =MessageBox("lnCertContext = 0",64,"Error")
    Return .F.
EndIf
lcStr = ""
lcCERT_CONTEXT = SYS(2600, lnCertContext, 20)
pCertInfo = CTOBIN(SubStr(lcCERT_CONTEXT, 13, 4), "4RS")
lcCERT_INFO = SYS(2600, pCertInfo, 112)
pAlgorithmOID = CTOBIN(SubStr(lcCERT_INFO, 13, 4), "4RS")
lnStrLen = lstrlen(pAlgorithmOID)
lcAlgorithmOID = SYS(2600, pAlgorithmOID, lnStrLen)
Do Case
    Case lcAlgorithmOID == szOID_CP_GOST_R3411_R3410EL  && "1.2.643.2.2.3"
        lcStr = "Algorythm GOST R 34.10-2001"
    Case lcAlgorithmOID == szOID_CP_GOST_R3411_12_256_R3410     && "1.2.643.7.1.1.3.2"
        lcStr = "Algorythm GOST R 34.10-2012, 256 bit"
    Case lcAlgorithmOID == szOID_CP_GOST_R3411_12_512_R3410  &&    "1.2.643.7.1.1.3.3"
        lcStr = "Algorythm GOST R 34.10-2012, 512 bit"
EndCase
lnCALG_GR3411 = CertOIDToAlgId(@lcAlgorithmOID)  && CALG_GR3411 = 32798, CALG_GR3411_2012_256 = 32801, CALG_GR3411_2012_512 = 32802
=MessageBox(lcStr,64,"Algorythm")
* Get the certificate thumbprint (SHA1 hash)
m.lGetProperty = .T.
m.pcbData = 0
If !CertGetCertificateContextProperty(lnCertContext, CERT_SHA1_HASH_PROP_ID, 0, @m.pcbData) = 0
    pvData = LocalAlloc(0x0040, m.pcbData)
    If !CertGetCertificateContextProperty(lnCertContext, CERT_SHA1_HASH_PROP_ID, pvData, @m.pcbData) = 0
        m.lcHashSha1 = SYS(2600, pvData, m.pcbData)
    Else
        lnResultCode = GetLastError()
        lcErr = "Error getting thumbprint"
        =MessageBox(lcErr,16,"Error: " + Transform(lnResultCode,"@0"))
        m.lGetProperty = .F.
    EndIf
Else
    lnResultCode = GetLastError()
    lcErr = "Error getting thumbprint"
    =MessageBox(lcErr,16,"Error: " + Transform(lnResultCode,"@0"))
    m.lGetProperty = .F.
EndIf
If m.lGetProperty
      nBufSize=0  
      = CryptBinaryToString(@m.lcHashSha1, Len(m.lcHashSha1), CRYPT_STRING_HEX, NULL, @nBufSize)
      lcThumbprint = REPLICATE(CHR(0), m.nBufSize)
      If !CryptBinaryToString(@m.lcHashSha1, Len(m.lcHashSha1), CRYPT_STRING_HEX, @lcThumbprint, @nBufSize) = 0
        lcThumbprint = StrTran(StrTran(StrTran(lcThumbprint,Chr(13),""),Chr(10),Chr(32)),Chr(32)+Chr(32),Chr(32))
        If Right(lcThumbprint, 1) == Chr(0)
            lcThumbprint = Left(lcThumbprint, Len(lcThumbprint)-1)
        EndIf
        lcThumbprint = Upper(AllT(lcThumbprint))
        lcStr = "Certificate thumbprint: " + AllT(lcThumbprint)
        =MessageBox(AllT(lcThumbprint),64,"Certificate Sha1 hash")
    Else
        =MessageBox("Can't get the certificate thumbprint!",16,"Error")
    EndIf
EndIf
If !Empty(pvData)
    LocalFree(pvData)
EndIf
pszUrlOid = URL_OID_CERTIFICATE_CRL_DIST_POINT  && URL_OID_CERTIFICATE_CRL_DIST_POINT_AND_OCSP 
pvPara = lnCertContext
dwFlags = CRYPT_GET_URL_FROM_EXTENSION
pUrlArray = NULL
pcbUrlArray = 0
pUrlInfo = NULL
pcbUrlInfo = 0
pvReserved = 0
nResp = CryptGetObjectUrl(@pszUrlOid, pvPara, dwFlags, pUrlArray, @pcbUrlArray, pUrlInfo, @pcbUrlInfo, pvReserved)
If nResp>0
    pUrlArray = Space(pcbUrlArray)
    pUrlInfo = Space(pcbUrlInfo)
    If !CryptGetObjectUrl(@pszUrlOid, pvPara, dwFlags, @pUrlArray, @pcbUrlArray, @pUrlInfo, @pcbUrlInfo, pvReserved) = 0
        lcCRLArray = STRCONV(pUrlArray,6)
        lcStartCDP = AT("http://", lcCRLArray)
        lnI = 1
        Do While lcStartCDP > 0
            lcCRLArray = SubStr(lcCRLArray, lcStartCDP)
            lcCDP = Left(lcCRLArray, AT(Chr(0), lcCRLArray)-1)
            lcCRLArray = SubStr(lcCRLArray, AT(Chr(0), lcCRLArray)+1)
            lcStartCDP = AT("http://", lcCRLArray)
            lcStr = "[" + AllT(Str(lnI)) + "] CDP point: " + AllT(lcCDP)
            =MessageBox(lcStr,64,"Info")
            lnI = lnI + 1
        EndDo
    Else
        =MessageBox("Can't get CRL List URL!",16,"Error")
    EndIf
EndIf
CertFreeCertificateContext(lnCertContext)
* CryptQueryObject types and flags
#DEFINE CERT_QUERY_OBJECT_FILE                 1
#DEFINE CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED     0x400  && 1024 OR (1 << 10) OR (1 shl 10) OR 2 ^ 10
#DEFINE CERT_QUERY_FORMAT_FLAG_BINARY             0x002  && 2 OR (1 << 1) OR (1 shl 1) OR 2 ^ 1
DECLARE LONG CryptQueryObject IN Crypt32;
      LONG dwObjectType,;
      STRING pvObject,;                               && I TRY THIS PARAMETER LONG AND STRING, BUT DON'T WORK...
      LONG dwExpectedContentTypeFlags,;
      LONG dwExpectedFormatTypeFlags,;
      LONG dwFlags,;
      LONG @pdwMsgAndCertEncodingType,;
      LONG @pdwContentType,;
      LONG @pdwFormatType,;
      LONG @phCertStore,;
      LONG @phMsg,;
      LONG @ppvContext
lcCRLFile = "C:\MyFolder\CRL\163d4290bf0a9c881766b9264f928470a3d705db.crl"
If File(lcCRLFile)
    dwObjectType = CERT_QUERY_OBJECT_FILE    && LONG dwObjectType
    pvData = StrConv("file://" + lcCRLFile + CHR(0), 5)  && STRING pvData
    dwExpectedContentTypeFlags = CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED
    dwExpectedFormatTypeFlags = CERT_QUERY_FORMAT_FLAG_BINARY
    dwFlags = 0
    dwEncoding = 0
    dwContentType = 0
    dwFormatType = 0
    hStore = 0
    hMsg = 0
    * Get message handle and store handle from the signed file (CRL)
    fResult = CryptQueryObject(dwObjectType, pvData, dwExpectedContentTypeFlags, dwExpectedFormatTypeFlags, dwFlags, @dwEncoding, @dwContentType, @dwFormatType, @hStore, @hMsg, 0)
    If Empty(fResult)
        lnResultCode = GetLastError()
        If lnResultCode = -2146885623  && 0x80092009
            lcErr = "Cannot find the requested object."  && CRYPT_E_NO_MATCH. May be file is not signed? No!
        Else
            lcErr = "CryptQueryObject error"
        EndIf
        =MessageBox(lcErr,16,"Error: " + Transform(lnResultCode,"@0"))
    EndIf
EndIf



#DEFINE CERT_QUERY_OBJECT_FILE                 		1#DEFINE CERT_QUERY_CONTENT_CRL 				3#DEFINE CERT_QUERY_FORMAT_BINARY			1#DEFINE CERT_QUERY_CONTENT_PKCS7_SIGNED_EMBED 		10#DEFINE CERT_QUERY_CONTENT_FLAG_CRL			BITLSHIFT(1,CERT_QUERY_CONTENT_CRL)#DEFINE CERT_QUERY_FORMAT_FLAG_BINARY			BITLSHIFT(1,CERT_QUERY_FORMAT_BINARY)#DEFINE CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED	BITLSHIFT(1,CERT_QUERY_CONTENT_PKCS7_SIGNED_EMBED)=DeclarationsAPIS()SET STEP ONlcCRLFile = "C:\PROYECTOS\VFPXADES\CERTIFICATES\CRLS\15b755e4888acc16e33eb05b79b6daf3e70d5ff7.crl"IF FILE(lcCRLFile)	dwObjectType = CERT_QUERY_OBJECT_FILE    && LONG dwObjectType 	pvData = STRCONV(lcCRLFile + CHR(0), 5)  && STRING pvData	dwExpectedContentTypeFlags = CERT_QUERY_CONTENT_FLAG_CRL	dwExpectedFormatTypeFlags = CERT_QUERY_FORMAT_FLAG_BINARY	dwFlags = 0	dwEncoding = 0	dwContentType = 0	dwFormatType = 0	hStore = 0	hMsg = 0	ppvContext = 0	*----- Get message handle and store handle from the signed file (CRL)	nResult = CryptQueryObject(dwObjectType, pvData, dwExpectedContentTypeFlags, dwExpectedFormatTypeFlags, dwFlags, @dwEncoding, @dwContentType, @dwFormatType, @hStore, @hMsg, @ppvContext)	IF nResult==0		lnResultCode = GetLastError()		IF lnResultCode = -2146885623  && 0x80092009			lcErr = "Cannot find the requested object."  && CRYPT_E_NO_MATCH. May be file is not signed? No!		ELSE			lcErr = "CryptQueryObject error"		ENDIF		=MESSAGEBOX(lcErr,16,"Error: " + TRANSFORM(lnResultCode,"@0"))	ELSE		? "dwEncoding: ", dwEncoding		? "dwContentType: ", dwContentType		? "dwFormatType: ", dwFormatType		? "hStore: ", hStore		? "hMsg: ", hMsg		? "ppvContext: ", ppvContext	ENDIFENDIF
PROCEDURE DeclarationsAPIS()	DECLARE LONG CryptQueryObject IN Crypt32;		LONG	 dwObjectType,;		STRING	 pvObject,;		LONG	 dwExpectedContentTypeFlags,;		LONG	 dwExpectedFormatTypeFlags,;		LONG	 dwFlags,;		LONG	 @pdwMsgAndCertEncodingType,;		LONG	 @pdwContentType,;		LONG	 @pdwFormatType,;		LONG	 @phCertStore,;		LONG	 @phMsg,;		LONG	 @ppvContextENDPROC
#DEFINE CRYPT_STRING_BASE64HEADER			0#DEFINE CRYPT_STRING_BASE64 				1#DEFINE CRYPT_STRING_BINARY				2#DEFINE CRYPT_STRING_ANY				7
#DEFINE URL_OID_CERTIFICATE_ISSUER			1#DEFINE URL_OID_CERTIFICATE_CRL_DIST_POINT		2#DEFINE	URL_OID_CTL_ISSUER				3#DEFINE	URL_OID_CTL_NEXT_UPDATE				4#DEFINE	URL_OID_CRL_ISSUER				5#DEFINE	URL_OID_CERTIFICATE_FRESHEST_CRL		6#DEFINE	URL_OID_CRL_FRESHEST_CRL			7#DEFINE	URL_OID_CROSS_CERT_DIST_POINT			8#DEFINE URL_OID_CERTIFICATE_OCSP			9#DEFINE	URL_OID_CERTIFICATE_OCSP_AND_CRL_DIST_POINT	10#DEFINE URL_OID_CERTIFICATE_CRL_DIST_POINT_AND_OCSP 	11#DEFINE	URL_OID_CROSS_CERT_SUBJECT_INFO_ACCESS		12#DEFINE	URL_OID_CERTIFICATE_ONLY_OCSP			13
#DEFINE CRYPT_GET_URL_FROM_ANY				0#DEFINE CRYPT_GET_URL_FROM_PROPERTY			1#DEFINE CRYPT_GET_URL_FROM_EXTENSION 			2#DEFINE CRYPT_GET_URL_FROM_UNAUTH_ATTRIBUTE		4#DEFINE CRYPT_GET_URL_FROM_AUTH_ATTRIBUTE		8
#DEFINE CERT_CONTEXT_REVOCATION_TYPE			1#DEFINE CERT_VERIFY_REV_CHAIN_FLAG			1#DEFINE CERT_VERIFY_CACHE_ONLY_BASED_REVOCATION		2#DEFINE CERT_VERIFY_REV_ACCUMULATIVE_TIMEOUT_FLAG 	4
#DEFINE CONTEXT_OID_CERTIFICATE				1#DEFINE CONTEXT_OID_CRL					2#DEFINE CONTEXT_OID_CTL					3#DEFINE CONTEXT_OID_PKCS7				4#DEFINE CONTEXT_OID_CAPI2_ANY				5#DEFINE CONTEXT_OID_OCSP_RESP				6
#DEFINE CRYPT_RETRIEVE_MULTIPLE_OBJECTS			1#DEFINE CRYPT_CACHE_ONLY_RETRIEVAL			2#DEFINE CRYPT_WIRE_ONLY_RETRIEVAL			4#DEFINE CRYPT_DONT_CACHE_RESULT				8#DEFINE CRYPT_ASYNC_RETRIEVAL				10#DEFINE CRYPT_STICKY_CACHE_RETRIEVAL			4096#DEFINE CRYPT_LDAP_SCOPE_BASE_ONLY_RETRIEVAL		8192#DEFINE CRYPT_OFFLINE_CHECK_RETRIEVAL			16384
#DEFINE X509_ASN_ENCODING    				1#DEFINE PKCS_7_ASN_ENCODING  				0x00010000#DEFINE FORMAT_MESSAGE_FROM_SYSTEM			4096
CLEAR=DeclarationsApi()lnCertContext = GetCertCreateCertificateContext(lcCertB64)IF lnCertContext>0	pszUrlOid = URL_OID_CERTIFICATE_CRL_DIST_POINT	pvPara = lnCertContext	dwFlags = CRYPT_GET_URL_FROM_ANY	pUrlArray = NULL	pcbUrlArray = 0	pUrlInfo = NULL	pcbUrlInfo = 0	pvReserved = 0	nResp = CryptGetObjectUrl(@pszUrlOid, pvPara, dwFlags, pUrlArray, @pcbUrlArray, pUrlInfo, @pcbUrlInfo, pvReserved)	IF nResp>0		pUrlArray = SPACE(pcbUrlArray)		pUrlInfo = SPACE(pcbUrlInfo)		CryptGetObjectUrl(@pszUrlOid, pvPara, dwFlags, @pUrlArray, @pcbUrlArray, @pUrlInfo, @pcbUrlInfo, pvReserved)		? "URL INFO: ", STRCONV(pUrlInfo,15)			nItems = CTOBIN(LEFT(pUrlArray,4), "4RS")		nPointer0 = CTOBIN(SUBSTR(pUrlArray, 5,4), "4RS")		nIniChr = 5		FOR X = 1 TO nItems			nIniChr = nIniChr + 4			cString = STRCONV(SYS(2600, CTOBIN(SUBSTR(pUrlArray,nIniChr,4), "4RS"), 600),6)			lcURL = ALLTRIM(SUBSTR(cString, 1, AT(CHR(0), cString)))+CHR(0)			? lcURL 			?			GetValidityCertInCRL(lcURL, lnCertContext)		NEXT	ENDIF	CertFreeCertificateContext(lnCertContext)ENDIF
PROCEDURE GetCertCreateCertificateContext()	PARAMETERS tcCertEncodedB64 AS String	pCertContext = 0	IF !EMPTY(tcCertEncodedB64)		dwCertEncodingType = BITOR(PKCS_7_ASN_ENCODING, X509_ASN_ENCODING)		pbCertEncoded = GetCryptStringToBinary(ALLTRIM(STRTRAN(tcCertEncodedB64, CHR(0), "")))		cbCertEncoded = LEN(pbCertEncoded)		pCertContext = CertCreateCertificateContext(dwCertEncodingType, pbCertEncoded, cbCertEncoded)	ENDIF	RETURN pCertContextENDPROC
PROCEDURE GetCryptStringToBinary()	PARAMETERS tcStringCert AS String	lcRespStr = ""	IF VARTYPE(tcStringCert)=="C"		dwBufferLen=0		pdwSkip=0		pdwFlags=0		nResp = CryptStringToBinary(tcStringCert, 0, CRYPT_STRING_ANY, NULL, @dwBufferLen, @pdwSkip, @pdwFlags)		pbCertEncoded = SPACE(dwBufferLen)		DO CASE		CASE pdwFlags=0			*------- Base64, with certificate beginning and ending headers.			CryptStringToBinary(tcStringCert, 0, CRYPT_STRING_BASE64HEADER, @pbCertEncoded, @dwBufferLen, @pdwSkip, @pdwFlags)		CASE pdwFlags=1			*------- Base64, without headers.			CryptStringToBinary(tcStringCert, 0, CRYPT_STRING_BASE64, @pbCertEncoded, @dwBufferLen, @pdwSkip, @pdwFlags)		CASE pdwFlags=2			*------- Pure binary copy.			pbCertEncoded=tcStringCert		ENDCASE		lcRespStr = pbCertEncoded	ENDIF	RETURN lcRespStrENDPROC
PROCEDURE GetValidityCertInCRL()	LPARAMETERS tcURL AS String, tpCertContext AS Long	pszUrl = tcURL	pszObjectOid = CONTEXT_OID_CRL	dwRetrievalFlags = CRYPT_WIRE_ONLY_RETRIEVAL	dwTimeout = 0	pCrlContext = 0	hAsyncRetrieve = NULL	pCredentials = NULL	pvVerify = NULL	pAuxInfo = NULL	nResp = CryptRetrieveObjectByUrlA(pszUrl, @pszObjectOid, dwRetrievalFlags, dwTimeout, @pCrlContext, hAsyncRetrieve, pCredentials, pvVerify, pAuxInfo)	? nResp, IIF(nResp=1, "retrieved Object", "Non Retrieved Object")	? pCrlContext, IIF(pCrlContext>0, "CRL Pointer" , "")	IF nResp>0		pTimeToVerify = 0		pCrlInfo = CTOBIN(SYS(2600, pCrlContext+ 12, 4), "4RS")		nResp = CertVerifyCRLTimeValidity(pTimeToVerify, pCrlInfo)		? nResp, IIF(nResp=0, "CRL time OK", "CRL time out")		IF nResp==0			pCert = tpCertContext			pCrlContext = pCrlContext			dwFlags = 0			pvReserved = 0			ppCrlEntry = 0			nResp = CertFindCertificateInCRL(pCert, pCrlContext, dwFlags, pvReserved, @ppCrlEntry)			? nResp, IIF(nResp=1, "Certificate searched", "Certificate non searched")			IF nResp=1				? ppCrlEntry, IIF(ppCrlEntry=0, "CERTIFICATE OK", "CERTIFICATE REVOCATE")			ENDIF			CertFreeCRLContext(pCrlContext)		ELSE			=GetMessageError()		ENDIF	ELSE		=GetMessageError()	ENDIFENDPROC
PROCEDURE GetMessageError(tnNumError AS Long)	IF VARTYPE(tnNumError)=="N"		lnErrorCode = tnNumError	ELSE		lnErrorCode = GetLastError()	ENDIF	lpBuffer = SPACE(128)	=FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 'WINERROR.H', lnErrorCode, 0, @lpBuffer, 128 , 0)	=MESSAGEBOX(lpBuffer, 16, "Error: " + TRANSFORM(lnErrorCode,"@0"))ENDPROC
PROCEDURE DeclarationsApi()	DECLARE LONG CryptStringToBinary IN Crypt32;		STRING 	@pszString, ;		LONG 	 cchString, ;		LONG 	 dwFlags,;		STRING 	@pbBinary, ;		LONG 	@pcbBinary,;		LONG 	@pdwSkip, ;		LONG 	@pdwFlags
	DECLARE LONG CertCreateCertificateContext IN Crypt32;		LONG	 dwCertEncodingType,;		STRING	 pbCertEncoded,;		LONG	 cbCertEncoded
	DECLARE LONG CryptGetObjectUrl IN CryptNet;		LONG	 pszUrlOid,;		LONG	 pvPara,;		LONG	 dwFlags,;		STRING	@pUrlArray,;		LONG	@pcbUrlArray,;		STRING	@pUrlInfo,;		LONG	@pcbUrlInfo,;		LONG	 pvReserved
	DECLARE LONG CryptRetrieveObjectByUrlA IN CryptNet;		STRING	 pszUrl,;		LONG	 pszObjectOid,;		LONG	 dwRetrievalFlags,;		LONG	 dwTimeout,;		LONG	@ppvObject,;		STRING	 hAsyncRetrieve,;		STRING	 pCredentials,;		STRING	 pvVerify,;		STRING	 pAuxInfo
	DECLARE LONG CertVerifyCRLTimeValidity IN Crypt32;		LONG	 pTimeToVerify,;		LONG	 pCrlInfo
	DECLARE LONG CertFindCertificateInCRL IN Crypt32;		LONG	 pCert,;		LONG	 pCrlContext,;		LONG	 dwFlags,;		LONG	 pvReserved,;		LONG	@ppCrlEntry		DECLARE LONG CertFreeCertificateContext IN Crypt32;		LONG 	 pCertContext
	DECLARE LONG CertFreeCRLContext IN Crypt32;		LONG 	 pCrlContext
	DECLARE LONG GetLastError IN Kernel32
	DECLARE LONG FormatMessage IN Kernel32;	  	LONG 	 dwFlags, ;	  	STRING 	@lpSource, ;	  	LONG 	 dwMessageId, ;	  	LONG 	 dwLanguageId, ;	  	STRING 	@lpBuffer, ;	  	LONG 	 nSize, ;	  	LONG 	 Arguments
ENDPROC