You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

160 lines
8.8 KiB

<!doctype html>
<head>
<link rel="stylesheet" href="css/style.v1.css">
<link rel="shortcut icon" href="https://www.pathcheck.org/hubfs/Favicon.png">
<title>Signed Vaccine Certificate Generator</title>
</head>
<body>
<div class="center">
<h3>Signed Vaccine Certificate Generator</h3>
<div class="left-div">
<table>
<tr><td class="input-label">Immunization Date</td><td><input id="qr-date" type="text" placeholder=""/></td></tr>
<tr><td class="input-label">Manufacturer</td><td><input id="qr-manuf" type="text" placeholder="Pfizer, Moderna, etc"/></td></tr>
<tr><td class="input-label">Product Name</td><td><input id="qr-product" type="text" placeholder="COVID-19"/></td></tr>
<tr><td class="input-label">Lot#</td><td><input id="qr-lot" type="text" placeholder="012L20A, ..."/></td></tr>
<tr><td class="input-label">Route</td><td><input id="qr-route" type="text" placeholder="Intramuscular, Intranasal, Subcut, Oral, ..."/></td></tr>
<tr><td class="input-label">Site</td><td><input id="qr-site" type="text" placeholder="Right Arm, Left Arm, ..."/></td></tr>
<tr><td class="input-label">Dose</td><td><input id="qr-dose" type="text" placeholder="1.0ml, 0.5ml, ..."/></td></tr>
<tr><td class="input-label">Required Doses</td><td><input id="qr-required_doses" type="text" placeholder="1, 2, 3, ..."/></td></tr>
<tr><td class="input-label">Next Dose (days)</td><td><input id="qr-next_dose_in_days" type="text" placeholder="21, 28, ..."/></td></tr>
<tr><td class="input-label">Vaccinator ID</td><td><input id="qr-vacinatorid" type="text" placeholder="Pharmacy Name, City, ..."/></td></tr>
<tr><td class="input-label">Vaccinee ID</td><td><input id="qr-vaccineeid" type="text" placeholder="Name, Phone Number, ID, Coupon, ..."/></td></tr>
</table>
</div>
<div class="right-div">
<label for="privkey">Private Key <small>(openssl genrsa -out rsa_1024_priv.pem 1024)</small></label><br/>
<textarea id="privkey" rows="16" cols="63">
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDlOJu6TyygqxfWT7eLtGDwajtNFOb9I5XRb6khyfD1Yt3YiCgQ
WMNW649887VGJiGr/L5i2osbl8C9+WJTeucF+S76xFxdU6jE0NQ+Z+zEdhUTooNR
aY5nZiu5PgDB0ED/ZKBUSLKL7eibMxZtMlUDHjm4gwQco1KRMDSmXSMkDwIDAQAB
AoGAfY9LpnuWK5Bs50UVep5c93SJdUi82u7yMx4iHFMc/Z2hfenfYEzu+57fI4fv
xTQ//5DbzRR/XKb8ulNv6+CHyPF31xk7YOBfkGI8qjLoq06V+FyBfDSwL8KbLyeH
m7KUZnLNQbk8yGLzB3iYKkRHlmUanQGaNMIJziWOkN+N9dECQQD0ONYRNZeuM8zd
8XJTSdcIX4a3gy3GGCJxOzv16XHxD03GW6UNLmfPwenKu+cdrQeaqEixrCejXdAF
z/7+BSMpAkEA8EaSOeP5Xr3ZrbiKzi6TGMwHMvC7HdJxaBJbVRfApFrE0/mPwmP5
rN7QwjrMY+0+AbXcm8mRQyQ1+IGEembsdwJBAN6az8Rv7QnD/YBvi52POIlRSSIM
V7SwWvSK4WSMnGb1ZBbhgdg57DXaspcwHsFV7hByQ5BvMtIduHcT14ECfcECQATe
aTgjFnqE/lQ22Rk0eGaYO80cc643BXVGafNfd9fcvwBMnk0iGX0XRsOozVt5Azil
psLBYuApa66NcVHJpCECQQDTjI2AQhFc1yRnCU/YgDnSpJVm1nASoRUnU8Jfm3Oz
uku7JUXcVpt08DFSceCEX9unCuMcT72rAQlLpdZir876
-----END RSA PRIVATE KEY-----
</textarea><br><br>
<label for="pubkey">Link to Public Key <small>(must be visible to any QR Reader app)</small></label><br/>
<textarea id="qr-link" rows="1" cols="63">vitorpamplona.com/vaccine-certificate-qrcode-generator/pub_key</textarea>
<br><br>
<label for="privkey">QR Code Format</label>
<pre><span class='protocol'>Protocol</span>:<span class='crypto-algo'>HashAlgo</span>\<span class='signature'>Signature</span>@<span class='pub-key'>PubKey</span>?<span class='message'>Message</span></pre>
</div>
<div class="full-div">
<br/><br/>
<div style="margin: 0 auto; width:500px;">
<button class="qr-btn center-in-div" onclick="loadDemo()">Load Demo Data</button>
<br/><br/><br/><br/>
<button class="qr-btn center-in-div" onclick="generateQRCode()">Sign Vaccine Certificate</button>
<br/><br/>
<canvas id="qr-code"></canvas><br/>
<pre id="qr-result" class="qr-data"></pre>
<pre id="qr-format" class="invisible"><span class='protocol'>Protocol</span>:<span class='crypto-algo'>HashAlgo</span>\<span class='signature'>Signature</span>@<span class='pub-key'>PubKey</span>?<span class='message'>Message</span></pre>
<p id="qr-verified"></p>
<br/>
<button
class="qr-btn center-in-div"
onclick="window.open('https://github.com/vitorpamplona/vaccine-certificate-tracking-app/releases','_blank')">
Download the Reader App
</button>
</div>
</div>
</div>
<script src="js/qrious.min.js"></script>
<script src="js/jsencrypt.min.js"></script>
<script src="js/sha256.js"></script>
<script>
function e(elem) {
return document.getElementById(elem);
}
function addIfExists(prefix, elemId) {
let value = encodeURIComponent(e(elemId).value);
return value ? prefix+value : "";
}
function verify(pubkey, message, signature, feedback_elem_id) {
// Download pubkey to verify
var client = new XMLHttpRequest();
client.open('GET', "https://" + pubkey);
client.addEventListener("load",
function() {
// Verify with the public key...
var verify = new JSEncrypt();
verify.setPublicKey(this.responseText);
var verified = verify.verify(message, signature, CryptoJS.SHA256);
e(feedback_elem_id).innerHTML = "Verified: " + verified;
}
);
client.send();
}
function generateQRCode() {
var message = "date="+e("qr-date").value;
message += addIfExists("&vaccinee=", "qr-vaccineeid");
message += addIfExists("&vaccinator=", "qr-vacinatorid");
message += addIfExists("&manuf=", "qr-manuf");
message += addIfExists("&name=", "qr-product");
message += addIfExists("&lot=", "qr-lot");
message += addIfExists("&route=", "qr-route");
message += addIfExists("&site=", "qr-site");
message += addIfExists("&dose=", "qr-dose");
message += addIfExists("&required_doses=", "qr-required_doses");
message += addIfExists("&next_dose_in_days=", "qr-next_dose_in_days");
var pubkey = e("qr-link").value.trim().replace("http://","");
var sign = new JSEncrypt();
sign.setPrivateKey(e('privkey').value);
var signature = sign.sign(message, CryptoJS.SHA256, "sha256");
var uri = "healthpass:SHA256\\"+signature+"@"+pubkey+"?"+message;
var qr = new QRious({
element: e('qr-code')
});
qr.set({
foreground: '#3654DD',
size: 500,
value: uri
});
e("qr-result").innerHTML= "<span class='protocol'>healthpass</span>:" +
"<span class='crypto-algo'>SHA256</span>\\" +
"<span class='signature'>" + signature + "</span>" + "@" +
"<span class='pub-key'>" + pubkey + "</span>" + "?" +
"<span class='message'>" + message + "</span>";
e("qr-format").className="visible";
e("qr-verified").innerHTML = "Verified: false";
verify(pubkey, message, signature, "qr-verified");
}
</script>
<script>
// Defaults
document.getElementById("qr-date").value = new Date().toJSON();
function loadDemo() {
e("qr-vacinatorid").value = "CVS Minute Clinic";
e("qr-manuf").value = "Moderna";
e("qr-product").value = "COVID-19";
e("qr-lot").value = "012L20A";
e("qr-route").value = "IM";
e("qr-site").value = "RA";
e("qr-dose").value = ".5ml";
e("qr-required_doses").value = "2";
e("qr-next_dose_in_days").value = "28";
e("qr-vaccineeid").value = "John Doe";
}
</script>
</body>
</html>