Discuss this help topic in SecureBlackbox Forum

DC add-on: Configure server side of distributed signing

To avoid confusion, we will refer to the part of DC that runs on the web server as 'server side' and to the part that runs in the browser or client environment as 'client side'. This is opposite to the roles of DC modules between each other (the web server part is actually a 'DC client', and the browser part is a 'DC server' from DC protocol point of view). So, we will stick to the widely used naming of the Internet parties and be saying that the DC client runs on the server side.

The server side of DC-based application consists of two independent modules, each responsible for a different DC function.

The first module, called a pre-signing module, in run on the first stage of the signing operation. On this stage the signing component (CMS, PDF, XML or any other compatible component) takes the input being signed, calculates a hash over it and prepares a 'DC request' - a piece of data containing the hash and some supportive data. This request is then sent to the signing party (the browser), often along with the in-browser signing module (Java applet or ActiveX object) for signing.

Besides the request, a so-called 'pre-signed document' is prepared. This is a 'draft version' of the signed document, but without the signature body. The 'pre-signed' document should be recorded (saved) somewhere on the server, as it will eventually be needed by the second (finalisation) module.

On this stage the pre-signing module terminates; it doesn't bother about any further life line of the operation and the document.

C#:


// we pass a pre-opened stream f, and it's closed in the method
void InitiateSigning(Stream f)
{
  TElPDFDocument doc = new TElPDFDocument();
  doc.Open(f);

  // creating and setting up the handler
  TElPDFAdvancedPublicKeySecurityHandler handler = new TElPDFAdvancedPublicKeySecurityHandler();
  handler.PAdESSignatureType = TSBPAdESSignatureType.pastEnhanced;
  handler.HashAlgorithm = SBConstants.__Global.SB_ALGORITHM_DGST_SHA1;
  handler.CustomName = "Adobe.PPKMS";

  // creating a signature object
  TElPDFSignature signature = doc.get_Signatures(doc.AddSignature());
  signature.SignatureType = SBPDF.__Global.stDocument;
  signature.Invisible = true;
  signature.Handler = handler;
  signature.SigningTime = DateTime.UtcNow;

  // initiating DC operation
  TElDCAsyncState state =
    doc.InitiateAsyncOperation(TSBDCAsyncSignMethod.asmPKCS7); // use PKCS#7 format for PAdES signatures
  output = new MemoryStream();
  state.SaveToStream(output, SBDCXMLEnc.__Global.DCXMLEncoding()); // saving 'DC request' to stream

  f.Close();
  // remember to save the contents of 'f' somewhere,
  // as it contains a pre-signed document
}
Delphi:

// we pass a pre-opened stream F, and it's closed in the method
procedure InitiateSigning(F : TStream);
var
  Doc : TElPDFDocument;
  Handler : TElPDFAdvancedPublicKeySecurityHandler;
  signature : TElPDFSignature;
  state  : TElDCAsyncState;
  output : TMemoryStream;
begin
  doc := TElPDFDocument.Create();
  doc.Open(f);

  // creating and setting up the handler
  handler := TElPDFAdvancedPublicKeySecurityHandler.Create();
  handler.PAdESSignatureType := pastEnhanced;
  handler.HashAlgorithm := SB_ALGORITHM_DGST_SHA1;
  handler.CustomName := 'Adobe.PPKMS';

  // creating a signature object
  signature := doc.Signatures[doc.AddSignature()];
  signature.SignatureType := stDocument;
  signature.Invisible := true;
  signature.Handler := handler;
  signature.SigningTime := UtcNow;

  // initiating DC operation
  state := doc.InitiateAsyncOperation(asmPKCS7); // use PKCS#7 format for PAdES signatures
  output := TMemoryStream.Create();
  state.SaveToStream(output, DCXMLEncoding()); // saving 'DC request' to stream
  ...
  FreeAndNil(f);
  // remember to save the contents of 'f' somewhere,
  // as it contains a pre-signed document
end;

When the client finishes signing the hash in their browser, they send the created 'DC response' to the specified web server URL. This is where the second module of DC (the 'finalisation module') resides. The task of the second module is to recover the pre-signed document from the location where it was saved by the first module and insert the actual signature to it:

C#:


void CompleteSigning(ByteArray signature)
{
  TElDCAsyncState state = new TElDCAsyncState();

  MemoryStream input = new MemoryStream(signature);
  state.LoadFromStream(input, SBDCXMLEnc.__Global.DCXMLEncoding()); // loading the response we've received from the browser into a state object

  TElPDFDocument doc = new TElPDFDocument();
  TElPDFPublicKeySecurityHandler handler = new TElPDFPublicKeySecurityHandler();
  handler.SignatureType = TSBPDFPublicKeySignatureType.pstPKCS7SHA1;

  FileStream file = new FileStream(fileName, FileMode.Open, FileAccess.ReadWrite);
  try
  {
      // finishing the operation
      doc.CompleteAsyncOperation(file, state, handler);
  }
  finally
  {
      file.Close();
  }
}
Delphi:

// we pass a pre-opened stream F, and it's closed in the method
procedure CompleteSigning(Signature : ByteArray);
var
  state  : TElDCAsyncState;
  Doc : TElPDFDocument;
  Handler : TElPDFPublicKeySecurityHandler;
  InputStream: TMemoryStream;
  FileStream : TFileStream;
begin
  State := TElDCAsyncState.Create();
  try
    InputStream = TMemoryStream.Create();
    InpuStream.Write(Signature[0], Length(Signature));
    InputStream.Position := 0;
    state.LoadFromStream(InputStream, DCXMLEncoding()); // loading the response we've received from the browser into a state object
    InputStream.Free;

    doc := TElPDFDocument.Create();
    handler := TElPDFPublicKeySecurityHandler.Create();
    handler.SignatureType := pstPKCS7SHA1;

    FileStream := TFileStream.Create(fileName, fmOpenReadWrite or fmShareDenyWrite);
    try
      // finishing the operation
      doc.CompleteAsyncOperation(FileStream, State, Handler);
    finally
      FreeAndNil(FileStream);
    end;
  finally
	State.Free;
  end;
end;

As said above, the pre-signing and finalisation modules can run independently from each other. The only connection between them is a pre-signed document, to which both of the modules must have access. It is a good idea to store pre-signed documents in a database.

Note, that if asmPKCS1 signing method is used (in particular in PAdES signing), the public part of the signing certificate should be present on the server side on the pre-signing stage. asmPKCS7 signing method doesn't have such requirements.

How To articles about Distributed Cryptography add-on.

Discuss this help topic in SecureBlackbox Forum