protected void Page_Load(object sender, EventArgs e) { // Get userfile from query string. var userfile = Request.QueryString["userfile"]; // Our action only works if a userfile is given to work with. if (string.IsNullOrEmpty(userfile)) { // Return "Not Found" HTTP response. Response.StatusCode = 404; Response.End(); return; } // Read document from storage. var fileContent = Storage.GetFile(userfile); // Open an validate signatures with PKI SDK based on the PAdES Basic policy. var signature = Lacuna.Pki.Pades.PadesSignature.Open(fileContent); var policyMapper = PadesPoliciesForGeneration.GetPadesBasic(Util.GetTrustArbitrator()); // Generate a model to be shown on the page from the PadesSignature instance computed from Open() // method above. This class can be inspected on SignatureModels.cs file. In this class, we validate // each signature based on the policy mapper defined above. var model = new PadesSignatureModel(signature, policyMapper); // Set property for rendering on page (see aspx file). this.Model = model; }
private IPadesPolicyMapper GetSignaturePolicy() { // Get our custom trust arbitrator which accepts test certificates (see Util.GetTrustArbitrator()). var arbitrator = Util.GetTrustArbitrator(); return(PadesPoliciesForGeneration.GetPadesBasic(arbitrator)); }
/** * This method defines the signature policy that will be used on the signature. */ private IPadesPolicyMapper getSignaturePolicy() { #if DEBUG // During debug only, we return a wrapper which will overwrite the policy's default trust arbitrator (which in this case // corresponds to the ICP-Brasil roots only), with our custom trust arbitrator which accepts test certificates // (see Util.GetTrustArbitrator()) return(PadesPoliciesForGeneration.GetPadesBasic(Util.GetTrustArbitrator())); #else return(PadesPoliciesForGeneration.GetPadesBasic(TrustArbitrators.PkiBrazil)); #endif }
static void Main(string[] args) { // This is a TRIAL token. It will expire at 31/08/2020. PkiConfig.LoadLicense(Convert.FromBase64String("AxAAIIy8jc59Q0q95BZrL57K5hEAUEtJIFN1aXRlIFNhbXBsZXMIAAD0Ze31HdgICACAXwryrU7YCAAAAAAAAAQAfwAAAAABL2+ls7EW5LHD/tEetd49d0JpmU7pXEjhH0pU1ZSp5qjvKxL8c8PZz6ODTf68+lfQtXkKaRlQH6hu7VTSU3fvhCmZovDB5ruKqJPn+MQRDBbS8Wkr/meVo9LBS+3NFOky+EY43ebFoFxTbVZl2lCjb0DuskJiZGuHOBJ1v2XpGdKCmh1c1LmMvpc+OPegzNuMCXoEzSN9DdRtKnDzRxvOnvPglCX9+oV89LWsmVzonRp1a+tluqa8Ron9pFdHI9cWBElcXpmwXbKbmP0Sy5yYbYpE+rYsNgD5sV/FwF8uOxGWA0/mRWLZlO3OcGWoYo7qBBDmCUApAcRmZR3tXqhELQ==")); var connection = new ConnectionBuilder() .WithLogging() .Build(); // "List Certificates" operation. connection.On <string, List <CertificateModel> >("list-certs", _ => { var store = WindowsCertificateStore.LoadPersonalCurrentUser(); return(store.GetCertificatesWithKey().Select(c => new CertificateModel(c.Certificate)).ToList()); }); // "Sign a PDF" operation. connection.On <SignatureRequestModel, string>("sign-pdf", request => { var signer = new PadesSigner(); var store = WindowsCertificateStore.LoadPersonalCurrentUser(); var signingCert = store.GetCertificatesWithKey().First(c => c.Certificate.ThumbprintSHA256.SequenceEqual(request.CertThumb)); signer.SetSigningCertificate(signingCert); signer.SetPdfToSign(request.FileToSign); var trustArbitrator = new LinkedTrustArbitrator(TrustArbitrators.PkiBrazil, TrustArbitrators.Windows); // For development purposes, we also trust in Lacuna Software's test certificates. var lacunaRoot = Lacuna.Pki.PKCertificate.Decode(Convert.FromBase64String("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")); // COMMENT the line below before production release trustArbitrator.Add(new TrustedRoots(lacunaRoot)); signer.SetPolicy(PadesPoliciesForGeneration.GetPadesBasic(trustArbitrator)); signer.ComputeSignature(); byte[] signedPdf = signer.GetPadesSignature(); var tempLocation = Path.GetTempFileName(); File.WriteAllBytes(tempLocation, signedPdf); return(tempLocation); }); // Acknowledges that the connection is running. connection.On <string, string>("ping", argument => "pong"); // wait for incoming requests connection.Listen(); }
public void Sign() { if (string.IsNullOrEmpty(PdfPath)) { MessageBox.Show("Please choose a PDF file sign"); return; } if (!File.Exists(PdfPath)) { MessageBox.Show("File not found: " + PdfPath); return; } if (SelectedCertificate == null) { MessageBox.Show("Please choose a certificate to sign the PDF"); return; } if (Width <= 0 || Height <= 0 || Bottom <= 0 || Left <= 0) { MessageBox.Show("Please fill all coordinates with positive numbers"); return; } try { var signer = new PadesSigner(); // Instantiate the PDF signer signer.SetSigningCertificate(selectedCertificate.CertificateWithKey); // certificate with private key associated signer.SetPdfToSign(PdfPath); // PDF file path signer.SetPolicy(PadesPoliciesForGeneration.GetPadesBasic(App.GetTrustArbitrator())); // Basic signature policy with the selected trust arbitrator signer.SetVisualRepresentation(getVisualRepresentation(selectedCertificate.CertificateWithKey.Certificate)); // Signature visual representation signer.ComputeSignature(); // computes the signature byte[] signedPdf = signer.GetPadesSignature(); // return the signed PDF bytes // saving signed PDF file var savePath = getSaveFilePath(); if (!string.IsNullOrEmpty(savePath)) { File.WriteAllBytes(savePath, signedPdf); Process.Start(savePath); } } catch (ValidationException ex) { new ValidationResultsDialog("Validation failed", ex.ValidationResults).ShowDialog(); } catch (Exception ex) { MessageBox.Show($"Error while signing PDF\r\n\r\n{ex}"); } }
protected void Page_Load(object sender, EventArgs e) { // Get verification code from query string. var formattedVerificationCode = Request.QueryString["c"]; // On PrinterFriendlyVersion.aspx, we stored the unformatted version of the verification code // (without hyphens) but used the formatted version (with hyphens) on the printer-friendly PDF. Now, // we remove the hyphens before looking it up. var verificationCode = AlphaCode.Parse(formattedVerificationCode); // Get document associated with verification code. var fileId = Storage.LookupVerificationCode(verificationCode); if (fileId == null) { // Invalid code given! // Small delay to slow down brute-force attacks (if you want to be extra careful you might want // to add a CAPTCHA to the process). Thread.Sleep(TimeSpan.FromSeconds(2)); // Return "Not Found" HTTP response. Response.StatusCode = 404; Response.End(); return; } // Read document from storage. var fileContent = Storage.GetFile(fileId); // Open and validate signatures with PKI SDK based on the PAdES Basic policy. var signature = Lacuna.Pki.Pades.PadesSignature.Open(fileContent); var policyMapper = PadesPoliciesForGeneration.GetPadesBasic(Util.GetTrustArbitrator()); // Generate a model to be shown on the page from the PadesSignature instance computed from Open() // method above. This class can be inspected on SignatureModels.cs file. In this class, we validate // each signature based on the policy mapper defined above. var model = new PadesSignatureModel(signature, policyMapper); // Set properties for rendering on page (see aspx file). this.FileId = fileId; this.Model = model; }
public ActionResult Start(SignatureStartModel model) { byte[] toSignBytes, transferData; SignatureAlgorithm signatureAlg; try { // Verify if the userfile exists and get its absolute path. string userfilePath; if (!StorageMock.TryGetFile(model.UserFile, out userfilePath)) { return(HttpNotFound()); } // Decode the user's certificate. var cert = PKCertificate.Decode(model.CertContent); // Get an instance of the PadesSigner class. var padesSigner = new PadesSigner(); // Set the file to be signed. padesSigner.SetPdfToSign(userfilePath); // REQUIRED! // Provide the signer's certificate. You must sign with a valid digital // certificate of a doctor, who was registered on CRM. In this sample, // we used a sample certificate stored on server to do the execute this // sample. padesSigner.SetSigningCertificate(cert); // REQUIRED! // Define the trust arbitrator, which will configure the signer to // some kind of certificate. In the case of this sample, only // ICP-Brasil certificates will be accepted in the defined standard. var trustArbitrator = new LinkedTrustArbitrator(TrustArbitrators.PkiBrazil); #if DEBUG // For development purposes, we also trust in Lacuna Software's test certificates. var lacunaRoot = Lacuna.Pki.PKCertificate.Decode(Convert.FromBase64String("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")); trustArbitrator.Add(new TrustedRoots(lacunaRoot)); #endif // REQUIRED! // Use a policy accepted by ICP-Brasil. We use the trust arbitrator // defined above to configure the policy. padesSigner.SetPolicy(PadesPoliciesForGeneration.GetPadesBasic(trustArbitrator)); // REQUIRED! // Use a custom signature field name. This field MUST have the // "Emitente" keyword as the last keyword. padesSigner.SetCustomSignatureFieldName("Signature1 Emitente"); // REQUIRED! // Set Certification Level to not allow changes after signed. padesSigner.SetCertificationLevel(PadesCertificationLevel.CertifiedNoChangesAllowed); // Set a visual representation for the signature. padesSigner.SetVisualRepresentation(PadesVisualElements.GetVisualRepresentationForPkiSdk(cert)); // Generate the "to-sign-bytes". This method also yields the signature algorithm that must // be used on the client-side, based on the signature policy, as well as the "transfer data", // a byte-array that will be needed on the next step. toSignBytes = padesSigner.GetToSignBytes(out signatureAlg, out transferData); } catch (ValidationException ex) { // Some of the operations above may throw a ValidationException, for instance if the certificate // encoding cannot be read or if the certificate is expired. ModelState.AddModelError("", ex.ValidationResults.ToString()); return(View()); } // On the next step (Complete action), we'll need once again some information: // - The thumbprint of the selected certificate. // - The "transfer data" used to validate the signature in complete action. Its content is stored in // a temporary file (with extension .bin) to be shared with the Complete action. // - The "to-sign-hash" (digest of the "to-sign-bytes") to be signed. (see signature-complete-form.js) // - The OID of the digest algorithm to be used during the signature operation. // We'll store these values on TempData, which is a dictionary shared between actions. TempData["SignatureCompleteModel"] = new SignatureCompleteModel() { CertThumb = model.CertThumb, TransferDataFileId = StorageMock.Store(transferData, ".bin"), ToSignHash = signatureAlg.DigestAlgorithm.ComputeHash(toSignBytes), DigestAlgorithmOid = signatureAlg.DigestAlgorithm.Oid }; return(RedirectToAction("Complete", new { userfile = model.UserFile })); }
public async Task <IActionResult> Post([FromBody] SignatureRequest request) { // 1. Retrieve key using certId stored on your database. byte[] pkcs12; try { pkcs12 = await _azureKeyVaultStore.GetPkcs12Async(request.CertId); } catch (InvalidIdentifierException ex) { return(UnprocessableEntity(new ErrorModel() { Code = ErrorCodes.InvalidIdentifier, Message = ex.Message, })); } // 2. Open PKCS#12, verifying valid of the provided password. Pkcs12CertificateStore store; try { store = Pkcs12CertificateStore.Load(pkcs12, request.Pkcs12Password); } catch (IncorrectPinException ex) { return(UnprocessableEntity(new ErrorModel() { Code = ErrorCodes.InvalidPIN, Message = ex.Message, })); } // 3. Retrieve certification info (include its key). var certs = store.GetCertificatesWithKey(); if (!certs.Any()) { return(UnprocessableEntity(new ErrorModel() { Code = ErrorCodes.InvalidPkcs12, Message = "The provided PKCS#12 file is not valid", })); } var cert = certs.First(); // 4. Perform signature. var signer = new PadesSigner(); signer.SetSigningCertificate(cert); signer.SetPdfToSign(Path.Combine(_webHostEnvironment.ContentRootPath, "Resources", "SamplePdf.pdf")); signer.SetPolicy(PadesPoliciesForGeneration.GetPadesBasic()); signer.SetVisualRepresentation(GetVisualRepresentation(cert.Certificate)); signer.ComputeSignature(); byte[] signedPdf = signer.GetPadesSignature(); // 5. Store signature file. if (!System.IO.File.Exists(Path.Combine(_webHostEnvironment.ContentRootPath, "App_Data"))) { Directory.CreateDirectory(Path.Combine(_webHostEnvironment.ContentRootPath, "App_Data")); } var fileId = Guid.NewGuid() + ".pdf"; System.IO.File.WriteAllBytes(Path.Combine(_webHostEnvironment.ContentRootPath, "App_Data", fileId), signedPdf); return(Ok(new SignatureResponse() { FileId = fileId, })); }
private static IPadesPolicyMapper GetSignaturePolicy() { return(PadesPoliciesForGeneration.GetPadesBasic(GetTrustArbitrator())); }
private byte[] generatePrinterFriendlyVersion(byte[] pdfContent, string verificationCode) { // The verification code is generated without hyphens to save storage space and avoid copy-and-paste // problems. On the PDF generation, we use the "formatted" version, with hyphens (which will later // be discarded on the verification page). var formattedVerificationCode = AlphaCode.Format(verificationCode); // Build the verification link from the constant "VerificationLinkFormat" (see above) and the // formatted verification code. var verificationLink = string.Format(VerificationLinkFormat, formattedVerificationCode); // 1. Inspect signatures on the PDF. var signature = Lacuna.Pki.Pades.PadesSignature.Open(pdfContent); // 2. Create PDF with verification information from the signed PDF. var pdfMarker = new PdfMarker(); // Build string with joined names of signers (see method getDisplayName() below). var signerNames = Util.JoinStringsPt(signature.Signers.Select(s => getDisplayName(s.Signer.SigningCertificate))); var allPagesMessage = string.Format("This document was digitally signed by {0}.\nTo verify the signatures go to {1} on {2} and inform the code {3}", signerNames, VerificationSiteNameWithArticle, VerificationSite, formattedVerificationCode); // ICP-Brasil logo on bottom-right corner of every page (except on the page which will be created at // the end of the document). pdfMarker.AddMark(new PdfMark() { PageOption = PdfMarkPageOptions.AllPages, Container = new PadesVisualRectangle() { Width = 1, Right = 1, Height = 1, Bottom = 1 }, Elements = new List <PdfMarkElement>() { new PdfMarkImage() { ImageContent = Storage.GetIcpBrasilLogoContent(), Opacity = 75 } } }); // Summary on bottom margin of every page (except on the page which will be created at the end of // the document). pdfMarker.AddMark(new PdfMark() { PageOption = PdfMarkPageOptions.AllPages, Container = new PadesVisualRectangle() { Height = 2, Bottom = 0, Left = 1.5, Right = 3.5 }, Elements = new List <PdfMarkElement>() { new PdfMarkText() { Texts = new List <PdfTextSection>() { new PdfTextSection() { Style = PdfTextStyle.Normal, Text = allPagesMessage } } } } }); // Summary on right margin of every page (except on the page which will be created at the end of the // document), rotated 90 degrees counterclockwise (text goes up). pdfMarker.AddMark(new PdfMark() { PageOption = PdfMarkPageOptions.AllPages, Container = new PadesVisualRectangle() { Width = 2, Right = 0, Top = 1.5, Bottom = 3.5 }, Elements = new List <PdfMarkElement>() { new PdfMarkText() { Rotation = PdfMarkRotation.D90, Texts = new List <PdfTextSection>() { new PdfTextSection() { Style = PdfTextStyle.Normal, Text = allPagesMessage } } } } }); // Create a "manifest" mark on a new page added on the end of the document. We'll add several // elements to this marks. var manifestMark = new PdfMark() { PageOption = PdfMarkPageOptions.NewPage, // This mark's container is the whole page with 1-inch margins. Container = new PadesVisualRectangle() { Top = 2.54, Bottom = 2.54, Right = 2.54, Left = 2.54 } }; // We'll keep track of our "vertical offset" as we add elements to the mark. double verticalOffset = 0; double elementHeight; elementHeight = 3; // ICP-Brasil logo on the upper-left corner. manifestMark.Elements.Add(new PdfMarkImage() { RelativeContainer = new PadesVisualRectangle() { Height = elementHeight, Top = verticalOffset, Width = elementHeight, /* Using elemengHeight as width because the image is square. */ Left = 0 }, ImageContent = Storage.GetIcpBrasilLogoContent() }); // QR Code with the verification link on the upper-right corner. We will generate a PdfMarkImage from // a QR Code generated using the QRCoder library. byte[] qrCodeImageContent; using (var qrGenerator = new QRCodeGenerator()) { using (var qrCodeData = qrGenerator.CreateQrCode(verificationLink, QRCodeGenerator.ECCLevel.M)) { using (var qrCode = new QRCode(qrCodeData)) { var qrCodeBitmap = qrCode.GetGraphic(10, Color.Black, Color.White, false); using (var buffer = new MemoryStream()) { qrCodeBitmap.Save(buffer, ImageFormat.Png); qrCodeImageContent = buffer.ToArray(); }; } } } manifestMark.Elements.Add(new PdfMarkImage() { RelativeContainer = new PadesVisualRectangle() { Height = elementHeight, Top = verticalOffset, Width = elementHeight, /* Using elemengHeight as width because the image is square. */ Right = 0 }, ImageContent = qrCodeImageContent }); // Header "SIGNATURES VERIFICATION" centered between ICP-Brasil logo and QR Code. manifestMark.Elements.Add(new PdfMarkText() { RelativeContainer = new PadesVisualRectangle() { Height = elementHeight, Top = verticalOffset + 0.2, Right = 0, Left = 0 }, Align = PadesHorizontalAlign.Center, Texts = new List <PdfTextSection>() { new PdfTextSection() { Style = PdfTextStyle.Normal, FontSize = NormalFontSize * 1.6, Text = "SIGNATURES\nVERIFICATION" } } }); verticalOffset += elementHeight; // Verifical padding. verticalOffset += 1.7; // Header with verification code. elementHeight = 2; manifestMark.Elements.Add(new PdfMarkText() { RelativeContainer = new PadesVisualRectangle() { Height = elementHeight, Top = verticalOffset, Right = 0, Left = 0 }, Align = PadesHorizontalAlign.Center, Texts = new List <PdfTextSection>() { new PdfTextSection() { Style = PdfTextStyle.Normal, FontSize = NormalFontSize * 1.2, Text = string.Format("Verification Code: {0}", formattedVerificationCode) } } }); verticalOffset += elementHeight; // Paragraph saying "this document was signed by the following signer etc" and mentioning the time zone of the // date/times below. elementHeight = 2.5; manifestMark.Elements.Add(new PdfMarkText() { RelativeContainer = new PadesVisualRectangle() { Height = elementHeight, Top = verticalOffset, Left = 0, Right = 0 }, Texts = new List <PdfTextSection>() { new PdfTextSection() { Style = PdfTextStyle.Normal, FontSize = NormalFontSize, Text = string.Format("This document was digitally signed by the following signers on the indicated dates ({0}):", TimeZoneDisplayName) } } }); verticalOffset += elementHeight; // Iterate signers. foreach (var signer in signature.Signers) { elementHeight = 1.5; // Validate signature based on the PAdES Basic policy. var policyMapper = PadesPoliciesForGeneration.GetPadesBasic(Util.GetTrustArbitrator()); var validationResults = signature.ValidateSignature(signer, policyMapper); // Green "check" or red "X" icon depending on result of validation for this signer. manifestMark.Elements.Add(new PdfMarkImage() { RelativeContainer = new PadesVisualRectangle() { Height = 0.5, Top = verticalOffset + 0.2, Width = 0.5, Left = 0 }, ImageContent = Storage.GetValidationResultIcon(validationResults.IsValid) }); // Description of signer (see method getSignerDescription() below). manifestMark.Elements.Add(new PdfMarkText() { RelativeContainer = new PadesVisualRectangle() { Height = elementHeight, Top = verticalOffset, Left = 0.8, Right = 0 }, Texts = new List <PdfTextSection>() { new PdfTextSection() { Style = PdfTextStyle.Normal, FontSize = NormalFontSize, Text = getSignerDescription(signer) } } }); verticalOffset += elementHeight; } // Some vertical padding from last signer. verticalOffset += 1; // Paragraph with link to veritifcation site and citing both the verification code above and the // verification link below. elementHeight = 2.5; manifestMark.Elements.Add(new PdfMarkText() { RelativeContainer = new PadesVisualRectangle() { Height = elementHeight, Top = verticalOffset, Right = 0, Left = 0 }, Texts = new List <PdfTextSection>() { new PdfTextSection() { Style = PdfTextStyle.Normal, FontSize = NormalFontSize, Text = string.Format("To verify the signatures, go to {0} on ", VerificationSiteNameWithArticle) }, new PdfTextSection() { Color = Color.Blue, Style = PdfTextStyle.Normal, FontSize = NormalFontSize, Text = VerificationSite }, new PdfTextSection() { Style = PdfTextStyle.Normal, FontSize = NormalFontSize, Text = " and inform the code above or follow the link below:" } } }); verticalOffset += elementHeight; // Verification link. elementHeight = 1.5; manifestMark.Elements.Add(new PdfMarkText() { RelativeContainer = new PadesVisualRectangle() { Height = elementHeight, Top = verticalOffset, Right = 0, Left = 0 }, Align = PadesHorizontalAlign.Center, Texts = new List <PdfTextSection>() { new PdfTextSection() { Color = Color.Blue, Style = PdfTextStyle.Normal, FontSize = NormalFontSize, Text = verificationLink } } }); pdfMarker.AddMark(manifestMark); // Prevent from throwing exception when the file to be marked already have a signature (default: true). pdfMarker.ThrowIfSignedPdf = false; // Note: Before applying the marks, all signature from the signed file will be removed. // Apply marks and return the printer-friendly PDF's content. byte[] pfvContent = pdfMarker.WriteMarks(pdfContent); return(pfvContent); }