Skip to content

Commit

Permalink
Add overloads for Java callers, fix examples
Browse files Browse the repository at this point in the history
Fixes #62
  • Loading branch information
nodh committed Sep 29, 2021
1 parent c4720c8 commit 4d9062b
Show file tree
Hide file tree
Showing 17 changed files with 84 additions and 46 deletions.
42 changes: 21 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,14 @@ Chain chain = DefaultChain.buildCreationChain(cryptoService);

// Load the input data from somewhere ...
String json = "{ \"sub\": { \"gn\": \"Gabriele\", ...";
String input = Json.decodeFromString<GreenCertificate>(json);
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);
String encodedImage = qrCodeService.encode(result.step5Prefixed);
byte[] encodedImage = qrCodeService.encode(result.getStep5Prefixed());
String encodedBase64QrCode = Base64.getEncoder().encodeToString(encodedImage);

// Then include in an HTML page or something ...
Expand All @@ -63,7 +63,7 @@ String input = "HC1:NCFC:MVIMAP2SQ20MU...";

DecodeResult result = chain.decode(input);
// Read metaInformation like expirationTime, issuedAt, issuer
VerificationResult verificationResult = result.getVerificationResult()
VerificationResult verificationResult = result.getVerificationResult();
boolean isValid = verificationResult.getError() == null;
// See list below for possible Errors, may be null
Error error = verificationResult.getError();
Expand Down Expand Up @@ -331,11 +331,11 @@ There is also an option to create (e.g. on a web service) a list of trusted cert
String privateKeyPem = "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADAN...";
String certificatePem = "-----BEGIN CERTIFICATE-----\nMIICsjCCAZq...";
CryptoService cryptoService = new FileBasedCryptoService(privateKeyPem, certificatePem);
Duration validity = Duration.hours(48);
TrustListV2EncodeService trustListService = new TrustListV2EncodeService(cryptoService, validity);
int validityHours = 48;
TrustListV2EncodeService trustListService = new TrustListV2EncodeService(cryptoService, validityHours);

// Load the list of trusted certificates from somewhere ...
Set<X509Certificate> trustedCerts = new HashSet<>(cert1, cert2, ...);
Set<CertificateAdapter> trustedCerts = new HashSet<>(cert1, cert2, ...);
SignedData trustList = trustListService.encode(trustedCerts);
// Write content file
new FileOutputStream(new File("trustlist.bin")).write(trustList.getContent());
Expand All @@ -362,7 +362,7 @@ SignedDataParsed parsed = result.getFirst();
TrustListV2 trustListContainer = result.getSecond();
for (TrustedCertificateV2 cert : trustListContainer.getCertificates()) {
// Parse it into your own data class
System.out.println(cert.getCertificate().asBase64();
System.out.println(ExtensionsKt.asBase64(cert.getCertificate()));
}
```

Expand Down Expand Up @@ -392,12 +392,12 @@ There is also an option to create (e.g. on a web service) a list of business rul
String privateKeyPem = "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADAN...";
String certificatePem = "-----BEGIN CERTIFICATE-----\nMIICsjCCAZq...";
CryptoService cryptoService = new FileBasedCryptoService(privateKeyPem, certificatePem);
Duration validity = Duration.hours(48);
BusinessRulesV1EncodeService rulesService = new BusinessRulesV1EncodeService(cryptoService, validity);
int validityHours = 48;
BusinessRulesV1EncodeService rulesService = new BusinessRulesV1EncodeService(cryptoService, validityHours);

// Load the list business rules
List<String> inputStrings = new ArrayList<>();
List<BusinessRule> input = inputStrings.stream().map(it -> new BusinessRule(it)).collect(Collectors.toList());
List<BusinessRule> input = inputStrings.stream().map(it -> new BusinessRule("identifier", it)).collect(Collectors.toList());
SignedData rules = rulesService.encode(input);
// Write content file
new FileOutputStream(new File("rules.bin")).write(rules.getContent());
Expand All @@ -414,10 +414,10 @@ CertificateRepository trustAnchor = new PrefilledCertificateRepository("-----BEG
byte[] rulesContent = new byte[0];
// Download rules signature, binary, e.g. from https://dgc.a-sit.at/ehn/rules/v1/sig
byte[] rulesSignature = new byte[0];
SignedData rules = new SignedData(rulesContent, rulesSignature);
SignedData rulesSigned = new SignedData(rulesContent, rulesSignature);

BusinessRulesDecodeService service = new BusinessRuleBusinessRulesDecodeService(trustAnchor);
Pair<SignedDataParsed, BusinessRulesContainer> result = service.decode(rules);
BusinessRulesDecodeService service = new BusinessRulesDecodeService(trustAnchor);
Pair<SignedDataParsed, BusinessRulesContainer> result = service.decode(rulesSigned);
// Contains "validFrom", "validUntil"
SignedDataParsed parsed = result.getFirst();
// Contains a list of business rules as raw JSON
Expand Down Expand Up @@ -454,8 +454,8 @@ There is also an option to create (e.g. on a web service) a list of value sets,
String privateKeyPem = "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADAN...";
String certificatePem = "-----BEGIN CERTIFICATE-----\nMIICsjCCAZq...";
CryptoService cryptoService = new FileBasedCryptoService(privateKeyPem, certificatePem);
Duration validity = Duration.hours(48);
ValueSetV1EncodeService valueSetService = new ValueSetV1EncodeService(cryptoService, validity);
int validityHours = 48;
ValueSetV1EncodeService valueSetService = new ValueSetV1EncodeService(cryptoService, validityHours);

// Load the list value sets
List<String> inputStrings = new ArrayList<>();
Expand All @@ -476,15 +476,15 @@ CertificateRepository trustAnchor = new PrefilledCertificateRepository("-----BEG
byte[] valueSetContent = new byte[0];
// Download valueSet signature, binary, e.g. from https://dgc.a-sit.at/ehn/values/v1/sig
byte[] valueSetSignature = new byte[0];
SignedData valueSet = new SignedData(valueSetContent, valueSetSignature);
SignedData valueSetSigned = new SignedData(valueSetContent, valueSetSignature);

ValueSetDecodeService service = new ValueSetDecodeService(trustAnchor);
Pair<SignedDataParsed, ValueSetContainer> result = service.decode(valueSet);
Pair<SignedDataParsed, ValueSetContainer> result = service.decode(valueSetSigned);
// Contains "validFrom", "validUntil"
SignedDataParsed parsed = result.getFirst();
// Contains a list of value sets as raw JSON
ValueSetContainer valueSet = result.getSecond();
for (ValueSet vs : valueSet.getValueSet()) {
for (ValueSet vs : valueSet.getValueSets()) {
// Parse it into your own data class
System.out.println(vs.getValueSet());
}
Expand Down Expand Up @@ -527,10 +527,10 @@ One example: The validity for the TrustList, as well as the validity of the HCER

```Java
CryptoService cryptoService = new RandomEcKeyCryptoService(256); // or some fixed key crypto service
HigherOrderValidationService higherOrdeValidationService = new DefaultHigherOrderValidationService();
HigherOrderValidationService higherOrderValidationService = new DefaultHigherOrderValidationService();
SchemaValidationService schemaValidationService = new DefaultSchemaValidationService(); // pass "false" to disable fallback schema validation
CborService cborService = new DefaultCborService();
CwtService cwtService = new DefaultCwtService("AT", Duration.hours(24)); // validity for HCERT content
CwtService cwtService = new DefaultCwtService("AT", 24); // validity for HCERT content
CoseService coseService = new DefaultCoseService(cryptoService);
CompressorService compressorService = new DefaultCompressorService(9); // level of compression
Base45Service base45Service = new DefaultBase45Service();
Expand Down Expand Up @@ -613,7 +613,7 @@ See these links for details:
## Changelog

Version NEXT:
- tbd
- Fix constructors and overloads fro Java callers

Version 1.3.2:
- Export `SignedDataDownloader` to JS
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ package ehn.techiop.hcert.kotlin.chain
import ehn.techiop.hcert.kotlin.chain.impl.*
import kotlinx.datetime.Clock
import kotlin.js.JsName
import kotlin.jvm.JvmOverloads
import kotlin.jvm.JvmStatic


object DefaultChain {
@JvmStatic
@JsName("buildCreationChain")
fun buildCreationChain(cryptoService: CryptoService) = Chain(
DefaultHigherOrderValidationService(),
Expand All @@ -21,6 +24,8 @@ object DefaultChain {
/**
* Builds a "default" chain for verifying, i.e. one with the implementation according to spec.
*/
@JvmStatic
@JvmOverloads
@JsName("buildVerificationChain")
fun buildVerificationChain(repository: CertificateRepository, clock: Clock = Clock.System) = Chain(
DefaultHigherOrderValidationService(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import ehn.techiop.hcert.kotlin.chain.CompressorService
import ehn.techiop.hcert.kotlin.chain.Error
import ehn.techiop.hcert.kotlin.chain.VerificationException
import ehn.techiop.hcert.kotlin.chain.VerificationResult
import kotlin.jvm.JvmOverloads

/**
* Compresses/decompresses input with ZLIB, [level] specifies the compression level (0-9)
*/
open class DefaultCompressorService(private val level: Int = 9) : CompressorService {
open class DefaultCompressorService @JvmOverloads constructor(private val level: Int = 9) : CompressorService {

private val adapter = CompressorAdapter()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import ehn.techiop.hcert.kotlin.chain.ContextIdentifierService
import ehn.techiop.hcert.kotlin.chain.Error
import ehn.techiop.hcert.kotlin.chain.VerificationException
import ehn.techiop.hcert.kotlin.chain.VerificationResult
import kotlin.jvm.JvmOverloads

/**
* Appends/drops the Context identifier prefix from input, e.g. "HC1:"
*/
open class DefaultContextIdentifierService(private val prefix: String = "HC1:") : ContextIdentifierService {
open class DefaultContextIdentifierService @JvmOverloads constructor(private val prefix: String = "HC1:") :
ContextIdentifierService {

override fun encode(input: String): String {
return "$prefix$input"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ open class DefaultCwtService constructor(
private val clock: Clock = Clock.System,
) : CwtService {

constructor(countryCode: String, validityHours: Int)
: this(countryCode, Duration.hours(validityHours), Clock.System)

override fun encode(input: ByteArray): ByteArray {
val issueTime = clock.now()
val expirationTime = issueTime + validity
Expand All @@ -47,7 +50,10 @@ open class DefaultCwtService constructor(
val issuedAt = Instant.fromEpochSeconds(issuedAtSeconds.toLong())
verificationResult.issuedAt = issuedAt
val certValidFrom = verificationResult.certificateValidFrom
?: throw VerificationException(Error.PUBLIC_KEY_NOT_YET_VALID, details = mapOf("certValidFrom" to "null"))
?: throw VerificationException(
Error.PUBLIC_KEY_NOT_YET_VALID,
details = mapOf("certValidFrom" to "null")
)

if (issuedAt > now)
throw VerificationException(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ import ehn.techiop.hcert.kotlin.chain.VerificationException
import ehn.techiop.hcert.kotlin.chain.VerificationResult
import ehn.techiop.hcert.kotlin.data.CborObject
import ehn.techiop.hcert.kotlin.data.GreenCertificate
import kotlin.jvm.JvmOverloads

/**
* Validates the HCERT data against the JSON schema.
* Beware: By default [useFallback] is true, so we are trying to verify
* the data against a very relaxed schema.
*/
class DefaultSchemaValidationService(private val useFallback: Boolean = true) : SchemaValidationService {
class DefaultSchemaValidationService @JvmOverloads constructor(private val useFallback: Boolean = true) :
SchemaValidationService {

override fun validate(cbor: CborObject, verificationResult: VerificationResult): GreenCertificate {
val adapter = SchemaValidationAdapter(cbor)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import ehn.techiop.hcert.kotlin.crypto.CryptoAdapter
import ehn.techiop.hcert.kotlin.crypto.KeyType
import ehn.techiop.hcert.kotlin.trust.ContentType
import kotlinx.datetime.Clock
import kotlin.jvm.JvmOverloads

class RandomEcKeyCryptoService(
class RandomEcKeyCryptoService @JvmOverloads constructor(
keySize: Int = 256,
contentType: List<ContentType> = ContentType.values().toList(),
clock: Clock = Clock.System
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import ehn.techiop.hcert.kotlin.crypto.CryptoAdapter
import ehn.techiop.hcert.kotlin.crypto.KeyType
import ehn.techiop.hcert.kotlin.trust.ContentType
import kotlinx.datetime.Clock
import kotlin.jvm.JvmOverloads

class RandomRsaKeyCryptoService(
class RandomRsaKeyCryptoService @JvmOverloads constructor(
keySize: Int = 2048,
contentType: List<ContentType> = ContentType.values().toList(),
clock: Clock = Clock.System
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@ package ehn.techiop.hcert.kotlin.chain.impl

import ehn.techiop.hcert.kotlin.chain.CertificateRepository
import ehn.techiop.hcert.kotlin.chain.Error
import ehn.techiop.hcert.kotlin.chain.toHexString
import ehn.techiop.hcert.kotlin.chain.VerificationException
import ehn.techiop.hcert.kotlin.chain.VerificationResult
import ehn.techiop.hcert.kotlin.chain.toHexString
import ehn.techiop.hcert.kotlin.crypto.CertificateAdapter
import ehn.techiop.hcert.kotlin.trust.SignedData
import ehn.techiop.hcert.kotlin.trust.TrustListDecodeService
import kotlinx.datetime.Clock
import kotlin.jvm.JvmOverloads


class TrustListCertificateRepository(
class TrustListCertificateRepository @JvmOverloads constructor(
trustList: SignedData,
certificateRepository: CertificateRepository,
clock: Clock = Clock.System,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,23 @@ import ehn.techiop.hcert.kotlin.trust.SignedDataParsed
import kotlinx.datetime.Clock
import kotlinx.serialization.cbor.Cbor
import kotlinx.serialization.decodeFromByteArray
import kotlin.jvm.JvmOverloads
import kotlin.time.Duration

/**
* Decodes a [SignedData] blob, expected to contain the content and signature of a [BusinessRulesContainer]
*
* - [repository] contains the trust anchor for the parsed file
* - [clock] defines the current time to use for validity checks
* - [clockSkew] defines the error margin when comparing time validity of the parsed file
* - [clockSkewSeconds] defines the error margin when comparing time validity of the parsed file in seconds
*/
class BusinessRulesDecodeService(
class BusinessRulesDecodeService @JvmOverloads constructor(
repository: CertificateRepository,
clock: Clock = Clock.System,
clockSkew: Duration = Duration.seconds(300)
clockSkewSeconds: Int = 300
) {

private val decodeService = SignedDataDecodeService(repository, clock, clockSkew)
private val decodeService = SignedDataDecodeService(repository, clock, clockSkewSeconds)

/**
* See [SignedData] for details about the content
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import ehn.techiop.hcert.kotlin.trust.SignedDataEncodeService
import kotlinx.datetime.Clock
import kotlinx.serialization.cbor.Cbor
import kotlinx.serialization.encodeToByteArray
import kotlin.jvm.JvmOverloads
import kotlin.time.Duration


Expand All @@ -19,6 +20,10 @@ class BusinessRulesV1EncodeService constructor(
clock: Clock = Clock.System,
) {

@JvmOverloads
constructor(signingService: CryptoService, validityHours: Int = 48)
: this(signingService, Duration.hours(validityHours), Clock.System)

private val signedDataService = SignedDataEncodeService(signingService, validity, clock)

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,17 @@ import ehn.techiop.hcert.kotlin.crypto.CoseHeaderKeys
import ehn.techiop.hcert.kotlin.crypto.CwtHeaderKeys
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import kotlin.jvm.JvmOverloads
import kotlin.time.Duration


/**
* Decodes a [SignedData] structure, and verify the validity of it
*/
class SignedDataDecodeService constructor(
class SignedDataDecodeService @JvmOverloads constructor(
private val repository: CertificateRepository,
private val clock: Clock = Clock.System,
private val clockSkew: Duration = Duration.seconds(300)
private val clockSkewSeconds: Int = 300
) {

@Throws(VerificationException::class)
Expand Down Expand Up @@ -49,7 +50,7 @@ class SignedDataDecodeService constructor(
)

val validFrom = Instant.fromEpochSeconds(notBefore.toLong())
if (validFrom > clock.now().plus(clockSkew))
if (validFrom > clock.now().plus(Duration.seconds(clockSkewSeconds)))
throw VerificationException(
Error.TRUST_LIST_NOT_YET_VALID,
"NotBefore>clock.now()",
Expand All @@ -63,7 +64,7 @@ class SignedDataDecodeService constructor(
)

val validUntil = Instant.fromEpochSeconds(expiration.toLong())
if (validUntil < clock.now().minus(clockSkew))
if (validUntil < clock.now().minus(Duration.seconds(clockSkewSeconds)))
throw VerificationException(
Error.TRUST_LIST_EXPIRED, "Expiration<clock.now()",
details = mapOf("expirationTime" to validUntil.toString(), "currentTime" to clock.now().toString())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,23 @@ import ehn.techiop.hcert.kotlin.crypto.CoseHeaderKeys
import kotlinx.datetime.Clock
import kotlinx.serialization.cbor.Cbor
import kotlinx.serialization.decodeFromByteArray
import kotlin.jvm.JvmOverloads
import kotlin.time.Duration

/**
* Decodes a [SignedData] blob, expected to contain the content and signature of a [TrustListV2]
*
* - [repository] contains the trust anchor for the parsed file
* - [clock] defines the current time to use for validity checks
* - [clockSkew] defines the error margin when comparing time validity of the parsed file
* - [clockSkewSeconds] defines the error margin when comparing time validity of the parsed file in seconds
*/
class TrustListDecodeService(
class TrustListDecodeService @JvmOverloads constructor(
repository: CertificateRepository,
clock: Clock = Clock.System,
clockSkew: Duration = Duration.seconds(300)
clockSkewSeconds: Int = 300
) {

private val decodeService = SignedDataDecodeService(repository, clock, clockSkew)
private val decodeService = SignedDataDecodeService(repository, clock, clockSkewSeconds)

/**
* See [SignedData] for details about the content
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import ehn.techiop.hcert.kotlin.crypto.CoseHeaderKeys
import kotlinx.datetime.Clock
import kotlinx.serialization.cbor.Cbor
import kotlinx.serialization.encodeToByteArray
import kotlin.jvm.JvmOverloads
import kotlin.time.Duration


Expand All @@ -18,6 +19,10 @@ class TrustListV2EncodeService constructor(
clock: Clock = Clock.System,
) {

@JvmOverloads
constructor(signingService: CryptoService, validityHours: Int = 48)
: this(signingService, Duration.hours(validityHours), Clock.System)

private val signedDataService = SignedDataEncodeService(signingService, validity, clock)

/**
Expand Down
Loading

0 comments on commit 4d9062b

Please sign in to comment.