Class Crypto

java.lang.Object
com.codename1.backend.Crypto

public final class Crypto extends Object
The crypto a server needs to authenticate a request. Every primitive comes from OpenSSL, which the backend already links for outbound TLS - none of it is implemented here, because hand-rolled HMAC and hand-rolled password hashing are the two most reliable ways to ship an authentication system that looks correct and is not.
  • Field Details

  • Method Details

    • sha256

      public static byte[] sha256(byte[] data)
    • sha1

      public static byte[] sha1(byte[] data)

      SHA-1, for the wire protocols that specify it by name: MySQL's mysql_native_password, and the RFC 6455 4.2.2 websocket handshake, where the digest of the client key and a fixed GUID becomes Sec-WebSocket-Accept.

      Never for anything this code CHOOSES: passwords go through hashPassword(String) and tokens through hmacSha256(byte[], byte[]). Both callers here are standards quoting the algorithm, and in neither is the result standing in for a signature -- the handshake value is a replay guard against caches and proxies, not an authenticator.

    • md5

      public static byte[] md5(byte[] data)
      MD5, for PostgreSQL's md5 authentication method. See sha1(byte[]).
    • pbkdf2Sha256

      public static byte[] pbkdf2Sha256(byte[] password, byte[] salt, int iterations, int length) throws IOException
      PBKDF2-HMAC-SHA-256. Exposed because SCRAM-SHA-256 -- how PostgreSQL authenticates by default -- is defined in terms of it with the server's iteration count, which hashPassword(String) does not let a caller choose.
      Throws:
      IOException
    • hmacSha256

      public static byte[] hmacSha256(byte[] key, byte[] data)
    • randomBytes

      public static byte[] randomBytes(int length) throws IOException
      Cryptographically secure bytes. Throws rather than returning weak ones.
      Throws:
      IOException
    • equalsConstantTime

      public static boolean equalsConstantTime(byte[] a, byte[] b)
      Compares without leaking where two values first differ. An early exit on the first differing byte lets a MAC be forged one byte at a time.
    • hashPassword

      public static String hashPassword(String password) throws IOException
      Hashes a password for storage. Returns "pbkdf2$iterations$salt$hash" with both binary parts base64url-encoded, so the iteration count travels with the hash and can be raised later without invalidating existing rows.
      Throws:
      IOException
    • verifyPassword

      public static boolean verifyPassword(String password, String stored)
      False for any malformed stored value rather than throwing.
    • sha384

      public static byte[] sha384(byte[] data)
      SHA-384; null for null.
    • sha512

      public static byte[] sha512(byte[] data)
      SHA-512; null for null.
    • hmac

      public static byte[] hmac(String digest, byte[] key, byte[] data)
      HMAC over one of SHA1, SHA256, SHA384 and SHA512. SHA-1 is here for what specifies it by name -- a TOTP secret an authenticator application already holds, Spring's oldest password format -- and not for anything new.
      Returns:
      the tag, or null when key or data is null
      Throws:
      IllegalArgumentException - for a digest that is not one of the four
    • pbkdf2

      public static byte[] pbkdf2(String digest, byte[] password, byte[] salt, int iterations, int length) throws IOException
      PBKDF2 over HMAC with one of SHA1, SHA256, SHA384 and SHA512, on the password's bytes as they are given.
      Throws:
      IOException - when a count is not positive or derivation fails
    • sign

      public static byte[] sign(String algorithm, byte[] privateKey, byte[] data) throws IOException

      Signs data with a private key in PKCS#8 DER, under one of RS256, RS384, RS512, PS256, ES256 and ES384.

      The key has to be of the kind the algorithm is defined over: an RSA key for the first four, a P-256 key for ES256, a P-384 key for ES384. Any other pairing is refused rather than adapted to.

      An ECDSA signature is returned as ASN.1 DER, the SEQUENCE of r and s both OpenSSL and the JDK produce. JOSE wants the two numbers side by side instead; com.codename1.backend.security.crypto.Der converts.

      Throws:
      IOException - when the key cannot be read, does not fit the algorithm, or signing fails
    • verify

      public static boolean verify(String algorithm, byte[] publicKey, byte[] data, byte[] signature) throws IOException
      Checks a signature against a public key in SubjectPublicKeyInfo DER. The algorithms and the pairing of key and algorithm are those of sign(String, byte[], byte[]), and an ECDSA signature is given as ASN.1 DER.
      Returns:
      true when signature is that key's signature of data; false when it is not, whatever is wrong with it
      Throws:
      IOException - when the question could not be asked: the key cannot be read or does not fit the algorithm. A key that is broken is never reported as a signature that is forged.
    • generateRsaKey

      public static byte[] generateRsaKey(int bits) throws IOException

      A new RSA private key as PKCS#8 DER, with the public exponent 65537.

      For a development profile, so a server that signs tokens starts without a key file. A key made at start-up is gone at the next one, and every token signed with it stops verifying: a deployed server loads its key.

      Parameters:
      bits - 2048 to 8192
      Throws:
      IOException
    • aesGcmEncrypt

      public static byte[] aesGcmEncrypt(byte[] key, byte[] iv, byte[] aad, byte[] plaintext) throws IOException

      AES-GCM. The result is the ciphertext followed by the 16 byte tag.

      The nonce must never repeat under one key: 12 bytes from randomBytes(int) for each call is the ordinary way, stored beside the result.

      Parameters:
      key - 16, 24 or 32 bytes
      iv - the nonce; 12 bytes unless a protocol says otherwise
      aad - data that is authenticated and not encrypted; null for none
      Throws:
      IOException
    • aesGcmDecrypt

      public static byte[] aesGcmDecrypt(byte[] key, byte[] iv, byte[] aad, byte[] sealed) throws IOException
      Returns:
      the plaintext, or null when the tag does not match: the key, the nonce, the associated data or the sealed bytes are not the ones it was made with
      Throws:
      IOException - when the sizes are not ones AES-GCM has