<chapter id="signature">
  <title>Doseta Digital Signature Framework</title>

  <para>Digital signatures allow you to protect the integrity of a message.
  They are used to verify that a message sent was sent by the actual user that
  sent the message and was modified in transit. Most web apps handle message
  integrity by using TLS, like HTTPS, to secure the connection between the
  client and server. Sometimes though, we have representations that are going
  to be forwarded to more than one recipient. Some representations may hop
  around from server to server. In this case, TLS is not enough. There needs
  to be a mechanism to verify who sent the original representation and that
  they actually sent that message. This is where digital signatures come
  in.</para>

  <para>While the mime type multiple/signed exists, it does have drawbacks.
  Most importantly it requires the receiver of the message body to understand
  how to unpack. A receiver may not understand this mime type. A better
  approach would be to put signatures in an HTTP header so that receivers that
  don't need to worry about the digital signature, don't have to.</para>

  <para>The email world has a nice protocol called <ulink
  url="http://dkim.org">Domain Keys Identified Mail</ulink> (DKIM). Work is
  also being done to apply this header to protocols other than email (i.e.
  HTTP) through the <ulink
  url="https://tools.ietf.org/html/draft-crocker-doseta-base-02">DOSETA
  specifications</ulink>. It allows you to sign a message body and attach the
  signature via a DKIM-Signature header. Signatures are calculated by first
  hashing the message body then combining this hash with an arbitrary set of
  metadata included within the DKIM-Signature header. You can also add other
  request or response headers to the calculation of the signature. Adding
  metadata to the signature calculation gives you a lot of flexibility to
  piggyback various features like expiration and authorization. Here's what an
  example DKIM-Signature header might look like.</para>

  <para><programlisting>DKIM-Signature: v=1;
                a=rsa-sha256;
                d=example.com;
                s=burke;
                c=simple/simple;
                h=Content-Type;
                x=0023423111111;
                bh=2342322111;
                b=M232234=

</programlisting></para>

  <para>As you can see it is a set of name value pairs delimited by a ';'.
  While its not THAT important to know the structure of the header, here's an
  explanation of each parameter:</para>

  <variablelist>
    <varlistentry>
      <term>v</term>

      <listitem>
        <para>Protocol version. Always 1.</para>
      </listitem>
    </varlistentry>

    <varlistentry>
      <term>a</term>

      <listitem>
        <para>Algorithm used to hash and sign the message. RSA signing and
        SHA256 hashing is the only supported algorithm at the moment by
        RESTEasy.</para>
      </listitem>
    </varlistentry>

    <varlistentry>
      <term>d</term>

      <listitem>
        <para>Domain of the signer. This is used to identify the signer as
        well as discover the public key to use to verify the signature.</para>
      </listitem>
    </varlistentry>

    <varlistentry>
      <term>s</term>

      <listitem>
        <para>Selector of the domain. Also used to identify the signer and
        discover the public key.</para>
      </listitem>
    </varlistentry>

    <varlistentry>
      <term>c</term>

      <listitem>
        <para>Canonical algorithm. Only simple/simple is supported at the
        moment. Basically this allows you to transform the message body before
        calculating the hash</para>
      </listitem>
    </varlistentry>

    <varlistentry>
      <term>h</term>

      <listitem>
        <para>Semi-colon delimited list of headers that are included in the
        signature calculation.</para>
      </listitem>
    </varlistentry>

    <varlistentry>
      <term>x</term>

      <listitem>
        <para>When the signature expires. This is a numeric long value of the
        time in seconds since epoch. Allows signer to control when a signed
        message's signature expires</para>
      </listitem>
    </varlistentry>

    <varlistentry>
      <term>t</term>

      <listitem>
        <para>Timestamp of signature. Numeric long value of the time in
        seconds since epoch. Allows the verifier to control when a signature
        expires.</para>
      </listitem>
    </varlistentry>

    <varlistentry>
      <term>bh</term>

      <listitem>
        <para>Base 64 encoded hash of the message body.</para>
      </listitem>
    </varlistentry>

    <varlistentry>
      <term>b</term>

      <listitem>
        <para>Base 64 encoded signature.</para>
      </listitem>
    </varlistentry>
  </variablelist>

  <para>To verify a signature you need a public key. DKIM uses DNS text
  records to discover a public key. To find a public key, the verifier
  concatenates the Selector (s parameter) with the domain (d parameter)</para>

  <para>&lt;selector&gt;._domainKey.&lt;domain&gt;</para>

  <para>It then takes that string and does a DNS request to retrieve a TXT
  record under that entry. In our above example burke._domainKey.example.com
  would be used as a string. This is a every interesting way to publish public
  keys. For one, it becomes very easy for verifiers to find public keys.
  There's no real central store that is needed. DNS is a infrastructure IT
  knows how to deploy. Verifiers can choose which domains they allow requests
  from. RESTEasy supports discovering public keys via DNS. It also instead
  allows you to discover public keys within a local Java KeyStore if you do
  not want to use DNS. It also allows you to plug in your own mechanism to
  discover keys.</para>

  <para>If you're interested in learning the possible use cases for digital
  signatures, here's a <ulink
  url="http://bill.burkecentral.com/2011/02/21/multiple-uses-for-content-signature/">blog</ulink>
  you might find interesting.</para>

  <section>
    <title>Maven settings</title>

    <para>You must include the resteasy-crypto project to use the digital
    signature framework.</para>

    <para><programlisting>        &lt;dependency&gt;
            &lt;groupId&gt;org.jboss.resteasy&lt;/groupId&gt;
            &lt;artifactId&gt;resteasy-crypto&lt;/artifactId&gt;
            &lt;version&gt;6.1.0.Beta3&lt;/version&gt;
        &lt;/dependency&gt;

</programlisting></para>
  </section>

  <section>
    <title>Signing API</title>

    <para>To sign a request or response using the RESTEasy client or server
    framework you need to create an instance of
    org.jboss.resteasy.security.doseta.DKIMSignature. This class represents
    the DKIM-Signature header. You instantiate the DKIMSignature object and
    then set the "DKIM-Signature" header of the request or response. Here's an
    example of using it on the server-side:</para>

    <para><programlisting>import org.jboss.resteasy.security.doseta.DKIMSignature;
import java.security.PrivateKey;


@Path("/signed")
public static class SignedResource
{
   @GET
   @Path("manual")
   @Produces("text/plain")
   public Response getManual()
   {
      PrivateKey privateKey = ....; // get the private key to sign message
      
      DKIMSignature signature = new DKIMSignature();
      signature.setSelector("test");
      signature.setDomain("samplezone.org");
      signature.setPrivateKey(privateKey);

      Response.ResponseBuilder builder = Response.ok("hello world");
      builder.header(DKIMSignature.DKIM_SIGNATURE, signature);
      return builder.build();
   }
}

// client example

DKIMSignature signature = new DKIMSignature();
PrivateKey privateKey = ...; // go find it
signature.setSelector("test");
signature.setDomain("samplezone.org");
signature.setPrivateKey(privateKey);

ClientRequest request = new ClientRequest("http://...");
request.header("DKIM-Signature", signature);
request.body("text/plain", "some body to sign");
ClientResponse response = request.put();
</programlisting>To sign a message you need a PrivateKey. This can be
    generated by KeyTool or manually using regular, standard JDK Signature
    APIs. RESTEasy currently only supports RSA key pairs. The DKIMSignature
    class also allows you to add and control how various pieces of metadata
    are added to the DKIM-Signature header and the signature calculation. See
    the javadoc for more details.</para>

    <para>If you are including more than one signature, then just add
    additional DKIMSignature instances to the headers of the request or
    response.</para>

    <section>
      <title>@Signed annotation</title>

      <para>Instead of using the API, RESTEasy also provides you an
      annotation alternative to the manual way of signing using a
      DKIMSignature instances is to use the
      @org.jboss.resteasy.annotations.security.doseta.Signed annotation. It is
      required that you configure a KeyRepository as described later in this
      chapter. Here's an example:</para>

      <para><programlisting>   @GET
   @Produces("text/plain")
   @Path("signedresource")
   @Signed(selector="burke", domain="sample.com", timestamped=true, expires=@After(hours=24))
   public String getSigned()
   {
      return "hello world";
   }

</programlisting>The above example using a bunch of the optional annotation
      attributes of @Signed to create the following Content-Signature
      header:</para>

      <para><programlisting>DKIM-Signature: v=1;
                a=rsa-sha256;
                c=simple/simple;
                domain=sample.com;
                s=burke;
                t=02342342341;
                x=02342342322;
                bh=m0234fsefasf==;
                b=mababaddbb==
   
</programlisting><parameter>This annotation also works with the client proxy
      framework.</parameter></para>
    </section>
  </section>

  <section>
    <title>Signature Verification API</title>

    <para>If you want fine grain control over verification, this is an API to
    verify signatures manually. Its a little tricky because you'll need the
    raw bytes of the HTTP message body in order to verify the signature. You
    can get at an unmarshalled message body as well as the underlying raw
    bytes by using a org.jboss.resteasy.spi.MarshalledEntity injection. Here's
    an example of doing this on the server side:</para>

    <para><programlisting>import org.jboss.resteasy.spi.MarshalledEntity;


@POST
@Consumes("text/plain")
@Path("verify-manual")
public void verifyManual(@HeaderParam("Content-Signature") DKIMSignature signature,
                         @Context KeyRepository repository, 
                         @Context HttpHeaders headers, 
                         MarshalledEntity&lt;String&gt; input) throws Exception
{
      Verifier verifier = new Verifier();
      Verification verification = verifier.addNew();
      verification.setRepository(repository);
      verification.setStaleCheck(true);
      verification.setStaleSeconds(100);
      try {
          verifier.verifySignature(headers.getRequestHeaders(), input.getMarshalledBytes, signature);
      } catch (SignatureException ex) {
      }
      System.out.println("The text message posted is: " + input.getEntity());
}
</programlisting>MarshalledEntity is a generic interface. The template
    parameter should be the Java type you want the message body to be
    converted into. You will also have to configure a KeyRepository. This is
    describe later in this chapter.</para>

    <para>The client side is a little bit different:</para>

    <para><programlisting>ClientRequest request = new ClientRequest("http://localhost:9095/signed"));


ClientResponse&lt;String&gt; response = request.get(String.class);
Verifier verifier = new Verifier();
Verification verification = verifier.addNew();
verification.setRepository(repository);
response.getProperties().put(Verifier.class.getName(), verifier);

// signature verification happens when you get the entity
String entity = response.getEntity();

</programlisting><parameter>On the client side, you create a verifier and add
    it as a property to the ClientResponse. This will trigger the verification
    interceptors.</parameter></para>

    <section>
      <title>Annotation-based verification</title>

      <para>The easiest way to verify a signature sent in a HTTP request on
      the server side is to use the
      @@org.jboss.resteasy.annotations.security.doseta.Verify (or
      @Verifications which is used to verify multiple signatures). Here's an
      example:</para>

      <para><programlisting>      @POST
      @Consumes("text/plain")
      @Verify
      public void post(String input)
      {
      }

</programlisting>In the above example, any DKIM-Signature headers attached to
      the posted message body will be verified. The public key to verify is
      discovered using the configured KeyRepository (discussed later in this
      chapter). You can also specify which specific signatures you want to
      verify as well as define multiple verifications you want to happen via
      the @Verifications annotation. Here's a complex example:</para>

      <para><programlisting>@POST
@Consumes("text/plain")
@Verifications(
   @Verify(identifierName="d", identiferValue="inventory.com", stale=@After(days=2)),
   @Verify(identifierName="d", identiferValue="bill.com")
}
public void post(String input) {...}
</programlisting>The above is expecting 2 different signature to be included
      within the DKIM-Signature header.</para>

      <para>Failed verifications will throw an
      org.jboss.resteasy.security.doseta.UnauthorizedSignatureException. This
      causes a 401 error code to be sent back to the client. If you catch this
      exception using an ExceptionHandler you can browse the failure
      results.</para>
    </section>
  </section>

  <section>
    <title>Managing Keys via a KeyRepository</title>

    <para>RESTEasy manages keys for you through a
    org.jboss.resteasy.security.doseta.KeyRepository. By default, the
    KeyRepository is backed by a Java KeyStore. Private keys are always
    discovered by looking into this KeyStore. Public keys may also be
    discovered via a DNS text (TXT) record lookup if configured to do so. You
    can also implement and plug in your own implementation of
    KeyRepository.</para>

    <section>
      <title>Create a KeyStore</title>

      <para>Use the Java keytool to generate RSA key pairs. Key aliases MUST
      HAVE the form of:</para>

      <para>&lt;selector&gt;._domainKey.&lt;domain&gt;</para>

      <para>For example:</para>

      <para><programlisting>$ keytool -genkeypair -alias burke._domainKey.example.com -keyalg RSA -keysize 1024 -keystore my-apps.jks </programlisting>You
      can always import your own official certificates too. See the JDK
      documentation for more details.</para>
    </section>

    <section>
      <title>Configure Restreasy to use the KeyRepository</title>

      <para>Next you need to configure the KeyRepository in your web.xml file
      so that it is created and made available to RESTEasy to discover private
      and public keys.You can reference a Java key store you want the Resteasy
      signature framework to use within web.xml using either
      <literal>resteasy.keystore.classpath</literal> or
      <literal>resteasy.keystore.filename</literal> context parameters. You
      must also specify the password (sorry its clear text) using the
      <literal>resteasy.keystore.password</literal> context parameter. The
      resteasy.context.objects is used to create the instance of the
      repository. For example:</para>

      <para><programlisting>    &lt;context-param&gt;
        &lt;param-name&gt;resteasy.doseta.keystore.classpath&lt;/param-name&gt;
        &lt;param-value&gt;test.jks&lt;/param-value&gt;
    &lt;/context-param&gt;
    &lt;context-param&gt;
        &lt;param-name&gt;resteasy.doseta.keystore.password&lt;/param-name&gt;
        &lt;param-value&gt;geheim&lt;/param-value&gt;
    &lt;/context-param&gt;
    &lt;context-param&gt;
        &lt;param-name&gt;resteasy.context.objects&lt;/param-name&gt;
        &lt;param-value&gt;org.jboss.resteasy.security.doseta.KeyRepository : org.jboss.resteasy.security.doseta.ConfiguredDosetaKeyRepository&lt;/param-value&gt;
    &lt;/context-param&gt;

</programlisting></para>

      <para>You can also manually register your own instance of a
      KeyRepository within an Application class. For example:</para>

      <para><programlisting>import org.jboss.resteasy.core.Dispatcher;
import org.jboss.resteasy.security.doseta.KeyRepository;
import org.jboss.resteasy.security.doseta.DosetaKeyRepository;

import jakarta.ws.rs.core.Application;
import jakarta.ws.rs.core.Context;

public class SignatureApplication extends Application
{
   private HashSet&lt;Class&lt;?&gt;&gt; classes = new HashSet&lt;Class&lt;?&gt;&gt;();
   private KeyRepository repository;

   public SignatureApplication(@Context Dispatcher dispatcher)
   {
      classes.add(SignedResource.class);

      repository = new DosetaKeyRepository();
      repository.setKeyStorePath("test.jks");
      repository.setKeyStorePassword("password");
      repository.setUseDns(false);
      repository.start();

      dispatcher.getDefaultContextObjects().put(KeyRepository.class, repository);
   }

   @Override
   public Set&lt;Class&lt;?&gt;&gt; getClasses()
   {
      return classes;
   }
}

</programlisting></para>

      <para>On the client side, you can load a KeyStore manually, by
      instantiating an instance of
      org.jboss.resteasy.security.doseta.DosetaKeyRepository. You then set a
      request attribute, "org.jboss.resteasy.security.doseta.KeyRepository",
      with the value of the created instance. Use the
      ClientRequest.getAttributes() method to do this. For example:</para>

      <para><programlisting>DosetaKeyRepository keyRepository = new DoestaKeyRepository();
repository.setKeyStorePath("test.jks");
repository.setKeyStorePassword("password");
repository.setUseDns(false);
repository.start();

DKIMSignature signature = new DKIMSignature();
signature.setDomain("example.com");

ClientRequest request = new ClientRequest("http://...");
request.getAttributes().put(KeyRepository.class.getName(), repository);
request.header("DKIM-Signature", signatures);
</programlisting></para>
    </section>

    <section>
      <title>Using DNS to Discover Public Keys</title>

      <para>Public keys can also be discover by a DNS text record lookup. You
      must configure web.xml to turn this feature:</para>

      <para><programlisting>    &lt;context-param&gt;
        &lt;param-name&gt;resteasy.doseta.use.dns&lt;/param-name&gt;
        &lt;param-value&gt;true&lt;/param-value&gt;
    &lt;/context-param&gt;
    &lt;context-param&gt;
        &lt;param-name&gt;resteasy.doseta.dns.uri&lt;/param-name&gt;
        &lt;param-value&gt;dns://localhost:9095&lt;/param-value&gt;
    &lt;/context-param&gt;

</programlisting>The resteasy.doseta.dns.uri context-param is optional and
      allows you to point to a specific DNS server to locate text
      records.</para>

      <section>
        <title>Configuring DNS TXT Records</title>

        <para>DNS TXT Records are stored via a format described by the DOSETA
        specification. The public key is defined via a base 64 encoding. You
        can obtain this text encoding by exporting your public keys from your
        keystore, then using a tool like openssl to get the text-based format.
        For example:</para>

        <para><programlisting>$ keytool -export -alias bill._domainKey.client.com -keystore client.jks -file bill.der 
$ openssl x509 -noout -pubkey -in bill.der -inform der &gt; bill.pem</programlisting>The
        output will look something like: </para>

        <programlisting>-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCKxct5GHz8dFw0mzAMfvNju2b3
oeAv/EOPfVb9mD73Wn+CJYXvnryhqo99Y/q47urWYWAF/bqH9AMyMfibPr6IlP8m
O9pNYf/Zsqup/7oJxrvzJU7T0IGdLN1hHcC+qRnwkKddNmD8UPEQ4BXiX4xFxbTj
NvKWLZVKGQMyy6EFVQIDAQAB
-----END PUBLIC KEY-----
</programlisting>

        <para>The DNS text record entry would look like this:</para>

        <programlisting>test2._domainKey        IN      TXT     "v=DKIM1; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCIKFLFWuQfDfBug688BJ0dazQ/x+GEnH443KpnBK8agpJXSgFAPhlRvf0yhqHeuI+J5onsSOo9Rn4fKaFQaQNBfCQpHSMnZpBC3X0G5Bc1HWq1AtBl6Z1rbyFen4CmGYOyRzDBUOIW6n8QK47bf3hvoSxqpY1pHdgYoVK0YdIP+wIDAQAB; t=s"
</programlisting>

        <para>Notice that the newlines are take out. Also, notice that the
        text record is a name value ';' delimited list of parameters. The p
        field contains the public key.</para>
      </section>
    </section>
  </section>
</chapter>
