Class AbstractAuthorizationBuilder<T extends AbstractAuthorizationBuilder<T>>

java.lang.Object
net.solarnetwork.security.AbstractAuthorizationBuilder<T>
Type Parameters:
T - the implementation type
Direct Known Subclasses:
SnsAuthorizationBuilder, Snws2AuthorizationBuilder

public abstract class AbstractAuthorizationBuilder<T extends AbstractAuthorizationBuilder<T>> extends Object
Base class for SolarNetwork authorization builder support.

This builder supports an HMAC-SHA256 style digest authentication scheme in the spirit of the AWS S3 Signature v4 scheme, but adapted for SolarNetwork. In this scheme, a generated authorization credential takes the form of an HTTP Authorization header:

 SCHEME Credential=C,SignedHeaders=H,Signature=S
 

The SCHEME is provided by {#link schemeName()}. The credential C is the identifier provided to the constructor, and represents a login or token ID. The signed headers H is a semicolon-delimited list of request header names used when computing the signature S. The signature S is a hex-encoded HMAC-SHA256 digest.

The signing key used to sign the HMAC-SHA256 signature S is a value derived from a provided secret (i.e. password or token secret) by computing a HMAC-SHA256 digest that uses a specific date (usually the current date). See computeSigningKey(Instant, String) for more details.

The signing message used in the HMAC-SHA256 signature S is a value derived from the configurable properties of this class: a date, verb, path, list of key/value headers, and any other properties defined by implementing subclasses. This message is provided by the computeCanonicalRequestMessage(String[]) method. See the computeSignatureData(Instant, String) method for more details.

Version:
1.0
Author:
matt
  • Field Details

    • AUTHORIZATION_COMPONENT_CREDENTIAL

      public static final String AUTHORIZATION_COMPONENT_CREDENTIAL
      The authorization header component for the credential (identifier).
      See Also:
    • AUTHORIZATION_COMPONENT_HEADERS

      public static final String AUTHORIZATION_COMPONENT_HEADERS
      The authorization header component for the signed header name list.
      See Also:
    • AUTHORIZATION_COMPONENT_SIGNATURE

      public static final String AUTHORIZATION_COMPONENT_SIGNATURE
      The authorization header component for the signature.
      See Also:
  • Constructor Details

    • AbstractAuthorizationBuilder

      public AbstractAuthorizationBuilder(String identifier)
      Construct with a credential.

      The builder will be initialized and then reset() will be called so default values are configured.

      Parameters:
      identifier - the bearer's identifier, such as a token ID or username
  • Method Details

    • reset

      public T reset()
      Reset all values to their defaults.

      All properties will be set to null except the following:

      verb
      will be set to GET
      path
      Will be set to /
      date
      will be set to the current time
      signingKey
      this value will not be changed
      Returns:
      The builder.
    • date

      public T date(Instant date)
      Set the request date.

      This will also set the date header with the date's formatted value.

      Parameters:
      date - the date to use, or null for the current system time via Instant.now(); will be truncated to second resolution
      Returns:
      this builder
    • getDate

      public Instant getDate()
      Get the date.
      Returns:
      the date
    • verb

      public T verb(String verb)
      Set the verb.

      The meaning of the verb depends on the communication protocol being used, such as a HTTP method or STOMP command.

      Parameters:
      verb - the verb
      Returns:
      this builder
      Throws:
      IllegalArgumentException - if verb is null
    • getVerb

      public String getVerb()
      Get the verb.
      Returns:
      the verb
    • path

      public T path(String path)
      Set the request path.
      Parameters:
      path - the request path to use
      Returns:
      this builder
      Throws:
      IllegalArgumentException - if path is null
    • getPath

      public String getPath()
      Get the request path.
      Returns:
      the path
    • host

      public T host(String host)
      Set the host.

      This is a shortcut for calling #header(String, String) with a host key.

      Parameters:
      host - the host value
      Returns:
      this builder
    • contentSha256

      public T contentSha256(byte[] digest)
      Set the body content SHA-256 digest value.
      Parameters:
      digest - the digest value to use or null for none; if provided, the array must have a length of 32 and will be copied
      Returns:
      this builder
    • getContentSha256

      public byte[] getContentSha256()
      Get the content SHA-256 value.
      Returns:
      the content SHA 256
    • appendContentSha256

      protected void appendContentSha256(StringBuilder buf)
      Append the content SHA 256 value to a string buffer.
      Parameters:
      buf - the buffer to append to
    • appendHeaders

      protected void appendHeaders(String[] headerNames, StringBuilder buf)
      Append a list of header name and value pairs.
      Parameters:
      headerNames - the header names to append
      buf - the buffer to append to
    • headers

      public T headers(org.springframework.util.MultiValueMap<String,String> headers)
      Set the headers.
      Parameters:
      headers - the headers to use
      Returns:
      this builder
    • getHeaders

      public org.springframework.util.MultiValueMap<String,String> getHeaders()
      Get the headers.
      Returns:
      the headers
    • header

      public T header(String headerName, String... headerValue)
      Set a header value.

      Header values are replaced if a given headerName is passed more than once.

      Parameters:
      headerName - the header name to set; this will be stored in lower-case
      headerValue - the header value(s) to set
      Returns:
      this builder
      Throws:
      IllegalArgumentException - if any argument is null
    • headerValue

      public String headerValue(String headerName)
      Get the first available header value.
      Parameters:
      headerName - the name of the header to get the first value for
      Returns:
      the header value, or null if the header does not exist
    • headerValues

      public List<String> headerValues(String headerName)
      Get all available header values.
      Parameters:
      headerName - the name of the header to get the values for
      Returns:
      the header values, or null if the header does not exist
    • saveSigningKey

      public T saveSigningKey(String secret)
      Compute and cache the signing key.

      Signing keys are derived from the a secret value and valid for 7 days, so this method can be used to compute a signing key so that build() can be called later. The signing date will be set to whatever date is currently configured via date(Instant), which defaults to the current time for newly created builder instances.

      Parameters:
      secret - the secret to sign the digest with
      Returns:
      this builder
      Throws:
      SecurityException - if any error occurs computing the key
    • signingKey

      public T signingKey(byte[] key)
      Set the signing key directly.

      Use this method if a signing key has been computed externally. The effect is the same as in saveSigningKey(String) in that the build() method can then be called to compute the authorization value using this key.

      Parameters:
      key - the signing key to set
      Returns:
      this builder
    • signingKeyHex

      public String signingKeyHex()
      Get the signing key, encoded as hex.
      Returns:
      the computed or saved signing key encoded as hex, or null if none computed or saved yet
    • signingKeyMessageLiteral

      protected abstract String signingKeyMessageLiteral()
      Get the signing key message.
      Returns:
      the signing key message
    • computeSigningKey

      public byte[] computeSigningKey(Instant date, String secret)
      Compute a signing key from a secret key and date.

      A signing key is derived from key and date using the following algorithm:

       
       HmacSha256(HmacSha256("SCHEME"+secret, "YYYYMMDD"), "SIGNING_KEY_MESSAGE_LITERAL")
       
       

      The HmacSha256(key, message) function computes a HMAC+SHA256 digest value. The YYYYMMDD value is date formatted in the UTC time zone.

      Parameters:
      date - the signing date
      secret - the secret to derive the signing key from
      Returns:
      the signing key
      Throws:
      IllegalArgumentException - if any argument is null
    • sortedHeaderNames

      public String[] sortedHeaderNames()
      Get all configured header names as a sorted array of lower-case values.
      Returns:
      the sorted array, never null
    • computeCanonicalRequestMessage

      public String computeCanonicalRequestMessage()
      Compute the canonical request message.
      Returns:
      the message content, never null
      See Also:
    • computeCanonicalRequestMessage

      protected abstract String computeCanonicalRequestMessage(String[] headerNames)
      Compute the canonical request message.
      Parameters:
      headerNames - the header names to include in the signature
      Returns:
      the canonical request message
    • computeSignatureData

      public String computeSignatureData(Instant date, String canonicalRequestMessage)
      Compute the final signature data.

      The message is computed using the following algorithm:

       
       SCHEME_NAME-HMAC-SHA256\n
       YYYYMMTDDHHmmssZ\n
       Hex(Sha256(canonicalRequestMessage))
       
       

      The date is formatted using the AuthorizationUtils.AUTHORIZATION_TIMESTAMP_FORMATTER.

      Parameters:
      date - the request date
      canonicalRequestMessage - the canonical request message, i.e. from computeCanonicalRequestMessage()
      Returns:
      the final data to be signed for the request
    • build

      public String build()
      Compute an Authorization header value from the configured properties on the builder, using a signing key created from a previous call to saveSigningKey(String) or signingKey(byte[]).

      The message is formatted using the following structure:

       
       SNS Credential=identifier,SignedHeaders=headerList,Signature=Hex(HmacSha256(signingKey,signatureData))
       
       

      Where identifier is the identifier passed to the constructor, headerList is a semicolon-delimited list of sortedHeaderNames(), signingKey is the signing key set via signingKey(byte[]) or computed via saveSigningKey(String), and signatureData is the computed signature data via computeSignatureData(Instant, String).

      Returns:
      the header value
      Throws:
      SecurityException - if any error occurs computing the header value
    • build

      public String build(String secret)
      Compute an Authorization header value from the configured properties on the builder, using the provided secret.
      Parameters:
      secret - the secret to sign the digest with; will use the configured date to compute the singing key
      Returns:
      the header value
      Throws:
      SecurityException - if any error occurs computing the header value
      See Also:
    • schemeName

      protected abstract String schemeName()
      Get the authorization scheme name.
      Returns:
      the scheme name
    • buildSignature

      public String buildSignature()
      Compute a signature value from the configured properties on the builder, using a signing key created from a previous call to saveSigningKey(String) or signingKey(byte[]).

      Note this method returns just the signature value, not a complete Authorization header value. Use the build() method to generate a complete header value.

      The signature is computed using the following algorithm:

       
       Hex(HmacSha256(signingKey,signatureData))
       
       

      Where signingKey is the signing key set via signingKey(byte[]) or computed via saveSigningKey(String), and signatureData is the computed signature data via computeSignatureData(Instant, String).

      Returns:
      the signature value
      Throws:
      SecurityException - if any error occurs computing the header value
    • buildSignature

      public String buildSignature(String secret)
      Compute a signature value from the configured properties on the builder, using the provided secret.

      Note this method returns just the signature value, not a complete Authorization header value. Use the build(String) method to generate a complete header value.

      Parameters:
      secret - the secret to sign the digest with; will use the configured date to compute the singing key
      Returns:
      the signature value
      Throws:
      SecurityException - if any error occurs computing the header value
      See Also: