From 23d73c2505cd59456d003f01ed74db19e1418043 Mon Sep 17 00:00:00 2001 From: Gianmarco Pettinato Date: Fri, 29 Oct 2021 18:58:58 +0200 Subject: [PATCH] update certificate management --- .../CertificateDownloader.ts | 60 ++- src/Services/dgcVerifier/Verifier.ts | 35 +- src/Services/dgcVerifier/valueSets.json | 389 ++++++++++++++++++ src/api/green/controller.ts | 1 + src/app.ts | 2 +- tsconfig.json | 2 +- 6 files changed, 447 insertions(+), 42 deletions(-) create mode 100644 src/Services/dgcVerifier/valueSets.json diff --git a/src/Services/SettingsDownloader/CertificateDownloader.ts b/src/Services/SettingsDownloader/CertificateDownloader.ts index 7d37e7a..a6b8dd1 100644 --- a/src/Services/SettingsDownloader/CertificateDownloader.ts +++ b/src/Services/SettingsDownloader/CertificateDownloader.ts @@ -7,14 +7,15 @@ interface CertificateData { serialNumber: string, subject:string, issuer: string, - notBefore: any, - notAfter: any, + notBefore: Date, + notAfter: Date, signatureAlgorithm: any, - fingerprint:any + fingerprint:any, + publicKeyAlgorithm:KeyAlgorithm|any, + publicKeyPem:string } export class CertificateDownloader{ - // static instance: CertificateDownloader; private readonly baseUrl = 'https://get.dgc.gov.it'; private readonly updateApi = '/v1/dgc/signercertificate/update' private readonly statusApi = '/v1/dgc/signercertificate/status' @@ -22,10 +23,11 @@ export class CertificateDownloader{ private readonly timeSpan = 86400000; // private readonly timeSpan = 1; // private certificatesCollection:{kid:string,certificate:string}[] = []; - private certificatesCollection: { [key: string]: string; } = {}; + // private certificatesCollection: { [key: string]: CertificateData; } = {}; + private certificatesCollection: Record = {}; private currentValidKids:string[] = []; - public async getCertificates(): Promise<{ [key: string]: string; }> { + public async getCertificates(): Promise> { let data = '{}'; try { const file = await fs.open(this.keyStorage,'r'); @@ -58,7 +60,7 @@ export class CertificateDownloader{ const currentKid:string = response.headers['x-kid']; if(this.currentValidKids.includes(currentKid)){ const cert = {kid:currentKid, certificate: response.data}; - this.certificatesCollection[currentKid] = await parseCertificate(response.data); + this.certificatesCollection[currentKid] = await this.parseCertificate(response.data); } exit = (response.status !== 200); } @@ -75,7 +77,7 @@ export class CertificateDownloader{ this.currentValidKids = await resp.data as string[]; } catch (error) { console.log('could not get keyChild ', error); - }parseCertificate + } } private async parseCertificate(certificate:string):Promise{ @@ -83,13 +85,49 @@ export class CertificateDownloader{ serialNumber: '', subject: 'UNKNOWN', issuer: 'UNKNOWN', - notBefore: '2020-01-01', - notAfter: '2030-01-01', + notBefore: new Date('2020-01-01'), + notAfter: new Date('2030-01-01'), signatureAlgorithm: '', fingerprint: '', - } + publicKeyAlgorithm:'', + publicKeyPem:'' + }; try{ const cert = new X509Certificate(certificate); + const publicInfo = await this.exportKey(cert.publicKey); + result.serialNumber = cert.serialNumber; + result.subject = cert.subject; + result.issuer = cert.issuer; + result.notAfter = cert.notAfter; + result.notBefore = cert.notBefore; + result.signatureAlgorithm = cert.signatureAlgorithm; + result.fingerprint = Buffer.from(await cert.getThumbprint(crypto)).toString('hex'); + result.publicKeyAlgorithm = publicInfo.publicKeyAlgorithm; + result.publicKeyPem = publicInfo.publicKeyPem; + } catch (error) { + console.log('This certificate has returned this error'); + const publicInfo = await this.exportKey(new PublicKey(certificate)); + result.publicKeyAlgorithm = publicInfo.publicKeyAlgorithm; + result.publicKeyPem = publicInfo.publicKeyPem; } + return result; + } + + /** + * @param {PublicKey} pubkey + * @returns {Promise<{ + * publicKeyAlgorithm: AlgorithmIdentifier | RsaHashedImportParams | EcKeyImportParams; + * publicKeyPem: string; + * }>} + */ + async exportKey(publicKey:PublicKey): Promise<{publicKeyAlgorithm:KeyAlgorithm, publicKeyPem:string}> { + const public_key = await publicKey.export(crypto); + const spki = await crypto.subtle.exportKey('spki', public_key); + + // Export the certificate data. + return { + publicKeyAlgorithm: public_key.algorithm, + publicKeyPem: Buffer.from(spki).toString('base64') + }; } } \ No newline at end of file diff --git a/src/Services/dgcVerifier/Verifier.ts b/src/Services/dgcVerifier/Verifier.ts index 1dbfb6d..316e17f 100644 --- a/src/Services/dgcVerifier/Verifier.ts +++ b/src/Services/dgcVerifier/Verifier.ts @@ -2,11 +2,8 @@ import { CertificateDownloader } from '../SettingsDownloader/CertificateDownload import { RuleDownloader } from '../SettingsDownloader/RuleDownloader'; import { CheckResult, VaccineVerifier } from './VaccineVerifier'; import {DCC} from 'dcc-utils'; -import jsrsasign from 'jsrsasign'; interface certificateResponse { - signature:{ - valid: boolean - }, + signature:string, valid:CheckResult, info:{ identity:{ @@ -38,37 +35,17 @@ export default class Verifier { } async checkCertificate(certificate:string): Promise{ - let result:certificateResponse = {signature:{valid: false}, valid:{valid:false, message:'nd'}, info:{identity:{fnt:'nd',fn:'nd',gnt:'nd',gn:'nd'},dob:'nd'}}; + let result:certificateResponse = {signature:'unsigned', valid:{valid:false, message:'nd'}, info:{identity:{fnt:'nd',fn:'nd',gnt:'nd',gn:'nd'},dob:'nd'}}; try { const dcc = await DCC.fromRaw(certificate); - const signatureValidity = (await this.checkKey(dcc)).valid; + const certificateSigner = await dcc.checkSignatureWithKeysList(await this.certDownloader.getCertificates()); const vaccineVerifier = new VaccineVerifier(await this.ruleDownloader.getRules()); - result = {signature:{valid: signatureValidity}, valid: vaccineVerifier.checkCertifcate(dcc.payload), info:{identity:dcc.payload.nam,dob:dcc.payload.dob}}; + console.log(dcc); + result = {signature:JSON.stringify(certificateSigner), valid: vaccineVerifier.checkCertifcate((dcc as any)._payload), info:{identity:(dcc as any)._payload.nam,dob:(dcc as any)._payload.dob}}; } catch (error) { console.log(error); - result = {signature:{valid: false}, valid:{valid:false, message:'nd'}, info:{identity:{fnt:'nd',fn:'nd',gnt:'nd',gn:'nd'},dob:'nd'}}; + result = {signature:'unsigned', valid:{valid:false, message:'nd'}, info:{identity:{fnt:'nd',fn:'nd',gnt:'nd',gn:'nd'},dob:'nd'}}; } return result; } - - async checkKey(dcc:DCC):Promise<{valid:boolean, key?:string}>{ - const publicCertificateCollection = await this.certDownloader.getCertificates(); - const result = {valid:false, key: ''}; - for(const tupla of publicCertificateCollection){ - try { - const cECDSA = (jsrsasign.KEYUTIL - .getKey('-----BEGIN CERTIFICATE-----\n' + tupla.certificate+ '-----END CERTIFICATE-----') as jsrsasign.KJUR.crypto.ECDSA).getPublicKeyXYHex(); - const signCheckResult = await dcc.checkSignature(cECDSA); - if(signCheckResult){ - result.valid = true; - result.key = tupla.kid; - break; - } - } catch (error) { - if(error.message != 'Signature missmatch') - console.log(error); //to silence the errors - } - } - return result; - } } diff --git a/src/Services/dgcVerifier/valueSets.json b/src/Services/dgcVerifier/valueSets.json new file mode 100644 index 0000000..532581a --- /dev/null +++ b/src/Services/dgcVerifier/valueSets.json @@ -0,0 +1,389 @@ +{ + "country-2-codes": [ + "AD", + "AE", + "AF", + "AG", + "AI", + "AL", + "AM", + "AO", + "AQ", + "AR", + "AS", + "AT", + "AU", + "AW", + "AX", + "AZ", + "BA", + "BB", + "BD", + "BE", + "BF", + "BG", + "BH", + "BI", + "BJ", + "BL", + "BM", + "BN", + "BO", + "BQ", + "BR", + "BS", + "BT", + "BV", + "BW", + "BY", + "BZ", + "CA", + "CC", + "CD", + "CF", + "CG", + "CH", + "CI", + "CK", + "CL", + "CM", + "CN", + "CO", + "CR", + "CU", + "CV", + "CW", + "CX", + "CY", + "CZ", + "DE", + "DJ", + "DK", + "DM", + "DO", + "DZ", + "EC", + "EE", + "EG", + "EH", + "ER", + "ES", + "ET", + "FI", + "FJ", + "FK", + "FM", + "FO", + "FR", + "GA", + "GB", + "GD", + "GE", + "GF", + "GG", + "GH", + "GI", + "GL", + "GM", + "GN", + "GP", + "GQ", + "GR", + "GS", + "GT", + "GU", + "GW", + "GY", + "HK", + "HM", + "HN", + "HR", + "HT", + "HU", + "ID", + "IE", + "IL", + "IM", + "IN", + "IO", + "IQ", + "IR", + "IS", + "IT", + "JE", + "JM", + "JO", + "JP", + "KE", + "KG", + "KH", + "KI", + "KM", + "KN", + "KP", + "KR", + "KW", + "KY", + "KZ", + "LA", + "LB", + "LC", + "LI", + "LK", + "LR", + "LS", + "LT", + "LU", + "LV", + "LY", + "MA", + "MC", + "MD", + "ME", + "MF", + "MG", + "MH", + "MK", + "ML", + "MM", + "MN", + "MO", + "MP", + "MQ", + "MR", + "MS", + "MT", + "MU", + "MV", + "MW", + "MX", + "MY", + "MZ", + "NA", + "NC", + "NE", + "NF", + "NG", + "NI", + "NL", + "NO", + "NP", + "NR", + "NU", + "NZ", + "OM", + "PA", + "PE", + "PF", + "PG", + "PH", + "PK", + "PL", + "PM", + "PN", + "PR", + "PS", + "PT", + "PW", + "PY", + "QA", + "RE", + "RO", + "RS", + "RU", + "RW", + "SA", + "SB", + "SC", + "SD", + "SE", + "SG", + "SH", + "SI", + "SJ", + "SK", + "SL", + "SM", + "SN", + "SO", + "SR", + "SS", + "ST", + "SV", + "SX", + "SY", + "SZ", + "TC", + "TD", + "TF", + "TG", + "TH", + "TJ", + "TK", + "TL", + "TM", + "TN", + "TO", + "TR", + "TT", + "TV", + "TW", + "TZ", + "UA", + "UG", + "UM", + "US", + "UY", + "UZ", + "VA", + "VC", + "VE", + "VG", + "VI", + "VN", + "VU", + "WF", + "WS", + "YE", + "YT", + "ZA", + "ZM", + "ZW" + ], + "disease-agent-targeted": [ + "840539006" + ], + "covid-19-lab-test-manufacturer-and-name": [ + "1065", + "1097", + "1114", + "1144", + "1162", + "1173", + "1180", + "1190", + "1199", + "1201", + "1215", + "1218", + "1223", + "1225", + "1232", + "1236", + "1242", + "1244", + "1253", + "1257", + "1263", + "1266", + "1267", + "1268", + "1271", + "1278", + "1295", + "1296", + "1304", + "1319", + "1331", + "1333", + "1341", + "1343", + "1360", + "1363", + "1365", + "1375", + "1392", + "1420", + "1437", + "1443", + "1456", + "1466", + "1468", + "1481", + "1484", + "1489", + "1490", + "1501", + "1574", + "1604", + "1606", + "1654", + "1736", + "1747", + "1763", + "1764", + "1767", + "1769", + "1815", + "1822", + "1833", + "1844", + "1870", + "1884", + "1906", + "1919", + "1934", + "2010", + "2017", + "2029", + "2074", + "2098", + "2101", + "2103", + "2104", + "2108", + "2109", + "2116", + "2128", + "2130", + "2139", + "2183", + "308", + "344", + "345", + "768" + ], + "covid-19-lab-result": [ + "260373001", + "260415000" + ], + "covid-19-lab-test-type": [ + "LP217198-3", + "LP6464-4" + ], + "vaccines-covid-19-auth-holders": [ + "Bharat-Biotech", + "Gamaleya-Research-Institute", + "ORG-100001417", + "ORG-100001699", + "ORG-100006270", + "ORG-100010771", + "ORG-100013793", + "ORG-100020693", + "ORG-100024420", + "ORG-100030215", + "ORG-100031184", + "ORG-100032020", + "Sinovac-Biotech", + "Vector-Institute" + ], + "vaccines-covid-19-names": [ + "BBIBP-CorV", + "CVnCoV", + "Convidecia", + "CoronaVac", + "Covaxin", + "EU/1/20/1507", + "EU/1/20/1525", + "EU/1/20/1528", + "EU/1/21/1529", + "EpiVacCorona", + "Inactivated-SARS-CoV-2-Vero-Cell", + "Sputnik-V" + ], + "sct-vaccines-covid-19": [ + "1119305005", + "1119349007", + "J07BX03" + ] +} diff --git a/src/api/green/controller.ts b/src/api/green/controller.ts index 52c57d2..330673e 100644 --- a/src/api/green/controller.ts +++ b/src/api/green/controller.ts @@ -7,6 +7,7 @@ export const get = async (req: Request, res: Response):Promise => { const cert = req.body['key']; try { const result = await verifier.checkCertificate(cert); + // const result = ''; res.status(200).send(result); } catch (error) { res.status(200).send({message:'unsigned certificate',error}); diff --git a/src/app.ts b/src/app.ts index f390120..6deccc5 100644 --- a/src/app.ts +++ b/src/app.ts @@ -7,7 +7,7 @@ const app = express(); app.set('port', process.env.PORT || 5050); app.use(cors()); app.use(express.json()); -app.use(express.urlencoded()); +app.use(express.urlencoded({ extended: true })); setupApis(app); export default app; \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 4932e6e..bf78080 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -33,7 +33,7 @@ // "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */ // "types": [], /* Specify type package names to be included without being referenced in a source file. */ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - // "resolveJsonModule": true, /* Enable importing .json files */ + "resolveJsonModule": true, /* Enable importing .json files */ // "noResolve": true, /* Disallow `import`s, `require`s or ``s from expanding the number of files TypeScript should add to a project. */ /* JavaScript Support */