Club Delphi  
    FTP   CCD     Buscar   Trucos   Trabajo   Foros

Retroceder   Foros Club Delphi > Principal > OOP
Registrarse FAQ Miembros Calendario Guía de estilo Temas de Hoy

Grupo de Teaming del ClubDelphi

Respuesta
 
Herramientas Buscar en Tema Desplegado
  #1  
Antiguo 26-03-2020
giulichajari giulichajari is offline
Miembro
 
Registrado: nov 2012
Posts: 306
Poder: 12
giulichajari Va por buen camino
Problema con certificado de AFIP y OpenSSL

Hola amigos..Estoy desarrollando un sistema para coenctarme al web service de facturacion electronica de AFIP.

Esta es la unidad de Autenticacion, a la cual llamo como parametro "wsfe":

Código Delphi [-]
unit UAutenticacion;
 
interface
uses
XMLIntf, XMLDoc,Winapi.shellAPI,
dialogs,Classes,SOAPHTTPClient,sysutils,DateUtils;
 
type
  TLogin = class
  private
 
    Fservicio: string;
    Fsign: string;
    Ftoken: string;
    Fexpiration: TDateTime;
    procedure Setservicio(const Value: string);
    procedure Setsign(const Value: string);
    procedure Settoken(const Value: string);
    procedure Setexpiration(const Value: TDateTime);
    public
      property expiration:TDateTime read Fexpiration write Setexpiration;
      property token:string read Ftoken write Settoken;
      property sign:string read Fsign write Setsign;
      property servicio:string read Fservicio write Setservicio;
 
      destructor destroy;
      function guardarXML():IXMLDocument;
      function armarsource:string;
      function generarCMS(const xml1:IXMLDocument):WideString;
      procedure respuestaXML(const s:string);
      function expiro():boolean;
      class function unicoLogin:TLogin;
      procedure actualizarsigntoken;
      constructor Loguearse(s:string);
 
  end;
var
login:TLogin;
implementation
 
{ TLogin }
 
uses tra,IniFiles,Forms, LoginCms1,loginresponse, string64, NuevoTicket;
procedure TLogin.actualizarsigntoken;
var
xml1:IXMLDocument;
cms:string;
begin
xml1:=NewXMLDocument();
xml1:=login.guardarXML();
cms:=login.generarCMS(xml1);
login.respuestaXML(cms);
end;
 
 
function TLogin.armarsource: string;
var
archivoini:TIniFile;
source,o,serialNumber,C,cn:string;
begin
 archivoini:=TIniFile.Create(ExtractFilePath(Application.ExeName)+ 'caja.ini');
 o:=archivoini.ReadString('EMPRESA','O','');
 serialNumber:=archivoini.ReadString('EMPRESA','CUIT','');
 cn:=archivoini.ReadString('EMPRESA','cn','');
 C:=archivoini.ReadString('EMPRESA','C','');
 source:='C='+C+',o='+o+',serialNumber='+serialNumber+',cn='+cn;
 Result:=source;
end;
 
 
destructor TLogin.destroy;
begin
inherited;
end;
 
function TLogin.expiro: boolean;
var
time:string;
diferencia:int64;
begin
diferencia:=2;
time:=FormatDateTime('hh:mm:ss',Now);
  if (MinuteSpan(StrToDateTime(time),login.expiration)<2) then
      begin
        result:=True;
      end
      else
      begin
        result:=False;
      end;
end;
 
function TLogin.generarCMS(const xml1:IXMLDocument):WideString;
var
rutabat,linea:string;
archivoCMS:TStringList;
textocms:WideString;
archivoplano:TextFile;
nombrearchivo:string;
begin
 
 
      rutabat:=ExtractFilePath(Application.ExeName)+'certificado\'+'cms.bat';
      ShellExecute(0,'open',PWideChar(rutabat),nil, nil,0);
 
 
    archivoCMS:=TStringList.Create;
 
    archivoCMS.LoadFromFile(ExtractFilePath(Application.ExeName) + 'loginticketrequest.xml.cms');
 
 
 
  result:=archivoCMS.Text;
 
end;
 
function TLogin.guardarXML():IXMLDocument;
var
loginticketrequest:IXMLLoginTicketRequestType;
loginheader:IXMLHeaderType;
archivoini:TIniFile;
diai,diaf,horai,time,horaf,horae,expiration:string;
XML1:IXMLDocument;
XMLanterior:IXMLDocument;
startnode,nodohijo,nodoexp:IXMLNode;
begin
 
    time:=FormatDateTime('hh:mm:ss',now);
 
  //crear archivo xml
          XML1 := NewXMLDocument;
 
          XML1.FileName:='loginticketrequest.xml';
 
 
             loginticketrequest:=NewloginTicketRequest;
          //asginar valores para loginticketrequest.xml
             // version
 
              loginticketrequest.Version:='1.0';
             //header
               //  uniqueid son constantes
 
              loginticketrequest.header.UniqueId:=4325399;
              //hora del pedido.. es ahora
               diai:=FormatDateTime('yyyy-mm-dd',Now);
               diaf:=FormatDateTime('yyyy-mm-dd',IncHour(Now,12));
               horai:=FormatDateTime('hh:mm:ss',now);
                 horaf:=FormatDateTime('hh:mm:ss',IncHour(Now,12));
              loginticketrequest.header.GenerationTime:=diai+ 'T' + horai + '-03:00';
              //hora de finalizacion (1 dia es el maximo);
               loginticketrequest.header.ExpirationTime:=diaf+'T'+horaf + '-03:00';
 
              //servicio a acceder
              loginticketrequest.Service:=Fservicio;
                XML1.XML.Add(loginticketrequest.XML);
               XML1.Active:=True;
 
           XML1.SaveToFile(ExtractFilePath(Application.ExeName) + 'loginticketrequest.xml');
 
            Result:=XML1;
 
 
 
end;
 
constructor TLogin.Loguearse(s:string);
var
XML1:IXMLDocument;
CMS:WideString;
CMS64:WideString;
TRA:TXMLDocument;
begin
  self.Fservicio:=s;
  XML1:=self.guardarXML;
  CMS:=self.generarCMS(XML1);
  ShowMessage(CMS);
  CMS64:=Base64Encode(CMS);
  ShowMessage(CMS64);
  self.respuestaXML(CMS64);
end;
 
procedure TLogin.respuestaXML(const s:string);
var
RIOLogin:THTTPRIO;
xml3:IXMLDocument;
content,expira,nodo:string;
nodohijo,nodohijosign,startnode,startnodesign:IXMLNode;
begin
 
  RIOLogin:=THTTPRIO.Create(nil);
  with RIOLogin do
    begin
      WSDLLocation:='https://wsaahomo.afip.gov.ar/ws/services/LoginCms?wsdl';
      Port:='LoginCms';
      Service:='LoginCMSService';
 
    end;
 
 
   xml3:=NewXMLDocument;
   content:=(RIOLogin as LoginCms).loginCms(s);
   xml3.XML.Text:=content;
   xml3.Active:=True;
   xml3.SaveToFile(ExtractFilePath(Application.ExeName) + 'respuestaxml.xml');
   startnode:=xml3.ChildNodes[1];
   nodohijo:=startnode.ChildNodes[0];
   nodo:=nodohijo.ChildNodes['expirationTime'].Text;
   expira:=Copy(nodo,12,8);
   Setexpiration(StrToDateTime(expira));
   //setear sign y token
   startNodesign := xml3.ChildNodes[1];
    nodohijosign:=startnodesign.ChildNodes[1];
 
 
    Setsign(nodohijosign.ChildNodes['sign'].Text);
 
    Settoken(nodohijosign.ChildNodes['token'].Text);
 
end;
 
 
 
procedure TLogin.Setexpiration(const Value: TDateTime);
begin
  Fexpiration := Value;
end;
 
procedure TLogin.Setservicio(const Value: string);
begin
  Fservicio := Value;
end;
 
procedure TLogin.Setsign(const Value: string);
begin
  Fsign := Value;
end;
 
 
procedure TLogin.Settoken(const Value: string);
begin
  Ftoken := Value;
end;
 
 
class function TLogin.unicoLogin:TLogin;
 
begin
  if login<>nil then
    begin
      if login.expiro=False then
         begin
            Result:=login;
         end
         else
         begin
           login.actualizarsigntoken;
           Result:=login;
         end;
    end
    else
    begin
    login:=Tlogin.Loguearse('padron-puc-ws-consulta-nivel3');
    login.respuestaXML(login.generarCMS(login.guardarXML()));
 
    end;
end;
 
end.

Y obtengo No se puede decodificar el BASE64.



Como veran en el constructor sigo los pasos de las especificaciones tecnicas de AFIP:



1- Creo el xml con tiempo de expiracion

2-Genero el CMS en un archivo, para lo mismo ejecuto un archivo BAT (Codigo DOS digamos):



c:\OpenSSL-Win32\bin\openssl.exe cms -sign -in G:\despensa\Win32\Debug\LoginTicketRequest.xml -out G:\despensa\Win32\Debug\LoginTicketRequest.xml.cms -signer G:\despensa\Win32\Debug\certificado\certificado.crt -inkey G:\despensa\Win32\Debug\certificado\caruso12021991.key -nodetach -outform PEM

y tambien podran ver que puse dos ShowMessage y segun veo el problema es que me carga el archivo LoginTicketRequest.xml.cms anterior, es decir lo carga y luego se regenera, ese es uno de los problemas principales.
Lo cual no entiendo porque sucede si el codigo del archivo BAT esta antes que la creacion del StringList.
Lo que me parece incorrecto es que en la unidad donde encodeo el cms a base 64 asi como las variables en el constructor son de tipo string, este mismo admite solo 256 caracteres, cuando el texto es mas largo:

Código Delphi [-]
unit string64;
 
interface
 
uses
    Winapi.Windows,Winapi.Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
    Dialogs, StdCtrls;
 
const
    B64Table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
 
function Base64Decode(const S: string): string;
function Base64Encode(const S: String): String;
 
implementation
 
 
function Base64Encode(const S: String): String;
var
    InBuf: array[0..2] of Byte;
    OutBuf: array[0..3] of Char;
    iI, iJ: Integer;
begin
    SetLength(Result, ((Length(S) + 2) div 3) * 4);
    for iI := 1 to ((Length(S) + 2) div 3) do
    begin
        if Length(S) < (iI * 3) then
            Move(S[(iI - 1) * 3 + 1], InBuf, Length(S) - (iI - 1) * 3)
        else
            Move(S[(iI - 1) * 3 + 1], InBuf, 3);
        OutBuf[0] := B64Table[((InBuf[0] and $FC) shr 2) + 1];
        OutBuf[1] := B64Table[(((InBuf[0] and $3) shl 4) or ((InBuf[1] and $F0) shr 4)) + 1];
        OutBuf[2] := B64Table[(((InBuf[1] and $F) shl 2) or ((InBuf[2] and $C0) shr 6)) + 1];
        OutBuf[3] := B64Table[(InBuf[2] and $3F) + 1];
        Move(OutBuf, Result[(iI - 1) * 4 + 1], 4);
    end;
    if Length(S) mod 3 = 1 then
    begin
        Result[Length(Result) - 1] := '=';
        Result[Length(Result)] := '=';
    end
    else if Length(S) mod 3 = 2 then
        Result[Length(Result)] := '=';
end;
 
function Base64Decode(const S: string): string;
var
    OutBuf: array[0..2] of Byte;
    InBuf: array[0..3] of Byte;
    iI, iJ: Integer;
begin
    if Length(S) mod 4 <> 0 then
        raise Exception.Create('Base64: Incorrect string format');
    SetLength(Result, ((Length(S) div 4) - 1) * 3);
    for iI := 1 to (Length(S) div 4) - 1 do
    begin
        Move(S[(iI - 1) * 4 + 1], InBuf, 4);
        for iJ := 0 to 3 do
            case InBuf[iJ] of
                43: InBuf[iJ] := 62;
                48..57: Inc(InBuf[iJ], 4);
                65..90: Dec(InBuf[iJ], 65);
                97..122: Dec(InBuf[iJ], 71);
            else
                InBuf[iJ] := 63;
            end;
        OutBuf[0] := (InBuf[0] shl 2) or ((InBuf[1] shr 4) and $3);
        OutBuf[1] := (InBuf[1] shl 4) or ((InBuf[2] shr 2) and $F);
        OutBuf[2] := (InBuf[2] shl 6) or (InBuf[3] and $3F);
        Move(OutBuf, Result[(iI - 1) * 3 + 1], 3);
    end;
    if Length(S) <> 0 then
    begin
        Move(S[Length(S) - 3], InBuf, 4);
        if InBuf[2] = 61 then
        begin
            for iJ := 0 to 1 do
                case InBuf[iJ] of
                    43: InBuf[iJ] := 62;
                    48..57: Inc(InBuf[iJ], 4);
                    65..90: Dec(InBuf[iJ], 65);
                    97..122: Dec(InBuf[iJ], 71);
                else
                    InBuf[iJ] := 63;
                end;
            OutBuf[0] := (InBuf[0] shl 2) or ((InBuf[1] shr 4) and $3);
            Result := Result + Char(OutBuf[0]);
        end
        else if InBuf[3] = 61 then
        begin
            for iJ := 0 to 2 do
                case InBuf[iJ] of
                    43: InBuf[iJ] := 62;
                    48..57: Inc(InBuf[iJ], 4);
                    65..90: Dec(InBuf[iJ], 65);
                    97..122: Dec(InBuf[iJ], 71);
                else
                    InBuf[iJ] := 63;
                end;
            OutBuf[0] := (InBuf[0] shl 2) or ((InBuf[1] shr 4) and $3);
            OutBuf[1] := (InBuf[1] shl 4) or ((InBuf[2] shr 2) and $F);
            Result := Result + Char(OutBuf[0]) + Char(OutBuf[1]);
        end
        else
        begin
            for iJ := 0 to 3 do
                case InBuf[iJ] of
                    43: InBuf[iJ] := 62;
                    48..57: Inc(InBuf[iJ], 4);
                    65..90: Dec(InBuf[iJ], 65);
                    97..122: Dec(InBuf[iJ], 71);
                else
                    InBuf[iJ] := 63;
                end;
            OutBuf[0] := (InBuf[0] shl 2) or ((InBuf[1] shr 4) and $3);
            OutBuf[1] := (InBuf[1] shl 4) or ((InBuf[2] shr 2) and $F);
            OutBuf[2] := (InBuf[2] shl 6) or (InBuf[3] and $3F);
            Result := Result + Char(OutBuf[0]) + Char(OutBuf[1]) + Char(OutBuf[2]);
        end;
    end;
end;
end.

Entonces quise convertir el certificado con openssl directamente a base64. Para lo mismo hago lo siguiente:

c:\OpenSSL-Win32\bin\openssl.exe x509 -inform der -in G:\despensa\Win32\Debug\certificado\certificado.crt -out G:\despensa\Win32\Debug\certificado\certificado.pem

Pero me devuelve:

unable to load certificate 7200:error:0D0680A8:asn1 encoding routines:ASN1_CHECK_TLEN:wrong tag:.\crypto\asn1\tasn_dec.c:1320: 7200:error:0D07803A:asn1 encoding routines:ASN1_ITEM_EX_D2I:nested asn1 error:.\crypto\asn1\tasn_dec.c:382:Type=X509
Responder Con Cita
  #2  
Antiguo 29-03-2020
aledieb aledieb is offline
Miembro
 
Registrado: jun 2005
Ubicación: Buenos Aires, Argentina
Posts: 141
Poder: 19
aledieb Va por buen camino
Hola, lo que te devuelve el openssl empieza con
Código:
-----BEGIN PKCS7-----
y termina con
Código:
-----END PKCS7-----
esas dos líneas debes eliminarlas para poder obtener el token y el sign de la afip.

Espero que te sea eso. Por las dudas te paso el comando openssl que yo uso, tal vez halla una diferencia.
Código:
openssl smime -sign -in E:\tmp\ticketsf.xml -out E:\tmp\ticketf.xml 
-inkey E:\certificados\ClavePriv.txt -signer E:\certificados\Certificado.crt -outform PEM -nodetach
__________________
Suerte
Alejandro
Responder Con Cita
Respuesta



Normas de Publicación
no Puedes crear nuevos temas
no Puedes responder a temas
no Puedes adjuntar archivos
no Puedes editar tus mensajes

El código vB está habilitado
Las caritas están habilitado
Código [IMG] está habilitado
Código HTML está deshabilitado
Saltar a Foro

Temas Similares
Tema Autor Foro Respuestas Último mensaje
Como utilizar Certificado en Homologacion AFIP apuentes Internet 1 29-11-2017 18:17:02
problema con openssl expertis Varios 1 11-10-2012 22:53:54


La franja horaria es GMT +2. Ahora son las 01:57:03.


Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi
Copyright 1996-2007 Club Delphi