Encryption Support
The CBOR library contains support forEncrypting
and Decrypting
arbitrary binary data using a specific scheme called
"CBOR Encryption Format" (CEF).
In similarity to CSF,
CEF also depends on
Deterministic Encoding
which is used to create authentication data to content encryption
algorithms like GCM.
The following is a CEF object (here expressed in CBOR diagnostic notation), using the algorithms
A256GCM
and ECDH-ES+A256KW
for content- respectively key-wrapping:
{
/ Content encryption algorithm = A256GCM /
1: 3,
/ Key encryption object /
2: {
/ Key encryption algorithm = ECDH-ES+A256KW /
1: -31,
/ Key Id /
3: "example.com:x25519",
/ Ephemeral public key descriptor in COSE format /
7: {
/ kty = OKP /
1: 1,
/ crv = X25519 /
-1: 4,
/ x /
-2: h'c219e35a9c09bfcf1bd6c6dcd1e05ecb36cb6f465d9caeb101795e33fd7db112'
},
/ CipherText (Encrypted key) /
10: h'842916c5c81f8a815ec5ef2a472981b7300bc33fe748928c26e8c4dfff7a4747ecff9caea7040585'
},
/ Tag /
8: h'84ad6926aa92d0de56e4674abf863390',
/ Initialization Vector (IV) /
9: h'c7cc6f77b9f984c15bc3cbeb',
/ Ciphertext (Encrypted Content) /
10: h'625c2b2a41907547b2210624e1e818991c00790aed16'
}
/ Content encryption algorithm = A256GCM /
1: 3,
/ Key encryption object /
2: {
/ Key encryption algorithm = ECDH-ES+A256KW /
1: -31,
/ Key Id /
3: "example.com:x25519",
/ Ephemeral public key descriptor in COSE format /
7: {
/ kty = OKP /
1: 1,
/ crv = X25519 /
-1: 4,
/ x /
-2: h'c219e35a9c09bfcf1bd6c6dcd1e05ecb36cb6f465d9caeb101795e33fd7db112'
},
/ CipherText (Encrypted key) /
10: h'842916c5c81f8a815ec5ef2a472981b7300bc33fe748928c26e8c4dfff7a4747ecff9caea7040585'
},
/ Tag /
8: h'84ad6926aa92d0de56e4674abf863390',
/ Initialization Vector (IV) /
9: h'c7cc6f77b9f984c15bc3cbeb',
/ Ciphertext (Encrypted Content) /
10: h'625c2b2a41907547b2210624e1e818991c00790aed16'
}
The same object expressed as hex-encoded CBOR:
a5010302a401381e03726578616d706c652e636f6d3a78323535313907a301012004215820c219e35a9c09bfcf1bd6c6dcd1e05ecb36cb6f465d9caeb101795e33fd7db1120a5828842916c5c81f8a815ec5ef2a472981b7300bc33fe748928c26e8c4dfff7a4747ecff9caea7040585085084ad6926aa92d0de56e4674abf863390094cc7cc6f77b9f984c15bc3cbeb0a56625c2b2a41907547b2210624e1e818991c00790aed16
Decryption of this object using the private key (here in JWK format)
{
"kid": "example.com:x25519",
"kty": "OKP",
"crv": "X25519",
"x": "6ZoM7yBYlJYNmxwFl4UT3MtCoTv7ztUjpRuKEXrV8Aw",
"d": "cxfl86EVmcqrR07mWENCf1F_5Ni5mt1ViGyERB6Q1vA"
}
"kid": "example.com:x25519",
"kty": "OKP",
"crv": "X25519",
"x": "6ZoM7yBYlJYNmxwFl4UT3MtCoTv7ztUjpRuKEXrV8Aw",
"d": "cxfl86EVmcqrR07mWENCf1F_5Ni5mt1ViGyERB6Q1vA"
}
"Hello encrypted world!"
.
The CEF decryption process is as follows:
- Read and save the content encryption parameters
tag
,iv
, andciphertext
. - Remove the
tag
,iv
, andciphertext
elements from the encryption object. Note that the top levelmap
object must be updated as well to reflect the changed number of elements. - Serialize the remaining CBOR encryption object and set the result to
Additional Authentication Data (
AAD
). - Recover the content encryption key using the elements in the
key encryption object (argument of label
2
). - Apply the content encryption algorithm (argument of label
1
in the main map) to the recovered content encryption key, the input parameters read in the first step, andAAD
. The result is the decrypted content.
The following figure shows the layout of CEF objects:
publicKey
andephemeralKey
elements must be compliant with COSE, but must not contain any other information than the public key itself and associated parameters.- ECDSA public keys must be supplied with a value parameter for the y-coordinate as well.
keyId
,publicKey
andcertificatePath
must not be combined.- If a
keyId
is used it must be supplied in the main map for symmetric encryption schemes, and in the sub map for schemes using key encryption. A compliantkeyId
must uniquely identify a specific encryption key. - The currently supported key encryption algorithms except for
ECDH-ES
require that the encrypted key is provided in thechipherText
element in the sub map. - All ECDH-ES variants must include an
ephemeralKey
element.
hmac
: The HKDF implementation must use HMAC with SHA-256salt
: N/A. The default extract mode handling must be implemented.info
: This parameter must consist of the actual COSE key encryption algorithm, expressed as a 32-bit (4 byte) signed big-endian integer.
Tagged and Custom Encryption Data
The CEF container can be enhanced through a couple of options described in Crypto Options. Such options are included in the data to be authenticated.Content Encryption Algorithms
Currently supported content encryption algorithms:
Name | Identifier | Compatibility |
---|---|---|
A128GCM | 1 | COSE, JOSE |
A192GCM | 2 | COSE, JOSE |
A256GCM | 3 | COSE, JOSE |
A128CBC-HS256 | 200 | JOSE |
A192CBC-HS384 | 201 | JOSE |
A256CBC-HS512 | 202 | JOSE |
Key Encryption Algorithms
Currently supported key encryption algorithms:
Name | Identifier | Compatibility |
---|---|---|
ECDH-ES | -25 | COSE, JOSE [1] |
ECDH-ES+A128KW | -29 | COSE, JOSE [1] |
ECDH-ES+A192KW | -30 | COSE, JOSE [1] |
ECDH-ES+A256KW | -31 | COSE, JOSE [1] |
RSA-OAEP | -40 | COSE, JOSE |
RSA-OAEP-256 | -41 | COSE, JOSE |
Test Vectors
An extensive set of test vectors is currently available at: https://github.com/cyberphone/openkeystore/tree/master/testdata/cbor-encryption .
Use CBORPrinter
to list contents in a human-friendly way.
Using the Encryption API
The following section outlines how the encryption API is supposed to be used.Sample program:
package cbor_api_demo;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import org.webpki.cbor.CBORAsymKeyDecrypter;
import org.webpki.cbor.CBORAsymKeyEncrypter;
import org.webpki.cbor.CBORKeyPair;
import org.webpki.cbor.CBORDecoder;
import org.webpki.crypto.ContentEncryptionAlgorithms;
import org.webpki.crypto.KeyEncryptionAlgorithms;
import org.webpki.util.HexaDecimal;
import org.webpki.util.UTF8;
public class EncryptionDemo {
// Message encoded in UTF-8.
static final byte[] SECRET_MESSAGE = UTF8.encode("A very secret message");
// X25519 private key in COSE format.
static final byte[] X25519_PRIVATE_KEY = HexaDecimal.decode(
"a401012004215820e99a0cef205894960d9b1c05978513dccb" +
"42a13bfbced523a51b8a117ad5f00c2358207317e5f3a11599" +
"caab474ee65843427f517fe4d8b99add55886c84441e90d6f0");
public static void main(String[] args) {
// Get keys in Java format.
KeyPair keyPair = CBORKeyPair.convert(CBORDecoder.decode(X25519_PRIVATE_KEY));
PrivateKey receiverKey = keyPair.getPrivate();
PublicKey senderKey = keyPair.getPublic();
// Encrypt data using CEF.
byte[] encryptionObject = new CBORAsymKeyEncrypter(senderKey,
KeyEncryptionAlgorithms.ECDH_ES,
ContentEncryptionAlgorithms.A256GCM)
.encrypt(SECRET_MESSAGE).encode();
// Decrypt data using CEF.
byte[] decryptedData = new CBORAsymKeyDecrypter(receiverKey)
.decrypt(CBORDecoder.decode(encryptionObject));
// Assume that the data is a string encoded in UTF-8.
String secretMessage = UTF8.decode(decryptedData);
System.out.println(secretMessage);
}
}
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import org.webpki.cbor.CBORAsymKeyDecrypter;
import org.webpki.cbor.CBORAsymKeyEncrypter;
import org.webpki.cbor.CBORKeyPair;
import org.webpki.cbor.CBORDecoder;
import org.webpki.crypto.ContentEncryptionAlgorithms;
import org.webpki.crypto.KeyEncryptionAlgorithms;
import org.webpki.util.HexaDecimal;
import org.webpki.util.UTF8;
public class EncryptionDemo {
// Message encoded in UTF-8.
static final byte[] SECRET_MESSAGE = UTF8.encode("A very secret message");
// X25519 private key in COSE format.
static final byte[] X25519_PRIVATE_KEY = HexaDecimal.decode(
"a401012004215820e99a0cef205894960d9b1c05978513dccb" +
"42a13bfbced523a51b8a117ad5f00c2358207317e5f3a11599" +
"caab474ee65843427f517fe4d8b99add55886c84441e90d6f0");
public static void main(String[] args) {
// Get keys in Java format.
KeyPair keyPair = CBORKeyPair.convert(CBORDecoder.decode(X25519_PRIVATE_KEY));
PrivateKey receiverKey = keyPair.getPrivate();
PublicKey senderKey = keyPair.getPublic();
// Encrypt data using CEF.
byte[] encryptionObject = new CBORAsymKeyEncrypter(senderKey,
KeyEncryptionAlgorithms.ECDH_ES,
ContentEncryptionAlgorithms.A256GCM)
.encrypt(SECRET_MESSAGE).encode();
// Decrypt data using CEF.
byte[] decryptedData = new CBORAsymKeyDecrypter(receiverKey)
.decrypt(CBORDecoder.decode(encryptionObject));
// Assume that the data is a string encoded in UTF-8.
String secretMessage = UTF8.decode(decryptedData);
System.out.println(secretMessage);
}
}
Sample key in diagnostic notation:
{
1: 1,
-1: 4,
-2: h'e99a0cef205894960d9b1c05978513dccb42a13bfbced523a51b8a117ad5f00c',
-4: h'7317e5f3a11599caab474ee65843427f517fe4d8b99add55886c84441e90d6f0'
}
1: 1,
-1: 4,
-2: h'e99a0cef205894960d9b1c05978513dccb42a13bfbced523a51b8a117ad5f00c',
-4: h'7317e5f3a11599caab474ee65843427f517fe4d8b99add55886c84441e90d6f0'
}
The resulting encryption object in diagnostic notation:
{
1: 3,
2: {
1: -25,
7: {
1: 1,
-1: 4,
-2: h'3e9c03b4e2ccb023272fe0f1a5a414645a7e5a0952a3da8199ba46812603ee1a'
}
},
8: h'4ac80be51285309b93b8f4cc38f6b8ba',
9: h'9fbd6e151bad2af177dd3382',
10: h'115b48dcffcf88dce70b2173d6c368b2cfe802521c'
}
1: 3,
2: {
1: -25,
7: {
1: 1,
-1: 4,
-2: h'3e9c03b4e2ccb023272fe0f1a5a414645a7e5a0952a3da8199ba46812603ee1a'
}
},
8: h'4ac80be51285309b93b8f4cc38f6b8ba',
9: h'9fbd6e151bad2af177dd3382',
10: h'115b48dcffcf88dce70b2173d6c368b2cfe802521c'
}
The resulting encryption object expressed in hexadecimal:
a5010302a201381807a3010120042158203e9c03b4e2ccb023272fe0f1a5a414645a7e5a0952a3da8199ba46812603ee1a08504ac80be51285309b93b8f4cc38f6b8ba094c9fbd6e151bad2af177dd33820a55115b48dcffcf88dce70b2173d6c368b2cfe802521c