public static void SignPDF( string filePath, string username, string domain, string password, int sigPageNum, int sigX, int sigY, int sigWidth, int sigHeight, string title) { const int SAPI_OK = 0; const int AR_PDF_FLAG_FILESIGN_VERTICAL = 0x00000020; int rc; SAPICrypt SAPI = new SAPICryptClass(); SESHandle sesHandle = null; GraphicImageHandle grpImgHandle = null; try { // Initialize SAPI library if ((rc = SAPI.Init()) != SAPI_OK) { throw new Exception(string.Format("Failed to initialize SAPI ({0})", rc.ToString("X"))); } // Acquire SAPI session handle if ((rc = SAPI.HandleAcquire(out sesHandle)) != SAPI_OK) { throw new Exception(string.Format("Failed in SAPIHandleAcquire() ({0})", rc.ToString("X"))); } // Personalize SAPI Session if ((rc = SAPI.Logon(sesHandle, username, domain, password)) != SAPI_OK) { throw new Exception(string.Format("Failed to authenticate user ({0})", rc.ToString("X"))); } // If the user has more than one graphical signatures, this function opens a new dialog window enabling the user to view and select an image with which to sign. if ((rc = SAPI.GraphicSigImageGUISelect(sesHandle, (int)0xFF, 0, SAPI_ENUM_GR_IMG_SELECT_MODE.SAPI_ENUM_GR_IMG_SEL_MODE_SELECT, out grpImgHandle)) != SAPI_OK) { throw new Exception(string.Format("Failed in SAPIGraphicSigImageGUISelect() ({0})", rc.ToString("X"))); } // Set the graphical image associated with the graphic image handle as the default graphic signature image if ((rc = SAPI.GraphicSigImageSetDefaultEx(sesHandle, grpImgHandle, 0)) != SAPI_OK) { throw new Exception(string.Format("Failed in SAPIGraphicSigImageSetDefaultEx() ({0})", rc.ToString("X"))); } SigFieldSettingsClass SFS = new SigFieldSettingsClass(); TimeFormatClass TF = new TimeFormatClass(); // Define signature field settings SFS.Page = sigPageNum; SFS.X = sigX; SFS.Y = sigY; SFS.Width = sigWidth; SFS.Height = sigHeight; SFS.AppearanceMask = (int)SAPI_ENUM_DRAWING_ELEMENT.SAPI_ENUM_DRAWING_ELEMENT_GRAPHICAL_IMAGE | (int)SAPI_ENUM_DRAWING_ELEMENT.SAPI_ENUM_DRAWING_ELEMENT_SIGNED_BY | (int)SAPI_ENUM_DRAWING_ELEMENT.SAPI_ENUM_DRAWING_ELEMENT_TIME; SFS.SignatureType = SAPI_ENUM_SIGNATURE_TYPE.SAPI_ENUM_SIGNATURE_DIGITAL; SFS.DependencyMode = SAPI_ENUM_DEPENDENCY_MODE.SAPI_ENUM_DEPENDENCY_MODE_INDEPENDENT; TF.DateFormat = "dd/MM/yyyy"; TF.TimeFormat = "hh:mm:ss"; TF.ExtTimeFormat = SAPI_ENUM_EXTENDED_TIME_FORMAT.SAPI_ENUM_EXTENDED_TIME_FORMAT_GMT; // Display GMT offset SFS.TimeFormat = TF; if (!string.IsNullOrEmpty(title)) { // Set signer's title if ((rc = SAPI.ConfigurationValueSet(sesHandle, SAPI_ENUM_CONF_ID.SAPI_ENUM_CONF_ID_TITLE, SAPI_ENUM_DATA_TYPE.SAPI_ENUM_DATA_TYPE_WSTR, title, 1)) != SAPI_OK) { throw new Exception(string.Format("Failed in SAPIConfigurationValueSet() ({0})", rc.ToString("X"))); } SFS.AppearanceMask |= (int)SAPI_ENUM_DRAWING_ELEMENT.SAPI_ENUM_DRAWING_ELEMENT_TITLE; } // Create and sign a new signature field in the document if (SAPI_OK != (rc = SAPI.SignatureFieldCreateSign(sesHandle, SAPI_ENUM_FILE_TYPE.SAPI_ENUM_FILE_ADOBE, filePath, SFS, AR_PDF_FLAG_FILESIGN_VERTICAL, null))) { throw new Exception(string.Format("Failed in SAPISignatureFieldCreateSign() ({0})", rc.ToString("X"))); } } catch (Exception ex) { if (sesHandle != null) { SAPI.Logoff(sesHandle); // Release user context SAPI.HandleRelease(sesHandle); // Release session handle } if (grpImgHandle != null) { SAPI.HandleRelease(grpImgHandle); } throw ex; } }
static void Main(string[] args) { const int SAPI_OK = 0; int rc; SAPICrypt SAPI = new SAPICryptClass(); SESHandle sesHandle = null; // Custom Values string filePath = @"c:\temp\demo.pdf"; // PDF file to sign string username = "******"; // CoSign account username string password = "******"; // CoSign account password string domain = null; // CoSign account domain int sigPageNum = 1; // Create signature on the first page int sigX = 145; // Signature field X location int sigY = 125; // Signature field Y location int sigWidth = 160; // Signature field width int sigHeight = 45; // Signature field height string timeFormat = "hh:mm:ss"; // Time appearance format mask string dateFormat = "dd/MM/yyyy"; // Date appearance format mask int appearanceMask = (int)SAPI_ENUM_DRAWING_ELEMENT.SAPI_ENUM_DRAWING_ELEMENT_GRAPHICAL_IMAGE | // Elements to display on the signature field (int)SAPI_ENUM_DRAWING_ELEMENT.SAPI_ENUM_DRAWING_ELEMENT_SIGNED_BY | (int)SAPI_ENUM_DRAWING_ELEMENT.SAPI_ENUM_DRAWING_ELEMENT_TIME; try { // Initialize SAPI library if ((rc = SAPI.Init()) != SAPI_OK) { throw new Exception(string.Format("Failed to initialize SAPI ({0})", rc.ToString("X"))); } // Acquire SAPI session handle if ((rc = SAPI.HandleAcquire(out sesHandle)) != SAPI_OK) { throw new Exception(string.Format("Failed in SAPIHandleAcquire() ({0})", rc.ToString("X"))); } // Personalize SAPI Session if ((rc = SAPI.Logon(sesHandle, username, domain, password)) != SAPI_OK) { throw new Exception(string.Format("Failed to authenticate user ({0})", rc.ToString("X"))); } SigFieldSettingsClass SFS = new SigFieldSettingsClass(); TimeFormatClass TF = new TimeFormatClass(); // Define signature field settings SFS.Page = sigPageNum; SFS.X = sigX; SFS.Y = sigY; SFS.Width = sigWidth; SFS.Height = sigHeight; SFS.AppearanceMask = appearanceMask; SFS.SignatureType = SAPI_ENUM_SIGNATURE_TYPE.SAPI_ENUM_SIGNATURE_DIGITAL; SFS.DependencyMode = SAPI_ENUM_DEPENDENCY_MODE.SAPI_ENUM_DEPENDENCY_MODE_INDEPENDENT; TF.DateFormat = dateFormat; TF.TimeFormat = timeFormat; TF.ExtTimeFormat = SAPI_ENUM_EXTENDED_TIME_FORMAT.SAPI_ENUM_EXTENDED_TIME_FORMAT_GMT; // Display GMT offset SFS.TimeFormat = TF; // Create and sign a new signature field in the document if (SAPI_OK != (rc = SAPI.SignatureFieldCreateSign( sesHandle, // Session Handle SAPI_ENUM_FILE_TYPE.SAPI_ENUM_FILE_ADOBE, // Type of the file to sign - PDF filePath, // Path to PDF file to sign SFS, // Signature Field details 0, // Flags null))) // Credentials (only if prompt-for-sign feature is enabled) { throw new Exception(string.Format("Failed in SAPISignatureFieldCreateSign() ({0})", rc.ToString("X"))); } Console.WriteLine("The document has been successfully signed!"); } catch (Exception ex) { Console.WriteLine(ex.Message); } finally { if (sesHandle != null) { SAPI.Logoff(sesHandle); // Release user context SAPI.HandleRelease(sesHandle); // Release session handle } } }
public static void SignXML( string Username, string Domain, string Password, string XMLDataToSign //The stream to read the data to be signed from ) { if (string.IsNullOrEmpty(Username)) { throw new ArgumentNullException("Username"); } if (string.IsNullOrEmpty(Password)) { throw new ArgumentNullException("Password"); } if (XMLDataToSign == null) { throw new ArgumentNullException("XMLDataToSign"); } //Make sure the SAPI library is loaded into the current process SAPIInit(); //Instantiate SAPI object SAPICrypt SAPI = new SAPICryptClass(); SigFieldSettings SFS = new SigFieldSettingsClass(); SESHandle hSes = null; int rc = 0; if ((rc = SAPI.HandleAcquire(out hSes)) != 0) { throw new Exception(string.Format( "Memory allocation error (#{0})", rc.ToString("X"))); } if (Domain.Trim() == "") { rc = SAPI.Logon(hSes, Username, null, Password); } else { rc = SAPI.Logon(hSes, Username, Domain, Password); } if (rc != 0) { SAPI.HandleRelease(hSes); throw new Exception(string.Format( "Failed to authenticate the user(#{0})", rc.ToString("X"))); } if ((rc = SAPI.SignatureFieldCreateSignEx2(hSes, SAPI_ENUM_FILE_TYPE.SAPI_ENUM_FILE_XML, XMLDataToSign, null, new SigFieldSettingsClass(), AR_XML_XML_SIG_TYPE_ENVELOPED | AR_XML_SIG_TYPE_XADES_BES, null)) != 0) { SAPI.Logoff(hSes); SAPI.HandleRelease(hSes); throw new Exception(string.Format( "Failed to initialize XML signing process(#{0})", rc.ToString("X"))); } //Cleanup memory SAPI.Logoff(hSes); SAPI.HandleRelease(hSes); }
public static SignatureDetails ValidateXMLSignature(string SignedXML) { if (SignedXML == null) { throw new ArgumentNullException("SignedXML"); } //Make sure the SAPI Library is loaded SAPIInit(); SignatureDetails SigDetails = new SignatureDetails(); SigFieldSettings SigFieldSettings = new SigFieldSettings(); SigFieldInfo SignatureFieldInfo = new SigFieldInfo(); SAPICrypt SAPI = new SAPICrypt(); SigFieldHandle hField = null; int rc; SESHandle hSession = new SESHandle(); if ((rc = SAPI.HandleAcquire(out hSession)) != 0) { throw new Exception(string.Format( "Memory allocation error (#{0})", rc.ToString("X"))); } SAPIContext ctxValidateSignature = new SAPIContext(); int num = 0; if ((rc = SAPI.SignatureFieldEnumInit(hSession, ctxValidateSignature, SAPI_ENUM_FILE_TYPE.SAPI_ENUM_FILE_XML, SignedXML, 0, ref num)) != 0) { SAPI.HandleRelease(hSession); throw new Exception(string.Format( "An error occured while initializing the signature validation process (#{0})", rc.ToString("X"))); } if (num < 1) { throw new Exception("The XML file is not signed!"); } if (num > 1) { throw new Exception("SAPI only supports a single signature per XML file!"); } if ((rc = SAPI.SignatureFieldEnumCont(hSession, ctxValidateSignature, out hField)) != 0) { SAPI.ContextRelease(ctxValidateSignature); SAPI.HandleRelease(hSession); throw new Exception(string.Format( "Failed to retrieve signature (#{0})", rc.ToString("X"))); } if ((rc = SAPI.SignatureFieldInfoGet(hSession, hField, SigFieldSettings, SignatureFieldInfo)) != 0) { SAPI.ContextRelease(ctxValidateSignature); SAPI.HandleRelease(hSession); throw new Exception(string.Format( "Failed to parse signature details (#{0})", rc.ToString("X"))); } CertStatus not_used = new CertStatus(); SigDetails.isValid = SAPI.SignatureFieldVerify(hSession, hField, not_used, 0) == 0; SigDetails.SignerCertificate = new X509Certificate2( (byte[])(((SAPIByteArray)SignatureFieldInfo.Certificate).ToArray())); SigDetails.SignerName = SignatureFieldInfo.SignerName; //Convert FILE_TIME to ticks ulong filetime = SignatureFieldInfo.SignatureTime.HighDateTime; filetime <<= 32; filetime += SignatureFieldInfo.SignatureTime.LowDateTime; SigDetails.SignatureTimeTicks = DateTime.FromFileTimeUtc((long)filetime).Ticks; //Cleanup memory SAPI.ContextRelease(ctxValidateSignature); SAPI.HandleRelease(hSession); return(SigDetails); }
public static void SignStream( string Username, string Domain, string Password, Stream DataToSign, //The stream to read the data to be signed from Stream SignatureData //The stream to write the signature data to ) { if (string.IsNullOrEmpty(Username)) { throw new ArgumentNullException("Username"); } if (string.IsNullOrEmpty(Password)) { throw new ArgumentNullException("Password"); } if (DataToSign == null) { throw new ArgumentNullException("DataToSign"); } if (SignatureData == null) { throw new ArgumentNullException("SignatureData"); } //Make sure the SAPI library is loaded into the current process SAPIInit(); //Instantiate SAPI object SAPICrypt SAPI = new SAPICryptClass(); SESHandle hSes = null; int rc = 0; if ((rc = SAPI.HandleAcquire(out hSes)) != 0) { throw new Exception(string.Format( "Memory allocation error (#{0})", rc.ToString("X"))); } if ((rc = SAPI.Logon(hSes, Username, Domain, Password)) != 0) { SAPI.HandleRelease(hSes); throw new Exception(string.Format( "Failed to authenticate the user(#{0})", rc.ToString("X"))); } //Allocate new signing context SAPIContext ctxBuffSign = new SAPIContextClass(); if ((rc = SAPI.BufferSignInit(hSes, ctxBuffSign, 0)) != 0) { SAPI.Logoff(hSes); SAPI.HandleRelease(hSes); throw new Exception(string.Format( "Failed to initialize buffer signing process(#{0})", rc.ToString("X"))); } int remaining = (int)DataToSign.Length; //Check that the stream is not empty if ((int)DataToSign.Length < 1) { SAPI.Logoff(hSes); SAPI.HandleRelease(hSes); throw new Exception("Cannot sign empty stream!"); } int chunkMaxSize = 1 << 20; //1MB //Calculate first chunk size int chunkSize = remaining < chunkMaxSize ? remaining : chunkMaxSize; while (remaining > 0) { Array chunk = new byte[chunkSize]; //Read in chunks of 1MB int read = DataToSign.Read((byte[])chunk, 0, chunkSize); if (read <= 0) { throw new EndOfStreamException(String.Format("End of stream reached with {0} bytes left to read", remaining)); } //Build SAPI-Compatible bytes array SAPIByteArray tmpBuff = new SAPIByteArrayClass(); tmpBuff.FromArray(ref chunk); //Add read buffer to the signature calculation if ((rc = SAPI.BufferSignCont(hSes, ctxBuffSign, tmpBuff)) != 0) { SAPI.ContextRelease(ctxBuffSign); SAPI.Logoff(hSes); SAPI.HandleRelease(hSes); throw new Exception(string.Format( "An error occured while calculating the digital signature (#{0})", rc.ToString("X"))); } remaining -= read; chunkSize = Math.Min(remaining, chunkSize); } SAPIByteArray signature = new SAPIByteArrayClass(); //Get the final signature if ((rc = SAPI.BufferSignEnd(hSes, ctxBuffSign, signature)) != 0) { SAPI.ContextRelease(ctxBuffSign); SAPI.Logoff(hSes); SAPI.HandleRelease(hSes); throw new Exception(string.Format( "Failed to sign the data (#{0})", rc.ToString("X"))); } //Write signature data to the stream byte[] tmpSig = (byte[])signature.ToArray(); SignatureData.Write(tmpSig, 0, tmpSig.Length); //Cleanup memory SAPI.ContextRelease(ctxBuffSign); SAPI.Logoff(hSes); SAPI.HandleRelease(hSes); }
public static SignatureDetails ValidateSignature(Stream SignedData, Stream Signature) { if (SignedData == null) { throw new ArgumentNullException("SignedData"); } if (Signature == null) { throw new ArgumentNullException("Signature"); } //Make sure the SAPI Library is loaded SAPIInit(); SignatureDetails SigDetails = new SignatureDetails(); int rc; SAPICrypt SAPI = new SAPICrypt(); SESHandle hSession = new SESHandle(); if ((rc = SAPI.HandleAcquire(out hSession)) != 0) { throw new Exception(string.Format( "Memory allocation error (#{0})", rc.ToString("X"))); } //Extract Signer Data from the Signature stream //Read Signature content from stream to the SAPI bytes array Array baSignature = new byte[(int)Signature.Length]; Signature.Read((byte[])baSignature, 0, (int)Signature.Length); SAPIByteArray sSignature = new SAPIByteArray(); sSignature.FromArray(ref baSignature); //SAPIByteArray sSignedData = new SAPIByteArray(); //Array t1 = (Array)SignedData; sSignedData.FromArray(ref t1); object Certificate; // Extract the signer's certificate from signature SAPI_ENUM_DATA_TYPE SAPIType = SAPI_ENUM_DATA_TYPE.SAPI_ENUM_DATA_TYPE_NONE; if ((rc = SAPI.PKCS7BlobGetValue(hSession, sSignature, SAPI_ENUM_PKCS7_FIELD.SAPI_ENUM_PKCS7_FIELD_CERT, out Certificate, ref SAPIType)) != 0) { SAPI.HandleRelease(hSession); throw new Exception(string.Format( "An error occured while extracting the signer's certificate from the signature stream (#{0})", rc.ToString("X"))); } SigDetails.SignerCertificate = new X509Certificate2((byte[])(((SAPIByteArray)Certificate).ToArray())); SigDetails.SignerName = SigDetails.SignerCertificate.GetNameInfo(X509NameType.SimpleName, false); SigDetails.SignerEmail = SigDetails.SignerCertificate.GetNameInfo(X509NameType.EmailName, false); //Run the signature validation process SAPIContext ctxValidateSignature = new SAPIContext(); if ((rc = SAPI.BufferVerifySignatureInit(hSession, ctxValidateSignature, sSignature, 0)) != 0) { SAPI.HandleRelease(hSession); throw new Exception(string.Format( "An error occured while initializing the signature validation process (#{0})", rc.ToString("X"))); } int remaining = (int)SignedData.Length; int chunkMaxSize = 1 << 20; //1MB //Calculate first chunk size int chunkSize = remaining < chunkMaxSize ? remaining : chunkMaxSize; while (remaining > 0) { Array chunk = new byte[chunkSize]; //Read in chunks of 1MB int read = SignedData.Read((byte[])chunk, 0, chunkSize); if (read <= 0) { throw new EndOfStreamException(String.Format("End of stream reached with {0} bytes left to read", remaining)); } //Build SAPI-Compatible byte array SAPIByteArray tmpBuff = new SAPIByteArray(); tmpBuff.FromArray(ref chunk); //Add read buffer to the validation calculation if ((rc = SAPI.BufferVerifySignatureCont(hSession, ctxValidateSignature, tmpBuff)) != 0) { SAPI.ContextRelease(ctxValidateSignature); SAPI.HandleRelease(hSession); throw new Exception(string.Format( "An error occured while validating the digital signature (#{0})", rc.ToString("X"))); } remaining -= read; chunkSize = Math.Min(remaining, chunkSize); } //Get the final validation result SAPIFileTime signingTime = new SAPIFileTime(); rc = SAPI.BufferVerifySignatureEnd(hSession, ctxValidateSignature, signingTime, new CertStatus()); if ((uint)rc == 0x90030360) //SAPI_SIGNATURE_NOT_VALID { SigDetails.isValid = false; SigDetails.SignatureTimeTicks = 0; } else if (rc == 0) { SigDetails.isValid = true; //Convert FILE_TIME to ticks ulong filetime = signingTime.HighDateTime; filetime <<= 32; filetime += signingTime.LowDateTime; SigDetails.SignatureTimeTicks = DateTime.FromFileTimeUtc((long)filetime).Ticks; } else { SAPI.ContextRelease(ctxValidateSignature); SAPI.HandleRelease(hSession); throw new Exception(string.Format( "Failed to validate Digital Signature (#{0})", rc.ToString("X"))); } //Cleanup memory SAPI.ContextRelease(ctxValidateSignature); SAPI.HandleRelease(hSession); return(SigDetails); }