在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
开源软件名称(OpenSource Name):ehn-dcc-development/hcert-kotlin开源软件地址(OpenSource Url):https://github.com/ehn-dcc-development/hcert-kotlin开源编程语言(OpenSource Language):Kotlin 94.6%开源软件介绍(OpenSource Introduction):Electronic Health Certificate Kotlin Multiplatform LibraryThis library implements a very basic validation and creation chain for electronic health certificates (HCERT):
All services are implemented according to https://github.com/ehn-digital-green-development/hcert-spec, Version 1.0.5 from 2021-04-18. The schemata for data classes are imported from https://github.com/ehn-digital-green-development/ehn-dgc-schema, up to Version 1.3.0, from 2021-06-11. Several other git repositories are included as submodules. Please clone this repository with This Kotlin library is a mulitplatform project, with targets for JVM and JavaScript. Usage (JVM)The main class for encoding and decoding HCERT data is Correct implementations of the service interfaces reside in Example for creation services: // Load the private key and certificate from somewhere ...
String privateKeyPem = "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADAN...";
String certificatePem = "-----BEGIN CERTIFICATE-----\nMIICsjCCAZq...";
CryptoService cryptoService = new FileBasedCryptoService(privateKeyPem, certificatePem);
Chain chain = DefaultChain.buildCreationChain(cryptoService); //optional custom prefix, e.g. "AT1:" to support AT-specific exemption certificates
// Load the input data from somewhere ...
String json = "{ \"sub\": { \"gn\": \"Gabriele\", ...";
GreenCertificate input = Json.Default.decodeFromString(GreenCertificate.Companion.serializer(), json);
// Apply all encoding steps from the Chain
ChainResult result = chain.encode(input);
// Optionally encode it as a QR-Code with 350 pixel in width and height
TwoDimCodeService qrCodeService = new DefaultTwoDimCodeService(350);
byte[] encodedImage = qrCodeService.encode(result.getStep5Prefixed());
String encodedBase64QrCode = Base64.getEncoder().encodeToString(encodedImage);
// Then include in an HTML page or something ...
String html = "<img src=\"data:image/png;base64," + encodedBase64QrCode + "\" />"; Example for the verification side, i.e. in apps: // Load the certificate from somewhere ...
String certificatePem = "-----BEGIN CERTIFICATE-----\nMIICsjCCAZq...";
CertificateRepository repository = new PrefilledCertificateRepository(certificatePem);
Chain chain = DefaultChain.buildVerificationChain(repository); //optional parameter atRepository to verify vaccination exemptions (prefix AT1:) against
// Scan the QR code from somewhere ...
String input = "HC1:NCFC:MVIMAP2SQ20MU...";
DecodeResult result = chain.decode(input);
// Read metaInformation like expirationTime, issuedAt, issuer
VerificationResult verificationResult = result.getVerificationResult();
boolean isValid = verificationResult.getError() == null;
// See list below for possible Errors, may be null
Error error = verificationResult.getError();
// Result data may be null
GreenCertificate greenCertificate = result.getChainDecodeResult().getEudgc(); You may also load a trust list from a server, that contains several trusted certificates: // PEM-encoded signer certificate of the trust list
String trustListAnchor = "-----BEGIN CERTIFICATE-----\nMIICsjCCAZq...";
CertificateRepository trustAnchor = new PrefilledCertificateRepository(trustListAnchor);
// Download trust list content, binary, e.g. from https://dgc.a-sit.at/ehn/cert/listv2
byte[] trustListContent = new byte[0];
// Download trust list signature, binary, e.g. from https://dgc.a-sit.at/ehn/cert/sigv2
byte[] trustListSignature = new byte[0];
SignedData trustList = new SignedData(trustListContent, trustListSignature);
CertificateRepository repository = new TrustListCertificateRepository(trustList, trustAnchor);
Chain chain = DefaultChain.buildVerificationChain(repository);
// Continue as in the example above .. Faulty ImplementationsThe usage of interfaces for all services (CBOR, CWT, COSE, ZLib, Context) in the chain may seem over-engineered at first, but it allows us to create wrongly encoded results, by passing faulty implementations of the service. Those services reside in a separate artifact named Sample data objects are provided in Debug ChainIn addition to the default (spec-compliant) verification behaviour, it is possible to continue verification even after certain errors.
While a faulty encoding or garbled CBOR structure will still result in fatal errors, an expired certificate, or unknown KID, will not terminate the verification procedure, when using the debug chain.
For details, see Anyonymising Personal Data (JVM only)Both the NOTE: This is blanking of personal data is limited to humanly comprehensible representations of processed data.
As such, even anonymised DO LOG OR PROCESS THIS DATA, EVEN WHEN USING ANONYMISED COPIES! YOU HAVE BEEN WARNED. Usage (JS)The build result of this library for JS is a module in UMD format, located under Build the module either for development or production for a browser target:
Build the module either for development or production (NodeJS target):
To verify a single QR code content: // PEM-encoded DSC
let pemCert = "-----BEGIN CERTIFICATE-----\nMIICsjCCAZq...";
// Would also accept more than one DSC
let verifier = new hcert.VerifierDirect([pemCert]); //optional second parameter: array of pem encoded certs to verify vaccination exemptions (prefix AT1:) against
// Scan the QR code from somewhere ...
let qr = "HC1:NCFC:MVIMAP2SQ20MU...";
let result = verifier.verify(qr);
let isValid = result.isValid;
// Read metaInformation like expirationTime, issuedAt, issuer
let metaInformation = result.metaInformation;
// See list below for possible Errors, may be null
let error = result.error;
// Result data may be null, contains decoded HCERT
let greenCertificate = result.greenCertificate; An alternative way of initializing the // PEM-encoded signer certificate of the trust list
let trustListAnchor = "-----BEGIN CERTIFICATE-----\nMIICsjCCAZq...";
// Download trust list content, binary, e.g. from https://dgc.a-sit.at/ehn/cert/listv2
let trustListContent = new ArrayBuffer(8);
// Download trust list signature, e.g. from https://dgc.a-sit.at/ehn/cert/sigv2
let trustListSignature = new ArrayBuffer(8);
let verifier = new hcert.VerifierTrustList(trustListAnchor, trustListContent, trustListSignature); //optional isAT flag as fourth parameter to
//update AT-specific trust ancors to verify
//vaccination exemptions (prefix AT1:) against
// Continue with example above with verifier.verify() If you want to save the instance of // PEM-encoded signer certificate of the trust list
let trustListAnchor = "-----BEGIN CERTIFICATE-----\nMIICsjCCAZq...";
// Download trust list content, binary, e.g. from https://dgc.a-sit.at/ehn/cert/listv2
let trustListContent = new ArrayBuffer(8);
// Download trust list signature, e.g. from https://dgc.a-sit.at/ehn/cert/sigv2
let trustListSignature = new ArrayBuffer(8);
verifier.updateTrustList(trustListAnchor, trustListContent, trustListSignature);
// Continue with example above with verifier.verify(); The meta information contains extracted data from the QR code contents, e.g.: {
"expirationTime": "2021-11-02T18:00:00Z",
"issuedAt": "2021-05-06T18:00:00Z",
"issuer": "AT",
"certificateValidFrom": "2021-05-05T12:41:06Z",
"certificateValidUntil": "2023-05-05T12:41:06Z",
"certificateValidContent": [
"TEST",
"VACCINATION",
"RECOVERY"
],
"certificateSubjectCountry": "AT",
"content": [
"VACCINATION"
],
"error": null
} Encoding of HCERT data, i.e. generating the input for an QR Code, as well as the QR Code picture: // Create a new, random EC key with 256 bits key size, i.e. on P-256
let generator = new hcert.GeneratorEcRandom(256);
// Provide valid HCERT data
let input = JSON.stringify({"ver": "1.2.1", "nam": { ... }});
// Get a result with all intermediate steps
let result = generator.encode(input);
// Print the contents of the QR code
console.info(result.step5Prefixed);
// Alternative: Get a data URL of the encoded QR code picture, e.g. "data:image/gif;base64,AAA..."
let moduleSize = 4;
let marginSize = 2;
let qrCode = generator.encodeToQrCode(input, moduleSize, marginSize); You may also load a fixed key pair with certificate: // PEM-encoded private key info, i.e. PKCS#8
let pemKey = "-----BEGIN PRIVATE KEY-----\nME0CAQAwE...";
// PEM-encoded certificate
let pemCert = "-----BEGIN CERTIFICATE-----\nMIICsjCCAZq...";
// Load the private key and certificate
let generator = new hcert.GeneratorFixed(pemKey, pemCert);
// Provide valid HCERT data
let input = JSON.stringify({"ver": "1.2.1", "nam": { ... }});
// Continue with example above with generator.encode() An alternative to calling {
"schemaVersion": "1.0.0",
"subject": {
"familyName": "Musterfrau-Gößinger",
"familyNameTransliterated": "MUSTERFRAU<GOESSINGER",
"givenName": "Gabriele",
"givenNameTransliterated": "GABRIELE"
},
"dateOfBirthString": "1998-02-26",
"vaccinations": [
{
"target": {
"key": "840539006",
"valueSetEntry": {
"display": "COVID-19",
"lang": "en",
"active": true,
"system": "http://snomed.info/sct",
"version": "http://snomed.info/sct/900000000000207008/version/20210131",
"valueSetId": null
}
},
"vaccine": {
"key": "1119305005",
"valueSetEntry": {
"display": "SARS-CoV-2 antigen vaccine",
"lang": "en",
"active": true,
"system": "http://snomed.info/sct",
"version": "http://snomed.info/sct/900000000000207008/version/20210131",
"valueSetId": null
}
},
"medicinalProduct": {
"key": "EU/1/20/1528",
"valueSetEntry": {
"display": "Comirnaty",
"lang": "en",
"active": true,
"system": "https://ec.europa.eu/health/documents/community-register/html/",
"version": "",
"valueSetId": null
}
},
"authorizationHolder": {
"key": "ORG-100030215",
"valueSetEntry": {
"display": "Biontech Manufacturing GmbH",
"lang": "en",
"active": true,
"system": "https://spor.ema.europa.eu/v1/organisations",
"version": "",
"valueSetId": "vaccines-covid-19-auth-holders"
}
},
"doseNumber": 1,
"doseTotalNumber": 2,
"date": "2021-02-18T00:00:00.000Z",
"country": "AT",
"certificateIssuer": "BMSGPK Austria",
"certificateIdentifier": "urn:uvci:01:AT:10807843F94AEE0EE5093FBC254BD813P"
}
],
"recoveryStatements": null,
"tests": null,
"dateOfBirth": "1998-02-26T00:00:00.000Z"
} Debug ChainIn addition to the default (spec-compliant) verification behaviour, it is possible to continue verification even after certain errors.
While a faulty encoding or garbled CBOR structure will still result in fatal errors, an expired certificate, or unknown KID, will not terminate the verification procedure, when using the debug chain.
Simply add a ErrorsThe field
On JavaScript, the methods {
"message_8yp7un$_0": "Hash not matching",
"cause_th0jdv$_0": null,
"stack": "n/</e.captureStack@file:///...",
"name": "VerificationException",
"error": {
"name$": "TRUST_LIST_SIGNATURE_INVALID",
"ordinal$": 14
}
} The important bits are TrustListThere is also an option to create (e.g. on a web service) a list of trusted certificates for verification of HCERTs: // Load the private key and certificate from somewhere ...
String privateKeyPem = "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADAN...";
String certificatePem = "-----BEGIN CERTIFICATE-----\nMIICsjCCAZq...";
CryptoService cryptoService = new FileBasedCryptoService(privateKeyPem, certificatePem);
int validityHours = 48;
TrustListV2EncodeService trustListService = new TrustListV2EncodeService(cryptoService, validityHours);
// Load the list of trusted certificates from somewhere ...
Set<CertificateAdapter> trustedCerts = new HashSet<>(cert1, cert2, ...);
SignedData trustList = trustListService.encode(trustedCerts);
// Write content file
new FileOutputStream(new File("trustlist.bin")).write(trustList.getContent());
// Write signature file
new FileOutputStream(new File("trustlist.sig")).write(trustList.getSignature()); Clients may load these files to get the Trusted Certificates plus meta information: // PEM-encoded signer certificate of the trustList
CertificateRepository trustAnchor = new PrefilledCertificateRepository("-----BEGIN CERTIFICATE-----\nMIICsjCCAZq...");
// Download trust list content, binary, e.g. from https://dgc.a-sit.at/ehn/cert/listv2
byte[] trustListContent = new byte[0];
// Download trust list signature, binary, e.g. from https://dgc.a-sit.at/ehn/cert/sigv2
byte[] trustListSignature = new byte[0];
SignedData trustList = new SignedData(trustListContent, trustListSignature);
TrustListDecodeService service = new TrustListDecodeService(trustAnchor);
Pair<SignedDataParsed, TrustListV2> result = service.decode(trustList);
// Contains "validFrom", "validUntil"
SignedDataParsed parsed = result.getFirst();
// Contains a list of certificates in X.509 encoding
TrustListV2 trustListContainer = result.getSecond();
for (TrustedCertificateV2 cert : trustListContainer.getCertificates()) {
// Parse it into your own data class
System.out.println(ExtensionsKt.asBase64(cert.getCertificate()));
} or in JavaScript: // PEM-encoded signer certificate of the trust list
let trustListAnchor = "-----BEGIN CERTIFICATE-----\nMIICsjCCAZq...";
// Download trust list content, binary, e.g. from https://dgc.a-sit.at/ehn/cert/listv2
let trustListContent = new ArrayBuffer(8);
// Download trust list signature, binary,, e.g. from https://dgc.a-sit.at/ehn/cert/sigv2
let trustListSignature = new ArrayBuffer(8);
let result = hcert.SignedDataDownloader.loadTrustList(trustListAnchor, trustListContent, trustListSignature);
// Contains "validFrom" and "validUntil" as JS Dates
console.log(result.first);
// Contains an array of "certificates", each with "kid" and "certificate" as Int8Array
console.log(result.second); |