eu-digital-green-certificates / dgc-participating-countries

All contents and information for onboarding representatives of participating countries.
Apache License 2.0
25 stars 16 forks source link

Make approved trust list publicly accessible #10

Closed mauimauer closed 3 years ago

mauimauer commented 3 years ago

Why would you put the master trust list behind mTLS auth? Priority should be assigned to distributing a verifiable trustlist to everyone who wishes to validate the contents of DGCs. The certificates/pkeys are not sensitive and should be made accessible to everyone.

If you don't want to serve this via an API at least consider hosting a static daily dump (+ signature) of the trustlist in a publicly accessible location.

jnischler commented 3 years ago

+1 on this. We are also searching for a public trust list.

We found a list hosted by the TU-Graz (AT) https://dgc.a-sit.at/ehn/cert/listv2 https://dgc.a-sit.at/ehn/cert/sigv2 https://dgc.a-sit.at/ehn/

regards Julian

jnischler commented 3 years ago

using this list we are able to validate barcodes issued in Austria the list also contains several other countries

daniel-eder commented 3 years ago

Edit: The EU currently does not plan on hosting such a list. Please contact the member state you are operating from or in contract with, they are responsible for distributing the trust list. For non-EU countries (or institutions operating in such countries) I do not yet have an update, but as soon as a decision is made on that front we'll update the issue.

dirkx commented 3 years ago

Most countries publish their public certificates - as they are public.

However the company & their expert suppliers that operates the joint gateway are concerned of all sort of 'attacks' - and for this reason insists on putting the joint list of public keys behind TLS-client auth.

The reasoning behind this is not entirely clear to most of us.

The Netherlands publishes its public keys at https://www.npkd.nl/csca-health.html - publicly.

jnischler commented 3 years ago

Thanks, ok I get the point, and its also not entirely clear for us ;)

Thanks for the list of NL keys. Do the Netherlands also publish the full joint list?

Austria is currently only publishing a test list here: https://dgc.a-sit.at/ehn/ But is just a test.

Will try to get into contact with officials and keep you updated.

thanks

vaizki commented 3 years ago

There seems to be a multitude of open issues around github regarding the access to a global trustlist and they all point here, let me share my experience and thoughts.

I was able to get access to the list via our (Finland) national provider in half a day (1 email & 1 webform today) and I've implemented a certificate database which downloads new certs and the list of valid key ids (for pruning old ones) regularly. I can validate the COSE of my own vaccination EHC successfully with the FI-issued public cert.

My database currently has 133 active certs from AT BE BG CY CZ DE DK EE ES FI FR GR HR HU IE IS IT LI LT LU LV MT NL NO PL PT RO SE SI & SK. Of course I cannot share the database or open a JWKS type service because that would put me in a man-in-the-middle position (no amount of disclaimers would stop people from trusting the informal list of keys).

I fully understand that the EU wishes to allow member states to implement their own backends for this and also filter certs or add national variants etc.. but still this seems overly complicated that not even the list of valid cert fingerprints (from which the first 8 bytes are used as key ids) is not published as a static & signed list anywhere. I strongly feel that some sort of discovery service run by the EU would be a bare minimum (returns a JSON object of country codes mapped to metadata including API URLs) and that member states should run a JWKS-style public service for keys.

I have no idea how well prepared other countries are to support developers and I applaud our officials for the straightforward process and quick turnaround (they are also updating the public websites based on my feedback).

Maybe this github repository could include links to every national provider of the trust store so developers know who to contact?

mauimauer commented 3 years ago

I totally agree. JWKS style public keys is what the SMART Health standard used by some canadian provinces and California is using for their vaccination QR codes: https://github.com/smart-on-fhir/health-cards/blob/main/docs/index.md

lovasoa commented 3 years ago

Here is my contribution: https://github.com/lovasoa/sanipasse/blob/master/src/assets/Digital_Green_Certificate_Signing_Keys.json

This is a versioned and automatically updated list of signing keys from all member states, extracted automatically from the french backend, and updated twice per day.

jbx1 commented 3 years ago

I agree with the discussion here. If the list is not publicly available, at least there should be a simple verification process to get an API key to get access to the list, with all the protection/throttling/terms and conditions associated with it. This is unnecessarily imposing restrictions for building on this further, especially for services across-state.

piotrblasiak commented 3 years ago

Here is my contribution: https://github.com/lovasoa/sanipasse/blob/master/src/assets/Digital_Green_Certificate_Signing_Keys.json

This is a versioned and automatically updated list of signing keys from all member states, extracted automatically from the french backend, and updated twice per day.

This is great - I am going to get in touch with the Swedish counterpart and see if I can get a list. If more people could do the same, one could simply download all of them to verify and not have to rely on just one.

daniel-eder commented 3 years ago

I agree with the discussion here. If the list is not publicly available, at least there should be a simple verification process to get an API key to get access to the list, with all the protection/throttling/terms and conditions associated with it. This is unnecessarily imposing restrictions for building on this further, especially for services across-state.

@jbx1 As it is, this is not a technical but a political issue. Member states have sovereignty over the trust list, not the European Commission, that is why there is no central list, nor a central place to request access. However member states are free to provide easy access to the list if they so choose.

dirkx commented 3 years ago

Actually - I’ve not heard that argument made. If that is indeed the reason - fair enough. But sofar - the arguments made by the operators is that they worry (rightly/fairly) about a deluge of trust-list fetches from verifiers in the field - with whom they have no relation / no desire to serve.

jbx1 commented 3 years ago

@daniel-eder Yes I understand the reasoning might have been political, but it makes no sense at all. How state A has sovereignty to share the public key of an unrelated state B within the territory of state A is beyond me. When state B is sharing its public key (which by its very nature is public, so safe to share) with all other member states, it is doing effectively the same thing. There could still be verifications that who is requesting them is an EU company/resident, just like is done in .eu domains, and make it available through an API key, to at least even protect against DDOS attacks etc.

panzi commented 3 years ago

I just found the trust list provided by Germany: https://de.dscg.ubirch.com/trustList/DSC/

Its in yet another format. Documentation: https://github.com/Digitaler-Impfnachweis/certification-apis/tree/master/dsc-update They say it's rate limited, but not how exactly.

I didn't manage to verify the signature of the trust list itself, though. See my question here: https://github.com/Digitaler-Impfnachweis/certification-apis/issues/157

jnischler commented 3 years ago

Here is my contribution: https://github.com/lovasoa/sanipasse/blob/master/src/assets/Digital_Green_Certificate_Signing_Keys.json

This is a versioned and automatically updated list of signing keys from all member states, extracted automatically from the french backend, and updated twice per day.

Nice & Thanks!

We have created a mirror for public known trustlists.

We have access to the German / Austrian and thanks to this post to the French trust list.

We well add the Austrian Trustlist in the next days.

https://github.com/section42/hcert-trustlist-mirror/settings

Maybe this will help someone, feel free to use. Updates automatically using GitHub actions

regards

panzi commented 3 years ago

Where is the official Austrian trust list? I'm from Austria and would need that one but can't find it, only the test list mentioned above.

jnischler commented 3 years ago

We found the trustlists by looking at the check apps provided by the different countries. Austria is for example using this web page: https://greencheck.gv.at/ And the app is making a request to this endpoint: https://greencheck.gv.at/api/masterdata were you can find the trustlist.

panzi commented 3 years ago

Thank you so much! So it's not an official endpoint, but better than nothing! Have to find out how it's encrypted tomorrow.

jnischler commented 3 years ago

i think a *.gv.at domain is official enough ;) but you are right the all of the lists we found so far, were discovered by reverse engineering

i think its base64 and cbor, but can tell you more tomorrow

mauimauer commented 3 years ago

Sweden: https://dgcg.covidbevis.se/tp/trust-list

panzi commented 3 years ago

CBOR and Base64, got it! Already implemented it. See: https://github.com/panzi/verify-ehc :D

jnischler commented 3 years ago

Sweden: https://dgcg.covidbevis.se/tp/trust-list

thanks, looks like some sort of base64 and jose ? will have a look at it 👍

CBOR and Base64, got it! Already implemented it. See: https://github.com/panzi/verify-ehc :D

cool, i have also pushed an GitHub action to fetch and decode: https://github.com/section42/hcert-trustlist-mirror feel free to use

alexandermainz commented 3 years ago

I just found the trust list provided by Germany: https://de.dscg.ubirch.com/trustList/DSC/

There's also a test/dev endpoint at: https://de.test.dscg.ubirch.com/trustList/DSC/

You should use this for development purposes to avoid throttling at the production endpoint.

The first line of the answer is a signature added by the national trust list issuer. This is to verify that the trust list has been issued by the German authority. The trust list in the format specified by EU-DGC begins at line 2. For details see https://github.com/Digitaler-Impfnachweis/certification-apis/blob/master/dsc-update/dgc-update-api.yaml and https://eu-digital-green-certificates.github.io/dgc-gateway/#/Trust%20Lists/downloadTrustList

lovasoa commented 3 years ago

We have a full implementation of DGC, including signature verification, implemented in typescript in sanipasse : https://github.com/lovasoa/sanipasse/blob/master/src/lib/digital_green_certificate.ts

Would you be interested in extracting that to a library and publishing it to npm ?

asitplus-pteufl commented 3 years ago

Austrian list implementation and verification in these cores: IOS: https://github.com/ehn-dcc-development/ValidationCore (verification only) Kotlin/Java/JS: https://github.com/ehn-dcc-development/hcert-kotlin (creation code and verification)

jbx1 commented 3 years ago

This is all very great guys, thanks to everyone posting his information.

Does any one know if the UK certificate or public keys were integrated/are going to be integrated any time soon? I know there were some discussions between the EU and the UK to make the NHS one interoperable with the DGC.

mauimauer commented 3 years ago

This seems to contain the UK public keys (no certs, just the keys) https://github.com/andypandy47/Answer-Digital-Verifier-App/blob/4d79843334c556729ab1c611d02c6244ba8d6f37/VerifierApp.Electron/src/assets/publicKeys.json

panzi commented 3 years ago

We have a full implementation of DGC, including signature verification, implemented in typescript in sanipasse : https://github.com/lovasoa/sanipasse/blob/master/src/lib/digital_green_certificate.ts

Would you be interested in extracting that to a library and publishing it to npm ?

What license would that have and would it work in the browser or only nodejs?

daviddem1971 commented 3 years ago

As a member of the public and EU vaccine certificate holder, I am here to say that it is absolutely crazy that we hold vaccine certificates that cannot be easily authenticated by anyone who asks worldwide. I appreciate that Luxembourg has issued a publicly accessible verifier app (covidcheck.lu), but how is your average immigration official in a developing country supposed to know that he has to use the Luxembourg app to authenticate EU certificates shown to him?

Our certifcates are basically useless outside the EU if nobody can easily verify their authenticity, either via a widely advertised EU app and/or via a link to a website that confirms the validity of the certificate, as many other countries do.

Even within the EU, how are event organisers, restaurants etc supposed to verify the vaccination status of their customers, should they wish to do so?

nicktencate commented 3 years ago

In the Netherlands we have two scanners. One for domestic use and one for border use.

The domestic scanner accepts all domestic QR codes and all EU (but non-NL) DCC QR codes. The border scanner accepts all EU DCC QR codes.

Both scanners are available in the Apple and Google stores.

The trust list of all EU certificates is provided via an API which is loaded by both scanner apps. It is publically available.

If you wish to take a look under the hood we publish all of our code on github: https://github.com/minvws/nl-covid19-coronacheck-app-coordination/

panzi commented 3 years ago

Anyone know how to verify the signature of the Austrian trust list? I've found a few pem certificates in their obfuscated JavaScript, but couldn't verify the trust list signature with any of them. Not like this:

pubkey.verify(sign, trust_list, ECDSA(hashes.SHA256()))

And not like this (this is what works for the German trust list):

r = int.from_bytes(sign[:len(sign)//2], byteorder="big", signed=False)
s = int.from_bytes(sign[len(sign)//2:], byteorder="big", signed=False)
sign_dds = encode_dss_signature(r, s)
pubkey.verify(sign_dds, trust_list, ECDSA(hashes.SHA256()))
lovasoa commented 3 years ago

What license would that have

Sanipasse is under the GPLv3 : https://github.com/lovasoa/sanipasse/blob/master/LICENSE

and would it work in the browser or only nodejs?

It works in node and the browser, and is in production as an isomorphic web app. The same certificate checking code runs on the frontend and the backend.

panzi commented 3 years ago

Yeah, GPL doesn't work for my use case, so I wrote something myself. Need to ask my boss if I can publish that under the MIT license.

lovasoa commented 3 years ago

I wouldn't recommend to anyone putting their private health data on a closed source app by a private company hosted on firebase !

You can already use https://sanipasse.fr , which preserves privacy, is open source, and hosted in Germany.

dirkx commented 3 years ago

It may be safer in this international context to use something like the Apache 2 license as it has the required contributor and patent clauses too.

mauimauer commented 3 years ago

I wouldn't recommend to anyone putting their private health data on a closed source app by a private company hosted on firebase !

You can already use https://sanipasse.fr , which preserves privacy, is open source, and hosted in Germany.

No worries at all, removed the link. However it would still be nice if ultimately a solution running entirely within the browser could be found.

lovasoa commented 3 years ago

I'm not sure what you mean. sanipasse does work entirely in the browser.

mauimauer commented 3 years ago

--nvm, I misread your comment. In that case. Splending a self contained browser solution is awesome.

jbx1 commented 3 years ago

@lovasoa any idea why the keys in the French list has some of the keys in a different format? Some of them start with "MFkwEwY" while a few start with "MII" (which seems to be the common format used also in the ACC environment).

When one checks the list of the German list, all of them start with "MII" too.

Checking individual KIDs, the KIDs match (I only checked a few like the German and Norway ones) but the public key is different. For example KID 0L7AaIwu+EY= starts with MIIHUTCC in the German list, but in the French list it starts with MFkwEwYHK.

lovasoa commented 3 years ago

Interesting finding. Did you investigate whether it was the same public keys (same prime product) encoded differently, or entirely different public keys ? The json list in sanipasse is exported by decoding the x509 certificates, extracting the public key, and reencoding it with webcrypto.

jbx1 commented 3 years ago

That's what I thought at first, but then some of them seem to be in the same format, starting with MII. I didn't investigate deeply to see if it was actually the same public key somehow encoded differently.

dirkx commented 3 years ago

Just op the base64 string in here. MII is indeed the ASN1 encoding of the heading of most X509's - and quite a few chuncks are identical.

If you want to read them - do

base64 -D | openssl x509 -text -noout -inform DER

to see what is in them. Or

base64 -D | openssl asn1parse -inform DER

if it is obscure. The 'base64 -D' is purely to go from Base64 to pure binary.

stapelberg commented 3 years ago

Specific example. The AT key in the DE trustlist:

    {
      "certificateType": "DSC",
      "country": "AT",
      "kid": "Is2JtrOJhik=",
      "rawData": "MIIB7zCCAZagAwIBAgIKAXnM+L47fmBcezAKBggqhkjOPQQDAjBEMQswCQYDVQQGEwJBVDEPMA0GA1UECgwGQk1TR1BLMQwwCgYDVQQFEwMwMDExFjAUBgNVBAMMDUFUIERHQyBDU0NBIDEwHhcNMjEwNjAyMTM0NTI0WhcNMjMwNjAyMTM0NTI0WjBGMQswCQYDVQQGEwJBVDEPMA0GA1UECgwGQk1TR1BLMQ8wDQYDVQQFEwYwMDEwMDExFTATBgNVBAMMDEFUIERHQyBEU0MgMTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABGBNuKiCpnXH0VlIdk6pJZH2ep8jQaV+FR3izMXxZfK5EPGZLtG3Jx+TmV3JJErfrSrPhRmfbSidVbTQ5nnZS+ujbjBsMA4GA1UdDwEB/wQEAwIHgDAdBgNVHQ4EFgQUNs2smrjBhuR5Bqxl6teE1x1o2ycwHwYDVR0jBBgwFoAUHyKsHGUWKbTBmLNjb7/dCZ27e3swGgYDVR0QBBMwEYEPMjAyMTEyMTYxNDQ1MjRaMAoGCCqGSM49BAMCA0cAMEQCIDjXHnyzq3sTisMX1uY8xQ2ZqCRL2xmxtYOPhSZ9ZacYAiAqHUMOC7WNgq4h28n31WLc1mMPAYauWslSEwnXC79AGw==",
      "signature": "MIAGCSqGSIb3DQEHAqCAMIACAQExDzANBglghkgBZQMEAgEFADCABgkqhkiG9w0BBwEAAKCAMIIB0zCCAXmgAwIBAgIKAXnM+Z2p0IjczzAKBggqhkjOPQQDAjBEMQswCQYDVQQGEwJBVDEPMA0GA1UECgwGQk1TR1BLMQwwCgYDVQQFEwMwMDExFjAUBgNVBAMMDUFUIERHQyBDU0NBIDEwHhcNMjEwNjAyMTM0NjIxWhcNMjIwNzAyMTM0NjIxWjBFMQswCQYDVQQGEwJBVDEPMA0GA1UECgwGQk1TR1BLMQ8wDQYDVQQFEwYwMDEwMDExFDASBgNVBAMMC0FUIERHQyBVUCAxMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAElD1yV24BEVOMfPyu8zrPmDSUPvfinTSdos8PqTQWIbiJlKsULudv/tPt5iv+Op+04GHORjhVTNQ6OEk0iF8B96NSMFAwDgYDVR0PAQH/BAQDAgeAMB0GA1UdDgQWBBQfnVl93OqxshorGeh28AEVN0BD2jAfBgNVHSMEGDAWgBQfIqwcZRYptMGYs2Nvv90Jnbt7ezAKBggqhkjOPQQDAgNIADBFAiAlsEe7pgYuY9VXjiHpziFQJoaEvEkUjoquEHWQ8jXpfAIhAM4ygjPC+FijbF+gtDzV0MtxdyiFmvvVaicALHXbUdaoAAAxggFWMIIBUgIBATBSMEQxCzAJBgNVBAYTAkFUMQ8wDQYDVQQKDAZCTVNHUEsxDDAKBgNVBAUTAzAwMTEWMBQGA1UEAwwNQVQgREdDIENTQ0EgMQIKAXnM+Z2p0IjczzANBglghkgBZQMEAgEFAKCBlTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0yMTA3MDIwOTMyMzVaMCoGCSqGSIb3DQEJNDEdMBswDQYJYIZIAWUDBAIBBQChCgYIKoZIzj0EAwIwLwYJKoZIhvcNAQkEMSIEICLNibaziYYp6Z0LGju9wly98mRMdY28rQJ+eTyediX1MAoGCCqGSM49BAMCBEYwRAIgc/M10UbXsN5jBeBLYNaGYt4JpjcJYqGRB7Hg/73K2J0CIAIGylViXcevYgbRZepG1Z1mb5KWFDEG/HkukVOXhjstAAAAAAAA",
      "thumbprint": "22cd89b6b3898629e99d0b1a3bbdc25cbdf2644c758dbcad027e793c9e7625f5",
      "timestamp": "2021-07-02T11:32:35+02:00"
    },

I can parse the rawData like so:

% echo -n 'MIIB7zCCAZagAwIBAgIKAXnM+L47fmBcezAKBggqhkjOPQQDAjBEMQswCQYDVQQGEwJBVDEPMA0GA1UECgwGQk1TR1BLMQwwCgYDVQQFEwMwMDExFjAUBgNVBAMMDUFUIERHQyBDU0NBIDEwHhcNMjEwNjAyMTM0NTI0WhcNMjMwNjAyMTM0NTI0WjBGMQswCQYDVQQGEwJBVDEPMA0GA1UECgwGQk1TR1BLMQ8wDQYDVQQFEwYwMDEwMDExFTATBgNVBAMMDEFUIERHQyBEU0MgMTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABGBNuKiCpnXH0VlIdk6pJZH2ep8jQaV+FR3izMXxZfK5EPGZLtG3Jx+TmV3JJErfrSrPhRmfbSidVbTQ5nnZS+ujbjBsMA4GA1UdDwEB/wQEAwIHgDAdBgNVHQ4EFgQUNs2smrjBhuR5Bqxl6teE1x1o2ycwHwYDVR0jBBgwFoAUHyKsHGUWKbTBmLNjb7/dCZ27e3swGgYDVR0QBBMwEYEPMjAyMTEyMTYxNDQ1MjRaMAoGCCqGSM49BAMCA0cAMEQCIDjXHnyzq3sTisMX1uY8xQ2ZqCRL2xmxtYOPhSZ9ZacYAiAqHUMOC7WNgq4h28n31WLc1mMPAYauWslSEwnXC79AGw==' | base64 -d | openssl x509 -text -inform der
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            01:79:cc:f8:be:3b:7e:60:5c:7b
        Signature Algorithm: ecdsa-with-SHA256
        Issuer: C = AT, O = BMSGPK, serialNumber = 001, CN = AT DGC CSCA 1
        Validity
            Not Before: Jun  2 13:45:24 2021 GMT
            Not After : Jun  2 13:45:24 2023 GMT
        Subject: C = AT, O = BMSGPK, serialNumber = 001001, CN = AT DGC DSC 1
[…]

The same AT key in the FR trustlist:

  "Is2JtrOJhik=": {
    "serialNumber": "0179ccf8be3b7e605c7b",
    "subject": "C=AT, O=BMSGPK, 2.5.4.5=001001, CN=AT DGC DSC 1",
    "issuer": "C=AT, O=BMSGPK, 2.5.4.5=001, CN=AT DGC CSCA 1",
    "notBefore": "2021-06-02T13:45:24.000Z",
    "notAfter": "2023-06-02T13:45:24.000Z",
    "signatureAlgorithm": "ECDSA",
    "fingerprint": "54e0dde0904f118b15b54d7a3da75049e29d3b63",
    "publicKeyAlgorithm": {
      "hash": {
        "name": "SHA-256"
      },
      "name": "ECDSA",
      "namedCurve": "P-256"
    },
    "publicKeyPem": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEYE24qIKmdcfRWUh2TqklkfZ6nyNBpX4VHeLMxfFl8rkQ8Zku0bcnH5OZXckkSt+tKs+FGZ9tKJ1VtNDmedlL6w=="
  },

I’m not succeeding at decoding publicKeyPem with openssl:

% echo -n 'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEYE24qIKmdcfRWUh2TqklkfZ6nyNBpX4VHeLMxfFl8rkQ8Zku0bcnH5OZXckkSt+tKs+FGZ9tKJ1VtNDmedlL6w==' | \
base64 -d | openssl x509 -text        
unable to load certificate
140430897976704:error:0909006C:PEM routines:get_name:no start line:crypto/pem/pem_lib.c:745:Expecting: TRUSTED CERTIFICATE

% echo -n 'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEYE24qIKmdcfRWUh2TqklkfZ6nyNBpX4VHeLMxfFl8rkQ8Zku0bcnH5OZXckkSt+tKs+FGZ9tKJ1VtNDmedlL6w==' | \
base64 -d | openssl x509 -text -inform der       
unable to load certificate
139890016028032:error:0D0680A8:asn1 encoding routines:asn1_check_tlen:wrong tag:crypto/asn1/tasn_dec.c:1149:
139890016028032:error:0D06C03A:asn1 encoding routines:asn1_d2i_ex_primitive:nested asn1 error:crypto/asn1/tasn_dec.c:713:
139890016028032:error:0D08303A:asn1 encoding routines:asn1_template_noexp_d2i:nested asn1 error:crypto/asn1/tasn_dec.c:646:Field=serialNumber, Type=X509_CINF
139890016028032:error:0D08303A:asn1 encoding routines:asn1_template_noexp_d2i:nested asn1 error:crypto/asn1/tasn_dec.c:646:Field=cert_info, Type=X509

% echo -n 'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEYE24qIKmdcfRWUh2TqklkfZ6nyNBpX4VHeLMxfFl8rkQ8Zku0bcnH5OZXckkSt+tKs+FGZ9tKJ1VtNDmedlL6w==' | \
base64 -d | openssl asn1parse -inform der
    0:d=0  hl=2 l=  89 cons: SEQUENCE          
    2:d=1  hl=2 l=  19 cons: SEQUENCE          
    4:d=2  hl=2 l=   7 prim: OBJECT            :id-ecPublicKey
   13:d=2  hl=2 l=   8 prim: OBJECT            :prime256v1
   23:d=1  hl=2 l=  66 prim: BIT STRING

Any tips?

stapelberg commented 3 years ago

In Go, it looks like https://golang.org/pkg/crypto/x509/#ParsePKIXPublicKey is the function to use to parse publicKeyPem after base64-decoding. Still not sure how to get the openssl(1) tool to decode them, though :)

dirkx commented 3 years ago

% echo -n 'MIIB7zCCAZagAwIBAgIKAXnM+L47fmBcezAKBggqhkjOPQQDAjBEMQswCQYDVQQGEwJBVDEPMA0GA1UECgwGQk1TR1BLMQwwCgYDVQQFEwMwMDExFjAUBgNVBAMMDUFUIERHQyBDU0NBIDEwHhcNMjEwNjAyMTM0NTI0WhcNMjMwNjAyMTM0NTI0WjBGMQswCQYDVQQGEwJBVDEPMA0GA1UECgwGQk1TR1BLMQ8wDQYDVQQFEwYwMDEwMDExFTATBgNVBAMMDEFUIERHQyBEU0MgMTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABGBNuKiCpnXH0VlIdk6pJZH2ep8jQaV+FR3izMXxZfK5EPGZLtG3Jx+TmV3JJErfrSrPhRmfbSidVbTQ5nnZS+ujbjBsMA4GA1UdDwEB/wQEAwIHgDAdBgNVHQ4EFgQUNs2smrjBhuR5Bqxl6teE1x1o2ycwHwYDVR0jBBgwFoAUHyKsHGUWKbTBmLNjb7/dCZ27e3swGgYDVR0QBBMwEYEPMjAyMTEyMTYxNDQ1MjRaMAoGCCqGSM49BAMCA0cAMEQCIDjXHnyzq3sTisMX1uY8xQ2ZqCRL2xmxtYOPhSZ9ZacYAiAqHUMOC7WNgq4h28n31WLc1mMPAYauWslSEwnXC79AGw==' | base64 -d | openssl x509 -text -inform der Certificate: Data: Version: 3 (0x2) Serial Number: 01:79:cc:f8:be:3b:7e:60:5c:7b Signature Algorithm: ecdsa-with-SHA256 Issuer: C = AT, O = BMSGPK, serialNumber = 001, CN = AT DGC CSCA 1

Good. The same AT key in the FR trustlist:

"Is2JtrOJhik=" : {

"serialNumber": "0179ccf8be3b7e605c7b" ,

.... "publicKeyPem": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEYE24qIKmdcfRWUh2TqklkfZ6nyNBpX4VHeLMxfFl8rkQ8Zku0bcnH5OZXckkSt+tKs+FGZ9tKJ1VtNDmedlL6w=="

},

I’m not succeeding at decoding publicKeyPem with openssl:

Correct - as this is a KEY - not an X509 certificate. Your next attempt is exactly right: % echo -n 'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEYE24qIKmdcfRWUh2TqklkfZ6nyNBpX4VHeLMxfFl8rkQ8Zku0bcnH5OZXckkSt+tKs+FGZ9tKJ1VtNDmedlL6w==' | \ base64 -d | openssl asn1parse -inform der 0:d=0 hl=2 l= 89 cons: SEQUENCE
2:d=1 hl=2 l= 19 cons: SEQUENCE
4:d=2 hl=2 l= 7 prim: OBJECT :id-ecPublicKey 13:d=2 hl=2 l= 8 prim: OBJECT :prime256v1 23:d=1 hl=2 l= 66 prim: BIT STRING

As here you are extracting exactly the public key.

To extract that from your above whole X509 do:

echo -n 'MIIB7zCCAZagAwIBAgIKAXnM+L47fmBcezAKBggqhkjOPQQDAjBEMQswCQYDVQQGEwJBVDEPMA0GA1UECgwGQk1TR1BLMQwwCgYDVQQFEwMwMDExFjAUBgNVBAMMDUFUIERHQyBDU0NBIDEwHhcNMjEwNjAyMTM0NTI0WhcNMjMwNjAyMTM0NTI0WjBGMQswCQYDVQQGEwJBVDEPMA0GA1UECgwGQk1TR1BLMQ8wDQYDVQQFEwYwMDEwMDExFTATBgNVBAMMDEFUIERHQyBEU0MgMTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABGBNuKiCpnXH0VlIdk6pJZH2ep8jQaV+FR3izMXxZfK5EPGZLtG3Jx+TmV3JJErfrSrPhRmfbSidVbTQ5nnZS+ujbjBsMA4GA1UdDwEB/wQEAwIHgDAdBgNVHQ4EFgQUNs2smrjBhuR5Bqxl6teE1x1o2ycwHwYDVR0jBBgwFoAUHyKsHGUWKbTBmLNjb7/dCZ27e3swGgYDVR0QBBMwEYEPMjAyMTEyMTYxNDQ1MjRaMAoGCCqGSM49BAMCA0cAMEQCIDjXHnyzq3sTisMX1uY8xQ2ZqCRL2xmxtYOPhSZ9ZacYAiAqHUMOC7WNgq4h28n31WLc1mMPAYauWslSEwnXC79AGw==' | base64 -d | openssl x509 -inform der -noout -pubkey -----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEYE24qIKmdcfRWUh2TqklkfZ6nyNB pX4VHeLMxfFl8rkQ8Zku0bcnH5OZXckkSt+tKs+FGZ9tKJ1VtNDmedlL6w== -----END PUBLIC KEY-----

Or more complete:

echo -n 'MIIB7zCCAZagAwIBAgIKAXnM+L47fmBcezAKBggqhkjOPQQDAjBEMQswCQYDVQQGEwJBVDEPMA0GA1UECgwGQk1TR1BLMQwwCgYDVQQFEwMwMDExFjAUBgNVBAMMDUFUIERHQyBDU0NBIDEwHhcNMjEwNjAyMTM0NTI0WhcNMjMwNjAyMTM0NTI0WjBGMQswCQYDVQQGEwJBVDEPMA0GA1UECgwGQk1TR1BLMQ8wDQYDVQQFEwYwMDEwMDExFTATBgNVBAMMDEFUIERHQyBEU0MgMTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABGBNuKiCpnXH0VlIdk6pJZH2ep8jQaV+FR3izMXxZfK5EPGZLtG3Jx+TmV3JJErfrSrPhRmfbSidVbTQ5nnZS+ujbjBsMA4GA1UdDwEB/wQEAwIHgDAdBgNVHQ4EFgQUNs2smrjBhuR5Bqxl6teE1x1o2ycwHwYDVR0jBBgwFoAUHyKsHGUWKbTBmLNjb7/dCZ27e3swGgYDVR0QBBMwEYEPMjAyMTEyMTYxNDQ1MjRaMAoGCCqGSM49BAMCA0cAMEQCIDjXHnyzq3sTisMX1uY8xQ2ZqCRL2xmxtYOPhSZ9ZacYAiAqHUMOC7WNgq4h28n31WLc1mMPAYauWslSEwnXC79AGw==' | base64 -d | openssl x509 -inform der -noout -pubkey | openssl asn1parse 0:d=0 hl=2 l= 89 cons: SEQUENCE
2:d=1 hl=2 l= 19 cons: SEQUENCE
4:d=2 hl=2 l= 7 prim: OBJECT :id-ecPublicKey 13:d=2 hl=2 l= 8 prim: OBJECT :prime256v1 23:d=1 hl=2 l= 66 prim: BIT STRING

Which is what yoiu are getting from the FR trustlist if all is well

bcsongor commented 3 years ago

Just for fun, I made a small web app (no server side components, everything happens on the frontend) that can read and verify EU DCC and UK NHS COVID Pass QR codes: https://covid-pass-verifier.com/

In case it's useful for anyone the list of certificates (140 as of now) it uses is here: https://covid-pass-verifier.com/assets/certificates.json

This JSON was generated from the CBOR encoded trust list provided by the Austrian Government's Greencheck app. Example element from the JSON:

{
  "kid": [211,107,221,0,230,63,250,2],
  "crt": [48,130,4,223,48,...,174,204,152],
  "iss": {
    "C": "LU",
    "O": "INCERT public agency",
    "CN": "Grand Duchy of Luxembourg CSCA"
  },
  "sub": {
    "C": "LU",
    "O": "Ministry of Health",
    "CN": "Grand Duchy of Luxembourg DS DCC 3"
  },
  "pub":{
    "x": [228,75,88,108,235,190,70,229,28,...,210,156,153,82],
    "y": [243,124,19,188,214,49,246,...,42,248,201,81,33,63]
  }
}
panzi commented 3 years ago

In theory a EHC could be revoked if some mistake happened. For that there should be revocation lists. I know that at least Switzerland supports a EHC revocation API (https://github.com/admin-ch/CovidCertificate-Apidoc/#api-docs).

Anyone know any revocation list APIs?

dirkx commented 3 years ago

Currently the way to revoke is at DSC level.

Revoking at DCC level quickly runs afoul of the GDPR - as the UCI (or anything that specifically pinpoints a DCC) is fundamentally a 'proxy' for a piece of medical information about a citizen.

And publishing lists of such PII's is expressly disallowed by the operators of the gateway (the European Commission). So therefore, at this time, the design does not allow for the revocation at DCC level - only for (whole) DSCs.

jbx1 commented 3 years ago

Just for fun, I made a small web app (no server side components, everything happens on the frontend) that can read and verify EU DCC and UK NHS COVID Pass QR codes: https://covid-pass-verifier.com/

@bcsongor Interesting, is the NHS COVID Pass QR code using the same format as the EC one? And which key are you using to verify it? The 140 ones published by DE and AT do not include any UK certificates.

cheers!

mauimauer commented 3 years ago

Yes, they seem to be using the DGC spec. I could only find their public keys, not full certs (pkeys + cert metadata) here: https://github.com/andypandy47/Answer-Digital-Verifier-App/blob/4d79843334c556729ab1c611d02c6244ba8d6f37/VerifierApp.Electron/src/assets/publicKeys.json