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

  <para>Digital signatures allow the protection of the integrity of a message.
  They are used to verify that a transmitted message was sent by the actual user and
      the sent message was not modified in transit. Most web applications handle message
  integrity by using TLS, like HTTPS, to secure the connection between the
  client and server. Sometimes there will be 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 it. 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 protocol called <ulink
  url="http://dkim.org">Domain Keys Identified Mail</ulink> (DKIM). Work is
  underway 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 the user 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. Other
  request or response headers can be added to the calculation of the signature. Adding
  metadata to the signature calculation gives 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>You can see it is a set of name value pairs delimited by a ';'.
  While it is 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. This allows the transform of 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>Signature expiration. 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>Signature timestamp. 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 a public key is needed. 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 the above example burke._domainKey.example.com
  would be used as a string. This is an 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 an infrastructure IT
  knows how to deploy. Verifiers can choose which domains they allow requests
  from. RESTEasy supports discovering public keys via DNS. It also
  allows the discovery of public keys within a local Java KeyStore if you do
  not want to use DNS. It also allows the user to plug in their own mechanism to
  discover keys.</para>

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

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

    <para>Archive, resteasy-crypto must include the 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.2.6.Final&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 create an instance of
    org.jboss.resteasy.security.doseta.DKIMSignature. This class represents
    the DKIM-Signature header. 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 a PrivateKey is needed. This can be
    generated by KeyTool or manually using regular, standard JDK Signature
    APIs. RESTEasy currently only supports RSA key pairs. The DKIMSignature
    class allows the user 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 including more than one signature, then 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 provides an
      annotation alternative to the manual way of signing using a
      DKIMSignature instances. RESTEasy provides annotation
      @org.jboss.resteasy.annotations.security.doseta.Signed. It is
      required that a KeyRepository be configured 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 uses 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>
        RESTEasy supports fine grain control over verification with an API to verify signatures manually.
        To verify the signature the raw bytes of the HTTP message body are needed.
        Using org.jboss.resteasy.spi.MarshalledEntity injection will provide access
        to the unmarshalled message body and the underlying raw bytes. Here is
    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 of the message body to be
    converted into. A KeyRepository will have to be configured. This is
    described later in this chapter.</para>

    <para>The client side is a little 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, 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 an 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). The user can specify which specific signatures to be
          verified as well as define multiple verifications want 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. Catching this
      exception using an ExceptionHandler allows the user to browse the failure
      results.</para>
    </section>
  </section>

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

    <para>RESTEasy manages keys through an
    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. The user
    can also implement and plug in their 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 RESTEasy to use the KeyRepository</title>

      <para>
          Three <code>context-param</code> elements must be declared in the application's web.xml
          in order for RESTEasy to properly be configured to use the KeyRepository.
          This information enables the KeyRepository to be created and made available to
          RESTEasy, which will use it to discover private and public keys.
      </para>
        <itemizedlist>
            <listitem>(1) The Java key store to be referenced
                by the Resteasy signature framework must be identified using either
                <literal>resteasy.keystore.classpath</literal> or
                <literal>resteasy.keystore.filename</literal> context parameters.
            </listitem>
            <listitem>(2) The password must be specified using the
                <literal>resteasy.keystore.password</literal> context parameter.
                Unfortunately the password must be in clear text.
            </listitem>
            <listitem>(3) The <literal>resteasy.context.objects parameter</literal> is used to identify the classes used
                in creating the repository
            </listitem>
        </itemizedlist>
        <para>
            For example:
        </para>

      <para><programlisting>
(1) &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;
(2)    &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;
(3)    &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>The user can manually register their 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, a KeyStore can be loaded manually, by
      instantiating an instance of
      org.jboss.resteasy.security.doseta.DosetaKeyRepository. 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 be discovered by a DNS text record lookup. The
      web.xml must be configured to enable 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 pointing 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. Text
            encoding can be obtained by exporting the public keys from your
            keystore and 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>
