Add signer backend

This commit adds a simple go backend calling openssl ca to sign CRS
coming from the client. The JavaScript code in src/index.html has been
extended to send requests to the sign endpoint and display the resulting
certificate in a separate div element.

A script setup_example_ca.sh and an openssl configuration file ca.cnf
has been added to allow quick setup of a simple example CA.
This commit is contained in:
Jan Dittberner 2020-11-30 00:08:05 +01:00
parent c751c51713
commit 5c3f0ea942
6 changed files with 233 additions and 43 deletions

View file

@ -41,7 +41,7 @@
<small id="keySizeHelp" class="form-text text-muted">An RSA key pair will be generated in your
browser. Longer key sizes provide better security but take longer to generate.</small>
</fieldset>
<button type="submit" id="gen-csr-button" class="btn btn-primary">Generate Signing Request</button>
<button type="submit" id="gen-csr-button" class="btn btn-primary">Generate signing request</button>
</form>
</div>
</div>
@ -55,58 +55,89 @@
</div>
<pre id="key"></pre>
<pre id="csr"></pre>
<pre id="crt"></pre>
<button type="button" disabled id="send-button" class="btn btn-default disabled">Send signing request</button>
</div>
<script src="../public/js/jquery.slim.min.js"></script>
<script src="../public/js/forge.all.min.js"></script>
<script src="../public/js/bootstrap.bundle.min.js"></script>
<script>
const keyElement = document.getElementById('key');
document.getElementById('csr-form').onsubmit = function (event) {
const subject = event.target["nameInput"].value;
const keySize = parseInt(event.target["keySize"].value);
if (isNaN(keySize)) {
return false;
}
const spinner = document.getElementById('status-spinner');
const statusText = document.getElementById('status-text');
const statusBlock = document.getElementById('status-block');
statusBlock.classList.remove('d-none');
spinner.classList.remove('d-none');
async function postData(url = '', data = {}) {
const response = await fetch(url, {
method: 'POST',
mode: 'cors',
cache: 'no-cache',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
},
redirect: "error",
referrerPolicy: "no-referrer",
body: JSON.stringify(data),
});
return response.json()
}
const state = forge.pki.rsa.createKeyPairGenerationState(keySize, 0x10001);
statusText.innerHTML = 'started key generation';
const startDate = new Date();
const step = function () {
let duration = (new Date()).getTime() - startDate.getTime();
let seconds = Math.floor(duration / 100) / 10;
if (!forge.pki.rsa.stepKeyPairGenerationState(state, 100)) {
setTimeout(step, 1);
statusText.innerHTML = `key generation running for ${seconds} seconds`;
} else {
statusText.innerHTML = `key generated in ${seconds} seconds`
spinner.classList.add('d-none');
const keys = state.keys;
keyElement.innerHTML = forge.pki.privateKeyToPem(keys.privateKey);
const csr = forge.pki.createCertificationRequest();
document.addEventListener("DOMContentLoaded", function () {
const keyElement = document.getElementById('key');
document.getElementById('csr-form').onsubmit = function (event) {
const subject = event.target["nameInput"].value;
const keySize = parseInt(event.target["keySize"].value);
if (isNaN(keySize)) {
return false;
}
const spinner = document.getElementById('status-spinner');
const statusText = document.getElementById('status-text');
const statusBlock = document.getElementById('status-block');
statusBlock.classList.remove('d-none');
spinner.classList.remove('d-none');
csr.publicKey = keys.publicKey;
csr.setSubject([{
name: 'commonName',
value: subject,
valueTagClass: forge.asn1.Type.UTF8,
}]);
csr.sign(keys.privateKey, forge.md.sha256.create());
const state = forge.pki.rsa.createKeyPairGenerationState(keySize, 0x10001);
statusText.innerHTML = 'started key generation';
const startDate = new Date();
const step = function () {
let duration = (new Date()).getTime() - startDate.getTime();
let seconds = Math.floor(duration / 100) / 10;
if (!forge.pki.rsa.stepKeyPairGenerationState(state, 100)) {
setTimeout(step, 1);
statusText.innerHTML = `key generation running for ${seconds} seconds`;
} else {
statusText.innerHTML = `key generated in ${seconds} seconds`
spinner.classList.add('d-none');
const keys = state.keys;
keyElement.innerHTML = forge.pki.privateKeyToPem(keys.privateKey);
const csr = forge.pki.createCertificationRequest();
const verified = csr.verify();
if (verified) {
document.getElementById("csr").innerHTML = forge.pki.certificationRequestToPem(csr);
csr.publicKey = keys.publicKey;
csr.setSubject([{
name: 'commonName',
value: subject,
valueTagClass: forge.asn1.Type.UTF8,
}]);
csr.sign(keys.privateKey, forge.md.sha256.create());
const verified = csr.verify();
if (verified) {
let csrPem = forge.pki.certificationRequestToPem(csr);
document.getElementById("csr").innerHTML = csrPem;
const sendButton =
document.getElementById("send-button");
sendButton.addEventListener("click", function () {
postData("/sign/", {"csr": csrPem, "commonName": subject})
.then(data => {
console.log(data);
document.getElementById("crt").innerHTML = data["certificate"];
});
})
sendButton.removeAttribute("disabled");
sendButton.classList.remove("disabled");
}
}
}
setTimeout(step);
return false;
};
setTimeout(step);
return false;
};
});
</script>
</body>
</html>
</html>