CDOC2 reference implementation (Java)
CDOC stands for 'Crypto Digidoc', encrypted file transmission format used in the Estonian eID ecosystem
CDOC2 is a new version of CDOC (CDOC lib cdoc4j), featuring additional security measures with optional server backend. CDOC version are not compatible.
Additional background info can be found in RIA CDOC2 presentation and id.ee CDOC 2.0 article
End-user software to create/decrypt CDOC2: https://github.com/open-eid/DigiDoc4-Client
Warning: Following scenario descriptions are simplification to give general idea, details and final truth is in CDOC2 specification.
- Sender downloads recipient's certificate from SK LDAP using recipient id (isikukood). Recipient certificate contains EC public key.
- Sender generates EC (elliptic curve) key pair using the same EC curve as in recipient EC public key 1
- Sender derives key encryption key (KEK) using ECDH (from sender EC private key and recipient EC public key)
- Sender generates file master key (FMK) using HKDF extract algorithm
- Sender derives content encryption key (CEK) and hmac key (HHK) from FMK using HKDF expand algorithm
- Sender encrypts FMK with KEK (xor)
- Sender adds encrypted FMK with senders and recipients public keys to CDoc header2
- Sender calculates header hmac using hmac key (HHK) and adds calculated hmac to CDoc
- Sender encrypts content3 with CEK (ChaCha20-Poly1305 with AAD)
- Sender sends CDoc to Recipient
- Recipient finds recipients public key from CDoc
- Recipient derives key encryption key (KEK) using ECDH (from recipient private key on id-kaart and sender public key) and decrypts FMK
- Recipient derives CEK and HHK from FMK using HKDF algorithm
- Recipient calculates hmac and checks it against hmac in CDoc
- Recipient decrypts content using CEK
- Follow steps from previous scenario 1-6
- Sender chooses key transaction server (preconfigured list)
- Sender sends sender public key and recipient public key to the key transfer server 4
- Server stores public keys in server and generates transaction id
- Sender adds key server id, recipient public key, transaction id and encrypted FMK to CDoc header
- Follow steps from previous scenario 8-10
- Recipient finds transaction id and server using his id-kaart public key from CDoc
- Recipient authenticates himself against key transfer server using certificate on id-kaart (mutual TLS)
- Recipient queries the server with transaction id 4
- If recipient certificate public key and recipient public key in transaction record match, then server answers with sender public key
- Follow steps from previous steps 12-15
Key transfer server benefits:
- After the key has been deleted from the key transfer server, the document cannot be decrypted even when keys on recipient's id-kaart have been compromised.
- Other scenarios can be implemented like expiring CDoc2 documents by deleting expired keys from key transfer server.
RSA-OAEP is similar to ECDH scenario, with difference that KEK is generated from secure random (not ECDH) and KEK is encrypted with recipient RSA public key and included into CDOC header (instead of sender public key).
- Sender acquires recipient's certificate from SK LDAP using recipient id or by some other means. Recipient certificate contains recipient RSA public key.
- Sender generates file master key (FMK) using HKDF extract algorithm.
- Sender generates encryption key (KEK) using secure random.
- Sender derives content encryption key (CEK) and hmac key (HHK) from FMK using HKDF expand algorithm.
- Sender encrypts FMK with KEK (xor).
- Sender encrypts KEK with recipient's RSA public key.
- Sender adds encrypted FMK and encrypted KEK with recipient's public key to CDoc header.
- Sender calculates header hmac using hmac key (HHK) and adds calculated hmac to CDoc.
- Sender encrypts content with CEK (ChaCha20-Poly1305 with AAD).
- Sender sends CDoc to recipient.
- Recipient searches CDoc header for recipient's record that contains his public key.
- Recipient decrypts key encryption key (KEK) using recipient's RSA private key.
- Recipient decrypts FMK using KEK.
- Recipient derives CEK and HHK from FMK using HKDF algorithm.
- Recipient calculates hmac and checks it against hmac in CDoc.
- Recipient decrypts content using CEK.
- Follow steps from RSA-OAEP scenario 1-6
- Sender chooses key capsule server (by providing server configuration)
- Sender sends recipient public key and encrypted KEK inside capsule to the key capsule server
- Server stores capsule containing recipient public key and encrypted KEK and responds with generated transaction id
- Sender adds key server id, recipient public key, transaction id and encrypted FMK to CDoc header
- Follow steps from RSA-OAEP scenario 8-10
- Recipient finds transaction id and server using his public RSA key from CDoc
- Recipient authenticates against server using RSA certificate (mutual TLS)
- Recipient queries the server with transaction id 4
- If recipient certificate public key and recipient public key in capsule record match, then server answers with capsule that contains encrypted KEK
- Follow steps from RSA-OAEP scenario steps 12-15
Similar to ECDH scenario, but KEK is derived from symmetric key (secret) identified by key_label using HKDF algorithm.
- Sender and recipient have a pre shared secret identified by key_label
- Sender derives key encryption key (KEK) from symmetric key, key_label and salt (generated using secure random) using HKDF algorithm
- Follow steps from ECDH scenario 4-6
- Sender adds encrypted FMK with key_label to CDoc header
- Follow steps from ECDH scenario 8-10
- Recipient searches CDoc header for key_label and finds salt and encrypted FMK
- Recipient derives encryption key (KEK) from salt, key_label and pre-shared symmetric key (secret)
- Recipient decrypts FMK using KEK.
- Follow steps from ECDH scenario 13-15
cdoc2-java-ref-impl does not provide solution for securely storing the secret, but most password managers can do that.
Similar to Symmetric Key scenario, but symmetric key is derived from password and salt using PBKDF2 algorithm.
- Sender and recipient have a pre shared password identified by key_label
- Symmetric key is created from password and salt (generated using secure random) using PBKDF2 algorithm
- Sender derives key encryption key (KEK) from symmetric key and previously generated salt using HKDF algorithm
- Follow steps from ECDH scenario 4-6
- Sender adds encrypted FMK with key_label to CDoc header
- Follow steps from ECDH scenario 8-10
- Recipient searches CDoc header for key_label and finds salt and encrypted FMK
- Recipient derives encryption key (KEK) from salt, key_label and pre-shared symmetric key (password)
- Recipient decrypts FMK using KEK.
- Follow steps from ECDH scenario 13-15
cdoc2-java-ref-impl does not provide solution for securely storing the password, but most password managers can do that.
- cdoc2-schema - flatbuffers schemas and code generation
- cdoc2-lib - CDOC2 creation and processing library
- cdoc2-client - client for communicating with cdoc2-capsule-server
- cdoc2-cli - Command line utility to create/process CDOC2 files
- test - Sample CDOC2 containers (with script to create and decrypt them) and automated tests for CLI
- cdoc2-example-app - Example, how to use cdoc2-java-ref-impl and cdoc4j together
Other CDOC2 repositories:
- https://github.com/open-eid/cdoc2-openapi CDOC2 OpenAPI specifications
- https://github.com/open-eid/cdoc2-capsule-server CDOC2 Capsule Server
- https://github.com/open-eid/cdoc2-gatling-tests Gatling tests for CDOC2 Capsule Server
- Java 17
- Maven 3.8.x
Depends on: https://github.com/open-eid/cdoc2-openapi OpenAPI specifications for client stub generation
Configure github package repo access https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-apache-maven-registry#authenticating-with-a-personal-access-token
Example <profile>
section of settings.xml
for using cdoc2 dependencies:
<profile>
<id>github</id>
<repositories>
<repository>
<id>central</id>
<url>https://repo1.maven.org/maven2</url>
</repository>
<repository>
<id>github</id>
<url>https://maven.pkg.github.com/open-eid/cdoc2-openapi</url>
</repository>
</repositories>
</profile>
Note: When pulling, the GitHub package index is based on the organization level , not the repository level.
So defining any Maven package repo from open-eid
is enough for pulling cdoc2-* dependencies.
All packages published under open-eid
can be found https://github.com/orgs/open-eid/packages
CDOC2 has been tested with JDK 17 and Maven 3.8.8
mvn clean install
Maven build is executed for GH event pull_request
an and push
to 'master'.
GH build workflow configures Maven repository automatically. For fork based pull_requests
Maven repo value will be set to github.event.pull_request.base.repo.full_name
. It can be overwritten
by defining repository variable
MAVEN_REPO
By default, tests that require smart-card are excluded from running. To execute all tests enable allTests maven profile
mvn -PallTests test
For more control set tests
maven property directly. For more info see
Junit5 Tag Expression
mvn -Dtests='!(slow | pkcs11)'
To run the tests using a physical PKCS11 device (smart card or usb token), execute:
mvn test -Dtests=pkcs11
The pkcs11 device configuration (PKCS11 library, slot, pin, etc) can be specified using
cdoc2.pkcs11.conf-file
system property, for example run with configuration file from filesystem
from the root of the project:
mvn test -Dtests=pkcs11 -Dcdoc2.pkcs11.conf-file=src/test/resources/pkcs11-test-safenet.properties
or
mvn test -Dtests=pkcs11 -Dcdoc2.pkcs11.conf-file=src/test/resources/pkcs11-test-idcard.properties
By default, the pkcs11 configuration is read from the file pkcs11-test-idcard.properties
.
Additional tests using Bats and cdoc2-cli
.
Refer test/README.md
In case the tests run slowly (probably due to waiting on entropy generation),
using an entropy source (e.g haveged
) may help on Linux:
apt-get install haveged
update-rc.d haveged defaults
service haveged start
See VERSIONING.md
Create release on tag done by VERSIONING.md process. It will trigger maven-release.yml
workflow that
will deploy Maven packages to GitHub Maven package repository.
Since build uses exists-maven-plugin
then altDeploymentRepository
doesn't work as it only works
for deploy
plugin. Set project.distributionManagement
user properties instead:
mvn deploy -Dproject.distributionManagement.repository.id=github \
-Dproject.distributionManagement.repository.url=https://maven.pkg.github.com/open-eid/cdoc2-java-ref-impl`
Footnotes
-
Current specification defines only SecP384R1 Elliptic Curve for key agreement, but in future other EC curves or algorithms can be added, see flatbuffers schemas in cdoc2-schema ↩
-
Header structure is defined in flatbuffers schema, see cdoc2-schema ↩
-
Content is zlib compressed tar archive ↩
-
key transfer server protocol is defined in cdoc2-openapi module ↩ ↩2 ↩3