Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create valid Gaia-X Credenials according to Gaia-X Tagus release #70

Closed
3 of 4 tasks
anjastrunk opened this issue Dec 22, 2023 · 19 comments · Fixed by #32 or #96
Closed
3 of 4 tasks

Create valid Gaia-X Credenials according to Gaia-X Tagus release #70

anjastrunk opened this issue Dec 22, 2023 · 19 comments · Fixed by #32 or #96
Assignees
Labels
SCS-VP10 Related to tender lot SCS-VP10

Comments

@anjastrunk
Copy link
Contributor

anjastrunk commented Dec 22, 2023

GX Credential Generator generates one huge json-ld file with ALL information about SCS cluster, currently. In favor to flexibility and maintenance, each credential should be serialized in its own file and referenced by its DID in other credentials.

This issue contains two tasks:

@anjastrunk anjastrunk self-assigned this Dec 22, 2023
@anjastrunk anjastrunk added the SCS-VP10 Related to tender lot SCS-VP10 label Dec 22, 2023
@anjastrunk
Copy link
Contributor Author

anjastrunk commented Apr 17, 2024

My first task will be to investigate DID, VCs and Gaia-X Compliance (defined by PRC Documnet in more detail and figure out, how these concepts relate to each other.

I will document results of my investigations below.

@anjastrunk
Copy link
Contributor Author

anjastrunk commented Apr 17, 2024

Here are the Key results of my investigations

Concept Explanation Scope in Gaia-X
DID (Decentralized Identifier) A digital identifier of an entity, which is self-hosted and not provided by a centralized Identity Provider. DIDs are formatted as URIs conformant with [RFC3986]. Note: DIDs do not map digital identifiers to physical ones.
DIDs are resolvable to DID Documents, which associates digital identifiers to cryptographic material and a set of services relevant to interact with the entity identified by a DID. To map a digital identifier to a physical identifier, additional services/concepts are required, such as Notary Services.
Used to identify Participants, Service Offerings and Resources. One of the first tasks would be to provide a way to create DIDs for SCS clusters and their providers. Note: Gaia-X does only support DID web as did-method , currently.
VC (Verifiable Credential A temper-evident set of claims, whose authorship can be cryptographically verified. Used to describe properties of participants, service offerings and resources. In context of SCS, each Cloud Service (SCS cluster) and each Cloud Service Provider needs its own VC. A further task, will be to add DID of provider and SCS cluster to generated GX Credentials.
Gaia-X/GX Credential Synonym for Verifiable Credentials using Gaia-X ontology
Gaia-X Compliance Set of rules, which define how a Gaia-X conformant Verifiable Credential for Participant, Service Offering or Resource looks like, i.e. which claims are mandatory.

A good starting point to play around with Gaia-X Credentials is Gaia-X Wizard offered by Gaia-X Lab. There is also a good tech theater, explaining the concepts behind Gaia-X on-boarding.

@anjastrunk
Copy link
Contributor Author

anjastrunk commented Apr 18, 2024

Task 1: Implement DID generator

Gaia-X Lab offers a DID generator. This generator supports did:web method specification only. There is also free python demo, how to generate DIDs. Additional python skeleton with respect to DID handling are published by Gaia-X Lab. I will have a closer look into these scripts...

The first thing, we need is a public-private key pair and a x509 certificate (chain). According to Gaia-X Trust Framework, for development purpose let's encrypt is sufficent, in production, we need at least an eIDAS certificate or EV SSL certificate .

I created a python script to generate did documents for a given DID. Files are stored in #88 and should be moved to its own repo.

@anjastrunk
Copy link
Contributor Author

anjastrunk commented Apr 22, 2024

Task 2: Add cluster's and provider's DID to GX Credential Generator

Gaia-X uses DIDs to identity Participants (CSP) Service Offerings and Resources. Each CSP must have a DID and each SCS cluster offered as Service in Gaia-X needs a DID, too. These DIDs have to be placed in GX Credentials. SCS GX Credential Generator is configured by config file. I will place DIDs there.

@anjastrunk
Copy link
Contributor Author

anjastrunk commented May 21, 2024

Task 3: Create Gaia-X compliant credential for CSP's and OpenStack clouds

Gaia-X published an ontology containing several classes and attributes to describe CSP's and service's properties. Some of them are mandatory and not discoverable from Cloud. Furthermore, some properties must be provided in a temper.proof way as Verifiable Credential issued by a Gaia-X Clearing House. This is, e.g. the case for CSP's registration number.

We have two options to handle these challenges

  1. CSPs provide any attributes, which are not discoverable in generator's configuration file. Every run of credential generator calls Gaia-X Clearing House Services to issue required credentials. This option is simple, but has two major drawbacks: (1) We mirror GX Credential Schema in configuration file, which cause a huge synchronization effort. Furthermore, we loose flexibility of linked data approach and self-sovereignty, where credential holder decides with whom to share which data.
  2. CSPs request credentials by themselves and store them in a wallet. Credential generator looks in wallet for information required by Gaia-X, automatically. Such a wallet can be a simple file system folder or a professional wallets, such as OCM from XCFS.

I go for option 1, first and limit data in configuration file to mandatory attributes of Legal Person (= GX Credential for CSP) and Service Offering. According to Trust Framework, these are:

  • Legal Person

    • RegistrationNumber as VAT
    • legalAddress.countryCode
    • headquarterAddress.countryCode
  • Service Offering

    • providedBy
    • termsAndConditions
    • servicePolicy
    • dataAccountExport

Source code can be found in #89

Challenges

Inconsistency of shapes in Gaia-X Clearing House and WG Service Characteristics GitLab

Shapes used in Gaia-X Clearing House for verification and shapes published in WG Service Characteristics GitLab are not in sync. Gaia-X Clearing House is based on version 22.10, released in October 2022! A lot of concepts, required to describe IaaS offerings are not included in version 22.10. There will be an update in release "Loire", but its release date is not jet published.
Furthermore Gaia-X ontology used to create Gaia-X Credentials still needs improvements.

I summarized my investigations in bug reports within the appropriate Gaia-X repositories:

Misleading/Inconsistent definition of Gaia-X Credential

Gaia-X dictates to publish all properties of CSPs and services as Gaia-X Credential. Gaia-X Credentials "... are W3C Verifiable Credentials with claims expressed in RDF [Gaia-X Architecture Framework, section Conceptual Model

The same document says in section "Operating Model", "Gaia-X Self-Descriptions ... refers to a self-signed set of claims. This is equivalent to a Verifiable Credential where the holder is the issuer. In general, the term “Gaia-X Self Description” is replaced by “Gaia-X Credential”" [Architecture Document - section Operating Model]

Wheres the first definition says a Gaia-X Credential is a Verifiable Credential, the second limits this to a Verifiable Credential, where the holder is the same entity as the issuer. IMO, both definitions are incorrect, as a Gaia-X Credential is a Verifiable Presentation, as defined by W3C.

Furthermore, W3C VC Spec does allow several viable proof mechanisms. However, Gaia-X does not mention, which proof they prefer or support, giving the impression all viable proof mechanisms are supported. But this is not he case.

I summarizes my findings in an issue waiting for clarification.

Non-conform usage of Verifiable Credentials

There is an example on how to create compliance Gaia-X Credentials in python. The example sets JSONWebSignature2020 as proof mechanism and uses the python library jwcrypto to create proof object. Gaia-X hashes credential and uses this hash as payload for calculation of signature. However, jwcrypto calculates hash for payload as input for signature, hence hash of hash is calculated. I neither know, if this is intended nor the side effects of this behavior. I opened an issue to raise this question.

Gaia-X uses a compact serialization as JWS, which is a string consisting of protected header, payload and signature. However, Gaia-X omit payload part. Instead of [header].[payload].[signature], Gaia-X uses [header]..[signature]. I claims, this is to reduce size of credential, however, I did not found any official source to do so. If this is a special feature of Gaia-X, it should be mentioned explicitly in official Gaia-X documents. Otherwise, verification of signature fails. I raised this question to Gaia-X lab.

Requesting and validating a VC

Some Verifiable Credentials (VCs) are not created and signed locally by the CSP but an external entity.
One example is the Legal Registration Number (LRN).
A request must be made to the Notary API of the Clearing House Service using a valid LRN such as a VAT ID.
The Clearing House Service will validate the LRN using a public index and then sign and return a VC.

After receiving the response from the "https://registrationnumber.notary.lab.gaia-x.eu/v1/registrationNumberVC" Notary API, the included proof.verificationMethod response field will contain a DID identifier that, when resolved, will point to the did.json of the signee (the Gaia-X Clearing House Service). This DID in turn references the validation public key certificate in the x5u field: https://registry.lab.gaia-x.eu/.well-known/x509CertificateChain.pem.

Using this certificate, the VC from the Notary API response can be cryptographically verified:

notary_url = "https://registrationnumber.notary.lab.gaia-x.eu/v1/registrationNumberVC"
lrn_vc_id = "https://bakeup.io/lrn.json"
lrn_response = requests.post(notary_url + "?vcid=" + lrn_vc_id, ...)
lrn = lrn_response.json()

# Certificate used as public key to verify the JWS
# (this URL is part of the DID referenced in the response's proof object within the x5u field of
# the verificationMethod entry)
verification_cert_url = "https://registry.lab.gaia-x.eu/.well-known/x509CertificateChain.pem"

verify_credential(json.dumps(lrn), verification_cert_url)

The verify_credential() function can be implemented as follows:

import requests
import json
from jwcrypto import jwk, jws
# utils refers to https://gitlab.com/gaia-x/lab/workshops/gaia-x-101/-/blob/master/utils.py?ref_type=heads
from utils import sha256_normalized_vc, normalize


def verify_credential(credential_json_str, cert_url):

    verifiable_credential = json.loads(credential_json_str)

    # Retrieve the registry certificate which serves as the verification
    # public key (JWK) for the JWS later
    reg_cert_response = requests.get(cert_url)
    if reg_cert_response.status_code != 200:
        raise Exception(
            f"Unable to retrieve verification certificate "
            f"from: {cert_url}"
        )
    verification_cert_pem = reg_cert_response.text.encode('UTF-8')
    verification_key = jwk.JWK.from_pem(verification_cert_pem)

    # The proof object is part of the credential response, however
    # it resembles JWS data applicable to the response without the
    # proof object. Hence, we need to strip the proof object from
    # the response.
    proof = verifiable_credential.pop("proof")

    # The remaining structure is the actual credential data that
    # JWS was created for. The signature was applied to its
    # normalized and hashed form, which we need to recreate here
    # in order to verify the signature.
    # See: https://w3c.github.io/vc-data-integrity/#how-it-works
    normalized_credential = normalize(verifiable_credential)
    hashed_credential = sha256_normalized_vc(normalized_credential)

    # Instantiate a JWS object based on the jws attribute of the
    # proof object, which contains a base64 representation of the JWS.
    received_jws_token = jws.JWS()
    received_jws_token.deserialize(proof["jws"])

    # Finally, use the verification key (Gaia X registry public cert)
    # and the hashed credential (which is the JWS' detached payload)
    # in conjunction with the JWS token to verify the credential.
    # This method will throw an exception if verification fails.
    received_jws_token.verify(
        verification_key,
        detached_payload=hashed_credential.hexdigest()
    )

Bug in linkML's pyhton generator

Gaia-X uses linkML to define Gaia-X Ontology. We use linkML's pyhton generator to create python classes for ontology. Several attributes of classes in Gaia-X ontology refers other classes. These, so called inlined objects will added to python object in special post initialization step via method self._normalize_inlined_as_dict() in method __post_init__(). For none obvious reason self._normalize_inlined_as_dict() fails with TypeError: unhashable type: 'list' while object creation. I will go deeper in this issue later on. For now and in favor of finalizing this issue, I will use a workaround. I comment self._normalize_inlined_as_dict() out.

@anjastrunk
Copy link
Contributor Author

anjastrunk commented May 21, 2024

Task 4: Put all credentials in a VP, sign it and retrieve Gaia-X Compliance Credential

Recap: technical prerequisites

The following preparing steps must be taken.
This is essentially a brief summary the steps resulting from tasks 1 through 3:

  1. All locally or externally signed Verifiable Credentials (VCs) that will be part of the Verifiable Presentation (VP).
  2. The public key that corresponds to the private key used to sign any locally signed VCs.
  3. A X.509 certificate chain in PEM format hosted publicly which contains the public key from (2) in the first certificate of the chain. A chain consisting of a single certificate is valid.
  4. A Decentralized Identifier (DID) of the CSP hosted at <csp-domain>/.well-known/did.json.
    • The DID must contain the public key (in JWK format) from (2).
    • The DID must reference the certificate chain from (3) in one of the verificationMethod.*.publicKeyJwk.x5u fields.

Note: it is not required for the X.509 certificate from (3) used for proof verification to be issued for a Common Name (CN) matching any of the CSP's hosting domains or having any relation to the FQDN of the DID host. The only requirements for this certificate are:

  • Its contained public key must match the other attributes in the corresponding verificationMethod.*.publicKeyJwk.* fields (including the public key encoded therein).
  • It must be hosted on a TLS-protected HTTP host.

Compliance process using Let's Encrypt

The list of Gaia-X Trust Anchors1 points to Mozilla's included certificates2 which lists the Let's Encrypt root certificates, such as "ISRG Root X1".
Hence, the Compliance process for Gaia-X should be achievable using certificates issued by Let's Encrypt.

I created a setup for establishing a Provider Participant using Verifiable Credentials based on a Let's Encrypt certificate and key pair. I took most of the implementation from gaia-x-101.ipynb and adjusted the code where necessary.

In short:

  1. set up a web server with a DNS record gaia-x-testing.cloudandheat.com
  2. use Let's Encrypt (LE) to generate key pair and signed certificate
    • it's best to forcibly switch LE to use a RSA key pair because the default ECDSA seems incompatible with the script in gaia-x-101.ipynb; as an alternative adjust utils.py to pass "alg": "ES256" to the jwcrypto library instead
  3. generate DID using did:web:gaia-x-testing.cloudandheat.com
  4. on the web server host two files:
    • /.well-known/did.json = the generated DID
    • /.well-known/cert.pem = a copy of the signed LE certificate
  5. use the process of gaia-x-101.ipynb in conjunction with the private/public LE key pair to generate the Verifiable Credentials and the Verifiable Presentation
  6. submit the Verifiable Presentation to compliance

Using the process of gaia-x-101.ipynb I'm failing at the very last step:

Exception: Unable to submit to compliance
{"message":"X509 certificate chain could not be resolved against
registry trust anchors for VC https://bakeup.io/tsandcs.json.",
"error":"Conflict","statusCode":409}

About using Let's Encrypt certificates as trust anchor

Based on a reported issue in the Gaia-X registry service3, it seems the above error happens when the Compliance API talks to the Registry API's trust anchors endpoint to verify the individual VC's certificate chains.

Let's Encrypt (LE) produces three certificate files with certbot:

  • /etc/letsencrypt/live/*/cert.pem = the leaf certificate for the DNS name
  • /etc/letsencrypt/live/*/chain.pem = an intermediate certificate called "R3", parent of the leaf certificate
  • /etc/letsencrypt/live/*/fullchain.pem = the concatenation of the above 2

Directly checking any of the three files against the Gaia-X Registry API fails:

sudo cp /etc/letsencrypt/live/gaia-x-testing.cloudandheat.com/fullchain.pem /srv/.well-known/cert.pem

curl -X POST -H "Content-Type: application/json" -d '''
  {
    "uri": "https://gaia-x-testing.cloudandheat.com/.well-known/cert.pem"
  }''' https://registry.gaia-x.eu/v1/api/trustAnchor/chain/file
{"message":"Unable to validate certificate chain","error":"The parent certificate did not issue the given child certificate; the child certificate's issuer does not match the parent's subject.","statusCode":409}

As such, the certificate chain generated by certbot seems insufficient as DID reference for the Gaia-X Registry Trust Anchor API.

Including the LE's root certificate

When accessing the webserver delivering the LE certificate using a browser such as Mozilla Firefox, a third certificate is added when downloading the certificate via the menu: the "ISRG Root X1" root certificate of Let's Encrypt.

This certificate chain will consist of the following certificates:

  1. Leaf certificate for the webserver
  2. Intermediate "R3" certificate
  3. ISRG Root X1

The last certificate (root certificate) is missing from the chain file produced by certbot of LE.
It has to be added manually. The link to the root certificate can be taken from the fingerprint column of Mozilla's index of included root certificates:

# start building the certificate chain from the leaf certificate and R3 intermediate
sudo cat \
    /etc/letsencrypt/live/gaia-x-testing.cloudandheat.com/cert.pem \
    /etc/letsencrypt/live/gaia-x-testing.cloudandheat.com/chain.pem \
    | sudo tee /srv/.well-known/fullchain.pem

# append the ISRG Root X1 (Let's Encrypt's root certificate)
curl -s \
    https://crt.sh/?d=96BCEC06264976F37460779ACF28C5A7CFE8A3C0AAE11A8FFCEE05C0BDDF08C6 \
    | sudo tee -a /srv/.well-known/fullchain.pem

In your did.json link this certificate chain as x5u:

{
  ...
  "verificationMethod": [
    {
      ...
      "publicKeyJwk": {
        ...
        "x5u": "https://gaia-x-testing.cloudandheat.com/.well-known/fullchain.pem"

Attempting to make a request to the Registry API of Gaia-X using the full chain now results in:

curl -X POST -H "Content-Type: application/json" -d '''
  {
    "uri": "https://gaia-x-testing.cloudandheat.com/.well-known/fullchain.pem"
  }''' https://registry.gaia-x.eu/v1/api/trustAnchor/chain/file
{"message":"Unable to validate certificate chain","error":"Certificate Root not found in trusted anchors","statusCode":409}

At first that seems to contradict the official documentation on trust anchors1 where the "ISRG Root X1" is listed as included by Mozilla4 within the "Extended Validation (EV) Secure Sockets Layer (SSL)" category which is marked as "temporarily valid Trust Service Providers".

However, the "v1" API of the Gaia-X Registry API only allows these root certificates in the context of EV SSL:

v1 respects the trustframework 2210 trusted-anchors section, thus allowing only EVSSL and EIDAS certificate.

Let's Encrypt itself does not offer EV SSL certificates at all: https://letsencrypt.org/docs/faq/#what-services-does-let-s-encrypt-offer

Using a development API version of the Gaia-X Registry API instead

As stated in the issue3, the "v1" API version of the Gaia-X Registry does not accept certificates acquirable by mere mortals and strictly requires EIDAS or EV SSL certificates.
Only the "development" API version accepts other certificate types.

Replacing the "registry.gaia-x.eu/v1/api/" by "registry.lab.gaia-x.eu/development/api/" as the target finally makes the request succeed (only if the full chain including the "ISRG Root X1" certificate as downloaded from the browser is present as explained above!):

curl -X POST -H "Content-Type: application/json" -d '''
  {
    "uri": "https://gaia-x-testing.cloudandheat.com/.well-known/fullchain.pem"
  }''' https://registry.lab.gaia-x.eu/development/api/trustAnchor/chain/file
{"result":true}

As such, the Registry API is now accepting the LE certificate.

Now after also redeploying the /.well-known/did.json on the webserver to reference the full certificate chain pem file (including the LE ISRG Root X1 certificate), the compliance submission can be re-attempted against the "development" (not "v1") Compliance API:

compliance_response = requests.post(
    "https://compliance.lab.gaia-x.eu/development/api/credential-offers",
    vp
)
Exception: Unable to submit to compliance:
{"statusCode":500,"message":"Internal server error"}

Any previous error is now gone but the server runs into an unspecified error.

Turns out that the "development" API environment is not stable enough but there is a "v1-staging"5 API path which serves as a middle ground: it does still accept Let's Encrypt certificates (unlike "v1") but does not run into internal 500 errors (unlike "development").

Conclusion

  1. Set the key pair type for the Let's Encrypt certificate to RSA in order to be compatible with the JWT signing process implemented by [gaia-x-101.ipynb](https://gitlab.com/gaia-x/lab/workshops/gaia-x-101/-/blob/master/gaia-x-101.
    • the default of Let's Encrypt is ECDSA which only works if the utils.py is adjusted to pass "alg": "ES256" to the jwcrypto library call
  2. When preparing the certificate chain for hosting the DID reference target, include all three LE certificate parts: ISRG Root X1, Intermediate "R3", leaf certificate
    • the fullchain.pem as produced by certbot is not sufficient as it lacks the root certificate
    • you have to manually add the ISRG Root X1 certificate to certbot's fullchain.pem
    • you can check compatibility by sending a POST request to https://registry.lab.gaia-x.eu/development/api/trustAnchor/chain/file with { "uri": "myserver.com/.well-known/cert-chain.pem" } for example
  3. Host a /.well-known/did.json that contains the public key of the leaf certificate as publicKeyJwk.n as well as the reference to the full certificate chain file from (2) as publicKeyJwk.x5u
    • you may host the certificate chain file from (2) under /.well-known/ as well; make sure the x5u path in the did.json is correct
  4. For submitting the Verifiable Presentation based on a Let's Encrypt key pair, use the "v1-staging" path of the Gaia-X Compliance API, i.e. https://compliance.lab.gaia-x.eu/v1-staging/api/credential-offers
    • do not use the "development" API path, as it will break with a 500 internal error5
    • do not use the "v1" stable API path, as it will not accept Let's Encrypt certificates3

Footnotes

  1. https://gaia-x.gitlab.io/policy-rules-committee/trust-framework/trust_anchors/#list-of-defined-trust-anchors 2

  2. https://wiki.mozilla.org/CA/Included_Certificates

  3. https://gitlab.com/gaia-x/lab/compliance/gx-registry/-/issues/32 2 3

  4. https://ccadb.my.salesforce-sites.com/mozilla/IncludedCACertificateReport

  5. https://gitlab.com/gaia-x/lab/compliance/gx-compliance/-/issues/66#note_1914512870 2

@markus-hentsch
Copy link
Contributor

markus-hentsch commented May 24, 2024

(content of this comment containing the LRN VC verification code sample has been merged into #70 (comment))

@markus-hentsch
Copy link
Contributor

Non-conform usage of Verifiable Credentials

I added comments to both issues12. I think neither is actually a mistake. I can see some ambiguity regarding the double hashing topic but I lean towards the opinion that it is correctly implemented. The omitted payload seems perfectly valid according to an appendix of the referenced RFC specifying this exact behavior.

Footnotes

  1. https://gitlab.com/gaia-x/lab/workshops/gaia-x-101/-/issues/1#note_1921122103

  2. https://gitlab.com/gaia-x/lab/workshops/gaia-x-101/-/issues/2#note_1920833026

@anjastrunk anjastrunk changed the title Support DID in generated Gaia-X Credentials Create valid Gaia-X Credenials according to Gaia-X Tagus release May 30, 2024
@markus-hentsch
Copy link
Contributor

markus-hentsch commented Jun 3, 2024

(content of this comment concerning the compliance process based on Let's Encrypt certificates has been merged into #70 (comment))

@markus-hentsch
Copy link
Contributor

markus-hentsch commented Jun 6, 2024

How to build a Gaia-X-compatible certificate chain from Let's Encrypt certbot files

Since certbot's files are not sufficient to build the full x5u-linked cert chain for the Gaia-X Compliance API (root certificate is missing), here's how to mix and match things to make it work without downloading the chain using a browser:

# start building the certificate chain from the leaf certificate and R3 intermediate
sudo cat \
    /etc/letsencrypt/live/gaia-x-testing.cloudandheat.com/cert.pem \
    /etc/letsencrypt/live/gaia-x-testing.cloudandheat.com/chain.pem \
    | sudo tee /srv/.well-known/fullchain.pem

# append the ISRG Root X1 (Let's Encrypt's root certificate)
curl -s \
    https://crt.sh/?d=96BCEC06264976F37460779ACF28C5A7CFE8A3C0AAE11A8FFCEE05C0BDDF08C6 \
    | sudo tee -a /srv/.well-known/fullchain.pem

(assuming /srv/ to be the web root of your server)

In your did.json link this certificate chain as x5u:

{
  ...
  "verificationMethod": [
    {
      ...
      "publicKeyJwk": {
        ...
        "x5u": "https://gaia-x-testing.cloudandheat.com/.well-known/fullchain.pem"

Note: if changing/updating the key, don't forget to update your did.json with the new public key as well!

Note: the crt.sh URL has been taken from the fingerprint link of the ISRG entry in https://ccadb.my.salesforce-sites.com/mozilla/IncludedCACertificateReport

@markus-hentsch
Copy link
Contributor

markus-hentsch commented Jun 6, 2024

Mystery surrounding the Legal Registration Number (LRN)

One part of the gaia-x-101.ipynb example is receving an externally signed VC for the LRN by making a request to registrationnumber.notary.lab.gaia-x.eu/v1/registrationNumberVC.

This is a special case because:

  1. unlike the VCs for Participant and Terms & Conditions this one is not signed by the provider but the Gaia-X Digital Clearing House (GXDCH) Notary API instead
  2. the VC request send to the Notary API seems to require a reference to an existing JSON that is a signed LRN VC

Wait ...

The request for acquiring a signed LRN VC requires a reference to an existing signed LRN VC JSON hosted by the provider? The request requires its own result? This looks like a chicken-and-egg problem. Where did this existing LRN VC come from?

Let's look at the gaia-x-101.ipynb example.

The LRN VC request generated by that example looks like this:

{
  ...
  "type": "gx:legalRegistrationNumber",
  "id": "https://bakeup.io/lrn.json#cs",
  ...
}

Looking at https://bakeup.io/lrn.json referenced by the request:

{
  ...
  "type": [
    "VerifiableCredential"
  ],
  "id": "https://bakeup.io/lrn.json",
  "issuer": "did:web:registration.lab.gaia-x.eu:v1",
  "credentialSubject": {
    ...
    "type": "gx:legalRegistrationNumber",
    ...
  },
  ...
  "proof": {
    ...
    "verificationMethod": "did:web:registration.lab.gaia-x.eu:v1#X509-JWK2020",
    ...
  }
}

This hosted lrn.json is indeed a LRN VC signed by the Notary API... used to request a signed LRN VC which it itself already is.
Specifying such JSON link in the request is in accordance to the API spec1 but where did the initial JSON come from?

"Fake it till you make it" approach

If you don't have an existing LRN VC just pretend to:

curl -s -o /dev/null -w "%{http_code}" \
    https://gaia-x-testing.cloudandheat.com/.well-known/lrn.json

404

The file does not exist. We can request a VC for it anyway:

{
  "@context": [
    "https://registry.lab.gaia-x.eu/development/api/trusted-shape-registry/v1/shapes/jsonld/participant"
  ],
  "type": "gx:legalRegistrationNumber",
  "id": "https://gaia-x-testing.cloudandheat.com/.well-known/lrn.json#cs",
  "gx:vatID": "..."
}

... and we get a valid signed VC that is accepted in a VP later on by the Complianca API (I tested this).
We can use this approach to get the initial LRN VC but is this intended? How is it supposed to work with the did:web: variant of the request?

DID not working for LRN request id

The above only works with the plain URL like also used in the gaia-x-101.ipynb example. It does not work if we use a did:web: URL for the initial request's "id" field as the API spec's example1 suggests:

{
  "@context": [
    "https://registry.lab.gaia-x.eu/development/api/trusted-shape-registry/v1/shapes/jsonld/participant"
  ],
  "type": "gx:legalRegistrationNumber",
  "id": "did:web:gaia-x.eu:legalRegistrationNumber.json",
  "gx:vatID": "FR79537407926"
}

(example taken from the Notary API documentation)

This will work for the LRN request at first but not for the Compliance API request later on as this one will try to resolve the DID reference. Such a DID reference will not properly resolve:

curl -X GET https://dev.uniresolver.io/1.0/identifiers/did:web:gaia-x.eu:legalRegistrationNumber.json
{"@context":"https://w3id.org/did-resolution/v1","didDocument":null,"didResolutionMetadata":{"error":"notFound","message":"resolver_error: DID must resolve to a valid https URL containing a JSON document: Error: Bad response Not Found","contentType":"application/did+ld+json","convertedFrom":"application/did+json","convertedTo":"application/did+ld+json"},"didDocumentMetadata":{}}

If we change to DID to point to our server via "did:web:gaia-x-testing.cloudandheat.com:lrn.json", we can see the following access log entry on our webserver:

GET /lrn.json/did.json HTTP/1.1" 404

It tries to find a did.json in the folder called /lrn.json/ on our webserver, which is dumb. A DID does not seem to be meant to resolve to files other than a did.json, which makes the whole example in the Notary API documentation1 misleading if not wrong.

Conclusion

From the research above I conclude the following for now:

  • do not use DID references in the "id" field of LRN VC requests, if they can't be resolved because the Compliance API will not accept such a LRN VC even if the Notary API is fine with it during the creation of the VC
  • according to the publicly available examples, it seems that the "id" field of the LRN request to the Notary API should contain a reference (e.g. URL) to the resulting LRN VC (it self-references itself)
    • for the first request it seems to be legit to point to a non-existing file (representing the place where you intend to put the response of the LRN request after making the request)

However, this does not really make sense to me yet so I filed an issue about this matter.

Footnotes

  1. https://registrationnumber.notary.gaia-x.eu/v1/docs/#/registrationNumberVC/checkRegistrationNumberVC 2 3

@anjastrunk
Copy link
Contributor Author

anjastrunk commented Jun 13, 2024

How to build a Gaia-X-compatible certificate chain from Let's Encrypt certbot files

I just figured out, that Mozilla Firefox is doing the job for you. If you download Let's encrypt certificate chain via Mozilla Firefox, you also get a certificate chain accepted by [Gaia-X Registry in deployment version1. However, this a very unstable deployment of Gaia-X Registry for LOIRE release (v2)

Footnotes

  1. https://registry.lab.gaia-x.eu/development/docs/)

@anjastrunk
Copy link
Contributor Author

I was finally able to reproduce lessons learned found out by @markus-hentsch and received an valid Gaia-X Credential from Compliance Service. I will integrate this in to GX Credential Generator now, see #96

@anjastrunk
Copy link
Contributor Author

Wrong evaluation of id of credential subject of Gaia-X Credential of VatID

Gaia-X requires each legal person to have a valid registration number, which is attested by GXDCH Notary Service. To receive a Gaia-X Credential for vatID, you have to provide three parameters: (1) vatID, (2) credentialSubjet id and (3) credential id

Using DE281093504 as vatID, did:web:gaia-x.cloudandheat.com as credential subject id and https://gaia-x.cloudandheat.com as credential id, we receive the following Gaia-X Credential

{
    "@context": [
        "https://www.w3.org/2018/credentials/v1",
        "https://w3id.org/security/suites/jws-2020/v1"
    ],
    "type": [
        "VerifiableCredential"
    ],
    "id": "https://gaia-x.cloudandheat.com/lrn.json",
    "issuer": "did:web:registration.lab.gaia-x.eu:v1-staging",
    "issuanceDate": "2024-06-24T19:44:01.096Z",
    "credentialSubject": {
        "@context": "https://registry.lab.gaia-x.eu/development/api/trusted-shape-registry/v1/shapes/jsonld/trustframework#",
        "type": "gx:legalRegistrationNumber",
        "id": "did:web:gaia-x.cloudandheat.com",
        "gx:vatID": "DE281093504",
        "gx:vatID-countryCode": "DE"
    },
    "evidence": [
        {
            "gx:evidenceURL": "https://ec.europa.eu/taxation_customs/vies/services/checkVatService",
            "gx:executionDate": "2024-06-24T19:44:01.096Z",
            "gx:evidenceOf": "gx:vatID"
        }
    ],
    "proof": {
        "type": "JsonWebSignature2020",
        "created": "2024-06-24T19:44:01.111Z",
        "proofPurpose": "assertionMethod",
        "verificationMethod": "did:web:registration.lab.gaia-x.eu:v1-staging#X509-JWK2020",
        "jws": "eyJhbGciOiJQUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..bWuYiFhg1yS4RAiEx45z8wLMZY8kyMvt49GjKCYcLFYmIXE6iPipp8j0MuMChX9MIcHXkDP2pk-yqi4ofsnWu5kaFqO_vYrhF5YmGSr0TSK5SCNkEdfFqz94zXz_nLy1uDzHwLMpEaHzXNvIESEGKuD44SOJo_JE9Jlkha-VPL-4K_8ogBIUAZ-sW2wWnncZ_4YLRLy59oCNxl7h_6IrJ3gyRLl5YYWheg9xRrkUbvIh4F5fs6O3H2oqDvLYuKiZT-0cfG_Z0QplUWV_jywqn1RBVatFUqx6xVlLSLijTzmYWDcYDUxwaefa2RCL7pTJQtOt8Pm2Fvi9sGPqegf5-g"
    }
}

Putting this into Verifiable Presentation for Legal Person results in the following json file

{
   "@context":"https://www.w3.org/2018/credentials/v1",
   "type":"VerifiablePresentation",
   "verifiableCredential":[
      {
         "@context":[
            "https://www.w3.org/2018/credentials/v1",
            "https://w3id.org/security/suites/jws-2020/v1"
         ],
         "credentialSubject":{
            "@context":"https://registry.lab.gaia-x.eu/development/api/trusted-shape-registry/v1/shapes/jsonld/trustframework#",
            "gx:vatID":"DE281093504",
            "gx:vatID-countryCode":"DE",
            "id":"did:web:gaia-x.cloudandheat.com",
            "type":"gx:legalRegistrationNumber"
         },
         "evidence":[
            {
               "gx:evidenceOf":"gx:vatID",
               "gx:evidenceURL":"https://ec.europa.eu/taxation_customs/vies/services/checkVatService",
               "gx:executionDate":"2024-06-24T19:46:59.092Z"
            }
         ],
         "id":"https://gaia-x.cloudandheat.com/lrn.json",
         "issuanceDate":"2024-06-24T19:46:59.092Z",
         "issuer":"did:web:registration.lab.gaia-x.eu:v1-staging",
         "proof":{
            "created":"2024-06-24T19:46:59.103Z",
            "jws":"eyJhbGciOiJQUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..GYUliHQiwvLpPhTulDgjTU6DBgftyW98UiFAJbjvGO7o2ia6Bf2QrwdayXqqrYL4UUhY-u3RRIlTv3hSR224uuB8U5kIs5dGLfX6GXmuWYem2DSdlreDm6PMR5LTrRnOId4FJTNYeqzWakR9EAz7dM2G3mRhDRooLz1v_SDM9FSCvFCaKgZj6eofOTXmpEoSPijHUfgbhmOIjGF3R9YxAedRTVEpQwmb5Z_rg7vmAAQMfQAZnV7Fe0bz2LKh7vLl9Gq5wNYPmRwhBEMFr6vjWYCbrjdQq2V_NaeicZgd1ptSwODqjpo_TfMBD56MeZPGSkV87GPGSHCN_tNN3IwiHA",
            "proofPurpose":"assertionMethod",
            "type":"JsonWebSignature2020",
            "verificationMethod":"did:web:registration.lab.gaia-x.eu:v1-staging#X509-JWK2020"
         },
         "type":[
            "VerifiableCredential"
         ]
      },
      {
         "@context":[
            "https://www.w3.org/2018/credentials/v1",
            "https://w3id.org/security/suites/jws-2020/v1",
            "https://registry.lab.gaia-x.eu/development/api/trusted-shape-registry/v1/shapes/jsonld/trustframework#"
         ],
         "credentialSubject":{
            "gx:termsAndConditions":"The PARTICIPANT signing the Self-Description agrees as follows:\n- to update its descriptions about any changes, be it technical, organizational, or legal - especially but not limited to contractual in regards to the indicated attributes present in the descriptions.\n\nThe keypair used to sign Verifiable Credentials will be revoked where Gaia-X Association becomes aware of any inaccurate statements in regards to the claims which result in a non-compliance with the Trust Framework and policy rules defined in the Policy Rules and Labelling Document (PRLD).",
            "id":"did:web:gaia-x.cloudandheat.com",
            "type":"gx:GaiaXTermsAndConditions"
         },
         "id":"https://gaia-x.cloudandheat.com/tandc.json",
         "issuanceDate":"2024-06-24T19:46:56.537466+00:00",
         "issuer":"did:web:gaia-x.cloudandheat.com",
         "proof":{
            "jws":"eyJhbGciOiJQUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..XXEENN6BG6F23shRsnsgLIsOnvGyAC-1YhRAUTSBfJ9cSex_UZT8WwT5LVApfZpJe798RHlPA7DpGOVNODUFuKdaex45abxZrhUn8WewszaUhILeXzeops7ESHpsFYC6JiL1l14ISeMLhoej48mC-0MwssPavXY8oXnqyYzalxcb4P2kCg7Z6GadjGeZPlChDvM0m9r1tyPAQHbn5GI1Rvi7QfLS5wVRgwvukJmpdz765EYceofNfi2cZv8rIUXzA_HA3vZE5vmFnKVO5V3Af3U-D3Diy_19OG7CdSp3kWnhOsyAJHikMGWNhUAKBIa7spAopG5-A2v7PaFknjor0rjRqld1Vz4FsEQUhTjxBsVs9d-cEDUQr1FbMNbl_crkA3_XerOZUZgQT3tHu-4QMbmteDm6Y1gbHEpoq_ZHH5NJqZziKEiH23zZ_8FfOMAuQGwu3SITWRxyr9kT5djvgWcO_IkpCCGFpxPEJ43tjCz46wZUTL7PkQurkK-_bHCYIFwCbotxTm7cO14IuNwv7XnxSJozg-lFC-Ya8lOO00Ivoz8LOXsNw8i5ZFUJfKyP0FWT91fQ5MwAhd0UDNfe9LQeFbzCpNQ4DS1CVOKzUWU477o279h0OFty7UNeqedzEL8PSxrfSW-9DYlAN3Lh5ieX5oayoZCXqDWtj9WXOoQ",
            "proofPurpose":"assertionMethod",
            "type":"JsonWebSignature2020",
            "verificationMethod":"did:web:gaia-x.cloudandheat.com#JWK2020-X509-2"
         },
         "type":"VerifiableCredential"
      },
      {
         "@context":[
            "https://www.w3.org/2018/credentials/v1",
            "https://w3id.org/security/suites/jws-2020/v1",
            "https://registry.lab.gaia-x.eu/development/api/trusted-shape-registry/v1/shapes/jsonld/trustframework#"
         ],
         "credentialSubject":{
            "gx:headquarterAddress":{
               "gx:countrySubdivisionCode":"DE-SN"
            },
            "gx:legalAddress":{
               "gx:countrySubdivisionCode":"DE-SN"
            },
            "gx:legalName":"Cloud\u0026Heat Technologies GmbH",
            "gx:legalRegistrationNumber":{
               "id":"https://gaia-x.cloudandheat.com/lrn.json"
            },
            "id":"did:web:gaia-x.cloudandheat.com",
            "type":"gx:LegalParticipant"
         },
         "id":"https://gaia-x.cloudandheat.com/legal_person.json",
         "issuanceDate":"2024-06-24T19:46:59.153346+00:00",
         "issuer":"did:web:gaia-x.cloudandheat.com",
         "proof":{
            "jws":"eyJhbGciOiJQUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..cuFuO6jokK9U23IBE-oPP2kjCU0N-iKykl4hqIijz1NSyCrT-864qv_271z2vK9_U5q6zakSeDfRrcpAFI5_VfOD5ldRpo_g-q8L42GouA3832N760xFSlVKKze5jmsXiaJFte3xGBWDkunyMqh7Rs1JBxUrQrei2fdMm9fEnNh9tchfBEqBekgjdcMa_fSRf9BizerB8U_PON1M-040Ruf8WWKmcye-c_lgAYRYfCUUOE9ONBoMSeroWwQMlM-InaXaLvFPDo2KTUyEi6P6ot4P41aPinYxY9ruYpRDSSjFEQ5QeFw9zMGnu7H7U7ajb2jBnkFjhl1NWjY-meKWNj0CIC8caMx6wR-2NMs-Z-SRvSRRRT3Ff-gKy3Bs7HqKO0tkCls_qN-RXiqgVut025KsL_O_0NAgbIGhzZRywSz_KNClCwz9G7Is_lxn7c4lZUh6MH0f3y9hCqweyjyRuvU8s_fbUv_t71XatiwtwToEd3a5Hmgr6xOAw6j-wjVFgRfRGTg-EwmANWUlp3KZ6tAI9wIMru-h-2wUYwjtEjmZa9dEBu4_XCFzmmnAyNsaKAXUUd-FDRRU9nMBVASz4RHxOMQaMlxxn1hPNFq31t0ALHzOMEPn9CBgcI0XiDONAfqdZiNkB0h4jGt0rPE0OZJw1MYK-9UcXegojScVjP8",
            "proofPurpose":"assertionMethod",
            "type":"JsonWebSignature2020",
            "verificationMethod":"did:web:gaia-x.cloudandheat.com#JWK2020-X509-2"
         },
         "type":"VerifiableCredential"
      }
   ]
}

Sending Verifiable Presentation to Compliance Service, receive in the following error:

{
    "statusCode": 409,
    "message": {
        "conforms": false,
        "results": [
            "ERROR: did:web:gaia-x.cloudandheat.com https://registry.lab.gaia-x.eu/development/api/trusted-shape-registry/v1/shapes/jsonld/trustframework#legalRegistrationNumber: Value does not have shape <https://registry.lab.gaia-x.eu/development/api/trusted-shape-registry/v1/shapes/jsonld/trustframework#legalRegistrationNumberShape>; DETAILS: https://registry.lab.gaia-x.eu/development/api/trusted-shape-registry/v1/shapes/jsonld/trustframework#taxID: Less than 1 values; https://registry.lab.gaia-x.eu/development/api/trusted-shape-registry/v1/shapes/jsonld/trustframework#EUID: Less than 1 values; https://registry.lab.gaia-x.eu/development/api/trusted-shape-registry/v1/shapes/jsonld/trustframework#EORI: Less than 1 values; null: At least one of taxID, vatID, EUID, EORI or leiCode must be defined.;"
        ]
    },
    "error": "Conflict"
}

Compliance Service is complaining about missing registration number of Legal Person. But this is not true. Legal Person is identified by did did:web:gaia-x.cloudandheat.com. Verifiable Presentation contains three Credentials: (1) Signed terms and conditions,(2) valid as legal registration number and (3) country code of legal and headquarter address as well as legal name of legal person. All three credentials having the same credential subject, identified by DID did:web:gaia-x.cloudandheat.com. Hence all mandatory attributes for Legal Person according to Gaia-X Trust Framework are given. But Gaia-X Compliance Service does not recognize this.

However, if you replace DID (did:web:gaia-x.cloudandheat.com) credential subject id of Gaia-X Credential on vatID with credential id https://gaia-x.cloudandheat.com/lrn.json and submit the resulting verifiable Presentation to Compliance Service, your request succeeds.

{
   "@context":"https://www.w3.org/2018/credentials/v1",
   "type":"VerifiablePresentation",
   "verifiableCredential":[
      {
         "@context":[
            "https://www.w3.org/2018/credentials/v1",
            "https://w3id.org/security/suites/jws-2020/v1"
         ],
         "credentialSubject":{
            "@context":"https://registry.lab.gaia-x.eu/development/api/trusted-shape-registry/v1/shapes/jsonld/trustframework#",
            "gx:vatID":"DE281093504",
            "gx:vatID-countryCode":"DE",
            "id":"https://gaia-x.cloudandheat.com/lrn.json",
            "type":"gx:legalRegistrationNumber"
         },
         "evidence":[
            {
               "gx:evidenceOf":"gx:vatID",
               "gx:evidenceURL":"https://ec.europa.eu/taxation_customs/vies/services/checkVatService",
               "gx:executionDate":"2024-06-24T19:56:38.006Z"
            }
         ],
         "id":"https://gaia-x.cloudandheat.com/lrn.json",
         "issuanceDate":"2024-06-24T19:56:38.006Z",
         "issuer":"did:web:registration.lab.gaia-x.eu:v1-staging",
         "proof":{
            "created":"2024-06-24T19:56:38.017Z",
            "jws":"eyJhbGciOiJQUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..lI0ksxCie07Hyhs9sOOiV1EMnl3H_aG2TqnZo-sSzNeG6H36ulUegjNypeEx-zErTCBQfaEm8Pk-BcyR0Z9ScsTEYOn_qYBsPS5qdwoqaBlsJQqkEcLAABb8dLYx0CZdbP0T9Hn6xsSPDnUmHWAGGSOucZbZSbwkl34HmkbKWjhfSG7_RQwQUxbedk2DhiJkPIO_kXosVWVnP3QKjrvdBl24xw6HgeqmvkOIn0kma7gngogl5vaB9e9ideE6eiOX4QDiEyRPUdlAhHvM3Fox5LL0mCi_pNqd9lWjSDmSo5qPh3dsfT6XXJQRe1O2w5l5Gu2KatdinWffn1cDtRpE0A",
            "proofPurpose":"assertionMethod",
            "type":"JsonWebSignature2020",
            "verificationMethod":"did:web:registration.lab.gaia-x.eu:v1-staging#X509-JWK2020"
         },
         "type":[
            "VerifiableCredential"
         ]
      },
      {
         "@context":[
            "https://www.w3.org/2018/credentials/v1",
            "https://w3id.org/security/suites/jws-2020/v1",
            "https://registry.lab.gaia-x.eu/development/api/trusted-shape-registry/v1/shapes/jsonld/trustframework#"
         ],
         "credentialSubject":{
            "gx:termsAndConditions":"The PARTICIPANT signing the Self-Description agrees as follows:\n- to update its descriptions about any changes, be it technical, organizational, or legal - especially but not limited to contractual in regards to the indicated attributes present in the descriptions.\n\nThe keypair used to sign Verifiable Credentials will be revoked where Gaia-X Association becomes aware of any inaccurate statements in regards to the claims which result in a non-compliance with the Trust Framework and policy rules defined in the Policy Rules and Labelling Document (PRLD).",
            "id":"did:web:gaia-x.cloudandheat.com",
            "type":"gx:GaiaXTermsAndConditions"
         },
         "id":"https://gaia-x.cloudandheat.com/tandc.json",
         "issuanceDate":"2024-06-24T19:56:35.083545+00:00",
         "issuer":"did:web:gaia-x.cloudandheat.com",
         "proof":{
            "jws":"eyJhbGciOiJQUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..U3PQiW-xcjK9UdvEtDlrCkqSrGsfrOsxCTAedHkhqI-hA3GxcrBq9JSEk-fT3kfXbxFLTnkSNBMdsC-5gVFNkGWh0XhMD90NbNnTn-6HnS0BoCf0BaPiZQP2S4NgY5nzfYjfKcM_F4ouht2rIYJNd-ouUBCKT7CErqdANa8MjNoZNNvCkBo_jE6swroXRM0lDLwuA5kLcXVuxnND8Y7fw5npx5h1Xqx1zfODy-mQ2eMySmZlMAkTCBi_876F-Xdsto60U1Od5jnxP9HSJjU230cKukdNanVvuuK5JGDLit4NTjRymAcddagNF5duzW_yEc9zehI977I6Suwiq1tXvNVPI54xb6CYS3v9xROxM70F28GG6KERwhbj0HdiQAoVPeiis27ekVOVxVKIs4x4LemMyDBR36QVBUb-hTLrkiu2-UynhE75WDScl7c0q1lpoyjTZqxQqOQreW7mKmvwf02fPULNl2NjJquWPHOzg4FCWj4T5yEm7hi7h2xzdS6pIx188ePH0YpBBh5kNzCyJ3Tii6_fcHXhg8uSSMGLJWtLbDZLclf0zdJne4fSMqi4zgKzvD3NjpEiXNOxXOkWvR9Efw-7Wl8zgzunqINplDP80vkGCiT33gOTetoA6iMzu8FIkfrnlHp8dTC_lTZEfyNt5Rzkq2sRmDakNI3SVUY",
            "proofPurpose":"assertionMethod",
            "type":"JsonWebSignature2020",
            "verificationMethod":"did:web:gaia-x.cloudandheat.com#JWK2020-X509-2"
         },
         "type":"VerifiableCredential"
      },
      {
         "@context":[
            "https://www.w3.org/2018/credentials/v1",
            "https://w3id.org/security/suites/jws-2020/v1",
            "https://registry.lab.gaia-x.eu/development/api/trusted-shape-registry/v1/shapes/jsonld/trustframework#"
         ],
         "credentialSubject":{
            "gx:headquarterAddress":{
               "gx:countrySubdivisionCode":"DE-SN"
            },
            "gx:legalAddress":{
               "gx:countrySubdivisionCode":"DE-SN"
            },
            "gx:legalName":"Cloud\u0026Heat Technologies GmbH",
            "gx:legalRegistrationNumber":{
               "id":"https://gaia-x.cloudandheat.com/lrn.json"
            },
            "id":"did:web:gaia-x.cloudandheat.com",
            "type":"gx:LegalParticipant"
         },
         "id":"https://gaia-x.cloudandheat.com/legal_person.json",
         "issuanceDate":"2024-06-24T19:56:38.130480+00:00",
         "issuer":"did:web:gaia-x.cloudandheat.com",
         "proof":{
            "jws":"eyJhbGciOiJQUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..U5uEoVY-9Nnc8DIvFH1m5shQuGv7eCY33lJvnbzDKL8RHVAZTksov6Skgv0UXsGXJqh_5joabY1O3Vn3mPQLt3JSogQC9jDjT2DZboiVmkvRyu4nWhKHM9WzwsbzZEAxZeJQxtpIdCWAhXB4zluSkKgvf8BFtndPvK_rWf255QeAozI4FEhE8ZTWnGjSR4JVfSWumc29v9qcuETEvbXVJ0KPyf9QbRGYXj_GCZZj78mtrSyardj32FX6BhOWvEiVQltEsqVMmyZiPmq13tKT1HCCOhUkkxzS-l_-_ZUzrBLTb-KdV5mFm8fWWjt6wPMor9SJnyAG7w9lahKS-hZy0QJxDjjQRc2VDMZnVC27zbO0ecqTajU4FTyaYPFCO24tGsGCvL7Qv2GE51mWqzc7xMFmTPJPjEOM-5KeKQH8u65Nw7YC8nc5okk1o_og1UUbWH-K6sb4z8lRa43y8mtrTOW24r0u0ynNIJt6xKws7N1kKCHKZulpuevnrEIgIv4VzTx6VZ_xLbby_TxS5qu9Ztj4YfZnNxHnxgVgNyPSJPRp8a_kYmUfGTCbJ66-P2490xV1s46Tdq1lfT1oWE8D-Hzl2EDXgAy1tp1goyYx-ng3XRKshiPUK_LpDOBHAn6aIHWiiTifOlj25ZMRPZLFkTieYGxmekH0kvxDnkOZ3F0",
            "proofPurpose":"assertionMethod",
            "type":"JsonWebSignature2020",
            "verificationMethod":"did:web:gaia-x.cloudandheat.com#JWK2020-X509-2"
         },
         "type":"VerifiableCredential"
      }
   ]
}

Answer from Gaia-X Compliance Service

{
    "@context": [
        "https://www.w3.org/2018/credentials/v1",
        "https://w3id.org/security/suites/jws-2020/v1",
        "https://registry.lab.gaia-x.eu/development/api/trusted-shape-registry/v1/shapes/jsonld/trustframework#"
    ],
    "type": [
        "VerifiableCredential"
    ],
    "id": "http://bakeup.io/compliance.json",
    "issuer": "did:web:compliance.lab.gaia-x.eu:v1-staging",
    "issuanceDate": "2024-06-24T19:56:44.904Z",
    "expirationDate": "2024-09-22T19:56:44.904Z",
    "credentialSubject": [
        {
            "type": "gx:compliance",
            "id": "https://gaia-x.cloudandheat.com/lrn.json",
            "gx:integrity": "sha256-630f22d373de9b257ab6da0d7a8eadc4aad662d0791c432da89bee8c5480472f",
            "gx:integrityNormalization": "RFC8785:JCS",
            "gx:version": "22.10",
            "gx:type": "gx:legalRegistrationNumber"
        },
        {
            "type": "gx:compliance",
            "id": "https://gaia-x.cloudandheat.com/tandc.json",
            "gx:integrity": "sha256-2c0dd4971171bd8abfb4b56c4783ff9a78b0a7388f9e7e6748306fbf7c91451d",
            "gx:integrityNormalization": "RFC8785:JCS",
            "gx:version": "22.10",
            "gx:type": "gx:GaiaXTermsAndConditions"
        },
        {
            "type": "gx:compliance",
            "id": "https://gaia-x.cloudandheat.com/legal_person.json",
            "gx:integrity": "sha256-c8cd4d741be3f3e61b352c86c58c37909b09f00d05ad1de97d24d48bfa659b5e",
            "gx:integrityNormalization": "RFC8785:JCS",
            "gx:version": "22.10",
            "gx:type": "gx:LegalParticipant"
        }
    ],
    "proof": {
        "type": "JsonWebSignature2020",
        "created": "2024-06-24T19:56:45.339Z",
        "proofPurpose": "assertionMethod",
        "verificationMethod": "did:web:compliance.lab.gaia-x.eu:v1-staging#X509-JWK2020",
        "jws": "eyJhbGciOiJQUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..P5jv_u8Hd6qjLCnxqhVRxZOFsCmaVEK3jo9zSV8_C3jmQJqDAlxr2nRJmyKJA-2ueWbWM_cRpuEX2NrflD9HRcn5S40aTXVm6xE-Po_pUMelwMkrL_GzAkOUKNIm4kOJEKyO6bxq8LU91tAN0hnKasvmyiMPQKFznG3TpqZxPZcCci84XCfNjSe2PfekhUyXeAMb8VAJi7R-eGWOVJo2a3U6wThRkiP_o2mqdRoLKo0_GBTHiKcCpbth_3pAfPMiP0o-xd4erNvy-sfPQBR8WKEZlWNC8FxZN6YNaCtWwca2vF8NFDK9_rXaysKtjdb0oiCFCrWLzEfN_IbFeuB4VQ"
    }
}

The only difference between invalid request and a valid one is the value of credential subject of Gaia-X Credential for vatID. Instead of DID did:web:gaia-x.cloudandheat.com a URL https://gaia-x.cloudandheat.com/lrn.json is used. IMO, this behavior of Compliance Service is not intended. As Gaia-X Credential for vatID and Gaia-X Credential for country code of legal and head quarter address as well as terms and conditions do not relate to the same subject. Former is an attestation for https://gaia-x.cloudandheat.com the later both for did:web:gaia-x.cloudandheat.com. Even Gaia-X Credential Service does not receive a legal registration number of Legal Person identified by did:web:gaia-x.cloudandheat.com, a Gaia-X Credential for a Gaia-X compliant Legal Person is issued.

@markus-hentsch
Copy link
Contributor

markus-hentsch commented Jun 25, 2024

Wrong evaluation of id of credential subject of Gaia-X Credential of VatID

I seem to have made the exact same observation myself: SovereignCloudStack/website#952 (comment)

EDIT: on closer inspection they are separate observations after all.


Your observation in a nutshell (as I understand it):

  • including a LRN VC in a VP where ...
    • ... the credentialSubject.id is a DID reference: does not work
    • ... the credentialSubject.id is a URL to the LRN VC in JSON format: does work
  • hence it seems to be mandatory that credentialSubject.id must be a resolvable VC JSON URL for the LRN VC

So the question is what the intended purpose of the id field is and which values are accepted.

This seems related to the conversation with the Gaia-X devs that I had here: https://gitlab.com/gaia-x/lab/compliance/gaia-x-notary-registrationnumber/-/issues/5#note_1940639707

From the documentation:

I infer two key points from this:

  1. It should not be necessary to choose an id which actually points to the JSON. This is in contrast to the responses I got in the Gaia-X issue linked above.
  2. The id must be unique (per issuer).

I did some more research and testing on this and I think the problem is the following:

  • The gx:LegalParticipant's gx:legalRegistrationNumber field contains an id that MUST match another credential's credentialSubject.id.
  • Your Verifiable Presentation contains three Verifiable Credentials that share the same "did:web:gaia-x.cloudandheat.com" credentialSubject.id. This violates the uniqueness constraint of identifiers as mandated by Gaia-X.
  • My assumption is: During the verification of your VP, the Compliance Service seems to grab the first VC that matches the id stated in your gx:LegalParticipant's gx:legalRegistrationNumber, which only with a chance of 1 out of 3 will be your LRN VC.

It works for me with the following example VP:

{
    "@context": "https://www.w3.org/2018/credentials/v1",
    "type": "VerifiablePresentation",
    "verifiableCredential": [
        {
            // The LRN VC
            "id": "did:does:not:matter",
            "credentialSubject": {
                "gx:vatID": "DE281093504",
                "id": "did:local:my-bogus-id",  // !!!
                "type": "gx:legalRegistrationNumber"
            },
        },
        {
            // The LegalParticipant VC
            "credentialSubject": {
                "gx:legalRegistrationNumber": {
                    "id": "did:local:my-bogus-id"  // !!!
                },
                "id": "https://gaia-x-testing.cloudandheat.com/.well-known/participant.json",
                "type": "gx:LegalParticipant"
            },
            "id": "https://gaia-x-testing.cloudandheat.com/.well-known/participant.json",
        }
    ]
}

(JSON truncated to the relevant bits)

Note that I'm setting an unresolvable bogus DID reference (did:local:my-bogus-id) as the credentialSubject.id of my LRN VC, which is valid as per Gaia-X documentation: "It is up to the issuer to decide if the @id is a resolvable URI or not.".
In gx:legalRegistrationNumber.id of my LegalParticipant VC I'm referencing this exact DID.
Since both VCs are in the same VP document, the ID can be resolved and the LRN VC does not need to be hosted as JSON after all.

Conclusion:

  • Each VC must receive a unique credentialSubject.id. Using the same DID here for every VC is invalid.
  • In contrast to my conversation in the Gaia-X issue, hosting the LRN as JSON and using that as the id is not a strict requirement. As long as the LegalParticipant VC references the id of the LRN VC correctly, the format of the id does not actually matter.

@markus-hentsch
Copy link
Contributor

markus-hentsch commented Jun 25, 2024

@anjastrunk I created some figures about my learnings above for the blog post as these learnings are also relevant and very important to integrate in the post in my opinion.
Maybe they help in understanding the problems you are facing.

The LRN credential and its two different identifiers:

gx-lrn-credential-structure

I have noticed that some examples such as the gaia-x-101 workshop or https://bakeup.io/lrn.json use a trick to specify a URL to the VC for both identifiers while not violating the rule to keep identifiers unique:

{
  "id": "https://bakeup.io/lrn.json",
  "credentialSubject": {
    "id": "https://bakeup.io/lrn.json#cs",
  }
}

They use a non-destructive # anchor, which enables re-use of a URL while keeping identifiers unique. Anchors are simply ignored when retrieving the JSON.

The identifier relation between LRN and Participant credential:

gx-participant-lrn-reference

As already mentioned, my testing confirmed that neither of the identifiers have to be URLs. They can be local identifiers as long as both VCs (LRN, Participant) are part of the same VP. The only requirement is for the subject identifier of the LRN VC to match the legalRegistrationNumber identifier reference in the Participant VC.

@markus-hentsch
Copy link
Contributor

markus-hentsch commented Jul 1, 2024

Obtaining an eIDAS certificate as an organization

Context:

  • Gaia-X states1: "To be compliant with the Gaia-X Trust Framework, all keypairs used to sign claims must have at least one of the Trust Anchors in their certificate chain."
  • Gaia-X Trust Anchors are2:
    • State-issued certificates
    • EU eIDAS certificates
    • EV SSL certificates

The EU provides a dashboard where eIDAS provider can be discovered at https://eidas.ec.europa.eu/efda/tl-browser/#/screen/home

Providers can be searched by offering type and location using https://eidas.ec.europa.eu/efda/tl-browser/#/screen/search/type/1

Relevant offering types are certificates intended either for "electronic signature" (ESig), "electronic seal" (ESeal) or which are defined3 as follows:

An electronic signature [...] which is used by the signatory to sign, where the signatory is a natural person.

An electronic seal is a data [...] where the creator of a seal is a legal person (unlike the electronic signature that is issued by a natural person).

This suggests that as an organization (legal person), the only choice would be ESeal.
However, Gaia-X further states2:

Issuers of Qualified Certificate for Electronic Signature as defined in eIDAS Regulation (EU) No 910/2014

... where eIDAS Regulation (EU) No 910/20144 says:

(58) When a transaction requires a qualified electronic seal from a legal person, a qualified electronic signature from the authorised representative of the legal person should be equally acceptable.

The current implementation of the Gaia-X Registry API5, which is responsible for checking the trust anchors, does not seem to differentiate between ESig and ESeal providers and only aggregates all eligible eIDAS providers from the official trusted lists.

This does indeed seem to confirm that ESig by a natural person representing the organization (legal person) would be sufficient for signing Verifiable Credentials as an organization being a participant of Gaia-X.
(EDIT: confirmed here)

At the time of writing, the Deutsche Telekom Security GmbH currently offers a qualified eIDAS signing certificate (ESig) based on a Smartcard with 2 year validity called "Qualified.ID Light"6 for 83,19€.

eIDAS Qualified Website Authentication Certificate (QWAC)

I created an issue at the Gaia-X repositories asking for clarification on whether both ESig and ESeal are accepted equally and was made aware of a third type: Qualified Website Authentication Certificate (QWAC).

At the time of writing, only 4 German providers are listed on the eIDAS Trust Service Search to offer QWACs and none of the links provided refer to an actual offering.

In contrast to eIDAS ESig and ESeal products I'm unable to find reliable information on any direct offering of QWACs to purchase and whether those are distinct products or a property of or extension to existing certificate types (e.g. SSL).

eIDAS alternative: EV SSL certificates

Besides eIDAS, EV SSL issuers are also accepted trust anchors.
In contrast to the above, EV SSL certificates are more straight-forward but also more expensive.

EV SSL certificates are still SSL certificates at their core so they can also be used for an organization's web presence simultaenously just like any other SSL certificate whereas the eIDAS ESig and ESeal cannot and would be limited to eIDAS use cases only.
They can be purchased by any authorized SSL certificate issuer (registered with Mozilla's database7 as per Gaia-X regulation8) that offers EV certificates.

As such, an EV SSL certificate is much more versatile. Due to the fact that it represents the highest grade of an SSL certificate in terms of trustworthiness, this could also be considered an upgrade to an organization's web presence if used as web SSL certificate in addition to representing the public key of Gaia-X Credentials.

However, this versatility comes at a cost: these certificates usually cost a few hundred Euros per year9, making them notably more expensive than eIDAS certificates on average.

Footnotes

  1. https://docs.gaia-x.eu/policy-rules-committee/trust-framework/22.10/trust_anchors/

  2. https://docs.gaia-x.eu/policy-rules-committee/trust-framework/22.10/trust_anchors/#list-of-defined-trust-anchors 2

  3. https://ec.europa.eu/digital-building-blocks/sites/display/digital/eSignature+FAQ

  4. https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=uriserv:OJ.L_.2014.257.01.0073.01.ENG

  5. https://gitlab.com/gaia-x/lab/compliance/gx-registry/-/blob/de5bd08f5bf6c6c32704c88d694380e2f6eeb076/src/trust-anchor/services/parsers/EiDASTrustedListParser.service.ts

  6. https://www.telesec.de/de/produkte/signaturkarte/ueberblick

  7. https://wiki.mozilla.org/CA/Included_Certificates

  8. https://docs.gaia-x.eu/policy-rules-committee/trust-framework/22.10/trust_anchors/#list-of-defined-trust-anchors

  9. https://www.sslplus.de/produkte/ssl-zertifikate/merkmale/multidomain-ev.html?isDSGVO=0&isEIDAS=0&multiyear=1&MultidomainMax=1&sortby=preis

@anjastrunk
Copy link
Contributor Author

anjastrunk commented Aug 9, 2024

I was finally able to create a Gaia-X compliant credential for an OpenStack cloud. However, as Tagus release does not support VirtualMachineServiceOffering type, we have to submit our credential as ServiceOffering having limited possibilities to express capabilities of VirtuaMachineServiceOffering as offered in Gaia-X Ontology.

In order to still publish detailed properties on OpenStack Cloud, we will publish a separate VC not confirmed by Compliance Service. As soon as Loire is released, we will update generator.

@anjastrunk
Copy link
Contributor Author

anjastrunk commented Aug 14, 2024

Wired usage of 'id' field in credentialSubject

According to W3C standard of Verifiable Credentials, of id field in credentialSubject of Verifiable Credentials defines the subject, a credentials is making claims.

The id property MUST express an identifier that others are expected to use when expressing statements about a specific thing identified by that identifier.

However, Gaia-X uses id field to link credentials with each other, which is wired and undermines trust, as explained in issue #77 and #78.

update from 19.08.24: Gaia-X agree to my trust concerns documented in #78, but does neither propose a solution nor mention a date to fix this. IMO and to the best of my knowledge fixing to undermine trust is a important and emergent issue, which is not rocket science to fix. I will propose my bugfix in #78.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment