Discuss this help topic in SecureBlackbox Forum
Sign document asynchronously
The topic of asynchronous signing is discussed in the corresponding how-to. Below you will find a complex example of asynchronous signing of an Office document.
C#:
EmulateDistribSigningOffice(@"sample.docx", @"sample-presigned.docx", @"sample-signed.docx",
@"state-01.xml", @"state-02.xml", @"cert.pfx", "password", false);
void EmulateDistribSigningOffice(string SourceFileName, string TempFileName, string DestFileName,
string StateFileName, string StateOutFileName, string CertFileName, string CertPassword,
bool BinarySignature)
{
TElDCAsyncState State = null;
// create a certificate for signing
TElX509Certificate PubCert = new TElX509Certificate(null);
using (TElX509Certificate Cert = new TElX509Certificate(null))
{
int k = Cert.LoadFromFileAuto(CertFileName, CertPassword);
if (k != 0)
throw new Exception("Failed to load certificate, error: " + k.ToString());
Cert.Clone(PubCert, false);
}
// Make a copy of the document, as we will need to save a partially signed document
File.Copy(SourceFileName, TempFileName, true);
TElOfficeDocument Document = new TElOfficeDocument(null);
try
{
// open the document
Document.Open(TempFileName);
// check if we can sign it
if (!Document.Signable)
throw new Exception("Failed to sign document");
// create a proper signature handler
// and initiate the asynchronous signing
if (Document.DocumentFormat == TSBOfficeDocumentFormat.Binary)
{
if (BinarySignature)
{
TElOfficeBinaryCryptoAPISignatureHandler Handler = new TElOfficeBinaryCryptoAPISignatureHandler(null);
Document.AddSignature(Handler, true);
State = Handler.InitiateAsyncSign(PubCert);
}
else
{
TElOfficeBinaryXMLSignatureHandler Handler = new TElOfficeBinaryXMLSignatureHandler(null);
Document.AddSignature(Handler, true);
State = Handler.InitiateAsyncSign(PubCert);
}
}
else
if (Document.DocumentFormat == TSBOfficeDocumentFormat.OpenXML)
{
TElOfficeOpenXMLSignatureHandler Handler = new TElOfficeOpenXMLSignatureHandler(null);
Document.AddSignature(Handler, true);
// sign something
Handler.AddDocument();
State = Handler.InitiateAsyncSign(PubCert);
}
else
if (Document.DocumentFormat == TSBOfficeDocumentFormat.OpenXPS)
{
TElOfficeOpenXPSSignatureHandler Handler = new TElOfficeOpenXPSSignatureHandler(null);
Document.AddSignature(Handler, true);
// sign something
Handler.AddDocument();
State = Handler.InitiateAsyncSign(PubCert);
}
else
if (Document.DocumentFormat == TSBOfficeDocumentFormat.OpenDocument)
{
TElOpenOfficeSignatureHandler Handler = new TElOpenOfficeSignatureHandler(null);
Document.AddSignature(Handler, true);
// sign something
Handler.AddDocument();
State = Handler.InitiateAsyncSign(PubCert);
};
}
finally
{
Document.Dispose();
PubCert.Dispose();
}
// save the obtained state to file for debug purposes
using (FileStream F = new FileStream(StateFileName, FileMode.Create))
{
State.SaveToStream(F, SBDCXMLEnc.Unit.DCXMLEncoding());
}
State = null;
// Process the state in a signature server
// In most cases this would be a browser applet,
// but a class in your module would also work
TElDCX509SignOperationHandler SigHandler = new TElDCX509SignOperationHandler();
SigHandler.CertStorage = new TElMemoryCertStorage(null);
try
{
using (FileStream F = new FileStream(CertFileName, FileMode.Open, FileAccess.Read))
{
int k = SigHandler.CertStorage.LoadFromStreamPFX(F, CertPassword);
if (k != 0)
throw new Exception("Failed to load certificate, error: " + k.ToString());
}
// In this block we usually load the state on the signature server side.
// As this sample emulates distributed signing, the same file is used
// to save the state by the signature client
// and to load it on the signature server.
using (TElDCStandardServer Server = new TElDCStandardServer())
{
Server.AddOperationHandler(SigHandler);
using (FileStream F = new FileStream(StateFileName, FileMode.Open, FileAccess.Read))
{
using (FileStream OutF = new FileStream(StateOutFileName, FileMode.Create))
{
Server.Process(F, OutF, SBDCXMLEnc.Unit.DCXMLEncoding(), SBDCXMLEnc.Unit.DCXMLEncoding());
}
}
}
}
finally
{
SigHandler.CertStorage.Dispose();
SigHandler.Dispose();
}
// Finalize the signature on the side which holds the document
State = new TElDCAsyncState();
using (FileStream F = new FileStream(StateOutFileName, FileMode.Open, FileAccess.Read))
{
State.LoadFromStream(F, SBDCXMLEnc.Unit.DCXMLEncoding());
}
// restore the half-signed document from the temporary storage
// and complete the signing procedure
File.Copy(TempFileName, DestFileName, true);
using (Document = new TElOfficeDocument(null))
{
Document.Open(DestFileName);
Document.CompleteAsyncSign(Document.get_SignatureHandlers(Document.SignatureHandlerCount - 1), State);
}
State = null;
}
Delphi:
EmulateDistribSigningOffice('sample.docx', 'sample-presigned.docx', 'sample-signed.docx',
'state-01.xml', 'state-02.xml', 'cert.pfx', 'password');
procedure EmulateDistribSigningOffice(const SourceFileName, TempFileName, DestFileName,
StateFileName, StateOutFileName,
CertFileName, CertPassword : string;
BinarySignature : Boolean = False);
var
F, OutF : TStream;
Document : TElOfficeDocument;
Handler : TElOfficeCustomSignatureHandler;
Cert, PubCert : TElX509Certificate;
State : TElDCAsyncState;
Server : TElDCStandardServer;
SigHandler : TElDCX509SignOperationHandler;
k : Integer;
begin
State := nil;
// create a certificate for signing
PubCert := TElX509Certificate.Create(nil);
try
Cert := TElX509Certificate.Create(nil);
try
k := Cert.LoadFromFileAuto(CertFileName, CertPassword);
if k <> 0 then
raise Exception.Create('Failed to load certificate, error: ' + IntToStr(k));
Cert.Clone(PubCert, False);
finally
FreeAndNil(Cert);
end;
// Make a copy of the document, as we will need to save a partially signed document
CopyFile(PChar(SourceFileName), PChar(TempFileName), False);
Document := TElOfficeDocument.Create(nil);
try
// open the document
Document.Open(TempFileName);
// check if we can sign it
if not Document.Signable then
raise Exception.Create('Failed to sign document');
// create a proper signature handler
// and initiate the asynchronous signing
if Document.DocumentFormat = dfBinary then
begin
if BinarySignature then
begin
Handler := TElOfficeBinaryCryptoAPISignatureHandler.Create(nil);
Document.AddSignature(Handler, true);
State := TElOfficeBinaryCryptoAPISignatureHandler(Handler).InitiateAsyncSign(PubCert);
end
else
begin
Handler := TElOfficeBinaryXMLSignatureHandler.Create(nil);
Document.AddSignature(Handler, true);
State := TElOfficeBinaryXMLSignatureHandler(Handler).InitiateAsyncSign(PubCert);
end;
end
else
if Document.DocumentFormat = dfOpenXML then
begin
Handler := TElOfficeOpenXMLSignatureHandler.Create(nil);
Document.AddSignature(Handler, true);
// sign something
TElOfficeOpenXMLSignatureHandler(Handler).AddDocument;
State := TElOfficeOpenXMLSignatureHandler(Handler).InitiateAsyncSign(PubCert);
end
else if Document.DocumentFormat = dfOpenXPS then
begin
Handler := TElOfficeOpenXPSSignatureHandler.Create(nil);
Document.AddSignature(Handler, true);
// sign something
TElOfficeOpenXPSSignatureHandler(Handler).AddDocument();
State := TElOfficeOpenXPSSignatureHandler(Handler).InitiateAsyncSign(PubCert);
end
else if Document.DocumentFormat = dfOpenDocument then
begin
Handler := TElOpenOfficeSignatureHandler.Create(nil);
Document.AddSignature(Handler, true);
// sign something
TElOpenOfficeSignatureHandler(Handler).AddDocument();
State := TElOpenOfficeSignatureHandler(Handler).InitiateAsyncSign(PubCert);
end;
finally
FreeAndNil(Document);
end;
finally
FreeAndNil(PubCert);
end;
// save the obtained state to file for debug purposes
F := TFileStream.Create(StateFileName, fmCreate);
try
State.SaveToStream(F, nil);
finally
FreeAndNil(F);
end;
FreeAndNil(State);
// Process the state in a signature server
// In most cases this would be a browser applet,
// but a class in your module would also work
SigHandler := TElDCX509SignOperationHandler.Create();
SigHandler.CertStorage := TElMemoryCertStorage.Create(nil);
try
F := TFileStream.Create(CertFileName, fmOpenRead);
try
k := SigHandler.CertStorage.LoadFromStreamPFX(F, CertPassword);
if k <> 0 then
raise Exception.Create('Failed to load certificate, error: ' + IntToStr(k));
finally
FreeAndNil(F);
end;
Server := TElDCStandardServer.Create();
Server.AddOperationHandler(SigHandler);
F := TFileStream.Create(StateFileName, fmOpenRead);
try
OutF := TFileStream.Create(StateOutFileName, fmCreate);
try
Server.Process(F, OutF, nil, nil);
finally
FreeAndNil(OutF);
end;
finally
FreeAndNil(F);
FreeAndNil(Server);
end;
finally
SigHandler.CertStorage.Free;
SigHandler.CertStorage := nil;
FreeAndNil(SigHandler);
end;
// Finalize the signature on the side which holds the document
State := TElDCAsyncState.Create();
F := TFileStream.Create(StateOutFileName, fmOpenRead);
try
State.LoadFromStream(F, nil);
finally
FreeAndNil(F);
end;
// restore the half-signed document from the temporary storage
// and complete the signing procedure
CopyFile(PChar(TempFileName), PChar(DestFileName), False);
Document := TElOfficeDocument.Create(nil);
try
Document.Open(DestFileName);
Document.CompleteAsyncSign(Document.SignatureHandlers[Document.SignatureHandlerCount - 1], State);
finally
FreeAndNil(Document);
end;
FreeAndNil(State);
end;