public abstract class KeyHandle
- Object
- KeyHandle
A key you can use and cannot read.
Why this is not a Key
Key is built around encoded bytes: the constructor requires them,
getEncoded() is final, and SecretKey is final on top of that.
Every one of those is correct for what that class is – a value object wrapping key material –
and every one of them makes it impossible to represent a key whose material does not exist as
bytes the application can reach. A browser CryptoKey created with extractable: false is
exactly that key, and so is an Android keystore key and an iOS Secure Enclave key. Subclassing
would have produced a getEncoded() that either lies or throws, and code that takes a Key
would keep compiling while silently getting neither.
So this is a separate type with no byte accessor at all. There is no getEncoded, no
export, and no flag that turns one on. isExportable() reports whether the underlying
material could be exported by some other means – it never provides the means.
What it does not protect against
A handle stops the key material being copied. It does not stop the key being used: while
the vault is unlocked, any code running in the application – including script an XSS
injected into the page – can call seal and open on a handle it can reach, exactly as
the application does. Non-extractability limits what an attacker can carry away, not what they
can do while they are there. destroy() and Vault.lock() cut that off for future calls and
cannot reach a plaintext already handed out.
Lifecycle
A handle obtained from a vault is invalidated when that vault locks. Calls afterwards fail
with VaultError.LOCKED rather than returning stale results, and an operation already in
flight when the lock happens does not deliver its result – see Vault.lock().
Constructors
protected KeyHandle() | Subclasses are created by the vault or by a port. |
Methods
Inherited methods
Constructor details
KeyHandle
protected KeyHandle()Method details
getAlgorithm
public abstract String getAlgorithm()"AES-GCM".getKeyId
public abstract String getKeyId()getVersion
public abstract int getVersion()Vault.rotateDataKey(); envelopes record the version
that sealed them, so an old envelope can still be opened after a rotation and can be
identified as needing a rewrite.isExportable
public abstract boolean isExportable()Whether the key material could be exported through some other path.
false is the interesting answer and means the platform holds the key in a form it will
not hand back – a non-extractable CryptoKey, a keystore alias, a Secure Enclave
reference. true means the material exists as bytes somewhere in the process; the handle
still will not give them to you, but it is not claiming the platform could not.
This method never enables an export. There is no method on this class that does.
getUsages
public abstract KeyUsage[] getUsages()permits
public boolean permits(KeyUsage usage)getProtection
public abstract ProtectionReport getProtection()ProtectionReport.seal
public abstract AsyncResource<byte[]> seal(byte[] plaintext, AssociatedData aad)SecureEnvelope: the nonce, the key id and the
version are managed here and are not the caller’s to choose, because a reused nonce
destroys AES-GCM and an API that let one be passed in would eventually see one.Parameters
plaintextbyte[]- the bytes to protect
aadAssociatedData- the binding the result may only be opened against, may be null
Returns
VaultExceptionopen
public abstract AsyncResource<byte[]> open(byte[] sealed, AssociatedData aad)Authenticated decryption of what seal produced.
On a tag mismatch the resource errors with VaultError.AUTHENTICATION_FAILED and no
plaintext is delivered – not a truncated one, not an unverified one. A caller that wants
the bytes anyway cannot have them from here.
mac
public AsyncResource<byte[]> mac(byte[] data)A message authentication tag over data, for a handle that permits KeyUsage.MAC.
The default reports VaultError.NOT_SUPPORTED; a handle whose platform provides HMAC
overrides it.
verifyMac
public AsyncResource<Boolean> verifyMac(byte[] data, byte[] tag)destroy
public abstract void destroy()Releases the handle and, where the material is held in this process, overwrites it.
Best effort, and said so plainly: a managed runtime may have copied the buffer during a
collection, the browser’s heap is not ours to scrub, and a JIT may hold a register copy.
What this does guarantee is that the handle stops working – subsequent calls fail with
VaultError.LOCKED – and that the application’s own reference is cleared.
isDestroyed
public abstract boolean isDestroyed()destroy() has been called, or the owning vault has locked.