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;

How To articles about common Office tasks

Discuss this help topic in SecureBlackbox Forum