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