Backend API. Server-side code: this runs in a Codename One backend, not in the app on the device.

public final class Crypto

  1. Object
  2. Crypto
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.

Fields

public static final int PASSWORD_ITERATIONS = 210000PBKDF2 iterations for a stored password.
public static final String SHA1 = "SHA-1"The digest names hmac and pbkdf2 take.
public static final String SHA256 = "SHA-256"
public static final String SHA384 = "SHA-384"
public static final String SHA512 = "SHA-512"
public static final String RS256 = "RS256"RSASSA-PKCS1-v1_5 over SHA-256, SHA-384 and SHA-512: the same bytes for the same key and message, every time.
public static final String RS384 = "RS384"
public static final String RS512 = "RS512"
public static final String PS256 = "PS256"RSASSA-PSS over SHA-256 with MGF1-SHA-256 and a 32 byte salt.
public static final String ES256 = "ES256"ECDSA over P-256 with SHA-256, and over P-384 with SHA-384.
public static final String ES384 = "ES384"

Methods

public static byte[] sha256(byte[] data)
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.
public static byte[] md5(byte[] data)MD5, for PostgreSQL’s md5 authentication method.
public static byte[] pbkdf2Sha256(byte[] password, byte[] salt, int iterations, int length) throws IOExceptionPBKDF2-HMAC-SHA-256.
public static byte[] hmacSha256(byte[] key, byte[] data)
public static byte[] randomBytes(int length) throws IOExceptionCryptographically secure bytes.
public static boolean equalsConstantTime(byte[] a, byte[] b)Compares without leaking where two values first differ.
public static String hashPassword(String password) throws IOExceptionHashes a password for storage.
public static boolean verifyPassword(String password, String stored)False for any malformed stored value rather than throwing.
public static byte[] sha384(byte[] data)SHA-384; null for null.
public static byte[] sha512(byte[] data)SHA-512; null for null.
public static byte[] hmac(String digest, byte[] key, byte[] data)HMAC over one of SHA1, SHA256, SHA384 and SHA512.
public static byte[] pbkdf2(String digest, byte[] password, byte[] salt, int iterations, int length) throws IOExceptionPBKDF2 over HMAC with one of SHA1, SHA256, SHA384 and SHA512, on the password’s bytes as they are given.
public static byte[] sign(String algorithm, byte[] privateKey, byte[] data) throws IOExceptionSigns data with a private key in PKCS#8 DER, under one of RS256, RS384, RS512, PS256, ES256 and ES384.
public static boolean verify(String algorithm, byte[] publicKey, byte[] data, byte[] signature) throws IOExceptionChecks a signature against a public key in SubjectPublicKeyInfo DER.
public static byte[] generateRsaKey(int bits) throws IOExceptionA new RSA private key as PKCS#8 DER, with the public exponent 65537.
public static byte[] aesGcmEncrypt(byte[] key, byte[] iv, byte[] aad, byte[] plaintext) throws IOExceptionAES-GCM.
public static byte[] aesGcmDecrypt(byte[] key, byte[] iv, byte[] aad, byte[] sealed) throws IOExceptionOpens what aesGcmEncrypt sealed.

Inherited methods

Field details

PASSWORD_ITERATIONS

public static final int PASSWORD_ITERATIONS = 210000
PBKDF2 iterations for a stored password. Deliberately expensive: the cost is paid once per login and multiplied by every guess an attacker makes against a stolen table.

SHA1

public static final String SHA1 = "SHA-1"
The digest names hmac and pbkdf2 take.

SHA256

public static final String SHA256 = "SHA-256"

SHA384

public static final String SHA384 = "SHA-384"

SHA512

public static final String SHA512 = "SHA-512"

RS256

public static final String RS256 = "RS256"
RSASSA-PKCS1-v1_5 over SHA-256, SHA-384 and SHA-512: the same bytes for the same key and message, every time.

RS384

public static final String RS384 = "RS384"

RS512

public static final String RS512 = "RS512"

PS256

public static final String PS256 = "PS256"
RSASSA-PSS over SHA-256 with MGF1-SHA-256 and a 32 byte salt.

ES256

public static final String ES256 = "ES256"
ECDSA over P-256 with SHA-256, and over P-384 with SHA-384.

ES384

public static final String ES384 = "ES384"

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 and tokens through hmacSha256. 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.

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 does not let a caller choose.

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.

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.

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, 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 int
2048 to 8192

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 for each call is the ordinary way, stored beside the result.

Parameters

key byte[]
16, 24 or 32 bytes
iv byte[]
the nonce; 12 bytes unless a protocol says otherwise
aad byte[]
data that is authenticated and not encrypted; null for none
plaintext byte[]
Not documented.

aesGcmDecrypt

public static byte[] aesGcmDecrypt(byte[] key, byte[] iv, byte[] aad, byte[] sealed) throws IOException
Opens what aesGcmEncrypt sealed.

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