Rotina em c# para harbour

Projeto [x]Harbour - Compilador de código aberto compatível com o Clipper.

Moderador: Moderadores

malcarli
Usuário Nível 3
Usuário Nível 3
Mensagens: 244
Registrado em: 20 Ago 2015 18:14
Localização: marilia/sp

Rotina em c# para harbour

Mensagem por malcarli »

Poderiam converter esta rotina em habour puro? a Primeira para é fácil (postei somente para conhecimento do uso), mas a segunda nem o chagt conseguiu uma forma funcional. obg

Att.

Marcelo A. L. Carli
Marília/SP
Capital Nacional do Alimento ®
https://malc-informatica.ueniweb.com
Insta: @malcarli25
Email / Skype: marceloalcarli@gmail.com



Código: Selecionar todos

** Program.cs

static class Program
    {
        /// 
        /// The main entry point for the application.
        /// 
        [STAThread]
        static void Main()
        {
            /*
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
              
            */
            if (System.IO.File.Exists(@"StringParaAssinar.txt"))
            {
                string[] lines = System.IO.File.ReadAllLines(@"StringParaAssinar.txt");
                string[] aDados = new string[2];
                
                aDados[0] = lines[0]; // Numero de Série Certificado;
                aDados[1] = lines[1]; // String a Assinar.
                MessageBox.Show(aDados[0]);
                MessageBox.Show(aDados[1]);
                AssinarRPS assina = new AssinarRPS();
                String assinatura = assina.AssinarRPSSP(aDados[0], aDados[1]);
                MessageBox.Show(assinatura);

                //Declaração do método StreamWriter passando o caminho e nome do arquivo que deve ser salvo
                StreamWriter writer = new StreamWriter(@"StringAssinada.txt");
                //Escrevendo o Arquivo e pulando uma linha
                writer.WriteLine(assinatura);
                //Fechando o arquivo
                writer.Close();
                //Limpando a referencia dele da memória
                writer.Dispose();

                
            }
        }
    }



*** ASSINARPS.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Security;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography;

namespace WindowsFormsApplication1
{
    public class AssinarRPS
    {
        public AssinarRPS() { }

        public string AssinarRPSSP(string serial, string original)
        {
            //X509Store store = new X509Store(StoreLocation.LocalMachine); 
            X509Store store = new X509Store();
            store.Open(OpenFlags.ReadOnly);
            string sn = serial, criptografada; //" put here your certificate serial number "; 
            criptografada = "";
            X509Certificate2Collection coll = store.Certificates.Find(X509FindType.FindBySerialNumber, serial, true);
            X509Certificate2 cert = null;

            Console.WriteLine(coll.Count);

            if (coll.Count > 0 && coll[0] != null)
            {
                cert = coll[0];
            }
            else return "Erro ao assinar RPS";

            //recebe o certificado e a string a ser assinada 
            System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();

            //pega a chave privada do certificado digital 
            rsa = cert.PrivateKey as RSACryptoServiceProvider;
            MessageBox.Show(rsa);

            //cria o array de bytes e realiza a conversao da string em array de bytes 
            byte[] sAssinaturaByte = enc.GetBytes(original);
            MessageBox.Show(sAssinaturaByte);

            RSAPKCS1SignatureFormatter rsaf = new RSAPKCS1SignatureFormatter(rsa);
            MessageBox.Show(rsaf);
            SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider();
            MessageBox.Show(sha1);

            //cria a variavel hash que armazena o resultado do sha1 
            byte[] hash;
            hash = sha1.ComputeHash(sAssinaturaByte);
            MessageBox.Show(hash);

            //definimos o metodo a ser utilizado na criptografia e assinamos 
            rsaf.SetHashAlgorithm("SHA1");
            sAssinaturaByte = rsaf.CreateSignature(hash);
            MessageBox.Show(sAssinaturaByte);

            //por fim fazemos a conversao do array de bytes para string 
            criptografada = Convert.ToBase64String(sAssinaturaByte);
            MessageBox.Show(criptografada);
            return criptografada;

        }
    }
}   
Avatar do usuário
Itamar M. Lins Jr.
Administrador
Administrador
Mensagens: 7989
Registrado em: 30 Mai 2007 11:31
Localização: Ilheus Bahia
Curtiu: 1 vez

Re: Rotina em c# para harbour

Mensagem por Itamar M. Lins Jr. »

Olá!
Vc deve explicar melhor o propósito dessa rotina, quais funções ela usa e se existe no Harbour, no caso cURL.
Saudações,
Itamar M. Lins Jr.
malcarli
Usuário Nível 3
Usuário Nível 3
Mensagens: 244
Registrado em: 20 Ago 2015 18:14
Localização: marilia/sp

Re: Rotina em c# para harbour

Mensagem por malcarli »

Beleza vou detalhar melhor e já evolui mais um pouco.

A rotina em c# foi feita para assinar a tag assinatura da nfse de são paulo. Não tem nada com assinatura do xml isto está funcionando com adaptação que fiz na classe do Mestre Quintas.

Conforme o manual da nfse é composto conforme o modelo abaixo:

Código: Selecionar todos

Passos básicos para assinatura de um RPS – versão 1: 
1º - Monte a string de caracteres conforme a tabela a apresentada anteriormente. 
A seguir apresentamos o exemplo de parte de uma mensagem XML de pedido de envio de RPS (os 
campos utilizados na montagem da cadeia de caracteres estão em negrito). 
... 
<ChaveRPS> 
<InscricaoPrestador>31000000</InscricaoPrestador> 
<SerieRPS>OL03</SerieRPS> 
<NumeroRPS>1</NumeroRPS> 
</ChaveRPS> 
<TipoRPS>RPS-M</TipoRPS> 
<DataEmissao>2007-01-03</DataEmissao> 
<StatusRPS>N</StatusRPS> 
<TributacaoRPS>T</TributacaoRPS> 
<ValorServicos>20500</ValorServicos> 
<ValorDeducoes>5000</ValorDeducoes> 
<CodigoServico>2658</CodigoServico> 
<AliquotaServicos>0.05</AliquotaServicos> 
<ISSRetido>false</ISSRetido> 
<CPFCNPJTomador> 
<CPF>13167474254</CPF> 
</CPFCNPJTomador> 
... 
<CPFCNPJIntermediario> 
<CNPJ>09999999000106</CNPJ> 
</CPFCNPJIntermediario> 
<InscricaoMunicipalIntermediario>99999999</InscricaoMunicipalIntermediario> 
<ISSRetidoIntermediario>true</ISSRetidoIntermediario> 
... 
Com base no trecho da mensagem XML apresentada, montamos a seguinte string de caracteres: 
"31000000OL03 
00000000000120070103TNN00000000205000000000000050000002658100013167474254209999999000106S" 
Note que o valor dos serviços (R$ 20.500,00) foi transformado em 2050000, o valor de deduções (R$ 
5.000,00) foi transformado em 500000. Também foi acrescentado à série do RPS um espaço em branco 
à direita para preencher as 5 posições. 
Observação: não é necessário informar os dados de intermediário na assinatura se não houver 
intermediário. Como exemplo, sem intermediário a string montada seria dessa forma: 
"31000000OL03 00000000000120070103TNN00000000205000000000000050000002658100013167474254” 
2º - Converta a cadeia de caracteres ASCII para bytes. 
3º - Gere o HASH (array de bytes) utilizando SHA1. 
4º - Assine o HASH (array de bytes) utilizando RSA-SHA1. 
Manual de Utilização – Web Service       
Versão do Manual: 3.3.4      
pág. 46 
ATENÇÃO! Na maioria das linguagens de programação, os passos 3 e 4 são feitos através de uma única 
função. Verifique a documentação de sua linguagem para evitar assinar um hash de um hash
se enviar esta string para assinar:

Código: Selecionar todos

37925504A    00000001234520251215TNN00000000000010000000000000000002800257567419000147
Tem que retornar assim:

Código: Selecionar todos

RZ2gOs00u38oPJXyLe9TMUoBd+gY5bL0XMxBTFKYrk180Flaws2OqlOGPNx5RTP2ND641yvcoegRXdr87mEea3a0hmU8WZSXmBbbwTrDndcTS8DCsNddbp01DdUWXc1Xkk+nY7sx+J/chgdMMAoFNLU4XrUFGbWnA9QXx2wA/J/zfRVil93BjfSW6WQsSnYp9MkoyOiL8amZbM0Cf2Pdr//oS5bMGV68bJ1Efas2SlTd1SGa4KYSU+OWI0AE2dXa55E8mhIRj94+gpAbITNAyqNqtAR496OnxV8HcQ9NCzr1c0XuPaSO8RyI8VVFTU1BNpXs4EV59BDf/jMwi++y5w==
Estou usando um certificado a1. A rotina em c# funciona, na qual foi gerado um executável, que ao chama-lo dentro do prg retorna a string assinada, mas quero colocar ela dentro do prg sem executável

Consegui fazer este trecho mas está dando erro agora nesta linha:

Código: Selecionar todos

FUNCTION AssinarTexto( cTextoOriginal)
   LOCAL aBytes
   LOCAL aHash
   LOCAL hPFX, hKey
   LOCAL aAssinatura
   LOCAL cBase64

Local oDOMDocument, xmldsig, oCert, oCapicomStore
Local SIGNEDKEY, DSIGKEY, oStoreMem
Local cXmlTagInicial, cXmlTagFinal, cRetorno := ''
Local cDllFile, acDllList := { 'msxml5.dll', 'msxml5r.dll', 'capicom.dll' }

   // 1. Converter string em bytes (igual Encoding.GetBytes)
   aBytes := {}
   FOR i := 1 TO Len( cTextoOriginal )
      AAdd( aBytes, Asc( SubStr( cTextoOriginal, i, 1 ) ) )
   NEXT

   // 2. Criar hash SHA1 dos bytes
   aHash := hb_SHA1( aBytes )

   oXml:CapicomEscolheCertificado()   /// pegou corretamente o certificado


xmldsig := win_OleCreateObject( "MSXML2.ServerXMLHTTP.5.0" )

oStoreMem:= win_OleCreateObject( "CAPICOM.Store" )

oStoreMem:open(2,'My',2)
oCerts:= oStoreMem:Certificates()
xmldsig:store := ocerts // oStoreMem  <<<<<<<< ERRO AQUI

eType      := oCert:PrivateKey:ProviderType   
sProvider  := oCert:PrivateKey:ProviderName   
sContainer := oCert:PrivateKey:ContainerName  

dsigKey    := xmldsig:createKeyFromCSP( eType, sProvider, sContainer, 0 )
signedKey := xmldsig:sign( dsigKey, _SIG_KEYINFO )

********************************************************************************************************
***  esta parte dar erro quando testo com harbour  mas o curioso é que funciona 100%  com xharbour *****
TRY
   signedKey := xmldsig:sign( dsigKey, _SIG_KEYINFO )
CATCH  oError
   aRetorno[ 'STATUS' ] := .F.
   aRetorno[ 'MSG' ]    := 'Erro ao atribuir a assinatura, verificar inconsistencia no xml ou no certificado.'
   RETURN aRetorno
END



/*
*    msginfo(CapicomAssinaXml(aHash, oXml:cCertificado , [12345678], .f.))

*   If ! AssinaLoadCertificado( cCertCN, @ocert, @oCapicomStore, cPassword, @cRetorno )

*oCapicomStore:= CapicomCertificado(oXml:cCertificado)
oCert:= CapicomCertificado(oXml:cCertificado)

 
      cRetorno := "Erro Assinatura: Não carregado MSXML2.MXDigitalSignature.5.0"
      xmldsig := win_OleCreateObject( "MSXML2.MXDigitalSignature.5.0" )

      cRetorno := "Erro Assinatura: Template de assinatura não encontrado"
   *   xmldsig:signature := oDOMDocument:selectSingleNode(".//ds:Signature")

      cRetorno := "Erro assinatura: Certificado pra assinar XmlDSig:Store"
    *  xmldsig:store := oCert

      dsigKey  := xmldsig:CreateKeyFromCSP( oCert:PrivateKey:ProviderType, oCert:PrivateKey:ProviderName, oCert:PrivateKey:ContainerName, 0 )
      If ( dsigKey = NIL )
         cRetorno := "Erro assinatura: Ao criar a chave do CSP."
msginfo(cRetorno)
*         BREAK
      EndIf
      cRetorno := "Erro assinatura: assinar XmlDSig:Sign()"
      SignedKey := XmlDSig:Sign( DSigKey, 2 )

      If signedKey == NIL
         cRetorno := "Erro Assinatura: Assinatura Falhou."
msginfo(cRetorno)
 *        BREAK
      EndIf
aAssinatura  :=  oDOMDocument:Xml 
 




   // 5. Converter para Base64 (igual Convert.ToBase64String)
   cBase64 := hb_Base64Encode( aAssinatura )



// 2. Extrair chave privada em PEM
   cPrivateKeyPem := hb_p12GetPrivateKey( oXml:cCertificado)

   // 3. Gerar o hash (SHA256)
   cHash := hb_hash( "SHA1", aBytes )

   // 4. Assinar usando RSA
   cAssinatura := hb_crypto( "RSA", "SIGN", cHash, cPrivateKeyPem )

   // 5. Converter para Base64

*/
   cAssinaturaB64 := hb_base64Encode( cAssinatura )
msginfo(cbase64, [resultado assinado])

   RETURN cBase64


Erro na linha acima mencionada

Código: Selecionar todos

Date: 15/12/2025 Time: 16:39:46
Application: D:\enzza\classe_nfse\demo_sp.exe
User: MALC / User
Time from start: 0 days 0 hours 0 mins 4 secs
Error WINOLE/1008 Nenhuma vari vel exportada: STORE OS Error: -2147352570=Unknown error Args: [1] = O WIN_OLEAUTO

--------------------------------- Stack Trace ---------------------------------
Called from WIN_OLEAUTO:_STORE(0)
Called from ASSINARTEXTO(202) in module: demo_sp.prg
Called from (b)MAIN(52) in module: demo_sp.prg
Called from _DOCONTROLEVENTPROCEDURE(2058) in module: h_windows.prg
Called from EVENTS(1697) in module: h_events.prg
Called from DOMESSAGELOOP(0)
Called from _ACTIVATEWINDOW(1652) in module: h_windows.prg
Called from DOMETHOD(6055) in module: h_controlmisc.prg
Called from MAIN(67) in module: demo_sp.prg

alxsts
Colaborador
Colaborador
Mensagens: 3100
Registrado em: 12 Ago 2008 15:50
Localização: São Paulo-SP-Brasil

Re: Rotina em c# para harbour

Mensagem por alxsts »

Olá!
malcarli escreveu: 15 Dez 2025 16:45 esta parte dar erro quando testo com harbour mas o curioso é que funciona 100% com xharbour
O comando TRY...CATCH... END TRY não é nativo das linguagens [x]Harbour. No xHarbour, criaram através do pré-processador. Tem que fazer o mesmo para aplicações Harbour ou usar BEGIN SEQUENCE...RECOVER..
END SEQUENCE.

Coloque as linhas abaixo no início do teu .Prg:

Código: Selecionar todos

#xcommand TRY => BEGIN SEQUENCE WITH { |o| Break( o ) }
#xcommand CATCH [<!oErr!>] => RECOVER [USING <oErr>] <-oErr->
#xcommand FINALLY => ALWAYS
[]´s
Alexandre Santos (AlxSts)
malcarli
Usuário Nível 3
Usuário Nível 3
Mensagens: 244
Registrado em: 20 Ago 2015 18:14
Localização: marilia/sp

Re: Rotina em c# para harbour

Mensagem por malcarli »

obg, mas não é ai que está dando o erro

Consegui evoluir mais ainda, mas a assinatura gerada pela rotina não confere com a assinatura calculada pelo executável, mas agora está mais perto da solução:

veja o trecho agora:

Código: Selecionar todos

FUNCTION AssinarTexto( cTexto )
   LOCAL oStore
   LOCAL oCerts
   LOCAL oCert
   LOCAL oSigner
   LOCAL oSignedData
   LOCAL cAssinatura

   /* 1. Abrir repositório de certificados */
   oStore := win_OleCreateObject( "CAPICOM.Store" )
   oStore:Open( 2, "My", 0 )   // 2 = CurrentUser, 0 = ReadOnly

   /* 2. Obter coleção de certificados */
   oCerts := oStore:Certificates

   IF oCerts:Count == 0
      RETURN NIL
   ENDIF

   /* 3 pegar o certificado por cnpj */
   oCert := CertificadoPorCNPJ( oCerts, [10229311000180] )

   IF oCert == NIL
      MsgInfo( "Certificado não encontrado para o CNPJ informado" )
      RETURN NIL
   ENDIF

   /* 4. Criar Signer */
   oSigner := win_OleCreateObject( "CAPICOM.Signer" )
   oSigner:Certificate := oCert

   /* 5. Criar SignedData */
   oSignedData := win_OleCreateObject( "CAPICOM.SignedData" )

   /* IMPORTANTE:
      CAPICOM assina STRING diretamente
      Ele mesmo faz SHA1 + RSA */
   oSignedData:Content := cTexto

   /* 6. Assinar (RSA-SHA1) */
   cAssinatura := oSignedData:Sign( oSigner, .F., 0 )

    if cAssinatura == [RZ2gOs00u38oPJXyLe9TMUoBd+gY5bL0XMxBTFKYrk180Flaws2OqlOGPNx5RTP2ND641yvcoegRXdr87mEea3a0hmU8WZSXmBbbwTrDndcTS8DCsNddbp01DdUWXc1Xkk+nY7sx+J/chgdMMAoFNLU4XrUFGbWnA9QXx2wA/J/zfRVil93BjfSW6WQsSnYp9MkoyOiL8amZbM0Cf2Pdr//oS5bMGV68bJ1Efas2SlTd1SGa4KYSU+OWI0AE2dXa55E8mhIRj94+gpAbITNAyqNqtAR496OnxV8HcQ9NCzr1c0XuPaSO8RyI8VVFTU1BNpXs4EV59BDf/jMwi++y5w==]
       msginfo([calculou corretamente])
   else
       msginfo(CASSINATURA + CRLF + ;
      [RZ2gOs00u38oPJXyLe9TMUoBd+gY5bL0XMxBTFKYrk180Flaws2OqlOGPNx5RTP2ND641yvcoegRXdr87mEea3a0hmU8WZSXmBbbwTrDndcTS8DCsNddbp01DdUWXc1Xkk+nY7sx+J/chgdMMAoFNLU4XrUFGbWnA9QXx2wA/J/zfRVil93BjfSW6WQsSnYp9MkoyOiL8amZbM0Cf2Pdr//oS5bMGV68bJ1Efas2SlTd1SGa4KYSU+OWI0AE2dXa55E8mhIRj94+gpAbITNAyqNqtAR496OnxV8HcQ9NCzr1c0XuPaSO8RyI8VVFTU1BNpXs4EV59BDf/jMwi++y5w==], [ERRADO])
   ENDIF
RETURN cAssinatura

FUNCTION CertificadoPorCNPJ( oCerts, cCNPJ )
   LOCAL i
   LOCAL oCert
   LOCAL cSubject

   /* Remove qualquer máscara do CNPJ */
   cCNPJ := StrTran( cCNPJ, ".", "" )
   cCNPJ := StrTran( cCNPJ, "/", "" )
   cCNPJ := StrTran( cCNPJ, "-", "" )

   FOR i := 1 TO oCerts:Count
      oCert := oCerts:Item( i )
      cSubject := Upper( oCert:SubjectName )
      IF At(cCNPJ, cSubject ) > 0
         RETURN oCert
      ENDIF
   NEXT

RETURN NIL
Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20365
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP
Curtiram: 1 vez

Re: Rotina em c# para harbour

Mensagem por JoséQuintas »

Sim — é totalmente possível usar CAPICOM sem MSXML5, mas com uma diferença importante:
✅ Você consegue gerar a assinatura digital (hash + assinatura RSA) usando CAPICOM
❌ Mas CAPICOM sozinho NÃO monta o XMLDSIG (o bloco <Signature> com SignedInfo, Reference, DigestValue, etc.)
Ou seja:
- CAPICOM assina os bytes
- MSXML5 monta o XMLDSIG
Se você remover o MSXML5, você terá que montar o XMLDSIG manualmente.
Vamos por partes.

✅ O que dá para fazer só com CAPICOM
Com CAPICOM você consegue:
✅ 1. Carregar o certificado
Set store = CreateObject("CAPICOM.Store")
store.Open 2, "My", 0
Set cert = store.Certificates.Item(1)


✅ 2. Criar o objeto SignedData
Set signed = CreateObject("CAPICOM.SignedData")
signed.Content = xmlSemAssinatura


✅ 3. Assinar
Set signer = CreateObject("CAPICOM.Signer")
signer.Certificate = cert

assinatura = signed.Sign(signer, True)


Isso gera uma assinatura PKCS#7 (P7S).
✅ Funciona
❌ Mas não é XMLDSIG
❌ Não serve para NF‑e, NFS‑e, CT‑e, etc.
❌ Não gera <ds:Signature>

✅ O que NÃO dá para fazer só com CAPICOM
CAPICOM não sabe gerar XMLDSIG, que é este formato:
<ds:Signature>
<ds:SignedInfo>...</ds:SignedInfo>
<ds:SignatureValue>...</ds:SignatureValue>
<ds:KeyInfo>...</ds:KeyInfo>
</ds:Signature>


Isso é exatamente o que o MSXML5.MXDigitalSignature fazia.
Sem MSXML5, você teria que:
- calcular o digest SHA1/SHA256
- canonicalizar o XML
- montar o SignedInfo
- assinar o SignedInfo
- montar o SignatureValue
- montar o KeyInfo
- inserir tudo no XML
Ou seja: trabalho enorme.

✅ Conclusão curta
✅ Dá para usar CAPICOM sem MSXML5
❌ Mas você perde a capacidade de gerar XMLDSIG automaticamente
✅ Você terá que montar o XMLDSIG manualmente (bem complexo)
José M. C. Quintas
Harbour 3.2, mingw, gtwvg mt, fivewin 25.04, multithread, dbfcdx, MySQL, ADOClass, PDFClass, SefazClass, (hwgui mt), (hmg3), (hmg extended), (oohg), PNotepad, ASP, stored procedure, stored function, Linux (Flagship/harbour 3.2)
"The world is full of kings and queens, who blind our eyes and steal our dreams Its Heaven and Hell"

https://github.com/JoseQuintas/
Responder