Página 1 de 1

WSAA - Factura Electronica AFIP - Ayuda en Traduccion de Codigo

Publicado: Jue Ene 13, 2022 7:33 pm
por ryder1912
Buenas a todos. Estoy implementando la facturacion electornica de la AFIP con Xailer, en realidad eso ya lo hice. Lo que me FALTA es la implementacion para obtener el ticket para facturar. Sin libreria ni programas de tercero sino con Crypt32, me pregunto si es posible hacer eso.
Para obtener el ticket se envia un mensaje generado con la firma electrónica utilizando SHA1+RSA.

Espacificacion Tecnica (Resumen de lo importante): https://www.afip.gob.ar/ws/WSAA/Especif ... _1.2.2.pdf

1.Generar el mensaje del TRA (LoginTicketRequest.xml)
2.Generar un CMS que contenga el TRA, su firma electrónica y el certificado X.509 (LoginTicketRequest.xml.cms)
3.Codificar en Base64 el CMS (LoginTicketRequest.xml.cms.bse64)
4.Invocar WSAA con el CMS y recibir LoginTicketResponse.xml
5.Extraer y validar la información de autorización (TA).

Aqui dejo el link del Web Service: https://www.afip.gob.ar/ws/documentacion/wsaa.asp
link del Manual del Desarrollador: https://www.afip.gob.ar/ws/WSAA/WSAAmanualDev.pdf

Buscando encontre que se pudo implementar con Visual Fox, hay varios links sobre eso pero me interesa el de este usuario que pudo manipular el certificado PFX, que es con el que nosotros trabajamos. El hilo es muy largo.
https://groups.google.com/g/publicesvfo ... CfnAsMAgAJ

Sobre el codigo en cuestion lo dejo aqui, la idea es poder traducirlo a Xailer y empezar a probar. De entrada la declaracion no la entiendo bien y despues hay 3 funciones que no las pude encontrar STRCONV, BINTOC, BINTOC creo que solo existen VFP, quizas alguna sea equivalente con Clipper.

Código: Seleccionar todo

*--------------- CryptoApi CSP
DECLARE LONG CertOpenSystemStore IN crypt32;
		LONG	hprov,;
		STRING	szSubsystemProtocol

DECLARE LONG CryptUIDlgSelectCertificateFromStore IN Cryptui;
	LONG	hCertStore,;
	LONG	hWnd,;
	STRING 	@pwszTitle,;
	STRING	@pwszDisplayString,;
	LONG	dwDontUseColumn,;
	LONG 	dwFlags,;
	STRING	pvReserved

DECLARE LONG CryptSignMessage IN Crypt32;
  	STRING	pSignPara,;  
  	LONG 	fDetachedSignature,;  
  	INTEGER	cToBeSigned,;  
  	STRING	@rgpbToBeSigned,;  
  	STRING	@rgcbToBeSigned,;  
  	STRING	@pbSignedBlob,;  
  	LONG	@pcbSignedBlob

DECLARE LONG CertFreeCertificateContext IN Crypt32;  
	LONG 	pCertContext

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

*--------------- Kernel
DECLARE LONG GetLastError IN Kernel32

DECLARE LONG FormatMessage IN Kernel32;
  	LONG 	dwFlags,;
  	STRING 	@lpSource,;
  	LONG 	dwMessageId,;
  	LONG 	dwLanguageId,;
  	STRING 	@lpBuffer,;
  	LONG 	nSize,;
  	LONG 	Arguments

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

*------------ Constantes
#DEFINE PKCS_7_ASN_ENCODING		0x00010000  
#DEFINE X509_ASN_ENCODING 		0x00000001
#DEFINE FORMAT_MESSAGE_FROM_SYSTEM	0x00001000


*AGREGADO POR MI

DECLARE LONG PFXImportCertStore IN Crypt32;
 	STRING  @pfx,;
	STRING  szPassword,;
	LONG dwFlags

DECLARE INTEGER PFXVerifyPassword IN Crypt32;
 	STRING 	pfx,;
	STRING  szPassword,;
	LONG 	dwFlags

DECLARE INTEGER CertFindCertificateInStore IN crypt32;
	INTEGER hCertStore,;
	LONG dwCertEncodingType,;
	LONG dwFindFlags,;
	LONG dwFindType,;
	INTEGER pvFindPara,;
	INTEGER pPrevCertContext  
	 
#DEFINE szOID_NIST_sha512 "2.16.840.1.101.3.4.2.3"	


*------------ Main
clear
cFileXML="archivo.xml"
IF EMPTY(cFileXML)
	RETURN .F.
ENDIF


*!*	**selecciona Certificado a traves de la interfase de usuario
*!*	hStore = CertOpenSystemStore(0, "MY")
*!*	pCertContext=CryptUIDlgSelectCertificateFromStore(hStore , 0, NULL, NULL, 1, 0, NULL)


*Seleccion Certificado directamente desde un archivo
*inicio
CerPFX  = FILETOSTR("C:\PROYECTOS\NEWFPRG\SOURCE\WEBSERVICE\CERT\CERT.PFX")
clave = "123456"
clave = STRCONV(clave,5)+CHR(0)
cbData = LEN(CerPFX)
pbData = HeapAlloc(GetProcessHeap(), 0, cbData)
RtlMoveMemory(pbData, @CerPFX, cbData)
pPFX = 0h + BINTOC(cbData,"4RS")+ BINTOC(pbData,"4RS") 

if PFXVerifyPassword(@pPFX,clave,0)=0
	MESSAGEBOX( "La clave no es correcta" )
	HeapFree(GetProcessHeap(), 0, pbData)
	RETURN
ENDIF
hStore = PFXImportCertStore(@pPFX, clave, 0)
pCertContext = CertFindCertificateInStore(hStore,BITOR(PKCS_7_ASN_ENCODING, X509_ASN_ENCODING),0, 0, 0, 0) 
*fin 

IF pCertContext=0
	=CertCloseStore(hStore, 0) 
	RETURN .F.
ENDIF
lcSignature=GetSignXml(cFileXML, pCertContext)
IF !EMPTY(lcSignature)
	STRTOFILE(lcSignature,"C:\proyectos\newfprg\source\webservice\tickets\TRA_wsfe.TMP")
	? lcSignature
	lbReturn=.T.
ELSE
	lbReturn=.F.
ENDIF
=CertCloseStore(hStore, 0)
=CertFreeCertificateContext(pCertContext)
RETURN lbReturn

*--------------- Región de Procedures
PROCEDURE GetSignXml(tcPathFileXml, tpCertContext)
	*SET STEP ON
	*----- Creamos Puntero de Memoria, colocamos el algoritmo de firma y armamos estructura CRYPT_ALGORITHM_IDENTIFIER
	lcOID = szOID_NIST_sha512
	lpOID = HeapAlloc(GetProcessHeap(), 0, LEN(lcOID))
	RtlMoveMemory(lpOID, @lcOID, LEN(lcOID)) 
	CRYPT_ALGORITHM_IDENTIFIER = 0h + BINTOC(lpOID,"4RS") + BINTOC(0, "4RS")
	*----- Creamos Puntero de Memoria y colocamos el Certificado
	lpCertificate = 0h + BINTOC(tpCertContext,"4RS")
	lpRgpMsgCert = HeapAlloc(GetProcessHeap(), 0, LEN(lpCertificate))
	RtlMoveMemory(lpRgpMsgCert, @lpCertificate, LEN(lpCertificate))
	*----- Armamos la Estructura CRYPT_SIGN_MESSAGE_PARA
	lnLenSP = 68 && (17 estructuras * 4 )
	nMsgCert = 1
	cbSize = BINTOC(lnLenSP,"4RS")
	dwMsgEncodingType = BINTOC(BITOR(PKCS_7_ASN_ENCODING, X509_ASN_ENCODING),"4RS")
	pSigningCert = BINTOC(tpCertContext,"4RS")
	HashAlgorithm = CRYPT_ALGORITHM_IDENTIFIER + BINTOC(0, "4RS") + BINTOC(0, "4RS")
	pvHashAuxInfo = 0h
	cMsgCert = BINTOC(nMsgCert,"4RS")
	rgpMsgCert = BINTOC(lpRgpMsgCert,"4RS")
	*----- Resto de estructuras opcionales, rellenar con CHR(0)
	*cMsgCrl, rgpMsgCrl, cAuthAttr, rgAuthAttr, cUnauthAttr, rgUnauthAttr, 
	*dwFlags, dwInnerContentType, HashEncryptionAlgorithm, pvHashEncryptionAuxInfo
	pSignPara = 0h + cbSize + dwMsgEncodingType + pSigningCert + HashAlgorithm + pvHashAuxInfo + cMsgCert + rgpMsgCert
	nlenpSign = LEN(pSignPara) + 1 
	pSignPara = pSignPara + REPLICATE(CHR(0), lnLenSP-nlenpSign)
	
	*----- Cargamos el archivo Xml a Firmar	
	lcFileXmlIn = tcPathFileXml
	lcXmlString = FILETOSTR(lcFileXmlIn) 
	*----- Colocamos la cadena Xml dentro de un puntero
	lpByToSigned = HeapAlloc(GetProcessHeap(), 0, LEN(lcXmlString) + 1)
	RtlMoveMemory(lpByToSigned, @lcXmlString, LEN(lcXmlString) + 1)
	*----- Procedemos a Firmar el Xml
	rgpbToBeSigned = 0h + BINTOC(lpByToSigned, "4RS")
	lnXmlStrLen = LEN(lcXmlString) 
	rgcbToBeSigned = 0h + BINTOC(lnXmlStrLen, "4RS")
	pcbSignedBlob = 0
	pbSignedBlob = ""
	IF CryptSignMessage(@pSignPara, 0, 1, @rgpbToBeSigned, @rgcbToBeSigned, @pbSignedBlob , @pcbSignedBlob)=0
		pbSignedBlob = SPACE(pcbSignedBlob)
		IF CryptSignMessage(@pSignPara, 0, 1, @rgpbToBeSigned, @rgcbToBeSigned, @pbSignedBlob, @pcbSignedBlob)=0
			=GetMensajeError()
		ENDIF				
	ENDIF
	*----- Liberamos los punteros de memoria
	HeapFree(GetProcessHeap(), 0, lpByToSigned)
	HeapFree(GetProcessHeap(), 0, lpRgpMsgCert)
	HeapFree(GetProcessHeap(), 0, lpOID)
	RETURN pbSignedBlob
ENDPROC


PROCEDURE GetMensajeError(tcNumError)
	IF VARTYPE(tcNumError)=="N"
		lnErrorCode = tcNumError
	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