Laws and Digital Signatures 

Suppose we have a document like this:

<?xml version="1.0"?>
<root xmlns="urn-foo-bar">
  <subroot>
    <value1>value1</value1>
    <value2>value2</value2>
  </subroot>
  <Signature xmlns="h
ttp://www.w3.org/2000/09/xmldsig#">
    <SignedInfo>
      <CanonicalizationMethod
        Algorithm="
http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
      <SignatureMethod
        Algorithm="
http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
      <Reference URI="">
        <Transforms>
          <Transform 
            Algorithm="
http://www.w3.org/2000/09/
              xmldsig#enveloped-signature
"/>
        </Transforms>
        <DigestMethod
          Algorithm="
http://www.w3.org/2000/09/xmldsig#sha1" />
        <DigestValue>1Xp...EOko=</DigestValue>
      </Reference>
    </SignedInfo>
    <SignatureValue>nls...cH0k=</SignatureValue>
    <KeyInfo>
      <KeyValue>
        <RSAKeyValue>
          <Modulus>9f3W...fxG0E=</Modulus>
          <Exponent>AQAB</Exponent>
        </RSAKeyValue>
      </KeyValue>
      <X509Data>
        <X509Certificate>MIIEi...ktYgN</X509Certificate>
      </X509Data>
    </KeyInfo>
  </Signature>
</root>

This document represents data and an enveloped digital signature over the complete XML document. The digital signature completeness is defined in the Reference element, which has URI attribute set to empty string (Reference Uri="").

Checking the Signature

The following should always be applied during signature validation:

  1. Validating the digital signature
  2. Validating the certificate(s) used to create the signature
  3. Validating the certificate(s) chain(s)

Note: In most situations this is the optimal validation sequence. Why? Signatures are broken far more frequently then certificates are revoked/expired. And certificates are revoked/expired far more frequently then their chains.

1. Validating the digital signature

First, get it out of there:

XmlNamespaceManager xmlns = new XmlNamespaceManager(xdkDocument.NameTable); [1]
xmlns.AddNamespace("ds", "
http://www.w3.org/2000/09/xmldsig#");
XmlNodeList nodeList = xdkDocument.SelectNodes("//ds:Signature", xmlns);
 
[1] xdkDocument should be an XmlDocument instance representing your document.

Second, construct a SignedXml instance:

foreach (XmlNode xmlNode in nodeList)
{
  // create signed xml object
  SignedXml signedXml = new SignedXml(xdkDocument); [2]

  // verify signature
  signedXml.LoadXml((XmlElement)xmlNode);
}

[2] Note that we are constructing the SignedXml instance from a complete document, not only the signature. Read this.

Third, validate:

bool booSigValid = signedXml.CheckSignature();

If booSigValid is true, proceed.

2. Validating the certificate(s) used to create the signature

First, get it out of there:

XmlNode xndCert = xmlNode.SelectSingleNode(".//ds:X509Certificate", xmlns); [3]

[3] There can be multiple X509Certificate elements qualified with http://www.w3.org/2000/09/xmldsig# namespace in there. Xml Digital Signature specification is allowing the serialization of a complete certificate chain of the certificate used to sign the document. Normally, the signing certificate should be the first to be serialized.

Second, get the X509Certificate2 instance:

byte[] bytCert = Convert.FromBase64String(xndCert.InnerText);
X509Certificate2 x509cert = new X509Certificate2(bytCert);

Third, validate:

bool booCertValid = x509cert.Verify();

If booCertValid is true, proceed.

3. Validating the certificate(s) chain(s)

Building and validating the chain:

X509Chain certChain = new X509Chain();
bool booChainValid = certChain.Build(x509cert);
int intChainLength = certChain.ChainElements.Count; [4]

If booChainValid is true, your signature is valid.

Some Rules and Some Laws

We have three booleans:

  • booSigValid - signature validity
  • booCertValid - certificate validity
  • booChainValid - certificate's chain validity

If booSigValid evaluates to false, there is no discussion. Someone changed the document.

What happens if one of the following two expressions evaluates to true:

1. ((booSigValid) && (!booCertValid) && (!booChainValid))
2. ((booSigValid) && (booCertValid) && (!booChainValid))

This normally means that either the certificate is not valid (CRLed or expired) [4], or one of the chain's certificate is not valid/expired.

[4] The premise is that one checked the signature according to 1, 2, 3 schema described above.

The Question

Is digital signature valid even if CA revoked the certificate after the signature has already been done? Is it valid even after the certificate expires? If signature is valid and certificate has been revoked, what is the legal validity of the signature?

In legal terms, the signature would be invalid on both upper assertions, 1 and 2.

This means, that once the generator of the signature is dead, or one of his predecessors is dead, all his children die too.

Timestamps to the Rescue

According to most country's digital signature laws the signature is valid only during the validity of the signing certificate and validity of the signing certificate's chain, both being checked for revocation and expiry date ... if you don't timestamp it.

If the source document has another signature from a trusted authority, and that authority is a timestamp authority, it would look like this:

<?xml version="1.0"?>
<root xmlns="urn-foo-bar">
  <subroot>
    <value1>value1</value1>
    <value2>value2</value2>
  </subroot>
  <Signature xmlns="
http://www.w3.org/2000/09/xmldsig#">
    ...
  </Signature>
  <dsig:Signature Id="TimeStampToken"
   
xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">
    <dsig:SignedInfo>
      <dsig:CanonicalizationMethod
        Algorithm="
http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
      <dsig:SignatureMethod
        Algorithm="
http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
      <dsig:Reference
        URI="#TimeStampInfo-113D2EEB158BBB2D7CC000000000004DF65">
        <dsig:DigestMethod
          Algorithm="
http://www.w3.org/2000/09/xmldsig#sha1" />
          <dsig:DigestValue>y+xw...scKg=</dsig:DigestValue>
      </dsig:Reference>
      <dsig:Reference URI="#TimeStampAuthority">
        <dsig:DigestMethod
          Algorithm="
http://www.w3.org/2000/09/xmldsig#sha1" />
        <dsig:DigestValue>KhFIr...Sv4=</dsig:DigestValue>
      <dsig:/Reference>
    </dsig:SignedInfo>
    <dsig:SignatureValue>R4m...k3aQ==</dsig:SignatureValue>
    <dsig:KeyInfo Id="TimeStampAuthority">
      <dsig:X509Data>
        <dsig:X509Certificate>MII...Osmg==</dsig:X509Certificate>
      </dsig:X509Data>
    </dsig:KeyInfo>
    <dsig:Object
      Id="TimeStampInfo-113D2EEB158BBB2D7CC000000000004DF65">
      <ts:TimeStampInfo
         xmlns:ts="
http://www.provider.com/schemas
           
/timestamp-protocol-20020207
"
         xmlns:ds="
http://www.w3.org/2000/09/xmldsig#">
        <ts:Policy id="
http://provider.tsa.com/documents" />
          <ts:Digest>
            <ds:DigestMethod Algorithm="
http://www.w3.org/2000/
              09/xmldsig#sha1"
/>
            <ds:DigestValue>V7+bH...Kmsec=</ds:DigestValue>
          </ts:Digest>
          <ts:SerialNumber>938...045</ts:SerialNumber>
          <ts:CreationTime>2008-04-13T11:31:42.004Z</ts:CreationTime>
          <ts:Nonce>121...780</ts:Nonce>
      </ts:TimeStampInfo>
    </dsig:Object>
  </dsig:Signature>
</root>

The second signature would be performed by an out-of-band authority, normally a TSA authority. It would only sign a hash value (in this case SHA1 hash) which was constructed by hashing the original document and the included digital signature.

This (second) signature should be checked using the same 1, 2, 3 steps. For the purpose of this mind experiment, let's say it would generate a booTimestampValid boolean.

Now, let's reexamine the booleans:

  1. ((booSigValid) && (!booCertValid) && (!booChainValid) && (booTimestampValid))
  2. ((booSigValid) && (booCertValid) && (!booChainValid) && (booTimestampValid))

In this case, even though the signature's certificate (or its chain) is invalid, the signature would pass legal validity if the timesamp's signature is valid, together with its certificate and certificate chain. Note that the TSA signature is generated with a different set of keys than the original digital signature.

Actually booTimestampValid is defined as ((booSigValid) && (booCertValid) && (booChainValid)) for the timestamp signature/certificate/certificate chain [5].

[5] Legal validity is guaranteed only in cases where 1 or 2 are true.

Categories:  Other | XML
Wednesday, April 16, 2008 6:32:29 PM (Central Europe Standard Time, UTC+01:00)  #    Comments

 

 WCF: Reliable Messaging and Retry Timeouts 

There is a serious limitation present in the RTM version of WCF 3.0/3.5 regarding control of WS-RM retry messages during a reliable session saga.

Let me try to explain the concept.

We have a sender (communication initiator) and a receiver (service). When a reliable session is constructed between the two, every message needs to come to the other side. In a request-reply world, the sender would be a client during the request phase. Then roles would switch during the response phase.

The problem arises when one of the sides does not get the message acknowledgement in time. WCF reliable messaging implementation retries the sending process and hopes for the acknowledgement. All is well.

The problem is that there is no way for the sending application to specify how long the retry timeout should be. There is a way to specify channel opening and closing timeouts, acknowledgement interval and more, but nothing will define how long should the initiator wait for message acks.

Let's talk about how WCF acknowledges messages.

During a request-reply exchange every request message is acknowledged in a response message. WS-RM SOAP headers regarding sequence definition (request) and acknowledgements (response) look like this:

a1 <r:Sequence s:mustUnderstand="1" u:Id="_2">
a2    <r:Identifier>urn:uuid:6c9d...ca90</r:Identifier>
a3    <r:MessageNumber>1</r:MessageNumber>
a4 </r:Sequence>

b1 <r:SequenceAcknowledgement u:Id="_3">
b2    <r:Identifier>urn:uuid:6c99...ca290</r:Identifier>
b3    <r:AcknowledgementRange Lower="1" Upper="1"/>
b4    <netrm:BufferRemaining
b5       xmlns:netrm="
http://schemas.microsoft.com/ws/2006/05/rm">
b6    </netrm:BufferRemaining>
b7 </r:SequenceAcknowledgement>

Request phase defines a sequence and sends the first message (a3). In response, there is the appropriate acknowledgement present, which acks the first message (b3) with Lower and Upper attributes. Lines b4-b6 define a benign and super useful WCF implementation of flow control, which allows the sender to limit the rate of sent messages if service side becomes congested.

When the session is setup, WCF will have a really small time waiting window for acks. Therefore, if ack is not received during this period, the infrastructure will retry the message.

Duplex contracts work slightly differently. There, the acknowledgement interval can be set. This configuration option (config attribute is called acknowledgementInterval) is named inappropriately, since it controls the service and not the client side.

It does not define the time limit on received acknowledgements, but the necessary time to send the acknowledgments back. It allows grouping of sent acks, so that multiple incoming messages can be acked together. Also, the infrastructure will not necessarily honor the specified value.

Now consider the following scenario:

  1. The client is on a reliable network
  2. Network bandwidth is so thin that the sending message takes 20s to come through [1]
  3. Service instancing is set to Multiple
  4. The solution uses a request-reply semantics

[1] It does not matter whether the initiator is on a dial up, or the message is huge.

What happens?

Service initiator sets up a reliable session, then:

  1. First message is being sent
  2. Since the retry interval is really small [2], the message will not get to the other end and the acknowledgement will not bounce back in time
  3. First message is retried, now two messages are being transported
  4. No acks received yet
  5. First message is retried again
  6. Network bandwidth is even thinner
  7. First message is acknowledged
  8. First message retry is discarded on the service side
  9. Second message retry is discarded on the service side

[2] Under 3s.

The number of retry messages depends on the bandwidth and message size. It can happen that tens of messages will be sent before first acknowledgement will be received.

Adaptability algorithms

A good thing is that there are undocumented algorithms implemented for retry timeout. The implementation increases the reply timeout exponentially when the infrastructure detects that the network conditions demand more time (slow throughput) and allows reliable delivery (no losses). If loses are present the reply timeout decreases.

Retry timeout is actually calculated when establishing an RM session. It is based on the roundtrip time and is bigger if the roundtrip time is long.

So, when first messages in a session are exchanged, don't be too surprised to see a couple of message retries.

Categories:  .NET 3.0 - WCF | .NET 3.5 - WCF
Tuesday, April 8, 2008 11:33:13 PM (Central Europe Standard Time, UTC+01:00)  #    Comments

 

Copyright © 2003-2024 , Matevž Gačnik
Recent Posts
RD / MVP
Feeds
RSS: Atom:
Archives
Categories
Blogroll
Legal

The opinions expressed herein are my own personal opinions and do not represent my company's view in any way.

My views often change.

This blog is just a collection of bytes.

Copyright © 2003-2024
Matevž Gačnik

Send mail to the author(s) E-mail