All Downloads are FREE. Search and download functionalities are using the official Maven repository.

name.neuhalfen.projects.crypto.symmetric.keygeneration.DerivedKeyGenerator Maven / Gradle / Ivy

The newest version!
package name.neuhalfen.projects.crypto.symmetric.keygeneration;


import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Objects.requireNonNull;

import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import javax.annotation.Nullable;
import name.neuhalfen.projects.crypto.internal.Preconditions;
import name.neuhalfen.projects.crypto.symmetric.keygeneration.impl.derivation.KeyDerivationFunction;

public class DerivedKeyGenerator {


  private final static int MAXIMUM_CONTEXT_ELEMENT_LENGTH = 0xffff;
  private final KeyDerivationFunction kdFwithMasterKeyMixin;
  @Nullable
  private final byte[] salt;


  /**
   * @param kdFwithMasterKeyMixin The key derivation function to use. The function is
   *     responsible to
   *     mix some secret input key material into the result
   * @param salt (from rfc5869): Ideally, the salt value is a random (or pseudorandom) string of
   *     the
   *     length HashLen (HashLen = 256/8 = 32 bytes). Yet, even a salt value of less quality
   *     (shorter in size or with limited entropy) may still make a significant contribution
   *     to the security of the output keying material; designers of applications are therefore
   *     encouraged to provide salt values to HKDF if such values can be obtained by the
   *     application.
   */
  public DerivedKeyGenerator(KeyDerivationFunction kdFwithMasterKeyMixin, @Nullable byte[] salt) {
    this.kdFwithMasterKeyMixin = kdFwithMasterKeyMixin;
    this.salt = salt == null ? null : Arrays.copyOf(salt, salt.length);
  }

  /**
   * @param contextName contextName and idUniqueInContext are combined to create the 'info'
   *     value
   *     from rfc5869: (from rfc5869): While the 'info' value is optional in the definition of
   *     HKDF,
   *     it is often of great importance in applications.  Its main objective is to bind the
   *     derived
   *     key material to application- and context-specific information.  For example, 'info' may
   *     contain a protocol number, algorithm identifiers, user identities, etc.  In particular, it
   *     may prevent the derivation of the same keying material for different contexts (when the
   *     same input key material (IKM) is used in such different contexts). It may also accommodate
   *     additional inputs to the key expansion part, if so desired (e.g., an application may want
   *     to bind the key material to its length L, thus making L part of the 'info' field).
   *     

* There is one technical requirement from 'info': it should be independent of the input * key material value IKM (the masterkey). * @param idUniqueInContext contextName and idUniqueInContext are combined to create the * 'info' * value from rfc5869. * @param recordVersion It is very important to use fresh keys to encrypt different * data * items. This also implies that an updated record must get a new key. The version * parameters ensures this. Store the version parameter with your record and increment it * every time you update the record. * @param desiredKeyLengthBytes the length of the derived key in bytes (e.g. 16 for AES-128) * * @return A derived key of length desiredKeyLengthBytes * * @throws GeneralSecurityException Something went wrong deriving the key. The reason is very * likely outside this libraries influence. */ public byte[] deriveKey(final String contextName, final String idUniqueInContext, final String recordVersion, int desiredKeyLengthBytes) throws GeneralSecurityException { final String derivedKeyIdentifierStr = constructDerivedKeyIdentifier(contextName, idUniqueInContext, recordVersion); final byte[] derivedKeyIdentifier = byteRepresentationOf(derivedKeyIdentifierStr); return kdFwithMasterKeyMixin .deriveKey(salt, derivedKeyIdentifier, desiredKeyLengthBytes * 8); } @SuppressWarnings({"PMD.AvoidReassigningParameters", "PMD.DefaultPackage", "PMD.LawOfDemeter"}) public String constructDerivedKeyIdentifier(@Nullable String contextName, final String idUniqueInContext, final String recordVersion) { if (contextName == null) { contextName = ""; } Preconditions.checkArgument(contextName.length() <= MAXIMUM_CONTEXT_ELEMENT_LENGTH, "ContextName must be <= " + MAXIMUM_CONTEXT_ELEMENT_LENGTH + " chars"); requireNonNull(idUniqueInContext, "idUniqueInContext must not be null"); Preconditions .checkArgument(!idUniqueInContext.isEmpty(), "idUniqueInContext must not be empty"); Preconditions.checkArgument( idUniqueInContext.length() <= MAXIMUM_CONTEXT_ELEMENT_LENGTH, "idUniqueInContext must be <= " + MAXIMUM_CONTEXT_ELEMENT_LENGTH + " chars"); requireNonNull(recordVersion, "recordVersion must not be null"); Preconditions.checkArgument(!recordVersion.isEmpty(), "recordVersion must not be empty"); Preconditions.checkArgument(recordVersion.length() <= MAXIMUM_CONTEXT_ELEMENT_LENGTH, "recordVersion must be <= " + MAXIMUM_CONTEXT_ELEMENT_LENGTH + " chars"); return String .format("%04x:%s:%04x:%s:%04x:%s", contextName.length(), contextName, idUniqueInContext.length(), idUniqueInContext, recordVersion.length(), recordVersion); } /* * in: String * out: byte[] of the byte representation of the UTF-8 string */ @SuppressWarnings("PMD.LawOfDemeter") private byte[] byteRepresentationOf(String identifier) { final ByteBuffer buffer = UTF_8.encode(CharBuffer.wrap(identifier)); final byte[] identifierByteRepresentation = new byte[buffer.limit()]; buffer.get(identifierByteRepresentation); return identifierByteRepresentation; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy