Signature Appliance Local API C#: Sign/Verify XML file

This C# WinForms application uses the DocuSign Signature Appliance Local API to sign and verify an XML file.

API Recipe source files are available on GitHub.

Signing an XML file

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);
}

Verifying an XML file

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 SigFieldSettingsClass();
	SigFieldInfo SignatureFieldInfo = new SigFieldInfoClass();
	SAPICrypt SAPI = new SAPICryptClass();
	SigFieldHandle hField = null;
	int rc;

	SESHandle hSession = new SESHandleClass();
	if ((rc = SAPI.HandleAcquire(out hSession)) != 0)
	{
		throw new Exception(string.Format(
			"Memory allocation error (#{0})", rc.ToString("X")));
	}

	SAPIContext ctxValidateSignature = new SAPIContextClass();
	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;
}