Jump to content

Assinatura digital PDF


Morfas3

Recommended Posts

Morfas3

@reznor tens de criar uma chain com o certificado do user que obteste  pelo serviço da Ama

 Org.BouncyCastle.X509.X509Certificate[] chain = new Org.BouncyCastle.X509.X509Certificate[] { certParse.ReadCertificate(cert.RawData) }; 

Link to post
Share on other sites
  • Replies 60
  • Created
  • Last Reply

Top Posters In This Topic

  • Morfas3

    24

  • reznor

    23

  • Labreu

    10

  • gatpereira

    1

Top Posters In This Topic

Popular Posts

Boas. Se alguém tiver interesser, criei uma demo online:  https://github.com/luisabreu/AmaCmdSigning

Bom dia  @reznor , Primeiro tem de ter o ao serviço soap que ama Disponibiliza, já tem isso? Faça uns testes a invocar isso, eles têm um manual que explicam como invocar os serviços. Is

têm de usar o singdeferred using (PdfReader reader = new PdfReader("_nome_ficheiro"))             {                 using (FileStream fileStream = new FileStream($"_nome_ficheiro_assinado_ouput",

reznor

@Morfas3
a.png

Este print é o resultado de quando valido o codigo SMS que me foi enviado. Perguntei à AMA se o campo "certificate" deveria vir a null e disseram-me que era normal e que o processo da CMD acaba aqui.

Este certificado que falas é suposto vir aqui ou é um outro método que eles disponibilizam chamado GetCertificate que me retorna 3 certificados distintos?
Obrigado

Link to post
Share on other sites
reznor

@Morfas3
Recebo 3 certificado nesse getCertificate. Construo uma chain onde só 2 deles são parsed com sucesso, depois tento assinar mas a assinatura vem inválida.
Estou a usar iTextSharp e não itext.
Sabes se sao necessários os 3 certificados que mandam no método?

Link to post
Share on other sites
Morfas3

eu usei o itext7 no fundo é quase a mesma coisa

Tens de fazer fazer uma assinatura em branco quando vais buscar o hash do documento antes de enviar para a AMA.

Eu tenho de ver melhor como foi feito para vos explicar, confesso que não posso espetar aqui o código de como foi feito, porque o código não me pertence.

 

Link to post
Share on other sites
reznor

@Morfas3
Certo nem é isso que se pretende, isto é um processo bastante complexo e ha pouca documentação.
A questão de criar a assinatura em braco e enviar o hash esta ok, já esta á a assinar só que não está válida...
Screenshot-2021-03-15-180024.png

 

Tenho de revalidar todo o codigo, pode ser alguma coisa que nao estou a fazer certo

Link to post
Share on other sites
Morfas3

@reznor sobre o certificado o serviço traz um .pem e é so usares esse ficheiro a construir a chain

sobre a assinatura em branco, tens de implementar outro container  a implementar a IExternalSignatureContainer em que tens de fazer o hash do documento com o prefixo que eles falam na documentação isto na implementação do método byte[] Sign(Stream data),

implementa também este metodo

public void ModifySigningDictionary(PdfDictionary signDic)
        {
            signDic.Put(PdfName.Filter, PdfName.Adobe_PPKLite);
            signDic.Put(PdfName.SubFilter, tPdfName.Adbe_pkcs7_detached);
        }

depois é fazer isto

 PdfSigner signer = new PdfSigner(pdfReader, filestream, new StampingProperties());
                   var chain = Getcertificado_do_user(filpath);


                    signer.SetFieldName("fieldname");
                    this.SetCustomSignature(signer.GetDocument(), signer.GetSignatureAppearance(), chain[0], signatureLocation, signatureReason);


                    IExternalSignatureContainer external = new ExternalContainer(PdfName.Adobe_PPKLite, PdfName.Adbe_pkcs7_detached);
                    signer.SignExternalContainer(external, 8192);



hashToSend = ((ExternalContainer)external).DocumentHash;

 

Link to post
Share on other sites
Morfas3

@reznor esquci-me de um promenor 

 

 this.SetCustomSignature é implementado por nós, mas basicamente é onde se faz a customização da assinatura, mas tem lá este pequeno truque importante

este parametro que passas no metodo,  signer.GetSignatureAppearance() 

serve para fazer isto abaixo

signerappereance .SetCertificate(chain do certificado utilizador);

 

Link to post
Share on other sites
reznor

@Morfas3
Bom dia, não percebi a parte de fazer a hash do documento com o prefixo eles falam na documentação...

De resto, penso que esta tudo implementado do meu lado ;) 

Link to post
Share on other sites
Morfas3

@reznor eles têm lá um conjunto de byes do documento, agora não encontro a documentação mas penso que vai de encontro a estes bytes abaixo

           byte[] sha256SigPrefix = new byte[] {
                 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09,
                 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01,
                 0x05, 0x00, 0x04, 0x20
                };

Link to post
Share on other sites
reznor

@Morfas3

ok, já percebi, o que não estou a entender é que é dito lá no documento que "O hash enviado para os serviços deve ser o prefixo seguido do hash do documento.", isto vai seguir o EMSA-PKCS1-v1_5, estou com dificuldade em fazer isto, como computar estas duas hash para enviar para a CMD.
É a primeira vez que estou a lidar com crypto, algumas coisas são completamente novas e peço desculpa se parecer demasiado parvo ou non-sense qualquer pergunta da minha parte

Link to post
Share on other sites
Morfas3
Posted (edited)

@reznor basicamente tens de fazer   byte[] docHash = Sha256.ComputeHash(array de bytes do doc),  e juntar este bytearray ao array de bytes do prefixo

Deixa lá quando andamos a implementar isto, quase que íamos chegar ao ponto de implementar o algorimto de hash que eles falam na documentação (visto que já não conseguiamos perceber o que estava mal), mas depois confirmei que o .net já tem isto. Foi muita tentativa e erro.

Edited by Morfas3
  • Vote 1
Link to post
Share on other sites
reznor

@Morfas3
Tentativa erra à fartazana, é como levar com brita na cara constantemente!!
Já consegui assinar à partida, tenho alguns warnings no PDF

Screenshot-2021-03-16-150512.png

Aconteceu-te algo do género?
Já agora, eu não sei se estes testes em Pré Produção da AMA devem dar assinaturas válidas atenção, tentei perguntar mas é complicado do lado deles responder...

Link to post
Share on other sites
Labreu

Boas pessoal. 

Tb estou a tentar implementar a assinatura digital mas estou a ter o mesmo erro na assinatura que o @reznor estava a ter. 

Na prática, esotu obter certificado sem pin, recuperar a chain de certificados (lista) através do bouncy castle e a utilizar a mm estratégia que estão usar aqui... Acho eu... Mas está mm a me faltar algo simples... @reznor alguma dica para passar da imagem 1 a 2? Já agora, estavas a ter o ber encoding error?

 

Obrigado. 

Link to post
Share on other sites
reznor

@Labreu
Basicamente estás a fazer o mesmo que estamos a fazer, em relação ao hash que envias para a AMA, colocaste o prefixo? Aquela dica que o @Morfas3 deu antes da minha segunda imagem?
Não estou a ter o encoding error, estou na fase em que tenho o doc assinado, se vir os detalhes de assinatura esta tudo ok (à primeira vista) e não sei qual sera o problema seguinte

Edited by reznor
Link to post
Share on other sites
Labreu

Sim, penso que estou a fazer corretamente isso. Basicamente, estou a fazer o seguinte (C#):
 

        public byte[] GetDocHashFromPreparedDocToSign(string pathToOriginalPdf, string pathToPreparedToBeSignedPdf, List<X509Certificate> certificates) {
            var pdfSigner = new PdfSigner(new PdfReader(pathToOriginalPdf),
                                          new FileStream(pathToPreparedToBeSignedPdf, FileMode.Create),
                                          new StampingProperties());
            pdfSigner.SetFieldName(_signatureFieldname);


            var appearance = pdfSigner.GetSignatureAppearance();
            appearance.SetPageRect(new Rectangle(144, 144, 200, 100))
                      .SetPageNumber(1)
                      .SetCertificate(certificates[0]);

            var container = new ExternalBlankSignatureContainer(PdfName.Adobe_PPKLite, PdfName.Adbe_pkcs7_detached);
            pdfSigner.SignExternalContainer(container, 8192);
            

           byte[] sha256SigPrefix = { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09,
                                         0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01,
                                         0x05, 0x00, 0x04, 0x20 };

           // get hash from pdf with reserved space and preprending with specified
            using var stream = File.OpenRead(pathToPreparedToBeSignedPdf);
            var data = DigestAlgorithms.Digest(stream, DigestAlgorithms.SHA256);
            var totalHash = new byte[sha256SigPrefix.Length + data.Length];
            sha256SigPrefix.CopyTo(totalHash, 0);
            data.CopyTo(totalHash, sha256SigPrefix.Length);
            return totalHash;
        }
 

O resultado deste metodo e passado para o servico da CMD:

    var pdfManager = new PdfManager();
    var pathToSigned = "c:\\temp\\doc1.pdf";
    var docHash = pdfManager.GetDocHashFromPreparedDocToSign(pathDocPdf, pathToSigned, certificates);
    var signReq = new SignRequest   {
        ApplicationId = applicationId,
        DocName = "Doc1.pdf",
        Hash = docHash,
        Pin = pinCmdBase64,
        UserId = userIdBase64
    };    
    SignStatus resScmdSign = await client.SCMDSignAsync(signReq);
 

O resultado devolvido esta ok (200), pelo que me limito a chamar o ValidateOptAsync:

    var resValidacaoPin = await client.ValidateOtpAsync(codeBase64, resScmdSign.ProcessId, applicationId);
    if(resValidacaoPin.Status.Code == "200") {
        var pathToSignedWithSiognature = "c:\\temp\\doc1_signed.pdf";
        // valid signature, replace temporary with new one
        pdfManager.SignPreparedToBeSignedDoc(pathToSigned, pathToSignedWithSiognature, resValidacaoPin.Signature);
    }
 

E já agora, o método SignPreparedToBeSignedDoc:

    public void SignPreparedToBeSignedDoc(string pathToPreparedToBeSignedPdf, string pathToSignedFile, byte[] signature) {
        var document = new PdfDocument(new PdfReader(pathToPreparedToBeSignedPdf));
        using var writer = new FileStream(pathToSignedFile, FileMode.Create);

        var container = new ExternalInjectingSignatureContainer(signature);
        PdfSigner.SignDeferred(document, _signatureFieldname, writer, container);
    }
 

    internal class ExternalInjectingSignatureContainer : IExternalSignatureContainer {
        private readonly byte[] _signature;

        public ExternalInjectingSignatureContainer(byte[] signature) {
            _signature = signature;
        }

        public byte[] Sign(Stream data) => _signature;

        public void ModifySigningDictionary(PdfDictionary signDic) {
        }
    }
 

Edição: Relativamente aos certificados, estou a recuperar a chain devolvida pela AMA e a utilizar o primeiro certificado. O código de recuperação dos certificados é o seguinte:

    public static List<X509Certificate> ConvertStringToCertificate(string certificate)    {
        
        byte[] bytes = Encoding.ASCII.GetBytes(certificate);
        var certs = new X509CertificateParser().ReadCertificates(bytes)
                                               .Cast<X509Certificate>()
                                               .ToList();
        return certs;
    }
 

 

Basicamente, faco o SignDeferred e passo o array de bytes devolvido pelo valdateopt da CMD...

À primeira vista, parece-me que isto está certo, mas às tantas estou a falhar algo óbvio...

 

 

Edited by Labreu
Mais informação
Link to post
Share on other sites
reznor

@Labreu
Penso que te falta algo no método Sign do IExternalSignatureContainer implementado:
 

public byte[] Sign(Stream data)
{
	PdfPKCS7 sgn = new PdfPKCS7(null, certificado.ToArray(), "SHA256", false);
    sgn.SetExternalDigest(this.signedBytes, null, "RSA");
    return sgn.GetEncodedPKCS7();
}

 

Onde "certificado" é a lista de certificados do método GetCertificate depois de tratado, podes validar?
 

Link to post
Share on other sites
Labreu

Damn...sim, já está melhor, mas ainda devo ter um erro aqui no cálculo do hash. Agora já mostra info assinatura mas dá o erro tipico do ficheiro modificado depois da assinatura...

 

 

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...

Important Information

By using this site you accept our Terms of Use and Privacy Policy. We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.