Página 1 de 1

Rotina em c# para harbour

Enviado: 12 Dez 2025 11:20
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;

        }
    }
}   

Re: Rotina em c# para harbour

Enviado: 13 Dez 2025 09:24
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.

Re: Rotina em c# para harbour

Enviado: 15 Dez 2025 16:45
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


Re: Rotina em c# para harbour

Enviado: 15 Dez 2025 17:25
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

Re: Rotina em c# para harbour

Enviado: 15 Dez 2025 17:42
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

Re: Rotina em c# para harbour

Enviado: 15 Dez 2025 18:44
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)