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

[INJICERT-642] add dev & integrator docs #145

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion docker-compose/docker-compose-injistack/README.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,31 @@
# Inji Stack Setup

This guide provides instructions for setting up and running Inji Stack.
This guide provides instructions for setting up and running Inji Stack for a custom use-case based on an existing configured foundational ID system.
An example for this could be a use-case where Authentication is performed from a pre-existing registry such as a **National ID** being put to use to deliver services such as **Farmer Identity Card** to eligible farmers.

## Prerequisites

- Docker and Docker Compose installed on your system
- Git (to clone the repository)
- Basic understanding of Docker and container operations
- Relevant Postman collections are [here](../../docs/postman-collections/), please add the `mock` ones and install the [pmlib library](https://joolfe.github.io/postman-util-lib/) as per the rules given under the heading `Postman Collection`.

### Building inji-web-proxy
Before running the docker-compose, you need to build the inji-web-proxy image:

```bash
# Clone the repository
git clone https://github.com/mosip/inji-web.git -b release-0.11.x
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please check the status on this with Inji web team, since they may move away from using the proxy project to nginx config and this step might not work
https://mosip.atlassian.net/browse/INJIWEB-1213

cd inji-web/inji-web-proxy
```
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

password config update step is missing for using the oidckeystore.p12 file of collab.
mosip.oidc.p12.password=


```bash
vharsh marked this conversation as resolved.
Show resolved Hide resolved
# Build the Docker image
docker build -t inji-web-proxy:local .
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use case in the docker compose is Farmer, but the mimoto config file below calls out as Mock which need to be changed correctly.
docker-compose/docker-compose-injistack/config/mimoto-issuers-config.json

```

## Directory Structure Setup

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Ensure all configuration files are properly updated in the config directory" - this is very level for someone to just try out a usecase using docker componse.
Can we give specific configuration they have to change ?
Like the mosip.certify.issuer.uri and mosip.certify.issuer.pub.key for example, since this needs to be changed once the did document setup is done.

Create the following directory structure before proceeding:

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change the below property file, since we don't want to point default to hitesh github file for csv changes.
mosip.certify.plugin.csv.file.uri=/home/mosip/config/farmer_identity_data.csv

Copy link
Member

@vishwa-vyom vishwa-vyom Dec 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Below changes in docker compose are required for this.
volumes:
- ./config/certify-default.properties:/home/mosip/config/certify-default.properties
- ./config/certify-mock-identity.properties:/home/mosip/config/certify-mock-identity.properties
- ./config/farmer_identity_data.csv:/home/mosip/config/farmer_identity_data.csv
- ./data/CERTIFY_PKCS12:/home/mosip/CERTIFY_PKCS12
- ./loader_path/certify/:/home/mosip/additional_jars/

```
Copy link
Member

@vishwa-vyom vishwa-vyom Dec 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have a CSV file along with photo, so the VC is created with photo
This means the context file, template all should have the photo also.
The default context file and did keys use the mosip-config github pages onces

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ mosip.certify.key-values={\
'format': 'ldp_vc',\
'scope' : 'farmer_vc_ldp',\
'cryptographic_binding_methods_supported': {'did:jwk'},\
'cryptographic_suites_supported': {'RsaSignature2018'},\
'cryptographic_suites_supported': {'Ed25519Signature2020'},\
'proof_types_supported': {'jwt'},\
'credential_definition': {\
'type': {'VerifiableCredential','FarmerCredential'},\
Expand Down Expand Up @@ -75,7 +75,7 @@ mosip.certify.key-values={\
'format': 'ldp_vc',\
'scope' : 'mock_identity_vc_ldp',\
vharsh marked this conversation as resolved.
Show resolved Hide resolved
'cryptographic_binding_methods_supported': {'did:jwk'},\
'credential_signing_alg_values_supported': {'RsaSignature2018'},\
'credential_signing_alg_values_supported': {'Ed25519Signature2020'},\
'proof_types_supported': {'jwt': {'proof_signing_alg_values_supported': {'RS256', 'PS256'}}},\
'credential_definition': {\
'type': {'VerifiableCredential','FarmerCredential'},\
Expand Down
131 changes: 131 additions & 0 deletions docs/Hosting-DID-Document.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# Creating a DID Document
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see link in the docker compose readme file to this document

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a link to this file from the docker compose README.md


A DID document is used to prove the existence and control of a DID(Decentralized Identifier). Besides other important functions it helps expose the public keys in a standards compliant way leading to interoperability. Thus, a Signed Document such as a Verifiable Credential can be assured of coming from the same source and without any unwanted tampering.

# Steps

The below steps have been written with for hosting a Ed25519VerificationKey2020 in a DID format. For other VerificationKeys please look through their specs and contribute back to this document.

**Pre-requisites**: A running Certify setup with an initialized DataProviderPlugin implementation(CertifyIssuer configured) configured and enabled.
Optional: An identity Holder, an identity Verifier

1. Get the Certificate for the KeyPair which was used to sign the Verifiable Credential from Certify. For Ed25519 key the applicationId & referenceId pair are "CERTIFY_MOCK_ED25519" & "ED25519_SIGN"

```bash
# This example demonstrates it for an Ed25519 Key created using github.com/mosip/keymanager
# If you've created a keypair with another applicationId & referenceId, do update it accordingly.

$ curl "https://${CERTIFY_HOST}/v1/certify/system-info/certificate?applicationId=CERTIFY_MOCK_ED25519&referenceId=ED25519_SIGN"
{
"responseTime": "2024-11-28T12:48:36.299Z",
"response": {
"certificate": "-----BEGIN CERTIFICATE-----[REDACTED]\n\[REDACTED]\n\[REDACTED]\n-----END CERTIFICATE-----\n",
"certSignRequest": null,
"issuedAt": "2024-11-04T04:12:39.000Z",
"expiryAt": "2026-11-04T04:12:39.000Z",
"timestamp": "2024-11-28T12:48:35.689Z"
},
"errors": []
}
```

2. Place the Certificate in a file and convert it into a public key after it's represented properly in a file.

```bash
$ echo "-----BEGIN CERTIFICATE-----[REDACTED]\n\[REDACTED]\n\[REDACTED]\n-----END CERTIFICATE-----\n" > cert.pem
$ openssl x509 -pubkey -noout -in cert.pem > cert-pub.pem
```

3. Get the public key and use the [multibase.py](../utils/multibase-script/multibase.py) and run this script.

```bash
# NOTE: You may have to do this in your own python virtual environment and install requisite dependencies
# Please refer to your Python Virtual Env Manager's documentation for this to get started.

$ python3 multibase.py cert-pub.pem
Loaded PEM public key from: cert-pub.pem
Multibase: [REDACTED]
Raw key length: 32
Raw key hex: [REDACTED]
Original key length: 32
Original key hex: [REDACTED]
Keys match: True
```

4. Copy the Multibase value and create a DID document.

```bash
# this example demonstrates using the an example DID hosted on https://example.github.io/DID/acme
$ cat did.json
{
"@context": [
"https://www.w3.org/ns/did/v1"
],
"id": "did:web:vharsh.github.io:DID:harsh",
"alsoKnownAs": [
"[email protected]"
],
"service": [],
"verificationMethod": [
{
"id": "did:web:example.github.io:DID:acme#key-0",
"type": "Ed25519VerificationKey2020",
"@context": "https://w3id.org/security/suites/ed25519-2020/v1",
"controller": "did:web:example.github.io:DID:acme",
"publicKeyMultibase": "[REDACTED]"

}
],
"authentication": [
"did:web:example.github.io:DID:acme#key-0"
],
"assertionMethod": [
"did:web:example.github.io:DID:acme#key-0"
]
}
$
```

5. Host the DID document on the matching HTTPS domain and verify if the `did.json` document is hosted correctly via a DID Resolver such as [Uniresolver](https://dev.uniresolver.io/).

```bash
$ curl https://example.github.io/DID/acme
{
"@context": [
"https://www.w3.org/ns/did/v1"
],
"id": "did:web:example.github.io:DID:acme",
"alsoKnownAs": [
"[email protected]"
],
"service": [],
"verificationMethod": [
{
"id": "did:web:example.github.io:DID:acme#key-0",
"type": "Ed25519VerificationKey2020",
"@context": "https://w3id.org/security/suites/ed25519-2020/v1",
"controller": "did:web:example.github.io:DID:acme",
"publicKeyMultibase": "[REDACTED]"
}
],
"authentication": [
"did:web:example.github.io:DID:acme#key-0"
],
"assertionMethod": [
"did:web:example.github.io:DID:acme#key-0"
]
}
```

# Specifications(for Further reading & better understanding)

For a more detailed understanding please go through the below specs

- [Ed25519Signature2020Algorithm Spec](https://www.w3.org/community/reports/credentials/CG-FINAL-di-eddsa-2020-20220724/#ed25519verificationkey2020)
- [did:web spec](https://w3c-ccg.github.io/did-method-web/)
- [DID Core spec](https://www.w3.org/TR/did-core/)


# Contributing

Please feel free to raise PRs to improve the docs or raise issues on the [MOSIP Community](https://community.mosip.io/) if you have doubts.
25 changes: 25 additions & 0 deletions docs/Local-Development.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Local Development of Inji Certify

**Pre-requisites**: Java 21 installed, Postgres DB installed & configured

1. Clone the repo, usually the active development happens on the `develop` branch but one can try out one of the `release-x.y.z` branches as well.
2. Run the DB init scripts present in `db_scripts/mosip_certify` , running `deploy.sh deploy.properties` is a good way to init the DB.
3. Decide on the issuance mode of Certify. Some plugins enable Certify to operate as a Proxy and others enable it to work as an Issuer, configure `mosip.certify.issuer` appropriately as `PluginIssuer` or `CertifyIssuer`.
* If you don't have another Issuance module such as Sunbird, MOSIP Stack, you may want to set it up with CertifyIssuer and configure `mosip.certify.issuer.vc-sign-algo`, `mosip.certify.issuer.pub.key`, `mosip.certify.issuer.uri` appropriately.
4. Decide on the VCI plugin for use locally and configure it, while running locally from an IDE such as Eclipse or IntelliJ one needs to add configuration to the `application-local.properties` and add the VCI plugin dependency to the pom.xml of Certify Service.
5. Get an eSignet 1.4.1 setup running configured with the appropriate Authenticator plugin implementation matching the VCI plugin.
* Configure `mosip.certify.authorization.url` to point to your Authorization service, this could be a working eSignet instance or another AuthZ provider configured with an [Authenticator plugin implementation](https://docs.esignet.io/integration/authenticator)
* Update the well known configuration in `mosip.certify.key-values` to match the Credential type, scope and other fields to match your VerifiableCredential.
* Appropriately configure the `mosip.certify.authn.allowed-audiences` to allowed audiences such that it matches with the AuthZ token when the Credential issue request is made to Certify.
6. Perform Authentication & VC Issuance to see if the Certify & AuthZ stack is working apprpriately. Look out for the Postman collections referred to in the main README.md of this project.


## Locally setting up CSV Plugin


The above README can be used to setup the [CSV Plugin](https://github.com/mosip/digital-credential-plugins/tree/develop/mock-certify-plugin) and it'll help showcase how one can setup a custom authored plugin for local testing.

Pre-requisites:

* a working Authorization service which gives an identifiable information in the end-user's ID in the `sub` field
* pre-populated CSV file configured with the matching identities to be authenticated against
8 changes: 8 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Developer READMEs

- [Local Development](./Local-Development.md)
- [How to decide b/w VCIssuance & DataProviderPlugin while writing your own](./VCIssuance-vs-DataProvider.md)

# Integrator READMEs

- [How to host a DID document](./Hosting-DID-Document.md)
28 changes: 28 additions & 0 deletions docs/VCIssuance-vs-DataProvider.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# VCIssuance vs DataProvider


Certify has a plugin model to issue VCs so that implementors can extend Inji Certify to issue various VCs and and the plugin can be of two types.

1. VCIssuancePlugin
2. DataProviderPlugin

While they are two types of Issuing Plugins, both types are issuing VCs by connecting to a configured datasource. The two types of Plugins enable Inji Certify to operate differ in where VC Signing happens.

## How to choose to implement either one?

- An integrator can choose to implement VCIssuancePlugin interface if they want to implement the VC Signing by themselves. This gives more power to the VC Plugin authors in choosing to support their own formats, signing algorithms which may or may not be supported by Certify.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has to come out little differently, we have to say if the implementor already has a VC generation system in place and wishes to use that and use the certify only for delivering the VC in OpenID4VCI way to the holder

Copy link
Member Author

@vharsh vharsh Dec 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing it to...

  • An integrator can choose to implement VCIssuancePlugin interface if they want to implement the VC Signing by themselves or already have a working/existing VC Generation System. This gives more power to the VC Plugin authors in choosing to support their own formats, signing algorithms which may or may not be supported by Certify and only using the Certify for delivering the VC in an OpenID4VCI compatible way to open up to a wider ecosystem of clients which are also compatible with the standard.
  • There may be a case, where an integrator might want Certify to deal with fewer aspects of VCIssuance, in this case the implementors can choose to implement DataProviderPlugin interface and only implement the business logic required to fetch the data based on the claims object.
  • Both plugins can leave some aspects of the configuration to the Certify's configuration provider which can be a bunch of static config files or something such as Spring Config Server.

- There may be a case, where an integrator might want Certify to deal with fewer aspects of VCIssuance or may not trust Certify with their unsigned data payload, in this case the implementors can choose to implement DataProviderPlugin interface and only implement the business logic required to fetch the data based on the claims object.
vharsh marked this conversation as resolved.
Show resolved Hide resolved
- Both plugins can leave some aspects of the configuration to the Certify's configuration provider which can be a bunch of static config files or something such as Spring Config Server.

# Summary

| **Property** | VCIssuancePlugin | DataProviderPlugin |
|-------------------|------------------------------------|--------------------|
| VC Signing | managed by the plugin itself | done by Inji Certify itself |
| Credential Creation | done by the plugin itself | done by plugin itself |
| Signing key management | can be done by plugin or delegated to keymanager lib | done by Inji Certify end-to-end via keymanager |
| VC Issuance | done by the plugin completely | data is given by the plugin, VC issuance is done by Inji Certify |
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this table to be little more simplified. what is the VC signing vs credential creation ?
Also, is it not implicitly understood that whoever is doing the VC signing will maintain the signing keys ?


# Doubts?

If you've further questions, do not hesitate to ask a question about the same in [MOSIP Community](cd )
101 changes: 101 additions & 0 deletions multibase-script/multibase.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import os
vharsh marked this conversation as resolved.
Show resolved Hide resolved
import argparse
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import ed25519
import base58

def pem_to_eddsa_multibase(pem_str, prefix='z'):
"""
Convert PEM to EdDSA and then to multibase
"""
try:
# Load PEM public key
public_key = serialization.load_pem_public_key(pem_str.encode())

# Convert to EdDSA raw bytes
raw_bytes = public_key.public_bytes(
encoding=serialization.Encoding.Raw,
format=serialization.PublicFormat.Raw
)

# Prepend multicodec prefix for Ed25519 (0xed01)
multicodec_bytes = bytes.fromhex('ed01')
final_bytes = multicodec_bytes + raw_bytes

# Convert to base58btc with prefix
return prefix + base58.b58encode(final_bytes).decode('utf-8')
except Exception as e:
print(f"Error converting to multibase: {str(e)}")
raise

def multibase_to_eddsa_key(multibase_str):
"""
Convert multibase back to EdDSA public key
"""
try:
# Remove prefix
base58_str = multibase_str[1:]

# Decode base58 to raw bytes
decoded = base58.b58decode(base58_str)

# Remove multicodec prefix (first 2 bytes)
raw_bytes = decoded[2:]

# Create EdDSA public key object
return ed25519.Ed25519PublicKey.from_public_bytes(raw_bytes)
except Exception as e:
print(f"Error converting from multibase: {str(e)}")
raise

def load_pem_file(file_path):
"""
Load the PEM file containing the public key
"""
if not os.path.isfile(file_path):
raise FileNotFoundError(f"The file {file_path} does not exist.")

with open(file_path, 'r') as pem_file:
return pem_file.read()

def main():
# Parse command-line arguments
parser = argparse.ArgumentParser(description="Convert a PEM public key to and from multibase encoding")
parser.add_argument("pem_file", help="Path to the PEM file containing the public key")
args = parser.parse_args()

# Load the public key from the PEM file
try:
pem_str = load_pem_file(args.pem_file)
print(f"Loaded PEM public key from: {args.pem_file}")

# Convert to multibase
multibase = pem_to_eddsa_multibase(pem_str)
print("Multibase:", multibase)

# Convert back and verify length
recovered_key = multibase_to_eddsa_key(multibase)
raw_bytes = recovered_key.public_bytes(
encoding=serialization.Encoding.Raw,
format=serialization.PublicFormat.Raw
)
print("Raw key length:", len(raw_bytes)) # Should be 32 bytes
print("Raw key hex:", raw_bytes.hex())

# Verify the original key bytes
original_key = serialization.load_pem_public_key(pem_str.encode())
original_bytes = original_key.public_bytes(
encoding=serialization.Encoding.Raw,
format=serialization.PublicFormat.Raw
)
print("Original key length:", len(original_bytes)) # Should be 32 bytes
print("Original key hex:", original_bytes.hex())

# Verify they match
print("Keys match:", raw_bytes == original_bytes)

except Exception as e:
print(f"Error: {str(e)}")

if __name__ == "__main__":
main()
21 changes: 21 additions & 0 deletions utils/multibase-script/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Setup instructions

**Pre-requisites**:

* Python interpreter
* Virutal Environment manager
* Required dependencies installed


To use this script:

Run

```bash
$ ls
multibase.py public-key.pem
$ python3 multibase.py public-key.pem
...
```

Detailed instructions available at [docs](../../docs/Hosting-DID-Document.md) for hosting an Ed25519VerificationKey2020.
Loading
Loading