CDOC stands for 'Crypto Digidoc', encrypted file transmission format used in the Estonian eID ecosystem.
(Digidoc is digital signature format specific to Estonia)
- CDOC1 - Unofficial term for all (XML-ENC based) CDOC formats preceding CDOC2.
- CDOC2 is a new version of CDOC, featuring additional security measures with optional server backend and support for long term cryptography.
CDOC1 and CDOC2 version are not compatible.
End-user software to create/decrypt CDOC1/CDOC2: https://github.com/open-eid/DigiDoc4-Client
Additional background info can be found in RIA CDOC2 presentation and id.ee CDOC 2.0 article
CDOC1 is xml document, examples can be found here
~/workspace/cdoc2-java-ref-impl/cdoc2-example-app/src/test/resources/cdoc$ head -1 valid_cdoc11_ECC.cdoc
<?xml version="1.0" encoding="UTF-8"?><denc:EncryptedData xmlns:denc="http://www.w3.org/2001/04/xmlenc#" MimeType="application/octet-stream">
CDOC1 specification and Java library cdoc4j
To create/decrypt CDOC1 documents see cdoc4j: Examples of how to use it
CDOC2 is binary document, where first 4 bytes are 0x43, 0x44, 0x4f, 0x43
or CDOC
in ASCII:
~/workspace/cdoc2-java-ref-impl/test/testvectors$ hd ec_simple.cdoc
00000000 43 44 4f 43 02 00 00 01 6c 0c 00 00 00 08 00 0c |CDOC....l.......|
CDOC2 specification and Java library cdoc2-java-ref-impl
cdoc2-java-ref-impl
is a repository that contains cdoc2-lib
and related submodules required for
creating/decrypting CDOC2 documents. To use modules from cdoc2-java-ref-impl
:
To use cdoc2-java-ref-impl
as Maven dependency you need to create a GitHub personal access token
with read:packages
scope.
Configure Maven settings.xml or configure Gradle
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-java-ref-impl</url>
</repository>
</repositories>
</profile>
Test that you have configured your Maven settings.xml
correctly (from cdoc2-example-app
directory):
./mvnw dependency::get -Dartifact=ee.cyber.cdoc2:cdoc2-lib:2.0.0
[INFO] Resolving ee.cyber.cdoc2:cdoc2-lib:jar:2.0.0 with transitive dependencies
Downloading from central: https://repo1.maven.org/maven2/ee/cyber/cdoc2/cdoc2-lib/2.0.0/cdoc2-lib-2.0.0.pom
Downloading from github: https://maven.pkg.github.com/open-eid/cdoc2-capsule-server/ee/cyber/cdoc2/cdoc2-lib/2.0.0/cdoc2-lib-2.0.0.pom
Downloaded from github: https://maven.pkg.github.com/open-eid/cdoc2-capsule-server/ee/cyber/cdoc2/cdoc2-lib/2.0.0/cdoc2-lib-2.0.0.pom (3.2 kB at 2.3 kB/s)
Downloading from central: https://repo1.maven.org/maven2/ee/cyber/cdoc2/cdoc2-lib/2.0.0/cdoc2-lib-2.0.0.jar
Downloading from github: https://maven.pkg.github.com/open-eid/cdoc2-capsule-server/ee/cyber/cdoc2/cdoc2-lib/2.0.0/cdoc2-lib-2.0.0.jar
Downloaded from github: https://maven.pkg.github.com/open-eid/cdoc2-capsule-server/ee/cyber/cdoc2/cdoc2-lib/2.0.0/cdoc2-lib-2.0.0.jar (157 kB at 136 kB/s)
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
Latest version of cdoc2-lib
can be found here
Define cdoc2-lib
dependency in your pom.xml
:
<dependency>
<groupId>ee.cyber.cdoc2</groupId>
<artifactId>cdoc2-lib</artifactId>
<version>2.0.0</version>
</dependency>
File cdoc2FileToCreate = Paths.get("/tmp/first.cdoc2").toFile();
File payloadFile1 = Paths.get("some_file1.txt").toFile();
File payloadFile2 = Paths.get("some_file2.txt").toFile();
File[] payloadFiles = new File[]{payloadFile1, payloadFile2};
char[] password = "myPlainTextPassword".toCharArray(); // don't store password in String in production code
Sting keyLabel = "labelFromExample";
EncryptionKeyMaterial km = EncryptionKeyMaterial.fromPassword(password, keyLabel);
CDocBuilder builder = new CDocBuilder()
.withPayloadFiles(Arrays.asList(payloadFiles))
.withRecipients(List.of(km))
.buildToFile(cdoc2FileToCreate);
Path cdoc2FileToDecrypt = Paths.get("/tmp/first.cdoc2");
Path destDir = Paths.get("/tmp");
char[] password = "myPlainTextPassword".toCharArray(); // don't store password in String in production code
Sting keyLabel = "labelFromExample";
List<String> extractedFileNames = new CDocDecrypter()
.withCDoc(cdoc2FileToDecrypt.toFile())
.withRecipient(DecryptionKeyMaterial.fromPassword(password, keyLabel))
.withDestinationDirectory(destDir.toFile())
.decrypt();
File cdoc2FileToCreate = Paths.get("/tmp/second.cdoc2").toFile();
String identificationCode = "3..."; // your id-code
File[] payloadFiles = new File[]{};//add some files
List<EncryptionKeyMaterial> recipients =
EncryptionKeyMaterial.collectionBuilder().fromEId(new String[]{identificationCode});
CDocBuilder builder = new CDocBuilder()
.withPayloadFiles(Arrays.asList(payloadFiles))
.withRecipients(recipients)
.buildToFile(cdoc2FileToCreate);
Note: this works only for end user id-cards. It doesn't work for test id-cards as test-id card certificates are not in SK LDAP.
Using id-card requires that you have openSC PKCS11 drivers installed. These are usually installed
by installing https://www.id.ee/en/article/install-id-software/ Before trying to decrypt CDOC2 files
with cdoc2-lib
verify that you can access id-card with
Path cdoc2FileToDecrypt = Paths.get("/tmp/second.cdoc2");
Path destDir = Paths.get("/tmp");
Integer slot = 0;
String alias = "Isikutuvastus";
DecryptionKeyMaterial dkm = DecryptionKeyMaterial.fromKeyPair(
Pkcs11Tools.loadFromPKCS11Interactively(
"/usr/lib/x86_64-linux-gnu/opensc-pkcs11.so", // pkcs11 driver location, differs on different platforms
slot,
alias
)
);
List<String> extractedFiles = new CDocDecrypter()
.withCDoc(cdoc2FileToDecrypt.toFile())
.withRecipient(dkm)
.withDestinationDirectory(destDir.toFile())
.decrypt();
/usr/lib/x86_64-linux-gnu/opensc-pkcs11.so
is location of OpenSC pkcs11 driver library. Some info
on setting up pcks11 on Ubuntu can be found in pkcs11.README
Most cdoc2 documents created by DigiDoc4 library will use cdoc2-capsule-server
To determine, what kind of recipient can decrypt CDOC2 document and if cdoc2-capsule-server was used, see source code for cdoc2-cli info
To create CDOC2 document with server scenario, cdoc2-capsule-server client needs to be configured.
Easiest is to use one of existing properties files from cdoc2-cli/config
directory and .withServerProperties
method:
File cdoc2FileToCreate = Paths.get("/tmp/second.cdoc2").toFile();
String identificationCode = "3..."; // your id-code
String keyServerPropertiesFile = "/path/to/cdoc2-cli/conf/id.properties";
Properties p = new Properties().load(
Resources.getResourceAsStream(keyServerPropertiesFile));
List<EncryptionKeyMaterial> recipients =
EncryptionKeyMaterial.collectionBuilder().fromEId(new String[]{identificationCode});
CDocBuilder builder = new CDocBuilder()
.withServerProperties(p)
.withPayloadFiles(Arrays.asList(payloadFiles))
.withRecipients(recipients)
.buildToFile(cdoc2FileToCreate);
Note: cdoc2-cli/config
contains usually several properties files. For id-card usage, use one with
the shortest name (without _pkcs12
or _p12
in name).
Uploading key material to server allows to invalidate CDOC2 documents that are may be affected from future security vulnerability like ROCA vulnerability
In non-server scenarios sender public key material is included in CDOC2 itself. For server scenarios sender public key material is uploaded to server and recipient needs first to download the sender public key material and then needs to decrypt CDOC2 with private key. Even when data stored in server gets compromised, it doesn't grant access to CDOC2 documents as final decryption is done with recipient private key (id-card).
Similar to previous example, to decrypt cdoc2 with server recipient,
cdoc2-capsule-serverclient needs to be configured
and easiest is to do it with existing properties, but CdocDecrypter
has method withKeyServers
that takes KeyCapsuleClientFactory
as parameter:
Path cdoc2FileToDecrypt = Paths.get("/tmp/second.cdoc2");
Path destDir = Paths.get("/tmp");
Integer slot = 0;
String alias = "Isikutuvastus";
String keyServerPropertiesFile = "/path/to/cdoc2-cli/conf/id.properties";
Properties p = new Properties().load(
Resources.getResourceAsStream(keyServerPropertiesFile));
KeyCapsuleClientFactory keyCapsulesClient = KeyCapsuleClientImpl.createFactory(p);
DecryptionKeyMaterial dkm = DecryptionKeyMaterial.fromKeyPair(
Pkcs11Tools.loadFromPKCS11Interactively(
"/usr/lib/x86_64-linux-gnu/opensc-pkcs11.so", // pkcs11 driver location, differs on different platforms
slot,
alias
)
);
List<String> extractedFiles = new CDocDecrypter()
.withCDoc(cdoc2FileToDecrypt.toFile())
.withKeyServers(keyCapsulesClient)
.withRecipient(dkm)
.withDestinationDirectory(destDir.toFile())
.decrypt();
Note: pkcs11-library
location can also be specified as Java system property
(-Dpkcs11-library=<path>
or System.setProperty("pkcs11-library", "<path>")
),
when not specified explicitly as method parameter. If pkcs11-library
system property is not set,
then pkcs11 library is looked for from default locations
Latest server configuration is available through https://id.eesti.ee/config.json
Scenarios with id-card are meant for transport cryptography only as id-card certificates expiry and also cdoc2-capsule-server deletes key material after some time. To store CDOC2 documents for longer time, it's recommended to re-encrypt CDOC2 documents with a password or symmetric key. When re-encrypting, then files in CDOC2 container are not written to disk - only parts of CDOC2 are decrypted temporary into memory. To re-encrypt existing CDOC2 document, use CDocReEncrypter class
For usage see CDocReEncryptCmd.java from cdoc2-cli
See cdoc2-example-app -
uses both cdoc4j
and cdoc2-lib