2.5.1.5 CryptoAPI Digital Signature Generation

The hash used to generate a document signature is created by recursively traversing the OLE compound file streams (1) and storages. Certain streams (1) and storages MUST NOT be used, as specified later in this section. A document can have more than one signature, each of which MUST be generated by using the GenerateSignature function. Each individual certificate MUST be stored in the CertificateInfoArray of the CryptoAPI Digital Signature structure.

Let H() be a hashing function, which MUST be MD5, and a plus sign (+) represent concatenation. Let HashObject be an object that can be initialized, that can append data in blocks into the object, and that can finalize to extract the resultant hash value Hfinal.

Let ClsID be the GUID identifier for an OLE compound file storage as specified in [MS-CFB].

Let TimeStamp be a FILETIME structure as specified in [MS-DTYP], containing the current system time, expressed in Coordinated Universal Time (UTC). TimeStamp MUST be stored in the CryptoAPI Digital Signature Structure SignTime field, as specified in section 2.5.1.3.

Let ExcludedStorages be defined as follows:

  • 0x06DataSpaces

  • 0x05Bagaaqy23kudbhchAaq5u2chNd

Let ExcludedStreams be defined as follows:

  • _signatures

  • 0x09DRMContent

     FUNCTION GenerateSignature
        PARAMETERS Storage, Certificate
        RETURNS Signature
      
        CALL HashObject.Initialize
        CALL GenerateSignatureHash(Storage, HashObject, IsFiltered, AppFilter)
        SET Hdata TO HashObject.Finalize
        SET Hfinal TO H(Hdata + TimeStamp)
        SET Signature TO RFC3447(Hfinal, Certificate)
        RETURN Signature
     END FUNCTION
    

In the GenerateSignatureHash function, IsFiltered MUST be true if the document conforms to the details as specified in [MS-XLS] and the stream (1) name is "Workbook" or if the document conforms to the details as specified in [MS-PPT] and the stream (1) name is "Current User". It MUST be false for all other document types and streams (1).

For documents that conform to the details as specified in [MS-XLS], let AppFilter be defined as the process specified in [MS-XLS] section 2.1.7.15, which appends data to HashObject, excluding a portion of the stream (1) from being used in the hashing operation.

For documents that conform to the details as specified in [MS-PPT], let AppFilter be defined as a process that returns without appending data to HashObject. The result is that the name of the CurrentUser stream (1) MUST be appended to the HashObject, but the data contained within the CurrentUser stream (1) MUST NOT be appended to the HashObject.

When stream (1) or storage names are appended to a HashObject, the terminating Unicode null character MUST NOT be included.

Let SORT be a string sorting method that is case sensitive and ascending and that skips any nonprintable characters, such that if two streams (1) named "Data" and "0x05DocumentSummaryInformation" are input, the stream (1) named "Data" is ordered first.

 FUNCTION GenerateSignatureHash
     PARAMETERS Storage, HashObject, IsFiltered, AppFilter
     RETURNS VOID
  
     DECLARE StorageNameArray as (empty array of Unicode strings)
     DECLARE StreamNameArray as (empty array of Unicode strings)
  
     SET ClsID TO Storage.GUID
     CALL HashObject.AppendData(ClsID)
     FOR EACH Child IN Storage.Children
         IF Child IS a storage AND Child.Name NOT IN ExcludedStorages
             APPEND Child.Name to StorageNameArray
         END IF
         IF Child IS a stream AND Child.Name NOT IN ExcludedStreams
             APPEND Child.Name to StreamNameArray
         END IF
     END FOR
     
     SORT StorageNameArray     SORT StreamNameArray
  
     FOR EACH StreamName IN StreamNameArray
         
         CALL HashObject.AppendData(StreamName)
  
         SET ChildStream TO Storage.Children[StreamName]
         IF IsFiltered IS true
             CALL AppFilter(ChildStream, HashObject)
         ELSE
             CALL HashObject.AppendData(ChildStream.Data)
         ENDIF
     ENDFOR
  
     FOR EACH StorageName IN StorageNameArray
  
        CALL HashObject.AppendData(StorageName)
  
     SET ChildStorage TO Storage.Children[StorageName]
        CALL GenerateSignatureHash(ChildStorage, HashObject,                                                IsFiltered, AppFilter)
     END FOR
  
 END FUNCTION

When signing Hfinal, the certificate MUST be an RSA certificate as specified in [RFC3447], and the signing operation MUST be performed as specified in [RFC3447] section 9.2.

If a document is protected as specified in section 2.2, the hash MUST be created by first appending the unencrypted form of the storage that is decrypted from the 0x09DRMContent stream (1), followed by the entire original encrypted file storage with the 0x09DRMContent stream (1) excluded as noted previously.