<commit_before>﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using iTextSharp.text;
using iTextSharp.text.pdf;
using iTextSharp.text.pdf.security;
using X509Certificate = Org.BouncyCastle.X509.X509Certificate;

namespace Affecto.Pdf.Toolkit
{
    public static class PdfSigner
    {       
        public static string SignFile(string fileName, PdfSignatureParameters parameters)
        {
            string tempPath = string.Empty;
            try
            {
                if (string.IsNullOrWhiteSpace(fileName))
                {
                    throw new ArgumentException("Filename must be given", nameof(fileName));
                }
                if (!File.Exists(fileName))
                {
                    throw new ArgumentException($"File {fileName} not found.");
                }
                if (parameters == null)
                {
                    throw new ArgumentNullException(nameof(parameters));
                }

                tempPath = GetTempPath(parameters.TempFolderPath);

                string targetFilePath = GetTargetFilePath(parameters.TempFolderPath, parameters.TargetFilePath);

                var signingCertificates = CertificateHelper.GetSigningCertificates();

                OcspClientBouncyCastle oscpClient = new OcspClientBouncyCastle(null);
                List<ICrlClient> clrClients = new List<ICrlClient> { new CrlClientOnline(signingCertificates.FinalChain) };

                using (FileStream targetFileStream = new FileStream(targetFilePath, FileMode.Create))
                using (PdfReader reader = new PdfReader(fileName))
                {
                    PdfStamper stamper = PdfStamper.CreateSignature(reader, targetFileStream, '0', tempPath, true);
                    PdfSignatureAppearance appearance = GetPdfSignatureAppearance(signingCertificates, stamper, reader, parameters);

                    CreateSignature(signingCertificates, appearance, clrClients, oscpClient);
                }
                
                return targetFilePath;
            }
            finally
            {
                if (!string.IsNullOrWhiteSpace(tempPath) && File.Exists(tempPath))
                {
                    File.Delete(tempPath);
                }
            }
        }

        private static string GetTempPath(string tempFolderPath)
        {
            if (!string.IsNullOrWhiteSpace(tempFolderPath) && Directory.Exists(tempFolderPath))
            {
                 return Path.Combine(tempFolderPath, Path.GetRandomFileName());
            }

            return Path.GetTempFileName();
        }

        private static string GetTargetFilePath(string tempFolderPath, string targetFilePath)
        {
            if (!string.IsNullOrWhiteSpace(tempFolderPath) && Directory.Exists(tempFolderPath))
            {
                return targetFilePath ?? Path.Combine(tempFolderPath, Path.GetRandomFileName());
            }

            return targetFilePath ?? Path.GetTempFileName();
        }

        private static void CreateSignature(SigningCertificates signingCertificates, PdfSignatureAppearance signatureAppearance, ICollection<ICrlClient> clrClients, IOcspClient oscpClient)
        {
            IExternalSignature externalSignature = new X509Certificate2Signature(signingCertificates.X509Certificate2, "SHA-1");

            MakeSignature.SignDetached(signatureAppearance, externalSignature, signingCertificates.FinalChain, clrClients, oscpClient, null, 0, CryptoStandard.CMS);
        }

        private static PdfSignatureAppearance GetPdfSignatureAppearance(SigningCertificates signingCertificates, PdfStamper stamper, PdfReader reader, PdfSignatureParameters parameters)
        {
            PdfSignatureAppearance appearance = stamper.SignatureAppearance;

            appearance.Reason = "";
            appearance.LocationCaption = "";
            appearance.Location = "";
            appearance.Layer4Text = "";
            appearance.Layer2Text = GetSignatureText(signingCertificates.X509Certificate, parameters);
            appearance.Acro6Layers = true;

            Rectangle cropBox = reader.GetCropBox(1);

            Rectangle rectangle = GetSignatureLocation(cropBox, parameters);
            appearance.SetVisibleSignature(rectangle, parameters.SignaturePageNumber, parameters.SignatureName);

            return appearance;
        }

        private static Rectangle GetSignatureLocation(Rectangle cropBox, PdfSignatureParameters parameters)
        {
            return new Rectangle(cropBox.GetLeft(parameters.SignatureLeftMargin), 
                cropBox.GetTop(parameters.SignatureYLocation + parameters.SignatureHeight), 
                cropBox.Width - parameters.SignatureRightMargin, 
                cropBox.GetTop(parameters.SignatureYLocation));
        }

        private static string GetSignatureText(X509Certificate x509Certificate, PdfSignatureParameters parameters)
        {
            Dictionary<string, string> subjectFields = CertificateHelper.GetSubjectFields(x509Certificate);
            string surName = subjectFields.Keys.Contains("SURNAME") ? subjectFields["SURNAME"] : string.Empty;
            string givenName = subjectFields.Keys.Contains("GIVENNAME") ? subjectFields["GIVENNAME"] : string.Empty;
            string signDate = DateTime.Now.ToString("d.M.yyyy");

            return parameters.SignatureTemplate
                .Replace("{lastname}", surName)
                .Replace("{firstname}", givenName)
                .Replace("{signdate}", signDate);
        }

        private static string UppercaseFirst(string s)
        {
            if (string.IsNullOrEmpty(s))
            {
                return string.Empty;
            }
            return char.ToUpper(s[0]) + s.Substring(1).ToLower();
        }
    }
}<commit_msg>Added  comments on pdf-signature to clarify what's happening. Removed unused method.<commit_after>﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using iTextSharp.text;
using iTextSharp.text.pdf;
using iTextSharp.text.pdf.security;
using X509Certificate = Org.BouncyCastle.X509.X509Certificate;

namespace Affecto.Pdf.Toolkit
{
    /*
    * For more information on pdf signatures see "Digital Signatures for PDF documents" in http://developers.itextpdf.com/books
    */

    public static class PdfSigner
    {       
        public static string SignFile(string fileName, PdfSignatureParameters parameters)
        {
            string tempPath = string.Empty;
            try
            {
                if (string.IsNullOrWhiteSpace(fileName))
                {
                    throw new ArgumentException("Filename must be given", nameof(fileName));
                }
                if (!File.Exists(fileName))
                {
                    throw new ArgumentException($"File {fileName} not found.");
                }
                if (parameters == null)
                {
                    throw new ArgumentNullException(nameof(parameters));
                }

                tempPath = GetTempPath(parameters.TempFolderPath);

                string targetFilePath = GetTargetFilePath(parameters.TempFolderPath, parameters.TargetFilePath);

                var signingCertificates = CertificateHelper.GetSigningCertificates();

                // Two clients for checking certificate revocation
                // * Online Certificate Status Protocol (OCSP) client
                // * Certificate Revocation Lists (CRL) client with online checking
                // Certificate will be checked when the signature is made
                OcspClientBouncyCastle oscpClient = new OcspClientBouncyCastle(null);
                List<ICrlClient> clrClients = new List<ICrlClient> { new CrlClientOnline(signingCertificates.FinalChain) };

                using (FileStream targetFileStream = new FileStream(targetFilePath, FileMode.Create))
                using (PdfReader reader = new PdfReader(fileName))
                {
                    PdfStamper stamper = PdfStamper.CreateSignature(reader, targetFileStream, '0', tempPath, true);
                    PdfSignatureAppearance appearance = GetPdfSignatureAppearance(signingCertificates, stamper, reader, parameters);

                    CreateSignature(signingCertificates, appearance, clrClients, oscpClient);
                }
                
                return targetFilePath;
            }
            finally
            {
                if (!string.IsNullOrWhiteSpace(tempPath) && File.Exists(tempPath))
                {
                    File.Delete(tempPath);
                }
            }
        }

        private static string GetTempPath(string tempFolderPath)
        {
            if (!string.IsNullOrWhiteSpace(tempFolderPath) && Directory.Exists(tempFolderPath))
            {
                 return Path.Combine(tempFolderPath, Path.GetRandomFileName());
            }

            return Path.GetTempFileName();
        }

        private static string GetTargetFilePath(string tempFolderPath, string targetFilePath)
        {
            if (!string.IsNullOrWhiteSpace(tempFolderPath) && Directory.Exists(tempFolderPath))
            {
                return targetFilePath ?? Path.Combine(tempFolderPath, Path.GetRandomFileName());
            }

            return targetFilePath ?? Path.GetTempFileName();
        }

        private static void CreateSignature(SigningCertificates signingCertificates, PdfSignatureAppearance signatureAppearance, ICollection<ICrlClient> clrClients, IOcspClient oscpClient)
        {
            IExternalSignature externalSignature = new X509Certificate2Signature(signingCertificates.X509Certificate2, "SHA-1");

            MakeSignature.SignDetached(signatureAppearance, externalSignature, signingCertificates.FinalChain, clrClients, oscpClient, null, 0, CryptoStandard.CMS);
        }

        private static PdfSignatureAppearance GetPdfSignatureAppearance(SigningCertificates signingCertificates, PdfStamper stamper, PdfReader reader, PdfSignatureParameters parameters)
        {
            PdfSignatureAppearance appearance = stamper.SignatureAppearance;

            appearance.Reason = "";
            appearance.LocationCaption = "";
            appearance.Location = "";
            appearance.Layer4Text = "";
            appearance.Layer2Text = GetSignatureText(signingCertificates.X509Certificate, parameters);
            appearance.Acro6Layers = true;

            Rectangle cropBox = reader.GetCropBox(1);

            Rectangle rectangle = GetSignatureLocation(cropBox, parameters);
            appearance.SetVisibleSignature(rectangle, parameters.SignaturePageNumber, parameters.SignatureName);

            return appearance;
        }

        private static Rectangle GetSignatureLocation(Rectangle cropBox, PdfSignatureParameters parameters)
        {
            return new Rectangle(cropBox.GetLeft(parameters.SignatureLeftMargin), 
                cropBox.GetTop(parameters.SignatureYLocation + parameters.SignatureHeight), 
                cropBox.Width - parameters.SignatureRightMargin, 
                cropBox.GetTop(parameters.SignatureYLocation));
        }

        private static string GetSignatureText(X509Certificate x509Certificate, PdfSignatureParameters parameters)
        {
            Dictionary<string, string> subjectFields = CertificateHelper.GetSubjectFields(x509Certificate);
            string surName = subjectFields.Keys.Contains("SURNAME") ? subjectFields["SURNAME"] : string.Empty;
            string givenName = subjectFields.Keys.Contains("GIVENNAME") ? subjectFields["GIVENNAME"] : string.Empty;
            string signDate = DateTime.Now.ToString("d.M.yyyy");

            return parameters.SignatureTemplate
                .Replace("{lastname}", surName)
                .Replace("{firstname}", givenName)
                .Replace("{signdate}", signDate);
        }
    }
}
{'fraction_non_alphanumeric': 0.07737250646857524, 'fraction_numerical': 0.006176446039562641, 'mean_word_length': 2.332035595105673, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7035551', 'n_tokens_mistral': 2932, 'n_tokens_neox': 2811, 'n_words': 559}