Skip to content
Snippets Groups Projects
Verified Commit fdf79eeb authored by Frank Sauerburger's avatar Frank Sauerburger
Browse files

UX improvement for CSR and P12

parent eb7358bf
No related branches found
No related tags found
1 merge request!16Resolve "In-browser key+cert converter"
Pipeline #7704 passed
...@@ -70,7 +70,6 @@ ...@@ -70,7 +70,6 @@
</div> </div>
</li> </li>
{% endif %} {% endif %}
</li>
</ul> </ul>
</div> </div>
</div> </div>
......
...@@ -33,16 +33,6 @@ ...@@ -33,16 +33,6 @@
<strong>{{ certificatesigningrequest.verification_text }}</strong> <strong>{{ certificatesigningrequest.verification_text }}</strong>
</p> </p>
<div class="alert alert-info">
<p>To use the certificate in your browser,</p>
<ol>
<li>combine your private key with the certificate
<pre>openssl pkcs12 -export -out cert.p12 -inkey key.pem -in cert.pem</pre>
</li>
<li>and import the <code>cert.p12</code> in your browser.</li>
</ol>
</div>
{% if certificatesigningrequest.verification == 10 %} {% if certificatesigningrequest.verification == 10 %}
<ul class="list-unstyled"> <ul class="list-unstyled">
{% for cert in certificatesigningrequest.certificates.all %} {% for cert in certificatesigningrequest.certificates.all %}
...@@ -50,12 +40,33 @@ ...@@ -50,12 +40,33 @@
{% endfor %} {% endfor %}
</ul> </ul>
<h3>Create PKCS12 bundle</h3>
<script src="/static/react/runtime.js"></script> <script src="/static/react/runtime.js"></script>
<script src="/static/react/vendors.js"></script> <script src="/static/react/vendors.js"></script>
<script src="/static/react/main.js"></script> <script src="/static/react/main.js"></script>
<h3>Create PKCS12 bundle with helper</h3> <div class="row mt-3">
<div id="helper-root">Loading helper...</div> <div class="col-lg-6" id="helper-root">
Loading helper..
</div>
<div class="col-lg-6">
<div class="card my-2">
<h5 class="card-header">Create PKCS12 bundle manually</h5>
<div class="card-body">
<p>To use the certificate in your browser,</p>
<ol>
<li>combine your private key with the certificate
<pre>openssl pkcs12 -export -out cert.p12 -inkey key.pem -in cert.pem</pre>
</li>
<li>and import the <code>cert.p12</code> in your browser.</li>
</ol>
</div>
</div>
</div>
</div>
<script> <script>
var certPemElements = document.getElementsByClassName('cert-pem-data') var certPemElements = document.getElementsByClassName('cert-pem-data')
keys.ReactDOM.render( keys.ReactDOM.render(
......
...@@ -24,10 +24,27 @@ ...@@ -24,10 +24,27 @@
<i class="fas fa-stamp"></i> {{ certificationauthority.title }}</a>:</p> <i class="fas fa-stamp"></i> {{ certificationauthority.title }}</a>:</p>
<p class="text-muted">{{ certificationauthority.comment }}</p> <p class="text-muted">{{ certificationauthority.comment }}</p>
<script src="/static/react/runtime.js"></script>
<script src="/static/react/vendors.js"></script>
<script src="/static/react/main.js"></script>
<h3 class="mt-3">Option A: Create CSR in your browser</h3>
<div id="helper-root">Loading helper...</div>
<script>
keys.ReactDOM.render(
keys.React.createElement(keys.CsrDialog, {
csrfToken: "{{csrf_token}}"
}),
document.getElementById('helper-root')
);
</script>
<hr />
<h3 class="mt-3">Option B: Create CSR manually</h3>
<div class="row mt-3"> <div class="row mt-3">
<div class="col-lg-6"> <div class="col-lg-6">
<div class="card my-2"> <div class="card my-2">
<h3 class="card-header">1. Create key and CSR</h3> <h5 class="card-header">1. Create key and CSR</h5>
<div class="card-body"> <div class="card-body">
<p>Generate a new private key and create a certificate signing request <p>Generate a new private key and create a certificate signing request
(CSR). For example, execute the following command and enter the (CSR). For example, execute the following command and enter the
...@@ -41,7 +58,7 @@ openssl req -new -key key.pem -out csr.pem</pre> ...@@ -41,7 +58,7 @@ openssl req -new -key key.pem -out csr.pem</pre>
<div class="col-lg-6"> <div class="col-lg-6">
<div class="card my-2"> <div class="card my-2">
<h3 class="card-header">2. Submit CSR</h3> <h5 class="card-header">2. Submit CSR</h5>
<div class="card-body"> <div class="card-body">
<p>Upload the certificate signing request in PEM format.</p> <p>Upload the certificate signing request in PEM format.</p>
<form action="" method="post" enctype="multipart/form-data"> <form action="" method="post" enctype="multipart/form-data">
...@@ -54,19 +71,4 @@ openssl req -new -key key.pem -out csr.pem</pre> ...@@ -54,19 +71,4 @@ openssl req -new -key key.pem -out csr.pem</pre>
</div> </div>
</div> </div>
<script src="/static/react/runtime.js"></script>
<script src="/static/react/vendors.js"></script>
<script src="/static/react/main.js"></script>
<h3>Create CSR with helper</h3>
<div id="helper-root">Loading helper...</div>
<script>
keys.ReactDOM.render(
keys.React.createElement(keys.CsrDialog, {
csrfToken: "{{csrf_token}}"
}),
document.getElementById('helper-root')
);
</script>
{% endblock %} {% endblock %}
...@@ -123,7 +123,11 @@ const CreateCsr = ({privateKey, csrfToken}) => { ...@@ -123,7 +123,11 @@ const CreateCsr = ({privateKey, csrfToken}) => {
onChange={e => setOrganizationUnit(e.target.value) || reset()} /> onChange={e => setOrganizationUnit(e.target.value) || reset()} />
</div> </div>
<div> <div>
<button disabled={!commonName} className="btn btn-primary m-1" type="submit"> <button disabled={!commonName}
className={classNames("btn", "m-1",
{"btn-primary": !downloadUrl},
{"btn-outline-primary": downloadUrl})}
type="submit">
Create CSR Create CSR
</button> </button>
</div> </div>
...@@ -131,14 +135,14 @@ const CreateCsr = ({privateKey, csrfToken}) => { ...@@ -131,14 +135,14 @@ const CreateCsr = ({privateKey, csrfToken}) => {
{ downloadUrl && <> { downloadUrl && <>
<hr /> <hr />
<form action="" method="post"> <form action="" method="post">
<a className="btn btn-primary m-1" download="csr.pem" href={downloadUrl}>
Download CSR
</a>
<input type="hidden" name="csr_pem" value={csrPem} /> <input type="hidden" name="csr_pem" value={csrPem} />
<input type="hidden" name="csrfmiddlewaretoken" value={csrfToken} /> <input type="hidden" name="csrfmiddlewaretoken" value={csrfToken} />
<button className="btn btn-primary m-1" type="submit"> <button className="btn btn-primary m-1" type="submit">
Submit CSR Submit CSR
</button> </button>
<a className="btn btn-outline-secondary m-1" download="csr.pem" href={downloadUrl}>
Download CSR
</a>
</form> </form>
</>} </>}
</>) </>)
......
...@@ -7,6 +7,7 @@ import KeyInfo from "./KeyInfo.jsx" ...@@ -7,6 +7,7 @@ import KeyInfo from "./KeyInfo.jsx"
import CreateCsr from "./CreateCsr.jsx" import CreateCsr from "./CreateCsr.jsx"
const CsrDialog = ({csrfToken}) => { const CsrDialog = ({csrfToken}) => {
const [enabled, setEnabled] = useState(true)
const [privateKeyPem, setPrivateKeyPem] = useState(null) const [privateKeyPem, setPrivateKeyPem] = useState(null)
const [keys, setKeys] = useState(null) const [keys, setKeys] = useState(null)
...@@ -15,10 +16,18 @@ const CsrDialog = ({csrfToken}) => { ...@@ -15,10 +16,18 @@ const CsrDialog = ({csrfToken}) => {
setKeys(keys) setKeys(keys)
} }
if (!enabled) {
return (<div>
<button className="btn btn-secondary" onClick={() => setEnabled(true)}>
Enable CSR tool
</button>
</div>)
}
return (<> return (<div className="row">
<div className="card m-2"> <div className="col-md-6">
<h3 className="card-header">Private key</h3> <div className="card my-2">
<h5 className="card-header">1. Generate private key</h5>
<div className="card-body"> <div className="card-body">
<GeneratePrivateKey <GeneratePrivateKey
onKeyCreated={(keys, pem) => handleKeyCreated(keys, pem)} onKeyCreated={(keys, pem) => handleKeyCreated(keys, pem)}
...@@ -28,13 +37,16 @@ const CsrDialog = ({csrfToken}) => { ...@@ -28,13 +37,16 @@ const CsrDialog = ({csrfToken}) => {
<KeyInfo privateKey={keys && keys.privateKey} privateKeyPem={privateKeyPem} /> <KeyInfo privateKey={keys && keys.privateKey} privateKeyPem={privateKeyPem} />
</div> </div>
</div> </div>
<div className="card m-2"> </div>
<h3 className="card-header">Certificate Signing Request</h3> <div className="col-md-6">
<div className="card my-2">
<h5 className="card-header">2. Create Certificate Signing Request</h5>
<div className="card-body"> <div className="card-body">
<CreateCsr csrfToken={csrfToken} privateKey={keys && keys.privateKey} /> <CreateCsr csrfToken={csrfToken} privateKey={keys && keys.privateKey} />
</div> </div>
</div> </div>
</>) </div>
</div>)
} }
export default CsrDialog export default CsrDialog
...@@ -8,6 +8,7 @@ const GeneratePrivateKey = ({onKeyCreated}) => { ...@@ -8,6 +8,7 @@ const GeneratePrivateKey = ({onKeyCreated}) => {
const [keyLength, setKeyLength] = useState(2048) const [keyLength, setKeyLength] = useState(2048)
const [password, setPassword] = useState("") const [password, setPassword] = useState("")
const [repeat, setRepeat] = useState("") const [repeat, setRepeat] = useState("")
const [hasKey, setHasKey] = useState(false)
if (!onKeyCreated) { if (!onKeyCreated) {
// Default callback // Default callback
...@@ -37,6 +38,7 @@ const GeneratePrivateKey = ({onKeyCreated}) => { ...@@ -37,6 +38,7 @@ const GeneratePrivateKey = ({onKeyCreated}) => {
setPassword("") setPassword("")
setRepeat("") setRepeat("")
onKeyCreated(keys, pem) onKeyCreated(keys, pem)
setHasKey(true)
}) })
} }
...@@ -69,7 +71,10 @@ const GeneratePrivateKey = ({onKeyCreated}) => { ...@@ -69,7 +71,10 @@ const GeneratePrivateKey = ({onKeyCreated}) => {
<div className="invalid-feedback">Password don't match</div> <div className="invalid-feedback">Password don't match</div>
</div> </div>
<button type="button" className="btn btn-primary" <button type="button"
className={classNames("btn",
{"btn-primary": !hasKey},
{"btn-outline-primary": hasKey})}
disabled={inProgress || (password != repeat)} disabled={inProgress || (password != repeat)}
onClick={() => generateKey()}> onClick={() => generateKey()}>
{ inProgress && <> { inProgress && <>
...@@ -77,7 +82,7 @@ const GeneratePrivateKey = ({onKeyCreated}) => { ...@@ -77,7 +82,7 @@ const GeneratePrivateKey = ({onKeyCreated}) => {
role="status" aria-hidden="true"></span> {' '} role="status" aria-hidden="true"></span> {' '}
</> </>
} }
Generate private key Generate {hasKey && "new" } private key
</button> </button>
</form> </form>
</>) </>)
......
...@@ -33,11 +33,13 @@ const KeyInfo = ({privateKey, privateKeyPem}) => { ...@@ -33,11 +33,13 @@ const KeyInfo = ({privateKey, privateKeyPem}) => {
{ fingerprint && { fingerprint &&
<p>Fingerprint: <code>{fingerprint}</code></p> <p>Fingerprint: <code>{fingerprint}</code></p>
} }
{ downloadUrl && { downloadUrl && <>
<p className="alert alert-info">Please keep the private safe and
secure.</p>
<a href={downloadUrl} download="key.pem" className="btn btn-primary"> <a href={downloadUrl} download="key.pem" className="btn btn-primary">
Download private key Download private key
</a> </a>
} </>}
{ (!fingerprint && !downloadUrl) && { (!fingerprint && !downloadUrl) &&
<p>No private key available</p> <p>No private key available</p>
} }
......
...@@ -6,6 +6,7 @@ import classNames from "classnames" ...@@ -6,6 +6,7 @@ import classNames from "classnames"
const KeyUpload = ({onKeyUpload}) => { const KeyUpload = ({onKeyUpload}) => {
const [pem, setPem] = useState(null) const [pem, setPem] = useState(null)
const [password, setPassword] = useState("") const [password, setPassword] = useState("")
const [uploaded, setUploaded] = useState(false)
if (!onKeyUpload) { if (!onKeyUpload) {
onKeyUpload = (key, password) => console.log(key) onKeyUpload = (key, password) => console.log(key)
...@@ -24,12 +25,13 @@ const KeyUpload = ({onKeyUpload}) => { ...@@ -24,12 +25,13 @@ const KeyUpload = ({onKeyUpload}) => {
privateKey = pki.privateKeyFromPem(pem) privateKey = pki.privateKeyFromPem(pem)
} }
onKeyUpload(privateKey, password) onKeyUpload(privateKey, password)
setUploaded(true)
} }
return (<form onSubmit={e => {e.preventDefault(); upload()}}> return (<form onSubmit={e => {e.preventDefault(); upload()}}>
<div className="form-group"> <div className="form-group">
<label htmlFor="file-upload"> <label htmlFor="file-upload">
Private key <small className="text-muted">(PEM format)</small> Private key <small className="text-muted">(PEM format)</small><br />
<small className="text-success">Your private key will not leave your local browser.</small> <small className="text-success">Your private key will not leave your local browser.</small>
</label> </label>
<input className="form-control-file" id="file-upload" type="file" <input className="form-control-file" id="file-upload" type="file"
...@@ -41,7 +43,10 @@ const KeyUpload = ({onKeyUpload}) => { ...@@ -41,7 +43,10 @@ const KeyUpload = ({onKeyUpload}) => {
value={password} value={password}
onChange={e => setPassword(e.target.value)} /> onChange={e => setPassword(e.target.value)} />
</div> </div>
<button type="button" className="btn btn-primary" <button type="button"
className={classNames("btn",
{"btn-primary": !uploaded},
{"btn-outline-primary": uploaded})}
disabled={!pem} disabled={!pem}
onClick={() => upload()}> onClick={() => upload()}>
Import Import
......
...@@ -10,17 +10,26 @@ const P12Dialog = ({csrfToken, certPemElements}) => { ...@@ -10,17 +10,26 @@ const P12Dialog = ({csrfToken, certPemElements}) => {
const [privateKey, setPrivateKey] = useState(null) const [privateKey, setPrivateKey] = useState(null)
const [password, setPassword] = useState(null) const [password, setPassword] = useState(null)
const [enabled, setEnabled] = useState(true)
const handleKeyUpload = (key, password) => { const handleKeyUpload = (key, password) => {
setPrivateKey(key) setPrivateKey(key)
setPassword(password) setPassword(password)
} }
if (!enabled) {
return (<div>
<button className="btn btn-secondary" onClick={() => setEnabled(true)}>
Enable PKCS12 bundle tool
</button>
</div>)
}
const certPems = Array.from(certPemElements).map(x => x.textContent) const certPems = Array.from(certPemElements).map(x => x.textContent)
return (<> return (<>
<div className="card m-2"> <div className="card my-2">
<h3 className="card-header">Create PKCS 12</h3> <h5 className="card-header">Create PKCS12</h5>
<div className="card-body"> <div className="card-body">
<KeyUpload <KeyUpload
onKeyUpload={(key, password) => handleKeyUpload(key, password)} onKeyUpload={(key, password) => handleKeyUpload(key, password)}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment