Jump to content

Recommended Posts

Posted
Em 16/11/2022 às 12:10, bugmen disse:

Eu também estou a desenvolver em PHP.  @Nel conseguiste adaptar o tcpdf? Podes dar-me umas luzes sff ? Andei à procura de novas versões do tcpdf mas sem sucesso.

Estou a fazer a partir deste exemplo: https://tcpdf.org/examples/example_052/

Depois para poderes trabalhar a partir de um pdf existente tens de ligar com o FPDI: https://manuals.setasign.com/fpdi-manual/v1/the-fpdf-tpl-class/

A ideia seria subtituir a obter da signature onde eles usam o openssl_pkcs7_sign, pela hash assinada remotamente pelo SAFE. 

Mas ainda não funciona bem. Depois há ainda a questão do selo temporal.

Posted (edited)

Acho que está mal porque diz que o documento foi alterado ou está corrompido, mesmo sem ltv deve aparecer o nome que está na assinatura digital mesmo que esteja invalida

 

 

O ltv nem é obrigatório,  é recomendado

Edited by laboss
Posted

Bem, já estou a integrar com o oAuth e a obter os tokens necessários para assinar documentos, agora estou com um problema... de (des)conhecimento...

Estou a obter uma string base64, com o conteudo do pdf (tipo isto: JVBERi0xLjQKJcfsj6IKNSAwIG9iago8PC9MZW5ndGggNiAwIFIvRmlsdGVyIC9GbGF0ZURlY29kZT4+CnN0cmVhbQp4nN1czZJdt3He36c4G6fmqjiH+P/RbiTSihxRpsQRUxUrC3o4HJF1hxRJKXb50bTNwpV1XiBVWajsrcubrNINoBt97sHcGVqzGlm0+sMBGt1Ao9EN4PLtpGZtJoX/I+LscnP/6zhdvN+oOXqbVZjWxLuLjYvaTS5kP4c4XTYYlUEI/034f7EBzeBsk5xjtNskGxkla5hDoRtzaIPIZ0VtECVb60F7H2t75Nzos03ttsHdporUYBW2sWigcj/bvPhok6Y/bPT0G/jz6tAgfP3ZnR2E7zZPNtZbaJtsxiKtU5pMdtO7882L+slr4/lTqh+ATW+z32DBKw0+iAbFJHmEvdZajDD07NqgIrBVg5ANAxgt...)

Ao aplicar o hash SHA256 a essa string obtenho uma string (tipo isto: 30b3192f1bea185ae50ca071860af59d5c9489a5b710b35ed0eeb64e193c2701), só que aqui esta string parece ser haxadecimal, as estou a usar uma função HASH(string a encripatar, algoritmo) disponibilizada pelo SAP SQL Anywhere.

Como não me parece que seja a string no ponto anterior, estou novamente a converter para base64, ficando tipo isto: MzBiMzE5MmYxYmVhMTg1YWU1MGNhMDcxODYwYWY1OWQ1Yzk0ODlhNWI3MTBiMzVlZDBlZWI2NGUxOTNjMjcwMQ==

Tenho que adicionar a essa string o prefixo do SHA256, que também tenho alguma dúvida no valor: estou a considerar o valor 628 em base64 que é "NjI4M".

Resumindo e baralhando, é-me retornada uma string base64 com a signature, mas pondo em pdf, dá erro a abrir o ficheiro.

A ferramenta de desenvolvimento é Delphi 6, o velhinho...

Alguma sugestão?

Posted

Fica aqui em ensaio funcional em C# feito com o iTextSharp -Version 5.5.13.1

Os certificados foram guardados a partir do credentials/info

Em PHP a dificuldade será fazer o que faz  sn.getAuthenticatedAttributeBytes. Irei experimentar o SetaPdf-Signer, mas é pago. A vantagem é que o @bioshock já disponibilizou o código.

 using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using iTextSharp.text;
using iTextSharp.text.pdf;
using iTextSharp.text.pdf.security;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.X509;
using X509Certificate = Org.BouncyCastle.X509.X509Certificate;

namespace SAFE
{

    public class SAFE_Sign
    {
        private static string src  = @"D:\SAFE\Exemplo.pdf";
        private static string dest = @"D:\SAFE\Exemplo-sign.pdf";
      
        private static string certFile1 = @"D:\SAFE\cert1.der";
        private static string certFile2 = @"D:\SAFE\cert2.der";
        private static string certFile3 = @"D:\SAFE\cert3.der";


        /*
         * https://www.linkedin.com/pulse/digital-signature-c-external-service-daniele-proietti/
         */

        //Install-Package iTextSharp -Version 5.5.13.1
        
        public void safe_sign()
        {

            X509CertificateParser parser = new X509CertificateParser();

            X509Certificate cert = parser.ReadCertificate(new FileStream(certFile1, FileMode.Open));
            X509Certificate cert2 = parser.ReadCertificate(new FileStream(certFile2, FileMode.Open));
            X509Certificate cert3 = parser.ReadCertificate(new FileStream(certFile3, FileMode.Open));

            using (PdfReader reader = new PdfReader(src))
            {
                using (MemoryStream ms = new MemoryStream())
                {
                    PdfStamper stamper = PdfStamper.CreateSignature(reader, ms, '\0');

                    // create the signature appearance
                    PdfSignatureAppearance sap = stamper.SignatureAppearance;
                    sap.SetVisibleSignature(new Rectangle(36, 748, 144, 780), 1, "sig");

                    sap.Reason = "";
                    sap.Location = "";

                    // It is then necessary to provide the PdfSignatureAppearance class with the certificate
                    // with the public key to be used for the signature.
                    sap.Certificate = cert; 


                    // The PDFSignature class contains information about the filter and sub-filters that are used
                    // for validating the signature embedded in a signed or certified PDF document.
                    PdfSignature dic = new PdfSignature(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
                    dic.Reason = sap.Reason;
                    dic.Location = sap.Location;
                    dic.Contact = sap.Contact;
                    dic.Date = new PdfDate(sap.SignDate);

                    sap.CryptoDictionary = dic;

                    Dictionary<PdfName, int> exc = new Dictionary<PdfName, int>();
                    exc.Add(PdfName.CONTENTS, (int)(8192 * 2 + 2));

                    sap.PreClose(exc);

                    //Now we need to extract the hash of the document that will be sent to the signature service.
                    Stream data = sap.GetRangeStream();


                    // The document digest is generated and put inside the attribute.
                    // The signing is done over the DER encoded authenticatedAttributes.
                    // This method provides that encoding and the parameters must be exactly the same as in GetEncodedPKCS7(byte[]).
                    List<X509Certificate> chain = new List<X509Certificate>() { cert, cert2, cert3 };
                    PdfPKCS7 sgn = new PdfPKCS7(null, chain, "SHA256", false);

                    byte[] hash = DigestAlgorithms.Digest(data, DigestAlgorithms.GetMessageDigest("SHA256"));

                    byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CMS);

                    //create sha256 message digest
                    using (SHA256 sha256 = SHA256.Create())
                    {
                        sh = sha256.ComputeHash(sh);
                    }

                    //Add sha256SigPrefix
                    byte[] sha256SigPrefix = { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 };
                    int length = sha256SigPrefix.Length + sh.Length;
                    byte[] sum = new byte[length];
                    sha256SigPrefix.CopyTo(sum, 0);
                    sh.CopyTo(sum, sha256SigPrefix.Length);
                    sh = sum;

                    //Now the byte array representation of the authenticatedAttributes is ready to be signed.    
                    //converte to base64
                    String base64encodedDigest = Convert.ToBase64String(sh);

                    //teste
                    File.WriteAllText(hashFile, base64encodedDigest);

                    Console.WriteLine("\nObtem SAFE signature");

                    string signatureBase64 = SAFE_Connect.getSignature(base64encodedDigest, "Exemplo.pdf");

                    Console.WriteLine("\nSAFE signature: " + signatureBase64);

                    //decode to bytes
                    byte[] signedHash = Convert.FromBase64String(signatureBase64);

                    //When we have the signed hash available, the PDF composition process must be completed.

                    sgn.SetExternalDigest(signedHash, null, "RSA");

                    byte[] encodedSig = sgn.GetEncodedPKCS7(hash, null, null, null, CryptoStandard.CMS);

                    byte[] paddedSig = new byte[8192];
                    encodedSig.CopyTo(paddedSig, 0);

                    PdfDictionary dic2 = new PdfDictionary();
                    dic2.Put(PdfName.CONTENTS, new PdfString(paddedSig).SetHexWriting(true));

                    sap.Close(dic2);

                    //Once finished the composition process of the PDF, we will have available in the “MemoryStream” the PDF signed;
                    //what we need to do is to transform it in a byte array and save it to the disk.
                    byte[] signed = ms.ToArray();
                    File.WriteAllBytes(dest, signed);

                }

            }

        }

    }
}

 

using System;
using System.IO;
using System.Net;
using System.Text;
using Newtonsoft.Json.Linq;

namespace SAFE
{
    public class SAFE_Connect
    {
        private static string baseURL = "https://pprsafe.autenticacao.gov.pt";

        private static string credentialID = "";
        private static string clientName   = "clientTest";
        private static string basicAuth    = "clientTest:Test";

        private static string SAFE_AccessToken = "";

        private static string processId = "a015722c-456e-4abc-9efb-d774936ce749";

        private static string SAFE_signAlgo  = "1.2.840.113549.1.1.11";

        public static string getSignature(string hash, string docname)
        {
            string token = SAFE_AccessToken;

            //get SAD
            string sad = authorize(token, hash, docname);

            //sign HASH
            string signature = null;

            if (sad != null) { 
                signature = signHash(token, hash, sad, SAFE_signAlgo);
            }

            return signature;
        }

        public static string authorize(string token, string hash,  string docname)
        {
            Uri loginURL = new Uri(baseURL + "/credentials/authorize");

            JObject reqData = new JObject();
            reqData.Add("clientData", 
                new JObject(
                    new JProperty("clientName", clientName),
                    new JProperty("documentNames", new JArray(docname)),
                    new JProperty("processId", processId)
               )
            );
            reqData.Add("credentialID", credentialID);
            reqData.Add("hashes", new JArray(hash) );
            reqData.Add("numSignatures", 1);

            var httpWebRequest = (HttpWebRequest)WebRequest.Create(loginURL);
            httpWebRequest.Method = "POST";
            httpWebRequest.ContentType = "application/json; charset=UTF-8";
            httpWebRequest.ContentLength = reqData.ToString().Length;

            httpWebRequest.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(basicAuth)) );
            httpWebRequest.Headers.Add("SAFEAuthorization", "Bearer " + token);


            //Send Request
            using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
            {
                streamWriter.Write(reqData.ToString());
            }

            //Get Response
            var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
            string result;
            using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
            {
                result = streamReader.ReadToEnd();
            }

            JObject respData = JObject.Parse(result);

            return respData.GetValue("sad").ToString();
        }

        public static string signHash(string token, string hash, string sad, string signAlgo)
        {
            Uri loginURL = new Uri(baseURL + "/signatures/signHash");

            JObject reqData = new JObject();
            reqData.Add("clientData",
                new JObject(
                    new JProperty("clientName", clientName),
                    new JProperty("processId", processId)
               )
            );
            reqData.Add("credentialID", credentialID);
            reqData.Add("hashes", new JArray(hash));
            reqData.Add("sad", sad);
            reqData.Add("signAlgo", signAlgo);

            var httpWebRequest = (HttpWebRequest)WebRequest.Create(loginURL);
            httpWebRequest.Method = "POST";
            httpWebRequest.ContentType = "application/json; charset=UTF-8";
            httpWebRequest.ContentLength = reqData.ToString().Length;

            httpWebRequest.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(basicAuth)));
            httpWebRequest.Headers.Add("SAFEAuthorization", "Bearer " + token);


            //Send Request
            using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
            {
                streamWriter.Write(reqData.ToString());
            }

            //Get Response
            var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
            string result;
            using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
            {
                result = streamReader.ReadToEnd();
            }

            JObject respData = JObject.Parse(result);

            return respData.GetValue("signatures")[0].ToString();
        }
    }
}

 

  • Vote 2
Posted (edited)

Depois de enviar as evidências todas e eles validarem os PDF foram 3 dias para receber as credenciais... mas já tinha assinado o protocolo 

 

Edited by laboss
Posted
Em 16/11/2022 às 13:13, Nel disse:

Estou a fazer a partir deste exemplo: https://tcpdf.org/examples/example_052/

Depois para poderes trabalhar a partir de um pdf existente tens de ligar com o FPDI: https://manuals.setasign.com/fpdi-manual/v1/the-fpdf-tpl-class/

A ideia seria subtituir a obter da signature onde eles usam o openssl_pkcs7_sign, pela hash assinada remotamente pelo SAFE. 

Mas ainda não funciona bem. Depois há ainda a questão do selo temporal.

Obrigado. Tenho estado analisar o código do TCPDF e também não sei muito bem com adaptá-lo. Assim que conseguir um avanço partilho aqui.

 

Uma questão no processo de autenticação OAuth da AMA. Que URL de redirecionamento devo utilizar no caso de ter "múltiplos" subdominios consoante o cliente? Exemplo:

Na documentação referem que pode ser https://sub.dominio.pt ou https://dominio.pt/ ....   Poderá ser no formato  *.sub.dominio.pt? Exemplo : cliente1.sub.dominio.pt , cliente2.sub.dominio.pt ..... ? Não sei se fui claro, mas se alguém passou por esta questão que abordagem seguiu? 

Estou a desenvolver em PHP. Obrigado.

Posted (edited)
On 11/19/2022 at 2:46 PM, laboss said:

Depois de enviar as evidências todas e eles validarem os PDF foram 3 dias para receber as credenciais... mas já tinha assinado o protocolo 

Já se passou 1 semana e nada 😔, ainda não me responderam ao email.

Edited by bioshock
Posted (edited)
On 11/22/2022 at 11:46 AM, bugmen said:

Obrigado. Tenho estado analisar o código do TCPDF e também não sei muito bem com adaptá-lo. Assim que conseguir um avanço partilho aqui.

 

Uma questão no processo de autenticação OAuth da AMA. Que URL de redirecionamento devo utilizar no caso de ter "múltiplos" subdominios consoante o cliente? Exemplo:

Na documentação referem que pode ser https://sub.dominio.pt ou https://dominio.pt/ ....   Poderá ser no formato  *.sub.dominio.pt? Exemplo : cliente1.sub.dominio.pt , cliente2.sub.dominio.pt ..... ? Não sei se fui claro, mas se alguém passou por esta questão que abordagem seguiu? 

Estou a desenvolver em PHP. Obrigado.

O que interessa é o dominio. 

Podes ter qualquer coisa desde que faça parte do dominio que registaste com eles.

Eles verificam depois se o dominio que usaste no redirect do oauth esta registado com eles. Por isso, podes usar qualquer subdominio desde que pertença ao dominio que registaste.

Edited by zeph
  • Thanks 1
Posted
On 10/29/2022 at 8:22 AM, laboss said:

Depois de meteres o container da assinatura e basicamente isso, o código não consigo passar pois é da empresa, mas é basicamente isto

https://stackoverflow.com/questions/54559547/external-signing-pdf-with-itext

Não usei essa lib usei devexpress mas a muita gente a usar essa

Hi, you have an example of a SAFE signature with devexpress? 
I did it with itext7 ... but it costs too much!

thank you 🙂

  • 2 weeks later...
Posted (edited)
Em 23/11/2022 às 15:31, bioshock disse:

Já se passou 1 semana e nada 😔, ainda não me responderam ao email.

Vamos la mais um aninho.

 

Quem será que nao conseguiu terminar a tempo para levar a adiamento.

Edited by blacksnake

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.