/**
   * {@inheritDoc}
   *
   * <p><strong>Algorithm Description:</strong> hex strings are generated using a 2-step process.
   *
   * <ol>
   *   <li>{@code len / 2 + 1} binary bytes are generated using the underlying Random
   *   <li>Each binary byte is translated into 2 hex digits
   * </ol>
   *
   * @param len the desired string length.
   * @return the random string.
   * @throws NotStrictlyPositiveException if {@code len <= 0}.
   */
  public String nextHexString(int len) throws NotStrictlyPositiveException {
    if (len <= 0) {
      throw new NotStrictlyPositiveException(LocalizedFormats.LENGTH, len);
    }

    // Get a random number generator
    RandomGenerator ran = getRandomGenerator();

    // Initialize output buffer
    StringBuilder outBuffer = new StringBuilder();

    // Get int(len/2)+1 random bytes
    byte[] randomBytes = new byte[(len / 2) + 1];
    ran.nextBytes(randomBytes);

    // Convert each byte to 2 hex digits
    for (int i = 0; i < randomBytes.length; i++) {
      Integer c = Integer.valueOf(randomBytes[i]);

      /*
       * Add 128 to byte value to make interval 0-255 before doing hex
       * conversion. This guarantees <= 2 hex digits from toHexString()
       * toHexString would otherwise add 2^32 to negative arguments.
       */
      String hex = Integer.toHexString(c.intValue() + 128);

      // Make sure we add 2 hex digits for each byte
      if (hex.length() == 1) {
        hex = "0" + hex;
      }
      outBuffer.append(hex);
    }
    return outBuffer.toString().substring(0, len);
  }
  /**
   * {@inheritDoc}
   *
   * <p><strong>Algorithm Description:</strong> hex strings are generated in 40-byte segments using
   * a 3-step process.
   *
   * <ol>
   *   <li>20 random bytes are generated using the underlying <code>SecureRandom</code>.
   *   <li>SHA-1 hash is applied to yield a 20-byte binary digest.
   *   <li>Each byte of the binary digest is converted to 2 hex digits.
   * </ol>
   *
   * @throws NotStrictlyPositiveException if {@code len <= 0}
   */
  public String nextSecureHexString(int len) throws NotStrictlyPositiveException {
    if (len <= 0) {
      throw new NotStrictlyPositiveException(LocalizedFormats.LENGTH, len);
    }

    // Get SecureRandom and setup Digest provider
    final RandomGenerator secRan = getSecRan();
    MessageDigest alg = null;
    try {
      alg = MessageDigest.getInstance("SHA-1");
    } catch (NoSuchAlgorithmException ex) {
      // this should never happen
      throw new MathInternalError(ex);
    }
    alg.reset();

    // Compute number of iterations required (40 bytes each)
    int numIter = (len / 40) + 1;

    StringBuilder outBuffer = new StringBuilder();
    for (int iter = 1; iter < numIter + 1; iter++) {
      byte[] randomBytes = new byte[40];
      secRan.nextBytes(randomBytes);
      alg.update(randomBytes);

      // Compute hash -- will create 20-byte binary hash
      byte[] hash = alg.digest();

      // Loop over the hash, converting each byte to 2 hex digits
      for (int i = 0; i < hash.length; i++) {
        Integer c = Integer.valueOf(hash[i]);

        /*
         * Add 128 to byte value to make interval 0-255 This guarantees
         * <= 2 hex digits from toHexString() toHexString would
         * otherwise add 2^32 to negative arguments
         */
        String hex = Integer.toHexString(c.intValue() + 128);

        // Keep strings uniform length -- guarantees 40 bytes
        if (hex.length() == 1) {
          hex = "0" + hex;
        }
        outBuffer.append(hex);
      }
    }
    return outBuffer.toString().substring(0, len);
  }