Storing Cryptographic Keys in Persistent Browser Storage

Update (March 5, 2025):
This post, and the presentation at ICMC 2017,
show how to use a browser as a credential wallet for same-device
presentation. Section 12.4 in
Chapter 12 of a
book I’m writing with
Sukhi Chuhan and Veronica Wojnas shows how it can also be used for
cross-device presentation, and how a WebView component of a native app
can be combined with a native code component to further support
proximity presentation over BlueTooth or NFC.


This blog post is a companion to a presentation made at the
2017 International Cryptographic Module Conference
and refers to the presentation
slides, revised after the
conference. Karen Lewison is a co-author of the presentation and of
this blog post.

Slide 2: Key storage in web clients

Most Web applications today use TLS, thus relying on cryptography to
provide a secure channel between client and server, and to
authenticate the server to the client by means of a cryptographic
credential
, consisting of a TLS server certificate and its
associated private key. But other uses of cryptography by Web
applications are still rare. Client authentication still relies
primarily on traditional username-and-password, one-time passwords,
proof of possession of a mobile phone, biometrics, or combinations of
two or more of such authentication factors. Web payments still rely
on a credit card number being considered a secret. Encrypted
messaging is on the rise, but is not Web-based.

A major obstacle to broader use of cryptography by Web applications is
the problem of where to store cryptographic keys on the client side.

Smartcards provide strong security for the storage of cryptographic
keys, but most Web app developers cannot ask their users to use
smartcards and card readers. Windows machines have Trusted Platform
Modules (TPM), which can be used to implement “virtual smart
cards”, obviating the need for physical smart cards and readers;
but most developers cannot require their users to use Windows
machines. A few years ago Microsoft introduced Windows CardSpace,
which allowed users to authenticate with “InfoCards” containing various kinds of credentials,
including traditional certified key pairs consisting of a public key
certificate and its associated private key, and U-Prove tokens that
provided selective disclosure of attributes and issue-show unlinkability; but CardSpace was soon
discontinued,
due to lack of acceptance, which was not surprising since it was Windows-specific.
It used to be possible to store cryptographic keys in ordinary
files, accessed by Java applets, but Java applets are now being
deprecated by Web browsers.

TLS supports client authentication in addition to server
authentication, but TLS client authentication is only suitable for
specific use cases. Using client authentication requires creating a
TLS client certificate and importing it into the browser. This can be
done manually or automatically. Doing it manually involves creating a
certificate-signing request using, e.g., OpenSSL, submitting it to a
certificate authority, retrieving the resulting certificate, and using
a browser user interface to import it. Web app developers
cannot ask non-technical users to do this. Doing it automatically
requires a computing environment set up by professional IT personnel,
such as Active Directory and/or Mobile Device Management, and is
therefore only feasible within an enterprise or other large
organization.

Several attempts have been made to make it easier for web applications
to store cryptographic keys on the client side.

HTML5 introduced the <keygen> HTML tag, which was
intended to allow a Web server to issue a TLS client certificate that
would be automatically imported into the browser. But it was not
supported by Internet Explorer, which at the time was still the
dominant browser, and it has recently been deprecated.

The recent
FIDO
specifications
allow a browser to authenticate to the back-end of
a Web application using a key pair generated and stored by an
“authenticator” accessible to the browser. The
authenticator could be a hardware device or component such as a USB
dongle, a TPM, a secure element or a TEE, or could be implemented in
software. The authenticator attests to the application back-end that
it belongs to a particular class of devices by signing the
authentication public key with an attestation private key, all devices
in the class carrying the same attestation private key.

In FIDO UAF, where UAF stands for “Universal Authentication
Framework”, the user activates the authenticator by
authenticating to it with a biometric sample or a PIN. In FIDO U2F,
where U2F stands for “Universal Second Factor”, the
authenticator should be a separate hardware device whose possession
serves as a second authentication factor. The authenticator device
may communicate with the user’s primary computing device via USB or
NFC. The user activates the authenticator device by pressing a button
on the device, or, if an NFC device, by tapping it against the primary
computing device. This does not count as an authentication factor;
the other authentication factor is instead an ordinary username and
password. The W3C has adopted FIDO U2F as the foundation for the
Web Authentication API.

FIDO requires browser plugins, but new web technologies have appeared
over the last few years that enable new key storage solutions without
plugins. This post and the companion presentation focus on the
storage of keys used in cryptographic credentials, but the techniques
discussed below can also be adapted for other purposes, briefly
mentioned below in connection with Slide 27.

Slide 3: New Web technologies

Five Web technologies have appeared over the last few years that
enable new solutions for storing cryptographic keys in persistent
browser storage. These technologies are Web APIs that browsers make
available to JavaScript code embedded in Web pages. They include, in
chronological order, the Web Storage API, the IndexedDB API, the Web
Cryptography API, the Service Worker API and the Web Authentication
API.

Slide 4: The Web Authentication API

The Web Authentication API is based on the FIDO U2F specification
described above, whose adoption by the W3C may eliminate the need for
browser plugins. It is scheduled to become available without plugins
on Chrome, Firefox and Edge later this year.

The Web Authentication API provides a new solution to the problem
faced by web applications of how to store client-side cryptographic
credentials. The authentication key pair is stored in the
authenticator, which is accessible to the browser. Since the
authenticator should be a separate hardware device, this does not seem
very different from storing credentials in a smart card. But the same
authenticator may be shared by any number of web applications, and
provisioning is automated. On the other hand the authenticator can
only store uncertified key pairs, and the Web Authentication API can
only be used to authenticate repeat visits of a user who has
previously registered with the application. It cannot be used for
identity proofing or for claiming attributes certified by an
authoritative third party. Therefore it does not provide a general
purpose key-storage solution.

The other four web technologies are not specifically intended for the
storage of cryptographic keys, but they can be combined in several
ways to obtain general purpose key-storage solutions.
They are reviewed in chronological order in slides 5-8.

Slide 5: The Web Storage API

The
Web
Storage API
is available in all browsers (including Opera but not
Opera Mini, which does not support any of the APIs that we are
concerned with). It was introduced as part of the WHATWG HTML5
specification, and has been available in most browsers since 2009,
before HTML5 was officially adopted by the W3C in 2014. (Detailed
availability history for all the Web technologies discussed here can
be found at
caniuse.com.) It is very simple. It
allows JavaScript code to store strings as properties of
a localStorage object or a sessionStorage
object, with the former providing persistent storage. Stored data is
protected by the same origin policy implemented by all
browsers. In first approximation, this means that if a string is
stored by JavaScript code embedded in a web page downloaded from a
particular DNS domain, it can only be retrieved by JavaScript code
that is also embedded in a web page downloaded from the same DNS
domain.

Slide 6: The IndexedDB API

The IndexedDB API also provides persistent storage. It is available
in all browsers today and has been available in most browsers since
2012. By contrast with the Web Storage API, it has a complex
asynchronous interface, which has earned it this warning in the
IndexedDB
API page of the Mozilla Developer Network
: “IndexedDB API is
powerful, but may seem too complicated for simple cases”. On
the other hand it can store arbitrary JavaScript objects rather than
simple JavaScript strings. Not having to convert an object to a
string is only a matter of convenience in the case of an ordinary
object, but makes a big difference when the object to be stored is a
cryptographic key, as explained below. Data stored through the
Indexed DB API is protected by the same origin policy of the browser,
just like data stored through the Web Storage API.

Slide 7: The Web Cryptography API

The
Web Cryptography API
has been supported by most browsers since 2014. It is available in
all browsers today, but Internet Explorer only supports an old version
of the specification, and Safari requires API references to be
prefixed with webkit. It provides a random bit generator,
and a number of cryptographic primitives invoked via a complex
asynchronous interface. The cryptographic primitives include two RSA
signature schemes, RSASSA-PSS and RSASSA-PKCS1-v1_5 (described in
RFC 3447), and ECDSA with NIST curves
P-256, P-384 and P-512 (described, e.g., in the
ECDSA
Certicom white paper
). Other primitives include ECDH, AES
(including AES-GCM), HMAC, SHA (including SHA-1, SHA-256, SHA-384 and
SHA-512), HKDF and PBKDF2. Surprisingly, DSA is not included.

In the Web Cryptography API, generation of an RSA or ECDSA key pair
produces two CryptoKey objects, one containing the private
key, the other containing the public key. When the key pair is
generated, the private key can be made non-extractable from
its CryptoKey object. This means that it cannot later be
extracted from the object by JavaScript code embedded in a Web page,
even if that Web page has the same origin as the Web page containing
the JavaScript code that invoked the key generation procedure.
A CryptoKey object is not persistent by itself, and it is
not an ordinary JavaScript object that could be encoded as a string
for storage in localStorage, but it can be stored in a
database accessed through the IndexedDB API.

Slide 8: The Service Worker API

The Service Worker API is a crucial web technology that allows the
front-end of a web app to work “offline”, without
communicating with the back-end. Besides its usage in connection with
key storage, it has the potential to allow apps to
become fully competitive with native apps, without requiring app
installation. It is available in Chrome, Firefox and Opera, under
development in Edge, and under consideration for Safari.

Here is how it works: the JavaScript front-end of the web application
registers a service worker with the browser, and configures it to
intercept certain requests to the back-end. The service worker can
handle an intercepted request by generating a Web page on the fly and
delivering it to the browser, which renders it as if it came from the
back-end. The generated page may include JavaScript code, which can
retrieve a cryptographic credential from persistent browser storage
and present it to a verifier.

Slide 9: Two combinations of web technologies

The table in Slide 9 has entries to be filled in with four solutions for
storing cryptographic keys, or more specifically cryptographic
credentials, in persistent browser storage.
The entries are filled in in Slide 19 after describing the first solution
in Slides 10-18.

The two columns of the table refer to
two possible combinations of the new web technologies.

The first column refers to the combination of localStorage
for storing a credential with the Service Worker API for presenting
the credential as described below. This combination supports the
storage and presentation of any kind of cryptographic credential,
including traditional certified key pairs, but also
anonymous
credentials
such as Idemix
(now available
on Github
from IBM) or
rich credentials.

The second column refers the combination of the IndexedDB API for
storing a credential with the Web Cryptography API for wrapping a
private key in a CryptoKey object and the Service Worker
API for presenting the credential. It only supports credentials based
on RSA or ECDSA key pairs, but the private key can be made
non-extractable by JavaScript code from its CryptoKey
object.

The meaning of the two rows is described below in connection with
Slide 19, after Solution 1 is described in Slides 10-18.

Slides 10-13: issuance of a cryptographic credential in Solution 1

Slides 10-13 are an animation that illustrates the issuance of a
cryptographic credential and its storage in localStorage in
Solution 1.

In Slide 10 the user visits the Web site of the credential
issuer
and downloads a credential issuance web page with embedded
JavaScript code, which comprise the front-end of the credential
issuance application.

In Slide 11 the front-end of the issuance application, or more specifically the embedded
JavaScript code, executes an issuance protocol with the back-end of
the application. In the simple case where the
credential is a certified key pair, this means that: (i) the front-end
generates a key pair; (ii) the front-end sends a certificate signing
request to the back-end of the issuance application, comprising the
public key component of the key pair and a proof of knowledge of the
associated private key; (iii) the back-end signs a certificate binding
the public key to attributes of the user and metadata; and (iv) the
back-end sends the certificate to the front-end.

In Slide 12 the JavaScript front-end of the issuance application
stores the cryptographic credential in localStorage.

In Slide 13 the JavaScript front-end registers a service worker with
the browser and configures it to intercept credential presentation
requests.

Slides 14-18: presentation of a cryptographic credential in Solution 1

Slides 14-18 are an animation that illustrates the presentation of a
cryptographic credential in
Solution 1.

In Slide 14 the user visits a web site that requires presentation of
the credential in order for the user to identify him/herself or
demonstrate possession or one or more attributes certified by the
credential.
(If the credential supports selective disclosure, as
anonymous credentials and rich credentials do, the user may only need
to disclose the attributes required by the verifier rather than
disclosing all the attributes included in the credential.)
This web site is called here the credential
verifier
, and could also be referred to as the relying
party
.

In Slide 15 the user clicks a button on a verifier web page, thus
sending an HTTP request that the verifier redirects to the credential
issuer, exactly as in a federated login protocol such as a login with
Facebook or Twitter. However, whereas in federated login the identity
provider has to be online and involved in each login (a tough
availability and performance requirement) and therefore observes each
login (a serious privacy drawback), here the redirected request is
intercepted by the service worker and never seen by the back-end of
the issuer. If the credential supports selective disclosure, the
redirected request also specifies the attributes required by the
verifier.

In Slide 16 the service worker generates a web page on the fly that
asks the user for consent to present the credential. If the
credential supports selective disclosure, the consent request page
also asks the user for permission to disclose the required attributes
to the verifier.

In Slide 17 JavaScript code embedded in the consent request page
retrieves the credential from localStorage. It can do so
because it has the same origin as the JavaScript code that stored the
credential in Slide 12.

In Slide 18 the JavaScript code embedded in the consent request page
executes a credential presentation protocol with the verifier.

In the
simple case of a certified key pair, the presentation protocol consists of sending
the certificate and proving knowledge of the associated private key.

In the case of a credential that supports selective disclosure, only
those attributes required by the verifier are disclosed.

In the case
of an anonymous credential, such as Idemix, that provides multishow
and issue-show unlinkability in addition to selective disclosure,
multiple presentations cannot be linked to each other or to issuance.

A rich credential, besides supporting selective disclosure, also
supports presentation of multiple verification factors, including
proof of knowledge of a password and proof of possession of one or
more biometric traits in addition to proof of knowledge of a private
key. Furthermore, it supports selective presentation of those
factors. Therefore, if the credential is a rich credential: (i) the
presentation protocol may include submission of a biometric sample,
with presentation attack detection by the verifier; (ii) in the
redirected request the verifier may specify the factors to be
presented in addition to specifying the attributes to be disclosed; and
(iii) the consent request page may ask the user for permission to
present the required factors in addition to requesting permission to
disclose the required attributes.

Slide 19: Three other solutions

In Solution 2 the cryptographic credential is a certified key pair.
The JavaScript front-end of the issuance application uses the Web
Cryptography API to generate the key pair, and makes the private key
unextractable from its CryptoKey object. Because a
CryptoKey object cannot be converted to a JavaScript string, the
credential is stored in a database by means of the IndexedDB API.

Solutions 3 and 4 make use of a trusted consent manager,
assumed to be honest and secure, which provides a Web application
whose front-end performs the functions performed by the issuer
front-end in Solutions 1 and 2. The JavaScript front-end of the
consent manager executes the credential issuance protocol with the
back-end of the issuer, stores the credential, and registers the
service worker, which later requests the user’s consent and presents
the credential to the verifier.

Slides 20-26: comparative security postures

Slides 20-26 show a table that compares the security postures of the
four solutions of Slide 19 to other methods of storing a cryptographic
credential. The slides refer to the user as the “subject”
because the table is concerned with a credential issued to the user.
The rows in the table refer to storage methods while the columns refer
to attacks. As explained in the overlay of Slide 20 that goes away in
Slide 21, each entry in the table is labeled “Capture” if
the corresponding attack succeeds in capturing the credential, or
“Use” if the attack succeeds in using the credential on
the subject’s computing device but cannot extract it for use on
another device, or “Secure” if the attack does not succeed
in capturing or using the credential.

The first two rows of the table refer to Solutions 1 and 2. The third
row refers to Solutions 3 and 4, which rely on a trusted consent
manager; no distinction is made between those two solutions because
the trusted consent manager is assumed to be honest and secure. The
next three rows are concerned with storing the credential in a smart
card. In the fourth row, the credential is provided to the subject by
the credential issuer with a preloaded credential. In the fifth row,
the credential is generated in the smartcard after the smartcard has
been provided to the subject by the issuer; e.g., if the credential is
a certified key pair, the key pair is generated on the card, then the
public key is certified by the issuer and the resulting certificate is
imported into the card. In the sixth row the credential is generated
on a card that the subject obtains from a trusted card manufacturer,
so that credential generation is performed by trusted firmware. In
the last two rows the credential is stored in secure hardware embedded
within the subject’s device, either tamper-proof hardware such as a
secure element or a TPM, or a Trusted Execution Environment that
provides protection against malware but not necessarily against
physical tampering.

The first column refers to an attack by a malicious or compromised
issuer at the time when the credential is issued. The second column
refers to an attack that originates from the issuer’s web site after
the credential has been issued. The attack may take place after the
issuer has been compromised or infiltrated by a malicious actor, or it
may be carried out by an adversary who exploits a vulnerability in the
issuer’s web site, such as a cross-site scripting (XSS) vulnerability.
The purpose of the third column is to illustrate the fact that
JavaScript code with a Web origin other than the issuer’s,
i.e. embedded in a Web page downloaded by the subject from a DNS
domain other than the domain of the credential issuance application,
cannot be used to capture or use the credential; all entries in that
column are labeled “Secure”. The fourth column refers to
an attack by malware, which is assumed to have taken complete control
of the subject’s computing device. The last column refers to an
attack by an adversary who physically captures the subject’s device
and is able to access all storage not contained in tamper-proof
hardware.

Slides 22-26 highlight a few observations that can be made by
examining the entries in the table.

Slide 22 highlights the fact that the four solutions of Slide 19 are
secure against attacks by malicious JavaScript having a Web origin
other than the domain of the credential issuance application. This
implies that they are secure under the assumptions that the issuer is
honest and secure, that there is no malware on the subject’s device, and
that the adversary does not physically capture the subject’s device. (As
already mentioned above, in the case of Solutions 3 and 4, there is
also an assumption that the trusted consent manager is honest and
secure.)

Slide 23 highlights the fact that the four solutions of Slide 19 are
not secure against malware on the subject’s device or physical capture
of the device.

Slide 24 highlights the fact that the four solutions of Slide 19 have
different security postures against attacks that originate from the
issuer after the credential has been issued. In Solution 1, malicious
JavaScript code of same Web origin as the credential issuance
application can retrieve the credential from localStorage,
including the private key if the credential is a certified key pair,
and deliver it to the adversary for use on any device. In Solution 2,
malicious JavaScript code of same Web origin as the credential
issuance application can retrieve the credential, which is an RSA or
ECDSA certified key pair, from the IndexedDB database where it is
stored, and use it by means of the Web Cryptography API, but it cannot
extract the private key from its CryptoKey object for use on a
different device, nor transfer the CryptoKey object itself to a
different device. In Solutions 3 and 4, JavaScript from an origin
other than the domain of the trusted consent manager cannot access the
credential; hence, assuming that the consent manager does not share a
DNS domain with the issuer, Solutions 3 and 4 are secure against
attacks that originate from the issuer after the credential has been
issued.

Slide 25 highlights the fact localStorage may be more
secure than smartcard storage under certain assumptions.
Specifically, if it can be assumed that the subject’s device is free
of malware and is not captured by the adversary, then Solution 3,
where a trusted consent manager, assumed to be honest and secure,
stores the credential in localStorage, is secure. By contrast,
credential storage in a smartcard provided by the issuer is
not secure against an issuer-originated attack at issuance, even if
the credential is a certified key pair and key pair generation takes
place on the card. This is because a malicious or compromised issuer
may have installed malicious firmware on the card before delivering it
to the subject. Smartcard security requires trusted firmware, which
in turn requires that the subject obtain the smartcard directly from a
trusted manufacturer rather than from the issuer, if the issuer is not
fully trusted. Thus a trusted card manufacturer plays a role in
smartcard credential storage analogous to that of a trusted consent
manager in browser storage.

Finally, Slide 26 highlights the fact that secure hardware embedded in
the subject’s device, either tamper-proof hardware or a TEE, provide
good security. Unfortunately, such credential storage solutions
cannot be used today by developers of Web applications. We heard at
the conference, however, that
GlobalPlatform may be
working on making credential storage in a TEE available to Web
applications.

Slide 27: Potential applications

Most of the talk has focused on the storage of cryptographic
credentials, which can be used for remote identity proofing, recurring
authentication, or privilege escalation. But persistent browser
storage can also be used for storing cryptographic keys used for other
purposes.

One possible application is end-to-end encrypted Web mail. Secure
messaging is becoming popular, but at this time it requires either a
native email client that supports S/MIME, or a native app that
implements ad-hoc encrypted messaging. Web mail is a Web application.
Messages are stored in a server, but are composed and read in the
browser. End-to-end encryption would require the messages to be
encrypted in the sender’s browser, and decrypted in the recipient’s
browser using the recipient’s private key. The above solutions could
be adapted to store the private key used to decrypt messages in the
recipient’s browser together with the associated certificate, as well as the
private key used for signing messages in the sender’s browser with its associated certificate.

Another possible application would be cryptographically secured online
payments.

Comments

2 responses to “Storing Cryptographic Keys in Persistent Browser Storage”

  1. Kevin Kozak Avatar
    Kevin Kozak

    Can you elaborate on what the trusted consent manager does and how it does it?

    1. Francisco Corella Avatar

      Good question. A trusted consent manager allows the user to specify his/her consent to present particular credentials to particular verifiers (a.k.a. relying parties), or to disclose particular attributes of selective disclosure credentials, at transaction time or during configuration. The concept was introduced in a fedetated identity management context. I believe it was used by Shibboleth in the InCommon Federation. Ken Klingestein of Internet2 gave a talk about it at IIW.

      The main benefit of a trusted consent manager is that it improves the user experience by centralizing in a unified framework with a common user interface the consent management for any number of issuers and any number of verifiers.

      In the talk referenced by the blog post, the JavaScript front-end of the TCM is issued credentials from the issuers, keeps them in browser storage where they can only be accessed by JS codde of same web origin as the TCM, and uses its own service worker to ask for consent and present each credential (or, more precisely, to construct a page that does that). Without a TCM, it is the front-end of each issuer that is issued each credential and registers a service worker to ask for consent and present the credential. Besides the user experience improvement, a TCM provides security posture benefits that are highlighted in slides 24-25.