update certificate management

This commit is contained in:
Gianmarco Pettinato 2021-10-29 18:58:58 +02:00
parent ddd38d2a11
commit 23d73c2505
6 changed files with 447 additions and 42 deletions

View File

@ -7,14 +7,15 @@ interface CertificateData {
serialNumber: string, serialNumber: string,
subject:string, subject:string,
issuer: string, issuer: string,
notBefore: any, notBefore: Date,
notAfter: any, notAfter: Date,
signatureAlgorithm: any, signatureAlgorithm: any,
fingerprint:any fingerprint:any,
publicKeyAlgorithm:KeyAlgorithm|any,
publicKeyPem:string
} }
export class CertificateDownloader{ export class CertificateDownloader{
// static instance: CertificateDownloader;
private readonly baseUrl = 'https://get.dgc.gov.it'; private readonly baseUrl = 'https://get.dgc.gov.it';
private readonly updateApi = '/v1/dgc/signercertificate/update' private readonly updateApi = '/v1/dgc/signercertificate/update'
private readonly statusApi = '/v1/dgc/signercertificate/status' private readonly statusApi = '/v1/dgc/signercertificate/status'
@ -22,10 +23,11 @@ export class CertificateDownloader{
private readonly timeSpan = 86400000; private readonly timeSpan = 86400000;
// private readonly timeSpan = 1; // private readonly timeSpan = 1;
// private certificatesCollection:{kid:string,certificate:string}[] = []; // private certificatesCollection:{kid:string,certificate:string}[] = [];
private certificatesCollection: { [key: string]: string; } = {}; // private certificatesCollection: { [key: string]: CertificateData; } = {};
private certificatesCollection: Record<string, any> = {};
private currentValidKids:string[] = []; private currentValidKids:string[] = [];
public async getCertificates(): Promise<{ [key: string]: string; }> { public async getCertificates(): Promise<Record<string, any>> {
let data = '{}'; let data = '{}';
try { try {
const file = await fs.open(this.keyStorage,'r'); const file = await fs.open(this.keyStorage,'r');
@ -58,7 +60,7 @@ export class CertificateDownloader{
const currentKid:string = response.headers['x-kid']; const currentKid:string = response.headers['x-kid'];
if(this.currentValidKids.includes(currentKid)){ if(this.currentValidKids.includes(currentKid)){
const cert = {kid:currentKid, certificate: response.data}; 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); exit = (response.status !== 200);
} }
@ -75,7 +77,7 @@ export class CertificateDownloader{
this.currentValidKids = await resp.data as string[]; this.currentValidKids = await resp.data as string[];
} catch (error) { } catch (error) {
console.log('could not get keyChild ', error); console.log('could not get keyChild ', error);
}parseCertificate }
} }
private async parseCertificate(certificate:string):Promise<CertificateData>{ private async parseCertificate(certificate:string):Promise<CertificateData>{
@ -83,13 +85,49 @@ export class CertificateDownloader{
serialNumber: '', serialNumber: '',
subject: 'UNKNOWN', subject: 'UNKNOWN',
issuer: 'UNKNOWN', issuer: 'UNKNOWN',
notBefore: '2020-01-01', notBefore: new Date('2020-01-01'),
notAfter: '2030-01-01', notAfter: new Date('2030-01-01'),
signatureAlgorithm: '', signatureAlgorithm: '',
fingerprint: '', fingerprint: '',
} publicKeyAlgorithm:'',
publicKeyPem:''
};
try{ try{
const cert = new X509Certificate(certificate); 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')
};
} }
} }

View File

@ -2,11 +2,8 @@ import { CertificateDownloader } from '../SettingsDownloader/CertificateDownload
import { RuleDownloader } from '../SettingsDownloader/RuleDownloader'; import { RuleDownloader } from '../SettingsDownloader/RuleDownloader';
import { CheckResult, VaccineVerifier } from './VaccineVerifier'; import { CheckResult, VaccineVerifier } from './VaccineVerifier';
import {DCC} from 'dcc-utils'; import {DCC} from 'dcc-utils';
import jsrsasign from 'jsrsasign';
interface certificateResponse { interface certificateResponse {
signature:{ signature:string,
valid: boolean
},
valid:CheckResult, valid:CheckResult,
info:{ info:{
identity:{ identity:{
@ -38,36 +35,16 @@ export default class Verifier {
} }
async checkCertificate(certificate:string): Promise<certificateResponse>{ async checkCertificate(certificate:string): Promise<certificateResponse>{
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 { try {
const dcc = await DCC.fromRaw(certificate); 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()); 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) { } catch (error) {
console.log(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; return result;
} }

View File

@ -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"
]
}

View File

@ -7,6 +7,7 @@ export const get = async (req: Request, res: Response):Promise<void> => {
const cert = req.body['key']; const cert = req.body['key'];
try { try {
const result = await verifier.checkCertificate(cert); const result = await verifier.checkCertificate(cert);
// const result = '';
res.status(200).send(result); res.status(200).send(result);
} catch (error) { } catch (error) {
res.status(200).send({message:'unsigned certificate',error}); res.status(200).send({message:'unsigned certificate',error});

View File

@ -7,7 +7,7 @@ const app = express();
app.set('port', process.env.PORT || 5050); app.set('port', process.env.PORT || 5050);
app.use(cors()); app.use(cors());
app.use(express.json()); app.use(express.json());
app.use(express.urlencoded()); app.use(express.urlencoded({ extended: true }));
setupApis(app); setupApis(app);
export default app; export default app;

View File

@ -33,7 +33,7 @@
// "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */ // "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. */ // "types": [], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ // "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 `<reference>`s from expanding the number of files TypeScript should add to a project. */ // "noResolve": true, /* Disallow `import`s, `require`s or `<reference>`s from expanding the number of files TypeScript should add to a project. */
/* JavaScript Support */ /* JavaScript Support */