diff --git a/app/keys_home/templates/keys_home/base.html b/app/keys_home/templates/keys_home/base.html index cd42963a15cebb82e529ff22046536f4aa80684f..e2fd7b3f7a0751a793d0ceac57c3df1cb5bf2421 100644 --- a/app/keys_home/templates/keys_home/base.html +++ b/app/keys_home/templates/keys_home/base.html @@ -70,7 +70,6 @@ </div> </li> {% endif %} - </li> </ul> </div> </div> diff --git a/app/owlca/templates/owlca/certificate_pickup.html b/app/owlca/templates/owlca/certificate_pickup.html index 7a6b1a72f384e2e965a8bbabdbd44bfdf48b8184..9c75c4331d17ca0d6b4090e9487775d674703fb8 100644 --- a/app/owlca/templates/owlca/certificate_pickup.html +++ b/app/owlca/templates/owlca/certificate_pickup.html @@ -33,16 +33,6 @@ <strong>{{ certificatesigningrequest.verification_text }}</strong> </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 %} <ul class="list-unstyled"> {% for cert in certificatesigningrequest.certificates.all %} @@ -50,12 +40,33 @@ {% endfor %} </ul> +<h3>Create PKCS12 bundle</h3> + <script src="/static/react/runtime.js"></script> <script src="/static/react/vendors.js"></script> <script src="/static/react/main.js"></script> -<h3>Create PKCS12 bundle with helper</h3> -<div id="helper-root">Loading helper...</div> +<div class="row mt-3"> + <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> var certPemElements = document.getElementsByClassName('cert-pem-data') keys.ReactDOM.render( diff --git a/app/owlca/templates/owlca/certificatesignrequest_create.html b/app/owlca/templates/owlca/certificatesignrequest_create.html index e225b5919312da0e33406777bb6e5e1c5bea654d..b2d91433664f6284c9ba68760b896f4fd6288363 100644 --- a/app/owlca/templates/owlca/certificatesignrequest_create.html +++ b/app/owlca/templates/owlca/certificatesignrequest_create.html @@ -24,10 +24,27 @@ <i class="fas fa-stamp"></i> {{ certificationauthority.title }}</a>:</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="col-lg-6"> <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"> <p>Generate a new private key and create a certificate signing request (CSR). For example, execute the following command and enter the @@ -41,7 +58,7 @@ openssl req -new -key key.pem -out csr.pem</pre> <div class="col-lg-6"> <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"> <p>Upload the certificate signing request in PEM format.</p> <form action="" method="post" enctype="multipart/form-data"> @@ -54,19 +71,4 @@ openssl req -new -key key.pem -out csr.pem</pre> </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 %} diff --git a/js/src/components/CreateCsr.jsx b/js/src/components/CreateCsr.jsx index d849a3f619e03f532a0da025419d76c5c40aefdf..8cad4c14472c291c5a76f789596ae7dc775b25df 100644 --- a/js/src/components/CreateCsr.jsx +++ b/js/src/components/CreateCsr.jsx @@ -123,7 +123,11 @@ const CreateCsr = ({privateKey, csrfToken}) => { onChange={e => setOrganizationUnit(e.target.value) || reset()} /> </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 </button> </div> @@ -131,14 +135,14 @@ const CreateCsr = ({privateKey, csrfToken}) => { { downloadUrl && <> <hr /> <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="csrfmiddlewaretoken" value={csrfToken} /> <button className="btn btn-primary m-1" type="submit"> Submit CSR </button> + <a className="btn btn-outline-secondary m-1" download="csr.pem" href={downloadUrl}> + Download CSR + </a> </form> </>} </>) diff --git a/js/src/components/CsrDialog.jsx b/js/src/components/CsrDialog.jsx index ef6662571983f88af18adf46414cfaddcef05f60..b37b0f2cc994047d9ed77eac489e27ba5ef1e1b7 100644 --- a/js/src/components/CsrDialog.jsx +++ b/js/src/components/CsrDialog.jsx @@ -7,6 +7,7 @@ import KeyInfo from "./KeyInfo.jsx" import CreateCsr from "./CreateCsr.jsx" const CsrDialog = ({csrfToken}) => { + const [enabled, setEnabled] = useState(true) const [privateKeyPem, setPrivateKeyPem] = useState(null) const [keys, setKeys] = useState(null) @@ -15,10 +16,18 @@ const CsrDialog = ({csrfToken}) => { setKeys(keys) } + if (!enabled) { + return (<div> + <button className="btn btn-secondary" onClick={() => setEnabled(true)}> + Enable CSR tool + </button> + </div>) + } - return (<> - <div className="card m-2"> - <h3 className="card-header">Private key</h3> + return (<div className="row"> + <div className="col-md-6"> + <div className="card my-2"> + <h5 className="card-header">1. Generate private key</h5> <div className="card-body"> <GeneratePrivateKey onKeyCreated={(keys, pem) => handleKeyCreated(keys, pem)} @@ -28,13 +37,16 @@ const CsrDialog = ({csrfToken}) => { <KeyInfo privateKey={keys && keys.privateKey} privateKeyPem={privateKeyPem} /> </div> </div> - <div className="card m-2"> - <h3 className="card-header">Certificate Signing Request</h3> + </div> + <div className="col-md-6"> + <div className="card my-2"> + <h5 className="card-header">2. Create Certificate Signing Request</h5> <div className="card-body"> <CreateCsr csrfToken={csrfToken} privateKey={keys && keys.privateKey} /> </div> </div> - </>) + </div> + </div>) } export default CsrDialog diff --git a/js/src/components/GeneratePrivateKey.jsx b/js/src/components/GeneratePrivateKey.jsx index 648d4c64932d4dbe549ffd8eac91dff194207f8d..f2a83f1d66bfe651d39523cdab1856e79f13af34 100644 --- a/js/src/components/GeneratePrivateKey.jsx +++ b/js/src/components/GeneratePrivateKey.jsx @@ -8,6 +8,7 @@ const GeneratePrivateKey = ({onKeyCreated}) => { const [keyLength, setKeyLength] = useState(2048) const [password, setPassword] = useState("") const [repeat, setRepeat] = useState("") + const [hasKey, setHasKey] = useState(false) if (!onKeyCreated) { // Default callback @@ -37,6 +38,7 @@ const GeneratePrivateKey = ({onKeyCreated}) => { setPassword("") setRepeat("") onKeyCreated(keys, pem) + setHasKey(true) }) } @@ -69,7 +71,10 @@ const GeneratePrivateKey = ({onKeyCreated}) => { <div className="invalid-feedback">Password don't match</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)} onClick={() => generateKey()}> { inProgress && <> @@ -77,7 +82,7 @@ const GeneratePrivateKey = ({onKeyCreated}) => { role="status" aria-hidden="true"></span> {' '} </> } - Generate private key + Generate {hasKey && "new" } private key </button> </form> </>) diff --git a/js/src/components/KeyInfo.jsx b/js/src/components/KeyInfo.jsx index 8d2939f671be0eae567d9859628635c200d818cd..2518ccb5db2034cc76205b34bfa3c126df8987b3 100644 --- a/js/src/components/KeyInfo.jsx +++ b/js/src/components/KeyInfo.jsx @@ -33,11 +33,13 @@ const KeyInfo = ({privateKey, privateKeyPem}) => { { fingerprint && <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"> Download private key </a> - } + </>} { (!fingerprint && !downloadUrl) && <p>No private key available</p> } diff --git a/js/src/components/KeyUpload.jsx b/js/src/components/KeyUpload.jsx index e5cab219bb597c69bb0a42f135866c91e8693007..79734d82db60168a1af1d5f75473a1e972ca3ae0 100644 --- a/js/src/components/KeyUpload.jsx +++ b/js/src/components/KeyUpload.jsx @@ -6,6 +6,7 @@ import classNames from "classnames" const KeyUpload = ({onKeyUpload}) => { const [pem, setPem] = useState(null) const [password, setPassword] = useState("") + const [uploaded, setUploaded] = useState(false) if (!onKeyUpload) { onKeyUpload = (key, password) => console.log(key) @@ -24,12 +25,13 @@ const KeyUpload = ({onKeyUpload}) => { privateKey = pki.privateKeyFromPem(pem) } onKeyUpload(privateKey, password) + setUploaded(true) } return (<form onSubmit={e => {e.preventDefault(); upload()}}> <div className="form-group"> <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> </label> <input className="form-control-file" id="file-upload" type="file" @@ -41,7 +43,10 @@ const KeyUpload = ({onKeyUpload}) => { value={password} onChange={e => setPassword(e.target.value)} /> </div> - <button type="button" className="btn btn-primary" + <button type="button" + className={classNames("btn", + {"btn-primary": !uploaded}, + {"btn-outline-primary": uploaded})} disabled={!pem} onClick={() => upload()}> Import diff --git a/js/src/components/P12Dialog.jsx b/js/src/components/P12Dialog.jsx index beeb10927f23cc5fa300772de4dcb6b1566c3a8a..4fd4df42b7e852498abd9e654152ced6ebc9677e 100644 --- a/js/src/components/P12Dialog.jsx +++ b/js/src/components/P12Dialog.jsx @@ -10,17 +10,26 @@ const P12Dialog = ({csrfToken, certPemElements}) => { const [privateKey, setPrivateKey] = useState(null) const [password, setPassword] = useState(null) + const [enabled, setEnabled] = useState(true) const handleKeyUpload = (key, password) => { setPrivateKey(key) 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) return (<> - <div className="card m-2"> - <h3 className="card-header">Create PKCS 12</h3> + <div className="card my-2"> + <h5 className="card-header">Create PKCS12</h5> <div className="card-body"> <KeyUpload onKeyUpload={(key, password) => handleKeyUpload(key, password)}