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 Summary

    Fields
    Modifier and Type
    Field
    Description
    static final String
    ECDSA over P-256 with SHA-256, and over P-384 with SHA-384.
    static final String
     
    static final String
    The NIST P-256 curve, for generateEcKey(String).
    static final String
    The NIST P-384 curve, for generateEcKey(String).
    static final int
    PBKDF2 iterations for a stored password.
    static final String
    RSASSA-PSS over SHA-256 with MGF1-SHA-256 and a 32 byte salt.
    static final String
    RSASSA-PKCS1-v1_5 over SHA-256, SHA-384 and SHA-512: the same bytes for the same key and message, every time.
    static final String
     
    static final String
     
    static final String
    static final String
     
    static final String
     
    static final String
     
  • Method Summary

    Modifier and Type
    Method
    Description
    static byte[]
    aesCbcDecryptBlocks(byte[] key, byte[] iv, byte[] blocks)
    Decrypts AES-CBC over whole blocks, with no padding: the inverse of aesCbcEncryptBlocks(byte[], byte[], byte[]).
    static byte[]
    aesCbcEncryptBlocks(byte[] key, byte[] iv, byte[] blocks)
    Encrypts with AES-CBC over whole blocks, with no padding.
    static byte[]
    aesGcmDecrypt(byte[] key, byte[] iv, byte[] aad, byte[] sealed)
    static byte[]
    aesGcmEncrypt(byte[] key, byte[] iv, byte[] aad, byte[] plaintext)
    AES-GCM.
    static byte[]
    ecdh(byte[] privateKey, byte[] peerPublicKey)
    ECDH key agreement: the x coordinate of the shared point, 32 bytes on P-256 and 48 on P-384, as SEC 1 and every TLS stack define it.
    static boolean
    equalsConstantTime(byte[] a, byte[] b)
    Compares without leaking where two values first differ.
    static byte[]
    A new EC private key as PKCS#8 DER, on P256 or P384, carrying its public point so Der.publicKeyOf can give the public half.
    static byte[]
    generateRsaKey(int bits)
    A new RSA private key as PKCS#8 DER, with the public exponent 65537.
    static String
    hashPassword(String password)
    Hashes a password for storage.
    static byte[]
    hmac(String digest, byte[] key, byte[] data)
    HMAC over one of SHA1, SHA256, SHA384 and SHA512.
    static byte[]
    hmacSha256(byte[] key, byte[] data)
     
    static void
    Installs the server's CryptoEngine: PBKDF2 through the native derivation [#pbkdf2(String, byte[], byte[], int, int)] uses, and secp256k1, Schnorr, Ed25519 and Keccak through the C kernels when the build linked them.
    static byte[]
    md5(byte[] data)
    MD5, for PostgreSQL's md5 authentication method.
    static byte[]
    pbkdf2(String digest, byte[] password, byte[] salt, int iterations, int length)
    PBKDF2 over HMAC with one of SHA1, SHA256, SHA384 and SHA512, on the password's bytes as they are given.
    static byte[]
    pbkdf2Sha256(byte[] password, byte[] salt, int iterations, int length)
    PBKDF2-HMAC-SHA-256.
    static byte[]
    randomBytes(int length)
    Cryptographically secure bytes.
    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.
    static byte[]
    sha256(byte[] data)
     
    static byte[]
    sha384(byte[] data)
    SHA-384; null for null.
    static byte[]
    sha512(byte[] data)
    SHA-512; null for null.
    static byte[]
    sign(String algorithm, byte[] privateKey, byte[] data)
    Signs data with a private key in PKCS#8 DER, under one of RS256, RS384, RS512, PS256, ES256 and ES384.
    static boolean
    verify(String algorithm, byte[] publicKey, byte[] data, byte[] signature)
    Checks a signature against a public key in SubjectPublicKeyInfo DER.
    static boolean
    verifyPassword(String password, String stored)
    False for any malformed stored value rather than throwing.

    Methods inherited from class Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Field Details

  • Method Details

    • installCryptoEngine

      public static void installCryptoEngine()
      Installs the server's CryptoEngine: PBKDF2 through the native derivation [#pbkdf2(String, byte[], byte[], int, int)] uses, and secp256k1, Schnorr, Ed25519 and Keccak through the C kernels when the build linked them. Called once, as the backend loads.
    • 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
    • generateEcKey

      public static byte[] generateEcKey(String curve) throws IOException
      A new EC private key as PKCS#8 DER, on P256 or P384, carrying its public point so Der.publicKeyOf can give the public half.
      Parameters:
      curve - "P-256" or "P-384"
      Throws:
      IOException
    • ecdh

      public static byte[] ecdh(byte[] privateKey, byte[] peerPublicKey) throws IOException

      ECDH key agreement: the x coordinate of the shared point, 32 bytes on P-256 and 48 on P-384, as SEC 1 and every TLS stack define it.

      The result is key material, not a key: run it through a KDF (HKDF, or the protocol's own) before using it. Both keys must be on the same curve; a public point that is not on its curve is refused.

      Parameters:
      privateKey - this side's PKCS#8 EC private key
      peerPublicKey - the other side's SubjectPublicKeyInfo
      Throws:
      IOException - when either key cannot be read, they are on different curves, or the curve is not P-256 or P-384
    • aesCbcEncryptBlocks

      public static byte[] aesCbcEncryptBlocks(byte[] key, byte[] iv, byte[] blocks) throws IOException

      Encrypts with AES-CBC over whole blocks, with no padding.

      This is the raw mode a format defined over CBC needs -- PKCS#12 and encrypted PKCS#8 -- and nothing more. CBC is not authenticated, so it is deliberately not a general cipher: there is no padding, and so no padding check whose pass or fail an attacker could probe (the padding oracle). A format that pads removes the padding itself, and only after it has verified the data's integrity some other way, as PKCS#12 does with its MAC. Seal new data with aesGcmEncrypt(byte[], byte[], byte[], byte[]).

      Encryption and decryption are two methods rather than one with a mode flag, so the rule that matters -- an encrypting IV is random and never repeated under one key -- applies to exactly the method it is about. Decrypting takes whatever IV the data carries, a fixed test vector included.

      Parameters:
      key - 16, 24 or 32 bytes
      iv - 16 bytes, random and never repeated under one key; use randomBytes(int)
      blocks - a whole number of 16 byte blocks
      Throws:
      IOException
    • aesCbcDecryptBlocks

      public static byte[] aesCbcDecryptBlocks(byte[] key, byte[] iv, byte[] blocks) throws IOException
      Decrypts AES-CBC over whole blocks, with no padding: the inverse of aesCbcEncryptBlocks(byte[], byte[], byte[]). Nothing is unpadded or checked here; see that method for why.
      Parameters:
      key - 16, 24 or 32 bytes
      iv - the 16 byte IV the data was encrypted under
      blocks - a whole number of 16 byte blocks
      Throws:
      IOException