diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8e99ab9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Michael Davis + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..8fb0e6e --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,7 @@ +include README.rst +include LICENSE +include requirements.txt +include requirements-*.txt +include tox.ini +graft docs +graft tests diff --git a/PKG-INFO b/PKG-INFO new file mode 100644 index 0000000..0f4788b --- /dev/null +++ b/PKG-INFO @@ -0,0 +1,136 @@ +Metadata-Version: 1.1 +Name: python-jose +Version: 3.1.0 +Summary: JOSE implementation in Python +Home-page: http://github.com/mpdavis/python-jose +Author: Michael Davis +Author-email: mike.philip.davis@gmail.com +License: MIT +Description: python-jose + =========== + + A JOSE implementation in Python + + |Build Status| |Coverage Status| |Docs| + + Docs are available on ReadTheDocs_. + + The JavaScript Object Signing and Encryption (JOSE) technologies - JSON + Web Signature (JWS), JSON Web Encryption (JWE), JSON Web Key (JWK), and + JSON Web Algorithms (JWA) - collectively can be used to encrypt and/or + sign content using a variety of algorithms. While the full set of + permutations is extremely large, and might be daunting to some, it is + expected that most applications will only use a small set of algorithms + to meet their needs. + + + Installation + ------------ + + :: + + $ pip install python-jose[cryptography] + + + Cryptographic Backends + ---------------------- + + As of 3.1.0, python-jose implements four different cryptographic backends. + The backend must be selected as an extra when installing python-jose. + If you do not select a backend, the native-python backend will be installed. + + Unless otherwise noted, all backends support all operations. + + Due to complexities with setuptools, the native-python backend is always installed, + even if you select a different backend on install. + We recommend that you remove unnecessary dependencies in production. + + #. cryptography + + * This backend uses `pyca/cryptography`_ for all cryptographic operations. + This is the recommended backend and is selected over all other backends if any others are present. + * Installation: ``pip install python-jose[cryptography]`` + * Unused dependencies: + + * ``rsa`` + * ``ecdsa`` + * ``pyasn1`` + + #. pycryptodome + + * This backend uses `pycryptodome`_ for all cryptographic operations. + * Installation: ``pip install python-jose[pycryptodome]`` + * Unused dependencies: + + * ``rsa`` + + #. native-python + + * This backend uses `python-rsa`_ and `python-ecdsa`_ for all cryptographic operations. + This backend is always installed but any other backend will take precedence if one is installed. + * Installation: ``pip install python-jose`` + + .. note:: + + The native-python backend cannot process certificates. + + #. pycrypto + + * This backend uses `pycrypto`_ for all cryptographic operations. + * Installation: ``pip install python-jose[pycrypto]`` + * Unused dependencies: + + * ``rsa`` + + .. warning:: + + The `pycrypto`_ project has not been maintained since 2013. + This backend is maintained for legacy compatibility purposes only. + Do not use this backend unless you cannot use any of the others. + + Usage + ----- + + .. code-block:: python + + >>> from jose import jwt + >>> token = jwt.encode({'key': 'value'}, 'secret', algorithm='HS256') + u'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJ2YWx1ZSJ9.FG-8UppwHaFp1LgRYQQeS6EDQF7_6-bMFegNucHjmWg' + + >>> jwt.decode(token, 'secret', algorithms=['HS256']) + {u'key': u'value'} + + + Thanks + ------ + + This library was originally based heavily on the work of the folks over at PyJWT_. + + .. |Build Status| image:: https://travis-ci.org/mpdavis/python-jose.svg?branch=master + :target: https://travis-ci.org/mpdavis/python-jose + .. |Coverage Status| image:: http://codecov.io/github/mpdavis/python-jose/coverage.svg?branch=master + :target: http://codecov.io/github/mpdavis/python-jose?branch=master + .. |Docs| image:: https://readthedocs.org/projects/python-jose/badge/ + :target: https://python-jose.readthedocs.org/en/latest/ + .. _ReadTheDocs: https://python-jose.readthedocs.org/en/latest/ + .. _PyJWT: https://github.com/jpadilla/pyjwt + .. _pyca/cryptography: http://cryptography.io/ + .. _pycryptodome: https://pycryptodome.readthedocs.io/en/latest/ + .. _pycrypto: https://www.dlitz.net/software/pycrypto/ + .. _python-ecdsa: https://github.com/warner/python-ecdsa + .. _python-rsa: https://stuvel.eu/rsa + +Keywords: jose jws jwe jwt json web token security signing +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Natural Language :: English +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Utilities diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..d5dcc40 --- /dev/null +++ b/README.rst @@ -0,0 +1,113 @@ +python-jose +=========== + +A JOSE implementation in Python + +|Build Status| |Coverage Status| |Docs| + +Docs are available on ReadTheDocs_. + +The JavaScript Object Signing and Encryption (JOSE) technologies - JSON +Web Signature (JWS), JSON Web Encryption (JWE), JSON Web Key (JWK), and +JSON Web Algorithms (JWA) - collectively can be used to encrypt and/or +sign content using a variety of algorithms. While the full set of +permutations is extremely large, and might be daunting to some, it is +expected that most applications will only use a small set of algorithms +to meet their needs. + + +Installation +------------ + +:: + + $ pip install python-jose[cryptography] + + +Cryptographic Backends +---------------------- + +As of 3.1.0, python-jose implements four different cryptographic backends. +The backend must be selected as an extra when installing python-jose. +If you do not select a backend, the native-python backend will be installed. + +Unless otherwise noted, all backends support all operations. + +Due to complexities with setuptools, the native-python backend is always installed, +even if you select a different backend on install. +We recommend that you remove unnecessary dependencies in production. + +#. cryptography + + * This backend uses `pyca/cryptography`_ for all cryptographic operations. + This is the recommended backend and is selected over all other backends if any others are present. + * Installation: ``pip install python-jose[cryptography]`` + * Unused dependencies: + + * ``rsa`` + * ``ecdsa`` + * ``pyasn1`` + +#. pycryptodome + + * This backend uses `pycryptodome`_ for all cryptographic operations. + * Installation: ``pip install python-jose[pycryptodome]`` + * Unused dependencies: + + * ``rsa`` + +#. native-python + + * This backend uses `python-rsa`_ and `python-ecdsa`_ for all cryptographic operations. + This backend is always installed but any other backend will take precedence if one is installed. + * Installation: ``pip install python-jose`` + + .. note:: + + The native-python backend cannot process certificates. + +#. pycrypto + + * This backend uses `pycrypto`_ for all cryptographic operations. + * Installation: ``pip install python-jose[pycrypto]`` + * Unused dependencies: + + * ``rsa`` + + .. warning:: + + The `pycrypto`_ project has not been maintained since 2013. + This backend is maintained for legacy compatibility purposes only. + Do not use this backend unless you cannot use any of the others. + +Usage +----- + +.. code-block:: python + + >>> from jose import jwt + >>> token = jwt.encode({'key': 'value'}, 'secret', algorithm='HS256') + u'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJ2YWx1ZSJ9.FG-8UppwHaFp1LgRYQQeS6EDQF7_6-bMFegNucHjmWg' + + >>> jwt.decode(token, 'secret', algorithms=['HS256']) + {u'key': u'value'} + + +Thanks +------ + +This library was originally based heavily on the work of the folks over at PyJWT_. + +.. |Build Status| image:: https://travis-ci.org/mpdavis/python-jose.svg?branch=master + :target: https://travis-ci.org/mpdavis/python-jose +.. |Coverage Status| image:: http://codecov.io/github/mpdavis/python-jose/coverage.svg?branch=master + :target: http://codecov.io/github/mpdavis/python-jose?branch=master +.. |Docs| image:: https://readthedocs.org/projects/python-jose/badge/ + :target: https://python-jose.readthedocs.org/en/latest/ +.. _ReadTheDocs: https://python-jose.readthedocs.org/en/latest/ +.. _PyJWT: https://github.com/jpadilla/pyjwt +.. _pyca/cryptography: http://cryptography.io/ +.. _pycryptodome: https://pycryptodome.readthedocs.io/en/latest/ +.. _pycrypto: https://www.dlitz.net/software/pycrypto/ +.. _python-ecdsa: https://github.com/warner/python-ecdsa +.. _python-rsa: https://stuvel.eu/rsa diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..0b6ad27 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,177 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# User-friendly check for sphinx-build +ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) +$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) +endif + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " xml to make Docutils-native XML files" + @echo " pseudoxml to make pseudoxml-XML files for display purposes" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/python-jose.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/python-jose.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/python-jose" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/python-jose" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +latexpdfja: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through platex and dvipdfmx..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +xml: + $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml + @echo + @echo "Build finished. The XML files are in $(BUILDDIR)/xml." + +pseudoxml: + $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml + @echo + @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..6a07674 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,267 @@ +# -*- coding: utf-8 -*- +# +# python-jose documentation build configuration file, created by +# sphinx-quickstart on Thu May 7 13:48:43 2015. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys +import os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) + +sys.path.append(os.path.join(os.path.dirname(__file__), '..')) +sys.path.append(os.path.join(os.path.dirname(__file__), '../jose')) +sys.path.append(os.path.join(os.path.dirname(__file__), '../jose/jws')) +sys.path.append(os.path.join(os.path.dirname(__file__), '../jose/jwt')) + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.coverage', + 'sphinxcontrib.napoleon' +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'python-jose' +copyright = u'2015, Michael Davis' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '0.2' +# The full version, including alpha/beta/rc tags. +release = '0.2.0' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +#keep_warnings = False + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +#html_extra_path = [] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'python-josedoc' + + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + ('index', 'python-jose.tex', u'python-jose Documentation', + u'Michael Davis', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'python-jose', u'python-jose Documentation', + [u'Michael Davis'], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('index', 'python-jose', u'python-jose Documentation', + u'Michael Davis', 'python-jose', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +#texinfo_no_detailmenu = False diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..63cd390 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,55 @@ + +python-jose +=========== + +A JOSE implementation in Python + +|Build Status| |Coverage Status| + +The JavaScript Object Signing and Encryption (JOSE) technologies - JSON +Web Signature (JWS), JSON Web Encryption (JWE), JSON Web Key (JWK), and +JSON Web Algorithms (JWA) - collectively can be used to encrypt and/or +sign content using a variety of algorithms. While the full set of +permutations is extremely large, and might be daunting to some, it is +expected that most applications will only use a small set of algorithms +to meet their needs. + + +Contents +-------- + +.. toctree:: + :maxdepth: 2 + + jws/index + jwt/index + jwk/index + + +APIs +---- + +.. toctree:: + :maxdepth: 2 + + jws/api + jwt/api + jwk/api + + +Principles +---------- + +This is a JOSE implementation that is fully compatible with Google App Engine +which requires the use of the PyCrypto library. + +Thanks +------ + +This library was originally based heavily on the work of the guys over at PyJWT_. + +.. |Build Status| image:: https://travis-ci.org/mpdavis/python-jose.svg?branch=master + :target: https://travis-ci.org/mpdavis/python-jose +.. |Coverage Status| image:: http://codecov.io/github/mpdavis/python-jose/coverage.svg?branch=master + :target: http://codecov.io/github/mpdavis/python-jose?branch=master +.. _PyJWT: https://github.com/jpadilla/pyjwt diff --git a/docs/jwk/api.rst b/docs/jwk/api.rst new file mode 100644 index 0000000..245e66d --- /dev/null +++ b/docs/jwk/api.rst @@ -0,0 +1,6 @@ + +JWK API +^^^^^^^ + +.. automodule:: jose.jwk + :members: diff --git a/docs/jwk/index.rst b/docs/jwk/index.rst new file mode 100644 index 0000000..ba82c98 --- /dev/null +++ b/docs/jwk/index.rst @@ -0,0 +1,39 @@ +JSON Web Key +============== + +JSON Web Keys (JWK) are a JSON data structure representing a cryptographic key. + +Examples +^^^^^^^^ + +Verifying token signatures +-------------------------- + +.. code:: python + + >>> from jose import jwk + >>> from jose.utils import base64url_decode + >>> + >>> token = "eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LTRkOWItNDcxYi1iZmQ2LWVlZjMxNGJjNzAzNyJ9.SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4.s0h6KThzkfBBBkLspW1h84VsJZFTsPPqMDA7g1Md7p0" + >>> hmac_key = { + "kty": "oct", + "kid": "018c0ae5-4d9b-471b-bfd6-eef314bc7037", + "use": "sig", + "alg": "HS256", + "k": "hJtXIZ2uSN5kbQfbtTNWbpdmhkV8FJG-Onbc6mxCcYg" + } + >>> + >>> key = jwk.construct(hmac_key) + >>> + >>> message, encoded_sig = token.rsplit('.', 1) + >>> decoded_sig = base64url_decode(encoded_sig) + >>> key.verify(message, decoded_sig) + + +Note +^^^^ +python-jose requires the use of public keys, as opposed to X.509 certificates. If you have an X.509 certificate that you would like to convert to a public key that python-jose can consume, you can do so with openssl. + +.. code:: bash + + > openssl x509 -pubkey -noout < cert.pem diff --git a/docs/jws/api.rst b/docs/jws/api.rst new file mode 100644 index 0000000..60bf7b3 --- /dev/null +++ b/docs/jws/api.rst @@ -0,0 +1,6 @@ + +JWS API +^^^^^^^ + +.. automodule:: jose.jws + :members: \ No newline at end of file diff --git a/docs/jws/index.rst b/docs/jws/index.rst new file mode 100644 index 0000000..54ac132 --- /dev/null +++ b/docs/jws/index.rst @@ -0,0 +1,52 @@ +JSON Web Signature +================== + +JSON Web Signatures (JWS) are used to digitally sign a JSON encoded +object and represent it as a compact URL-safe string. + +Supported Algorithms +^^^^^^^^^^^^^^^^^^^^ + +The following algorithms are currently supported. + ++-------------------+---------------------------------------+ +| Algorithm Value | Digital Signature or MAC Algorithm | ++===================+=======================================+ +| HS256 | HMAC using SHA-256 hash algorithm | ++-------------------+---------------------------------------+ +| HS384 | HMAC using SHA-384 hash algorithm | ++-------------------+---------------------------------------+ +| HS512 | HMAC using SHA-512 hash algorithm | ++-------------------+---------------------------------------+ +| RS256 | RSASSA using SHA-256 hash algorithm | ++-------------------+---------------------------------------+ +| RS384 | RSASSA using SHA-384 hash algorithm | ++-------------------+---------------------------------------+ +| RS512 | RSASSA using SHA-512 hash algorithm | ++-------------------+---------------------------------------+ +| ES256 | ECDSA using SHA-256 hash algorithm | ++-------------------+---------------------------------------+ +| ES384 | ECDSA using SHA-384 hash algorithm | ++-------------------+---------------------------------------+ +| ES512 | ECDSA using SHA-512 hash algorithm | ++-------------------+---------------------------------------+ + +Examples +^^^^^^^^ + +Signing tokens +-------------- + +.. code:: python + + >>> from jose import jws + >>> signed = jws.sign({'a': 'b'}, 'secret', algorithm='HS256') + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhIjoiYiJ9.jiMyrsmD8AoHWeQgmxZ5yq8z0lXS67_QGs52AzC8Ru8' + +Verifying token signatures +-------------------------- + +.. code:: python + + >>> jws.verify(signed, 'secret', algorithms=['HS256']) + {'a': 'b'} diff --git a/docs/jwt/api.rst b/docs/jwt/api.rst new file mode 100644 index 0000000..ec7a7d9 --- /dev/null +++ b/docs/jwt/api.rst @@ -0,0 +1,6 @@ + +JWT API +^^^^^^^ + +.. automodule:: jose.jwt + :members: \ No newline at end of file diff --git a/docs/jwt/index.rst b/docs/jwt/index.rst new file mode 100644 index 0000000..3ce1a78 --- /dev/null +++ b/docs/jwt/index.rst @@ -0,0 +1,22 @@ +JSON Web Token +============== + +JSON Web Tokens (JWT) are a JWS with a set of reserved claims to be used +in a standardized manner. + +JWT Reserved Claims +^^^^^^^^^^^^^^^^^^^ + ++---------+--------------+--------------------+-----------------------------------------------+ +| Claim | Name | Format | Usage | ++=========+==============+====================+===============================================+ +| 'exp' | Expiration | int | The time after which the token is invalid. | ++---------+--------------+--------------------+-----------------------------------------------+ +| 'nbf' | Not Before | int | The time before which the token is invalid. | ++---------+--------------+--------------------+-----------------------------------------------+ +| 'iss' | Issuer | str | The principal that issued the JWT. | ++---------+--------------+--------------------+-----------------------------------------------+ +| 'aud' | Audience | str or list(str) | The recipient that the JWT is intended for. | ++---------+--------------+--------------------+-----------------------------------------------+ +| 'iat' | Issued At | int | The time at which the JWT was issued. | ++---------+--------------+--------------------+-----------------------------------------------+ diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..a841996 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,242 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=_build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . +set I18NSPHINXOPTS=%SPHINXOPTS% . +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% + set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. texinfo to make Texinfo files + echo. gettext to make PO message catalogs + echo. changes to make an overview over all changed/added/deprecated items + echo. xml to make Docutils-native XML files + echo. pseudoxml to make pseudoxml-XML files for display purposes + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + + +%SPHINXBUILD% 2> nul +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\python-jose.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\python-jose.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdf" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf + cd %BUILDDIR%/.. + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdfja" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf-ja + cd %BUILDDIR%/.. + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "texinfo" ( + %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. + goto end +) + +if "%1" == "gettext" ( + %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The message catalogs are in %BUILDDIR%/locale. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +if "%1" == "xml" ( + %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The XML files are in %BUILDDIR%/xml. + goto end +) + +if "%1" == "pseudoxml" ( + %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. + goto end +) + +:end diff --git a/jose/__init__.py b/jose/__init__.py new file mode 100644 index 0000000..ddf2dcb --- /dev/null +++ b/jose/__init__.py @@ -0,0 +1,11 @@ + +__version__ = "3.1.0" +__author__ = 'Michael Davis' +__license__ = 'MIT' +__copyright__ = 'Copyright 2016 Michael Davis' + + +from .exceptions import JOSEError # noqa: F401 +from .exceptions import JWSError # noqa: F401 +from .exceptions import ExpiredSignatureError # noqa: F401 +from .exceptions import JWTError # noqa: F401 diff --git a/jose/backends/__init__.py b/jose/backends/__init__.py new file mode 100644 index 0000000..d1b9fa1 --- /dev/null +++ b/jose/backends/__init__.py @@ -0,0 +1,13 @@ + +try: + from jose.backends.cryptography_backend import CryptographyRSAKey as RSAKey # noqa: F401 +except ImportError: + try: + from jose.backends.pycrypto_backend import RSAKey # noqa: F401 + except ImportError: + from jose.backends.rsa_backend import RSAKey # noqa: F401 + +try: + from jose.backends.cryptography_backend import CryptographyECKey as ECKey # noqa: F401 +except ImportError: + from jose.backends.ecdsa_backend import ECDSAECKey as ECKey # noqa: F401 diff --git a/jose/backends/_asn1.py b/jose/backends/_asn1.py new file mode 100644 index 0000000..e252cc7 --- /dev/null +++ b/jose/backends/_asn1.py @@ -0,0 +1,82 @@ +"""ASN1 encoding helpers for converting between PKCS1 and PKCS8. + +Required by rsa_backend and pycrypto_backend but not cryptography_backend. +""" +from pyasn1.codec.der import decoder, encoder +from pyasn1.type import namedtype, univ + +RSA_ENCRYPTION_ASN1_OID = "1.2.840.113549.1.1.1" + + +class RsaAlgorithmIdentifier(univ.Sequence): + """ASN1 structure for recording RSA PrivateKeyAlgorithm identifiers.""" + componentType = namedtype.NamedTypes( + namedtype.NamedType("rsaEncryption", univ.ObjectIdentifier()), + namedtype.NamedType("parameters", univ.Null()) + ) + + +class PKCS8PrivateKey(univ.Sequence): + """ASN1 structure for recording PKCS8 private keys.""" + componentType = namedtype.NamedTypes( + namedtype.NamedType("version", univ.Integer()), + namedtype.NamedType("privateKeyAlgorithm", RsaAlgorithmIdentifier()), + namedtype.NamedType("privateKey", univ.OctetString()) + ) + + +class PublicKeyInfo(univ.Sequence): + """ASN1 structure for recording PKCS8 public keys.""" + componentType = namedtype.NamedTypes( + namedtype.NamedType("algorithm", RsaAlgorithmIdentifier()), + namedtype.NamedType("publicKey", univ.BitString()) + ) + + +def rsa_private_key_pkcs8_to_pkcs1(pkcs8_key): + """Convert a PKCS8-encoded RSA private key to PKCS1.""" + decoded_values = decoder.decode(pkcs8_key, asn1Spec=PKCS8PrivateKey()) + + try: + decoded_key = decoded_values[0] + except IndexError: + raise ValueError("Invalid private key encoding") + + return decoded_key["privateKey"] + + +def rsa_private_key_pkcs1_to_pkcs8(pkcs1_key): + """Convert a PKCS1-encoded RSA private key to PKCS8.""" + algorithm = RsaAlgorithmIdentifier() + algorithm["rsaEncryption"] = RSA_ENCRYPTION_ASN1_OID + + pkcs8_key = PKCS8PrivateKey() + pkcs8_key["version"] = 0 + pkcs8_key["privateKeyAlgorithm"] = algorithm + pkcs8_key["privateKey"] = pkcs1_key + + return encoder.encode(pkcs8_key) + + +def rsa_public_key_pkcs1_to_pkcs8(pkcs1_key): + """Convert a PKCS1-encoded RSA private key to PKCS8.""" + algorithm = RsaAlgorithmIdentifier() + algorithm["rsaEncryption"] = RSA_ENCRYPTION_ASN1_OID + + pkcs8_key = PublicKeyInfo() + pkcs8_key["algorithm"] = algorithm + pkcs8_key["publicKey"] = univ.BitString.fromOctetString(pkcs1_key) + + return encoder.encode(pkcs8_key) + + +def rsa_public_key_pkcs8_to_pkcs1(pkcs8_key): + """Convert a PKCS8-encoded RSA private key to PKCS1.""" + decoded_values = decoder.decode(pkcs8_key, asn1Spec=PublicKeyInfo()) + + try: + decoded_key = decoded_values[0] + except IndexError: + raise ValueError("Invalid public key encoding.") + + return decoded_key["publicKey"].asOctets() diff --git a/jose/backends/base.py b/jose/backends/base.py new file mode 100644 index 0000000..37fc2ea --- /dev/null +++ b/jose/backends/base.py @@ -0,0 +1,21 @@ +class Key(object): + """ + A simple interface for implementing JWK keys. + """ + def __init__(self, key, algorithm): + pass + + def sign(self, msg): + raise NotImplementedError() + + def verify(self, msg, sig): + raise NotImplementedError() + + def public_key(self): + raise NotImplementedError() + + def to_pem(self): + raise NotImplementedError() + + def to_dict(self): + raise NotImplementedError() diff --git a/jose/backends/cryptography_backend.py b/jose/backends/cryptography_backend.py new file mode 100644 index 0000000..b9bdc0d --- /dev/null +++ b/jose/backends/cryptography_backend.py @@ -0,0 +1,371 @@ +from __future__ import division + +import math + +import six + +try: + from ecdsa import SigningKey as EcdsaSigningKey, VerifyingKey as EcdsaVerifyingKey +except ImportError: + EcdsaSigningKey = EcdsaVerifyingKey = None + +from jose.backends.base import Key +from jose.utils import base64_to_long, long_to_base64 +from jose.constants import ALGORITHMS +from jose.exceptions import JWKError + +from cryptography.exceptions import InvalidSignature +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import ec, rsa, padding +from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature, encode_dss_signature +from cryptography.hazmat.primitives.serialization import load_pem_private_key, load_pem_public_key +from cryptography.utils import int_from_bytes, int_to_bytes +from cryptography.x509 import load_pem_x509_certificate + + +class CryptographyECKey(Key): + SHA256 = hashes.SHA256 + SHA384 = hashes.SHA384 + SHA512 = hashes.SHA512 + + def __init__(self, key, algorithm, cryptography_backend=default_backend): + if algorithm not in ALGORITHMS.EC: + raise JWKError('hash_alg: %s is not a valid hash algorithm' % algorithm) + + self.hash_alg = { + ALGORITHMS.ES256: self.SHA256, + ALGORITHMS.ES384: self.SHA384, + ALGORITHMS.ES512: self.SHA512 + }.get(algorithm) + self._algorithm = algorithm + + self.cryptography_backend = cryptography_backend + + if hasattr(key, 'public_bytes') or hasattr(key, 'private_bytes'): + self.prepared_key = key + return + + if None not in (EcdsaSigningKey, EcdsaVerifyingKey) and isinstance(key, (EcdsaSigningKey, EcdsaVerifyingKey)): + # convert to PEM and let cryptography below load it as PEM + key = key.to_pem().decode('utf-8') + + if isinstance(key, dict): + self.prepared_key = self._process_jwk(key) + return + + if isinstance(key, six.string_types): + key = key.encode('utf-8') + + if isinstance(key, six.binary_type): + # Attempt to load key. We don't know if it's + # a Public Key or a Private Key, so we try + # the Public Key first. + try: + try: + key = load_pem_public_key(key, self.cryptography_backend()) + except ValueError: + key = load_pem_private_key(key, password=None, backend=self.cryptography_backend()) + except Exception as e: + raise JWKError(e) + + self.prepared_key = key + return + + raise JWKError('Unable to parse an ECKey from key: %s' % key) + + def _process_jwk(self, jwk_dict): + if not jwk_dict.get('kty') == 'EC': + raise JWKError("Incorrect key type. Expected: 'EC', Received: %s" % jwk_dict.get('kty')) + + if not all(k in jwk_dict for k in ['x', 'y', 'crv']): + raise JWKError('Mandatory parameters are missing') + + x = base64_to_long(jwk_dict.get('x')) + y = base64_to_long(jwk_dict.get('y')) + curve = { + 'P-256': ec.SECP256R1, + 'P-384': ec.SECP384R1, + 'P-521': ec.SECP521R1, + }[jwk_dict['crv']] + + public = ec.EllipticCurvePublicNumbers(x, y, curve()) + + if 'd' in jwk_dict: + d = base64_to_long(jwk_dict.get('d')) + private = ec.EllipticCurvePrivateNumbers(d, public) + + return private.private_key(self.cryptography_backend()) + else: + return public.public_key(self.cryptography_backend()) + + def _sig_component_length(self): + """Determine the correct serialization length for an encoded signature component. + + This is the number of bytes required to encode the maximum key value. + """ + return int(math.ceil(self.prepared_key.key_size / 8.0)) + + def _der_to_raw(self, der_signature): + """Convert signature from DER encoding to RAW encoding.""" + r, s = decode_dss_signature(der_signature) + component_length = self._sig_component_length() + return int_to_bytes(r, component_length) + int_to_bytes(s, component_length) + + def _raw_to_der(self, raw_signature): + """Convert signature from RAW encoding to DER encoding.""" + component_length = self._sig_component_length() + if len(raw_signature) != int(2 * component_length): + raise ValueError("Invalid signature") + + r_bytes = raw_signature[:component_length] + s_bytes = raw_signature[component_length:] + r = int_from_bytes(r_bytes, "big") + s = int_from_bytes(s_bytes, "big") + return encode_dss_signature(r, s) + + def sign(self, msg): + if self.hash_alg.digest_size * 8 > self.prepared_key.curve.key_size: + raise TypeError("this curve (%s) is too short " + "for your digest (%d)" % (self.prepared_key.curve.name, + 8 * self.hash_alg.digest_size)) + signature = self.prepared_key.sign(msg, ec.ECDSA(self.hash_alg())) + return self._der_to_raw(signature) + + def verify(self, msg, sig): + try: + signature = self._raw_to_der(sig) + self.prepared_key.verify(signature, msg, ec.ECDSA(self.hash_alg())) + return True + except Exception: + return False + + def is_public(self): + return hasattr(self.prepared_key, 'public_bytes') + + def public_key(self): + if self.is_public(): + return self + return self.__class__(self.prepared_key.public_key(), self._algorithm) + + def to_pem(self): + if self.is_public(): + pem = self.prepared_key.public_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PublicFormat.SubjectPublicKeyInfo + ) + return pem + pem = self.prepared_key.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.TraditionalOpenSSL, + encryption_algorithm=serialization.NoEncryption() + ) + return pem + + def to_dict(self): + if not self.is_public(): + public_key = self.prepared_key.public_key() + else: + public_key = self.prepared_key + + crv = { + 'secp256r1': 'P-256', + 'secp384r1': 'P-384', + 'secp521r1': 'P-521', + }[self.prepared_key.curve.name] + + # Calculate the key size in bytes. Section 6.2.1.2 and 6.2.1.3 of + # RFC7518 prescribes that the 'x', 'y' and 'd' parameters of the curve + # points must be encoded as octed-strings of this length. + key_size = (self.prepared_key.curve.key_size + 7) // 8 + + data = { + 'alg': self._algorithm, + 'kty': 'EC', + 'crv': crv, + 'x': long_to_base64(public_key.public_numbers().x, size=key_size), + 'y': long_to_base64(public_key.public_numbers().y, size=key_size), + } + + if not self.is_public(): + data['d'] = long_to_base64( + self.prepared_key.private_numbers().private_value, + size=key_size + ) + + return data + + +class CryptographyRSAKey(Key): + SHA256 = hashes.SHA256 + SHA384 = hashes.SHA384 + SHA512 = hashes.SHA512 + + def __init__(self, key, algorithm, cryptography_backend=default_backend): + if algorithm not in ALGORITHMS.RSA: + raise JWKError('hash_alg: %s is not a valid hash algorithm' % algorithm) + + self.hash_alg = { + ALGORITHMS.RS256: self.SHA256, + ALGORITHMS.RS384: self.SHA384, + ALGORITHMS.RS512: self.SHA512 + }.get(algorithm) + self._algorithm = algorithm + + self.cryptography_backend = cryptography_backend + + # if it conforms to RSAPublicKey interface + if hasattr(key, 'public_bytes') and hasattr(key, 'public_numbers'): + self.prepared_key = key + return + + if isinstance(key, dict): + self.prepared_key = self._process_jwk(key) + return + + if isinstance(key, six.string_types): + key = key.encode('utf-8') + + if isinstance(key, six.binary_type): + try: + if key.startswith(b'-----BEGIN CERTIFICATE-----'): + self._process_cert(key) + return + + try: + self.prepared_key = load_pem_public_key(key, self.cryptography_backend()) + except ValueError: + self.prepared_key = load_pem_private_key(key, password=None, backend=self.cryptography_backend()) + except Exception as e: + raise JWKError(e) + return + + raise JWKError('Unable to parse an RSA_JWK from key: %s' % key) + + def _process_jwk(self, jwk_dict): + if not jwk_dict.get('kty') == 'RSA': + raise JWKError("Incorrect key type. Expected: 'RSA', Received: %s" % jwk_dict.get('kty')) + + e = base64_to_long(jwk_dict.get('e', 256)) + n = base64_to_long(jwk_dict.get('n')) + public = rsa.RSAPublicNumbers(e, n) + + if 'd' not in jwk_dict: + return public.public_key(self.cryptography_backend()) + else: + # This is a private key. + d = base64_to_long(jwk_dict.get('d')) + + extra_params = ['p', 'q', 'dp', 'dq', 'qi'] + + if any(k in jwk_dict for k in extra_params): + # Precomputed private key parameters are available. + if not all(k in jwk_dict for k in extra_params): + # These values must be present when 'p' is according to + # Section 6.3.2 of RFC7518, so if they are not we raise + # an error. + raise JWKError('Precomputed private key parameters are incomplete.') + + p = base64_to_long(jwk_dict['p']) + q = base64_to_long(jwk_dict['q']) + dp = base64_to_long(jwk_dict['dp']) + dq = base64_to_long(jwk_dict['dq']) + qi = base64_to_long(jwk_dict['qi']) + else: + # The precomputed private key parameters are not available, + # so we use cryptography's API to fill them in. + p, q = rsa.rsa_recover_prime_factors(n, e, d) + dp = rsa.rsa_crt_dmp1(d, p) + dq = rsa.rsa_crt_dmq1(d, q) + qi = rsa.rsa_crt_iqmp(p, q) + + private = rsa.RSAPrivateNumbers(p, q, d, dp, dq, qi, public) + + return private.private_key(self.cryptography_backend()) + + def _process_cert(self, key): + key = load_pem_x509_certificate(key, self.cryptography_backend()) + self.prepared_key = key.public_key() + + def sign(self, msg): + try: + signature = self.prepared_key.sign( + msg, + padding.PKCS1v15(), + self.hash_alg() + ) + except Exception as e: + raise JWKError(e) + return signature + + def verify(self, msg, sig): + try: + self.prepared_key.verify( + sig, + msg, + padding.PKCS1v15(), + self.hash_alg() + ) + return True + except InvalidSignature: + return False + + def is_public(self): + return hasattr(self.prepared_key, 'public_bytes') + + def public_key(self): + if self.is_public(): + return self + return self.__class__(self.prepared_key.public_key(), self._algorithm) + + def to_pem(self, pem_format='PKCS8'): + if self.is_public(): + if pem_format == 'PKCS8': + fmt = serialization.PublicFormat.SubjectPublicKeyInfo + elif pem_format == 'PKCS1': + fmt = serialization.PublicFormat.PKCS1 + else: + raise ValueError("Invalid format specified: %r" % pem_format) + pem = self.prepared_key.public_bytes( + encoding=serialization.Encoding.PEM, + format=fmt + ) + return pem + + if pem_format == 'PKCS8': + fmt = serialization.PrivateFormat.PKCS8 + elif pem_format == 'PKCS1': + fmt = serialization.PrivateFormat.TraditionalOpenSSL + else: + raise ValueError("Invalid format specified: %r" % pem_format) + + return self.prepared_key.private_bytes( + encoding=serialization.Encoding.PEM, + format=fmt, + encryption_algorithm=serialization.NoEncryption() + ) + + def to_dict(self): + if not self.is_public(): + public_key = self.prepared_key.public_key() + else: + public_key = self.prepared_key + + data = { + 'alg': self._algorithm, + 'kty': 'RSA', + 'n': long_to_base64(public_key.public_numbers().n), + 'e': long_to_base64(public_key.public_numbers().e), + } + + if not self.is_public(): + data.update({ + 'd': long_to_base64(self.prepared_key.private_numbers().d), + 'p': long_to_base64(self.prepared_key.private_numbers().p), + 'q': long_to_base64(self.prepared_key.private_numbers().q), + 'dp': long_to_base64(self.prepared_key.private_numbers().dmp1), + 'dq': long_to_base64(self.prepared_key.private_numbers().dmq1), + 'qi': long_to_base64(self.prepared_key.private_numbers().iqmp), + }) + + return data diff --git a/jose/backends/ecdsa_backend.py b/jose/backends/ecdsa_backend.py new file mode 100644 index 0000000..8b8b9a2 --- /dev/null +++ b/jose/backends/ecdsa_backend.py @@ -0,0 +1,144 @@ +import hashlib +import six + +from jose.backends.base import Key +import ecdsa + +from jose.constants import ALGORITHMS +from jose.exceptions import JWKError +from jose.utils import base64_to_long, long_to_base64 + + +class ECDSAECKey(Key): + """ + Performs signing and verification operations using + ECDSA and the specified hash function + + This class requires the ecdsa package to be installed. + + This is based off of the implementation in PyJWT 0.3.2 + """ + SHA256 = hashlib.sha256 + SHA384 = hashlib.sha384 + SHA512 = hashlib.sha512 + + CURVE_MAP = { + SHA256: ecdsa.curves.NIST256p, + SHA384: ecdsa.curves.NIST384p, + SHA512: ecdsa.curves.NIST521p, + } + + def __init__(self, key, algorithm): + if algorithm not in ALGORITHMS.EC: + raise JWKError('hash_alg: %s is not a valid hash algorithm' % algorithm) + + self.hash_alg = { + ALGORITHMS.ES256: self.SHA256, + ALGORITHMS.ES384: self.SHA384, + ALGORITHMS.ES512: self.SHA512 + }.get(algorithm) + self._algorithm = algorithm + + self.curve = self.CURVE_MAP.get(self.hash_alg) + + if isinstance(key, (ecdsa.SigningKey, ecdsa.VerifyingKey)): + self.prepared_key = key + return + + if isinstance(key, dict): + self.prepared_key = self._process_jwk(key) + return + + if isinstance(key, six.string_types): + key = key.encode('utf-8') + + if isinstance(key, six.binary_type): + # Attempt to load key. We don't know if it's + # a Signing Key or a Verifying Key, so we try + # the Verifying Key first. + try: + key = ecdsa.VerifyingKey.from_pem(key) + except ecdsa.der.UnexpectedDER: + key = ecdsa.SigningKey.from_pem(key) + except Exception as e: + raise JWKError(e) + + self.prepared_key = key + return + + raise JWKError('Unable to parse an ECKey from key: %s' % key) + + def _process_jwk(self, jwk_dict): + if not jwk_dict.get('kty') == 'EC': + raise JWKError("Incorrect key type. Expected: 'EC', Recieved: %s" % jwk_dict.get('kty')) + + if not all(k in jwk_dict for k in ['x', 'y', 'crv']): + raise JWKError('Mandatory parameters are missing') + + if 'd' in jwk_dict: + # We are dealing with a private key; the secret exponent is enough + # to create an ecdsa key. + d = base64_to_long(jwk_dict.get('d')) + return ecdsa.keys.SigningKey.from_secret_exponent(d, self.curve) + else: + x = base64_to_long(jwk_dict.get('x')) + y = base64_to_long(jwk_dict.get('y')) + + if not ecdsa.ecdsa.point_is_valid(self.curve.generator, x, y): + raise JWKError("Point: %s, %s is not a valid point" % (x, y)) + + point = ecdsa.ellipticcurve.Point(self.curve.curve, x, y, self.curve.order) + return ecdsa.keys.VerifyingKey.from_public_point(point, self.curve) + + def sign(self, msg): + return self.prepared_key.sign(msg, hashfunc=self.hash_alg, sigencode=ecdsa.util.sigencode_string) + + def verify(self, msg, sig): + try: + return self.prepared_key.verify(sig, msg, hashfunc=self.hash_alg, sigdecode=ecdsa.util.sigdecode_string) + except Exception: + return False + + def is_public(self): + return isinstance(self.prepared_key, ecdsa.VerifyingKey) + + def public_key(self): + if self.is_public(): + return self + return self.__class__(self.prepared_key.get_verifying_key(), self._algorithm) + + def to_pem(self): + return self.prepared_key.to_pem() + + def to_dict(self): + if not self.is_public(): + public_key = self.prepared_key.get_verifying_key() + else: + public_key = self.prepared_key + + crv = { + ecdsa.curves.NIST256p: 'P-256', + ecdsa.curves.NIST384p: 'P-384', + ecdsa.curves.NIST521p: 'P-521', + }[self.prepared_key.curve] + + # Calculate the key size in bytes. Section 6.2.1.2 and 6.2.1.3 of + # RFC7518 prescribes that the 'x', 'y' and 'd' parameters of the curve + # points must be encoded as octed-strings of this length. + key_size = self.prepared_key.curve.baselen + + data = { + 'alg': self._algorithm, + 'kty': 'EC', + 'crv': crv, + 'x': long_to_base64(public_key.pubkey.point.x(), size=key_size), + 'y': long_to_base64(public_key.pubkey.point.y(), size=key_size), + } + + if not self.is_public(): + data['d'] = long_to_base64( + self.prepared_key.privkey.secret_multiplier, + size=key_size + ) + + return data diff --git a/jose/backends/pycrypto_backend.py b/jose/backends/pycrypto_backend.py new file mode 100644 index 0000000..a12e861 --- /dev/null +++ b/jose/backends/pycrypto_backend.py @@ -0,0 +1,212 @@ +from base64 import b64encode + +import six + +import Crypto.Hash.SHA256 +import Crypto.Hash.SHA384 +import Crypto.Hash.SHA512 + +from Crypto.PublicKey import RSA +from Crypto.Signature import PKCS1_v1_5 +from Crypto.Util.asn1 import DerSequence + +from jose.backends.base import Key +from jose.backends._asn1 import rsa_public_key_pkcs8_to_pkcs1 +from jose.utils import base64_to_long, long_to_base64 +from jose.constants import ALGORITHMS +from jose.exceptions import JWKError +from jose.utils import base64url_decode + + +# We default to using PyCryptodome, however, if PyCrypto is installed, it is +# used instead. This is so that environments that require the use of PyCrypto +# are still supported. +if hasattr(RSA, 'RsaKey'): + _RSAKey = RSA.RsaKey +else: + _RSAKey = RSA._RSAobj + + +def _der_to_pem(der_key, marker): + """ + Perform a simple DER to PEM conversion. + """ + pem_key_chunks = [('-----BEGIN %s-----' % marker).encode('utf-8')] + + # Limit base64 output lines to 64 characters by limiting input lines to 48 characters. + for chunk_start in range(0, len(der_key), 48): + pem_key_chunks.append(b64encode(der_key[chunk_start:chunk_start + 48])) + + pem_key_chunks.append(('-----END %s-----' % marker).encode('utf-8')) + + return b'\n'.join(pem_key_chunks) + + +class RSAKey(Key): + """ + Performs signing and verification operations using + RSASSA-PKCS-v1_5 and the specified hash function. + This class requires PyCrypto package to be installed. + This is based off of the implementation in PyJWT 0.3.2 + """ + + SHA256 = Crypto.Hash.SHA256 + SHA384 = Crypto.Hash.SHA384 + SHA512 = Crypto.Hash.SHA512 + + def __init__(self, key, algorithm): + + if algorithm not in ALGORITHMS.RSA: + raise JWKError('hash_alg: %s is not a valid hash algorithm' % algorithm) + + self.hash_alg = { + ALGORITHMS.RS256: self.SHA256, + ALGORITHMS.RS384: self.SHA384, + ALGORITHMS.RS512: self.SHA512 + }.get(algorithm) + self._algorithm = algorithm + + if isinstance(key, _RSAKey): + self.prepared_key = key + return + + if isinstance(key, dict): + self._process_jwk(key) + return + + if isinstance(key, six.string_types): + key = key.encode('utf-8') + + if isinstance(key, six.binary_type): + if key.startswith(b'-----BEGIN CERTIFICATE-----'): + try: + self._process_cert(key) + except Exception as e: + raise JWKError(e) + return + + try: + self.prepared_key = RSA.importKey(key) + except Exception as e: + raise JWKError(e) + return + + raise JWKError('Unable to parse an RSA_JWK from key: %s' % key) + + def _process_jwk(self, jwk_dict): + if not jwk_dict.get('kty') == 'RSA': + raise JWKError("Incorrect key type. Expected: 'RSA', Recieved: %s" % jwk_dict.get('kty')) + + e = base64_to_long(jwk_dict.get('e', 256)) + n = base64_to_long(jwk_dict.get('n')) + params = (n, e) + + if 'd' in jwk_dict: + params += (base64_to_long(jwk_dict.get('d')),) + + extra_params = ['p', 'q', 'dp', 'dq', 'qi'] + + if any(k in jwk_dict for k in extra_params): + # Precomputed private key parameters are available. + if not all(k in jwk_dict for k in extra_params): + # These values must be present when 'p' is according to + # Section 6.3.2 of RFC7518, so if they are not we raise + # an error. + raise JWKError('Precomputed private key parameters are incomplete.') + + p = base64_to_long(jwk_dict.get('p')) + q = base64_to_long(jwk_dict.get('q')) + qi = base64_to_long(jwk_dict.get('qi')) + + # PyCrypto does not take the dp and dq as arguments, so we do + # not pass them. Furthermore, the parameter qi specified in + # the JWK is the inverse of q modulo p, whereas PyCrypto + # takes the inverse of p modulo q. We therefore switch the + # parameters to make the third parameter the inverse of the + # second parameter modulo the first parameter. + params += (q, p, qi) + + self.prepared_key = RSA.construct(params) + + return self.prepared_key + + def _process_cert(self, key): + pemLines = key.replace(b' ', b'').split() + certDer = base64url_decode(b''.join(pemLines[1:-1])) + certSeq = DerSequence() + certSeq.decode(certDer) + tbsSeq = DerSequence() + tbsSeq.decode(certSeq[0]) + self.prepared_key = RSA.importKey(tbsSeq[6]) + return + + def sign(self, msg): + try: + return PKCS1_v1_5.new(self.prepared_key).sign(self.hash_alg.new(msg)) + except Exception as e: + raise JWKError(e) + + def verify(self, msg, sig): + try: + return PKCS1_v1_5.new(self.prepared_key).verify(self.hash_alg.new(msg), sig) + except Exception: + return False + + def is_public(self): + return not self.prepared_key.has_private() + + def public_key(self): + if self.is_public(): + return self + return self.__class__(self.prepared_key.publickey(), self._algorithm) + + def to_pem(self, pem_format='PKCS8'): + if pem_format == 'PKCS8': + pkcs = 8 + elif pem_format == 'PKCS1': + pkcs = 1 + else: + raise ValueError("Invalid pem format specified: %r" % (pem_format,)) + + if self.is_public(): + # PyCrypto/dome always export public keys as PKCS8 + if pkcs == 8: + pem = self.prepared_key.exportKey('PEM') + else: + pkcs8_der = self.prepared_key.exportKey('DER') + pkcs1_der = rsa_public_key_pkcs8_to_pkcs1(pkcs8_der) + pem = _der_to_pem(pkcs1_der, 'RSA PUBLIC KEY') + return pem + else: + pem = self.prepared_key.exportKey('PEM', pkcs=pkcs) + return pem + + def to_dict(self): + data = { + 'alg': self._algorithm, + 'kty': 'RSA', + 'n': long_to_base64(self.prepared_key.n), + 'e': long_to_base64(self.prepared_key.e), + } + + if not self.is_public(): + # Section 6.3.2 of RFC7518 prescribes that when we include the + # optional parameters p and q, we must also include the values of + # dp and dq, which are not readily available from PyCrypto - so we + # calculate them. Moreover, PyCrypto stores the inverse of p + # modulo q rather than the inverse of q modulo p, so we switch + # p and q. As far as I can tell, this is OK - RFC7518 only + # asserts that p is the 'first factor', but does not specify + # what 'first' means in this case. + dp = self.prepared_key.d % (self.prepared_key.p - 1) + dq = self.prepared_key.d % (self.prepared_key.q - 1) + data.update({ + 'd': long_to_base64(self.prepared_key.d), + 'p': long_to_base64(self.prepared_key.q), + 'q': long_to_base64(self.prepared_key.p), + 'dp': long_to_base64(dq), + 'dq': long_to_base64(dp), + 'qi': long_to_base64(self.prepared_key.u), + }) + + return data diff --git a/jose/backends/rsa_backend.py b/jose/backends/rsa_backend.py new file mode 100644 index 0000000..c1f5539 --- /dev/null +++ b/jose/backends/rsa_backend.py @@ -0,0 +1,263 @@ +import binascii + +import six +from pyasn1.error import PyAsn1Error + +import rsa as pyrsa +import rsa.pem as pyrsa_pem + +from jose.backends.base import Key +from jose.backends._asn1 import ( + rsa_private_key_pkcs1_to_pkcs8, + rsa_private_key_pkcs8_to_pkcs1, + rsa_public_key_pkcs1_to_pkcs8, +) +from jose.constants import ALGORITHMS +from jose.exceptions import JWKError +from jose.utils import base64_to_long, long_to_base64 + + +LEGACY_INVALID_PKCS8_RSA_HEADER = binascii.unhexlify( + "30" # sequence + "8204BD" # DER-encoded sequence contents length of 1213 bytes -- INCORRECT STATIC LENGTH + "020100" # integer: 0 -- Version + "30" # sequence + "0D" # DER-encoded sequence contents length of 13 bytes -- PrivateKeyAlgorithmIdentifier + "06092A864886F70D010101" # OID -- rsaEncryption + "0500" # NULL -- parameters +) +ASN1_SEQUENCE_ID = binascii.unhexlify("30") +RSA_ENCRYPTION_ASN1_OID = "1.2.840.113549.1.1.1" + +# Functions gcd and rsa_recover_prime_factors were copied from cryptography 1.9 +# to enable pure python rsa module to be in compliance with section 6.3.1 of RFC7518 +# which requires only private exponent (d) for private key. + + +def _gcd(a, b): + """Calculate the Greatest Common Divisor of a and b. + + Unless b==0, the result will have the same sign as b (so that when + b is divided by it, the result comes out positive). + """ + while b: + a, b = b, (a % b) + return a + + +# Controls the number of iterations rsa_recover_prime_factors will perform +# to obtain the prime factors. Each iteration increments by 2 so the actual +# maximum attempts is half this number. +_MAX_RECOVERY_ATTEMPTS = 1000 + + +def _rsa_recover_prime_factors(n, e, d): + """ + Compute factors p and q from the private exponent d. We assume that n has + no more than two factors. This function is adapted from code in PyCrypto. + """ + # See 8.2.2(i) in Handbook of Applied Cryptography. + ktot = d * e - 1 + # The quantity d*e-1 is a multiple of phi(n), even, + # and can be represented as t*2^s. + t = ktot + while t % 2 == 0: + t = t // 2 + # Cycle through all multiplicative inverses in Zn. + # The algorithm is non-deterministic, but there is a 50% chance + # any candidate a leads to successful factoring. + # See "Digitalized Signatures and Public Key Functions as Intractable + # as Factorization", M. Rabin, 1979 + spotted = False + a = 2 + while not spotted and a < _MAX_RECOVERY_ATTEMPTS: + k = t + # Cycle through all values a^{t*2^i}=a^k + while k < ktot: + cand = pow(a, k, n) + # Check if a^k is a non-trivial root of unity (mod n) + if cand != 1 and cand != (n - 1) and pow(cand, 2, n) == 1: + # We have found a number such that (cand-1)(cand+1)=0 (mod n). + # Either of the terms divides n. + p = _gcd(cand + 1, n) + spotted = True + break + k *= 2 + # This value was not any good... let's try another! + a += 2 + if not spotted: + raise ValueError("Unable to compute factors p and q from exponent d.") + # Found ! + q, r = divmod(n, p) + assert r == 0 + p, q = sorted((p, q), reverse=True) + return (p, q) + + +def pem_to_spki(pem, fmt='PKCS8'): + key = RSAKey(pem, ALGORITHMS.RS256) + return key.to_pem(fmt) + + +def _legacy_private_key_pkcs8_to_pkcs1(pkcs8_key): + """Legacy RSA private key PKCS8-to-PKCS1 conversion. + + .. warning:: + + This is incorrect parsing and only works because the legacy PKCS1-to-PKCS8 + encoding was also incorrect. + """ + # Only allow this processing if the prefix matches + # AND the following byte indicates an ASN1 sequence, + # as we would expect with the legacy encoding. + if not pkcs8_key.startswith(LEGACY_INVALID_PKCS8_RSA_HEADER + ASN1_SEQUENCE_ID): + raise ValueError("Invalid private key encoding") + + return pkcs8_key[len(LEGACY_INVALID_PKCS8_RSA_HEADER):] + + +class RSAKey(Key): + SHA256 = 'SHA-256' + SHA384 = 'SHA-384' + SHA512 = 'SHA-512' + + def __init__(self, key, algorithm): + if algorithm not in ALGORITHMS.RSA: + raise JWKError('hash_alg: %s is not a valid hash algorithm' % algorithm) + + self.hash_alg = { + ALGORITHMS.RS256: self.SHA256, + ALGORITHMS.RS384: self.SHA384, + ALGORITHMS.RS512: self.SHA512 + }.get(algorithm) + self._algorithm = algorithm + + if isinstance(key, dict): + self._prepared_key = self._process_jwk(key) + return + + if isinstance(key, (pyrsa.PublicKey, pyrsa.PrivateKey)): + self._prepared_key = key + return + + if isinstance(key, six.string_types): + key = key.encode('utf-8') + + if isinstance(key, six.binary_type): + try: + self._prepared_key = pyrsa.PublicKey.load_pkcs1(key) + except ValueError: + try: + self._prepared_key = pyrsa.PublicKey.load_pkcs1_openssl_pem(key) + except ValueError: + try: + self._prepared_key = pyrsa.PrivateKey.load_pkcs1(key) + except ValueError: + try: + der = pyrsa_pem.load_pem(key, b'PRIVATE KEY') + try: + pkcs1_key = rsa_private_key_pkcs8_to_pkcs1(der) + except PyAsn1Error: + # If the key was encoded using the old, invalid, + # encoding then pyasn1 will throw an error attempting + # to parse the key. + pkcs1_key = _legacy_private_key_pkcs8_to_pkcs1(der) + self._prepared_key = pyrsa.PrivateKey.load_pkcs1(pkcs1_key, format="DER") + except ValueError as e: + raise JWKError(e) + return + raise JWKError('Unable to parse an RSA_JWK from key: %s' % key) + + def _process_jwk(self, jwk_dict): + if not jwk_dict.get('kty') == 'RSA': + raise JWKError("Incorrect key type. Expected: 'RSA', Recieved: %s" % jwk_dict.get('kty')) + + e = base64_to_long(jwk_dict.get('e')) + n = base64_to_long(jwk_dict.get('n')) + + if 'd' not in jwk_dict: + return pyrsa.PublicKey(e=e, n=n) + else: + d = base64_to_long(jwk_dict.get('d')) + extra_params = ['p', 'q', 'dp', 'dq', 'qi'] + + if any(k in jwk_dict for k in extra_params): + # Precomputed private key parameters are available. + if not all(k in jwk_dict for k in extra_params): + # These values must be present when 'p' is according to + # Section 6.3.2 of RFC7518, so if they are not we raise + # an error. + raise JWKError('Precomputed private key parameters are incomplete.') + + p = base64_to_long(jwk_dict['p']) + q = base64_to_long(jwk_dict['q']) + return pyrsa.PrivateKey(e=e, n=n, d=d, p=p, q=q) + else: + p, q = _rsa_recover_prime_factors(n, e, d) + return pyrsa.PrivateKey(n=n, e=e, d=d, p=p, q=q) + + def sign(self, msg): + return pyrsa.sign(msg, self._prepared_key, self.hash_alg) + + def verify(self, msg, sig): + try: + pyrsa.verify(msg, sig, self._prepared_key) + return True + except pyrsa.pkcs1.VerificationError: + return False + + def is_public(self): + return isinstance(self._prepared_key, pyrsa.PublicKey) + + def public_key(self): + if isinstance(self._prepared_key, pyrsa.PublicKey): + return self + return self.__class__(pyrsa.PublicKey(n=self._prepared_key.n, e=self._prepared_key.e), self._algorithm) + + def to_pem(self, pem_format='PKCS8'): + + if isinstance(self._prepared_key, pyrsa.PrivateKey): + der = self._prepared_key.save_pkcs1(format='DER') + if pem_format == 'PKCS8': + pkcs8_der = rsa_private_key_pkcs1_to_pkcs8(der) + pem = pyrsa_pem.save_pem(pkcs8_der, pem_marker='PRIVATE KEY') + elif pem_format == 'PKCS1': + pem = pyrsa_pem.save_pem(der, pem_marker='RSA PRIVATE KEY') + else: + raise ValueError("Invalid pem format specified: %r" % (pem_format,)) + else: + if pem_format == 'PKCS8': + pkcs1_der = self._prepared_key.save_pkcs1(format="DER") + pkcs8_der = rsa_public_key_pkcs1_to_pkcs8(pkcs1_der) + pem = pyrsa_pem.save_pem(pkcs8_der, pem_marker='PUBLIC KEY') + elif pem_format == 'PKCS1': + der = self._prepared_key.save_pkcs1(format='DER') + pem = pyrsa_pem.save_pem(der, pem_marker='RSA PUBLIC KEY') + else: + raise ValueError("Invalid pem format specified: %r" % (pem_format,)) + return pem + + def to_dict(self): + if not self.is_public(): + public_key = self.public_key()._prepared_key + else: + public_key = self._prepared_key + + data = { + 'alg': self._algorithm, + 'kty': 'RSA', + 'n': long_to_base64(public_key.n), + 'e': long_to_base64(public_key.e), + } + + if not self.is_public(): + data.update({ + 'd': long_to_base64(self._prepared_key.d), + 'p': long_to_base64(self._prepared_key.p), + 'q': long_to_base64(self._prepared_key.q), + 'dp': long_to_base64(self._prepared_key.exp1), + 'dq': long_to_base64(self._prepared_key.exp2), + 'qi': long_to_base64(self._prepared_key.coef), + }) + + return data diff --git a/jose/constants.py b/jose/constants.py new file mode 100644 index 0000000..eb14654 --- /dev/null +++ b/jose/constants.py @@ -0,0 +1,39 @@ +import hashlib + + +class Algorithms(object): + NONE = 'none' + HS256 = 'HS256' + HS384 = 'HS384' + HS512 = 'HS512' + RS256 = 'RS256' + RS384 = 'RS384' + RS512 = 'RS512' + ES256 = 'ES256' + ES384 = 'ES384' + ES512 = 'ES512' + + HMAC = {HS256, HS384, HS512} + RSA = {RS256, RS384, RS512} + EC = {ES256, ES384, ES512} + + SUPPORTED = HMAC.union(RSA).union(EC) + + ALL = SUPPORTED.union([NONE]) + + HASHES = { + HS256: hashlib.sha256, + HS384: hashlib.sha384, + HS512: hashlib.sha512, + RS256: hashlib.sha256, + RS384: hashlib.sha384, + RS512: hashlib.sha512, + ES256: hashlib.sha256, + ES384: hashlib.sha384, + ES512: hashlib.sha512, + } + + KEYS = {} + + +ALGORITHMS = Algorithms() diff --git a/jose/exceptions.py b/jose/exceptions.py new file mode 100644 index 0000000..22334d6 --- /dev/null +++ b/jose/exceptions.py @@ -0,0 +1,36 @@ + + +class JOSEError(Exception): + pass + + +class JWSError(JOSEError): + pass + + +class JWSSignatureError(JWSError): + pass + + +class JWSAlgorithmError(JWSError): + pass + + +class JWTError(JOSEError): + pass + + +class JWTClaimsError(JWTError): + pass + + +class JWTSignatureError(JWTError): + pass + + +class ExpiredSignatureError(JWTError): + pass + + +class JWKError(JOSEError): + pass diff --git a/jose/jwk.py b/jose/jwk.py new file mode 100644 index 0000000..87f30b4 --- /dev/null +++ b/jose/jwk.py @@ -0,0 +1,142 @@ + +import hashlib +import hmac +import six + +from jose.constants import ALGORITHMS +from jose.exceptions import JWKError +from jose.utils import base64url_decode, base64url_encode +from jose.utils import constant_time_string_compare +from jose.backends.base import Key + +try: + from jose.backends import RSAKey # noqa: F401 +except ImportError: + pass + +try: + from jose.backends import ECKey # noqa: F401 +except ImportError: + pass + + +def get_key(algorithm): + if algorithm in ALGORITHMS.KEYS: + return ALGORITHMS.KEYS[algorithm] + elif algorithm in ALGORITHMS.HMAC: + return HMACKey + elif algorithm in ALGORITHMS.RSA: + from jose.backends import RSAKey # noqa: F811 + return RSAKey + elif algorithm in ALGORITHMS.EC: + from jose.backends import ECKey # noqa: F811 + return ECKey + return None + + +def register_key(algorithm, key_class): + if not issubclass(key_class, Key): + raise TypeError("Key class not a subclass of jwk.Key") + ALGORITHMS.KEYS[algorithm] = key_class + ALGORITHMS.SUPPORTED.add(algorithm) + return True + + +def construct(key_data, algorithm=None): + """ + Construct a Key object for the given algorithm with the given + key_data. + """ + + # Allow for pulling the algorithm off of the passed in jwk. + if not algorithm and isinstance(key_data, dict): + algorithm = key_data.get('alg', None) + + if not algorithm: + raise JWKError('Unable to find a algorithm for key: %s' % key_data) + + key_class = get_key(algorithm) + if not key_class: + raise JWKError('Unable to find a algorithm for key: %s' % key_data) + return key_class(key_data, algorithm) + + +def get_algorithm_object(algorithm): + algorithms = { + ALGORITHMS.HS256: 'SHA256', + ALGORITHMS.HS384: 'SHA384', + ALGORITHMS.HS512: 'SHA512', + ALGORITHMS.RS256: 'SHA256', + ALGORITHMS.RS384: 'SHA384', + ALGORITHMS.RS512: 'SHA512', + ALGORITHMS.ES256: 'SHA256', + ALGORITHMS.ES384: 'SHA384', + ALGORITHMS.ES512: 'SHA512', + } + key = get_key(algorithm) + attr = algorithms.get(algorithm, None) + return getattr(key, attr) + + +class HMACKey(Key): + """ + Performs signing and verification operations using HMAC + and the specified hash function. + """ + SHA256 = hashlib.sha256 + SHA384 = hashlib.sha384 + SHA512 = hashlib.sha512 + + def __init__(self, key, algorithm): + if algorithm not in ALGORITHMS.HMAC: + raise JWKError('hash_alg: %s is not a valid hash algorithm' % algorithm) + self._algorithm = algorithm + self.hash_alg = get_algorithm_object(algorithm) + + if isinstance(key, dict): + self.prepared_key = self._process_jwk(key) + return + + if not isinstance(key, six.string_types) and not isinstance(key, bytes): + raise JWKError('Expecting a string- or bytes-formatted key.') + + if isinstance(key, six.text_type): + key = key.encode('utf-8') + + invalid_strings = [ + b'-----BEGIN PUBLIC KEY-----', + b'-----BEGIN RSA PUBLIC KEY-----', + b'-----BEGIN CERTIFICATE-----', + b'ssh-rsa' + ] + + if any(string_value in key for string_value in invalid_strings): + raise JWKError( + 'The specified key is an asymmetric key or x509 certificate and' + ' should not be used as an HMAC secret.') + + self.prepared_key = key + + def _process_jwk(self, jwk_dict): + if not jwk_dict.get('kty') == 'oct': + raise JWKError("Incorrect key type. Expected: 'oct', Recieved: %s" % jwk_dict.get('kty')) + + k = jwk_dict.get('k') + k = k.encode('utf-8') + k = bytes(k) + k = base64url_decode(k) + + return k + + def sign(self, msg): + return hmac.new(self.prepared_key, msg, self.hash_alg).digest() + + def verify(self, msg, sig): + return constant_time_string_compare(sig, self.sign(msg)) + + def to_dict(self): + return { + 'alg': self._algorithm, + 'kty': 'oct', + 'k': base64url_encode(self.prepared_key), + } diff --git a/jose/jws.py b/jose/jws.py new file mode 100644 index 0000000..293b32a --- /dev/null +++ b/jose/jws.py @@ -0,0 +1,273 @@ + +import binascii +import json +import six + +try: + from collections.abc import Mapping, Iterable # Python 3 +except ImportError: + from collections import Mapping, Iterable # Python 2, will be deprecated in Python 3.8 + +from jose import jwk +from jose.constants import ALGORITHMS +from jose.exceptions import JWSError +from jose.exceptions import JWSSignatureError +from jose.utils import base64url_encode +from jose.utils import base64url_decode + + +def sign(payload, key, headers=None, algorithm=ALGORITHMS.HS256): + """Signs a claims set and returns a JWS string. + + Args: + payload (str or dict): A string to sign + key (str or dict): The key to use for signing the claim set. Can be + individual JWK or JWK set. + headers (dict, optional): A set of headers that will be added to + the default headers. Any headers that are added as additional + headers will override the default headers. + algorithm (str, optional): The algorithm to use for signing the + the claims. Defaults to HS256. + + Returns: + str: The string representation of the header, claims, and signature. + + Raises: + JWSError: If there is an error signing the token. + + Examples: + + >>> jws.sign({'a': 'b'}, 'secret', algorithm='HS256') + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhIjoiYiJ9.jiMyrsmD8AoHWeQgmxZ5yq8z0lXS67_QGs52AzC8Ru8' + + """ + + if algorithm not in ALGORITHMS.SUPPORTED: + raise JWSError('Algorithm %s not supported.' % algorithm) + + encoded_header = _encode_header(algorithm, additional_headers=headers) + encoded_payload = _encode_payload(payload) + signed_output = _sign_header_and_claims(encoded_header, encoded_payload, algorithm, key) + + return signed_output + + +def verify(token, key, algorithms, verify=True): + """Verifies a JWS string's signature. + + Args: + token (str): A signed JWS to be verified. + key (str or dict): A key to attempt to verify the payload with. Can be + individual JWK or JWK set. + algorithms (str or list): Valid algorithms that should be used to verify the JWS. + + Returns: + str: The str representation of the payload, assuming the signature is valid. + + Raises: + JWSError: If there is an exception verifying a token. + + Examples: + + >>> token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhIjoiYiJ9.jiMyrsmD8AoHWeQgmxZ5yq8z0lXS67_QGs52AzC8Ru8' + >>> jws.verify(token, 'secret', algorithms='HS256') + + """ + + header, payload, signing_input, signature = _load(token) + + if verify: + _verify_signature(signing_input, header, signature, key, algorithms) + + return payload + + +def get_unverified_header(token): + """Returns the decoded headers without verification of any kind. + + Args: + token (str): A signed JWS to decode the headers from. + + Returns: + dict: The dict representation of the token headers. + + Raises: + JWSError: If there is an exception decoding the token. + """ + header, claims, signing_input, signature = _load(token) + return header + + +def get_unverified_headers(token): + """Returns the decoded headers without verification of any kind. + + This is simply a wrapper of get_unverified_header() for backwards + compatibility. + + Args: + token (str): A signed JWS to decode the headers from. + + Returns: + dict: The dict representation of the token headers. + + Raises: + JWSError: If there is an exception decoding the token. + """ + return get_unverified_header(token) + + +def get_unverified_claims(token): + """Returns the decoded claims without verification of any kind. + + Args: + token (str): A signed JWS to decode the headers from. + + Returns: + str: The str representation of the token claims. + + Raises: + JWSError: If there is an exception decoding the token. + """ + header, claims, signing_input, signature = _load(token) + return claims + + +def _encode_header(algorithm, additional_headers=None): + header = { + "typ": "JWT", + "alg": algorithm + } + + if additional_headers: + header.update(additional_headers) + + json_header = json.dumps( + header, + separators=(',', ':'), + sort_keys=True, + ).encode('utf-8') + + return base64url_encode(json_header) + + +def _encode_payload(payload): + if isinstance(payload, Mapping): + try: + payload = json.dumps( + payload, + separators=(',', ':'), + ).encode('utf-8') + except ValueError: + pass + + return base64url_encode(payload) + + +def _sign_header_and_claims(encoded_header, encoded_claims, algorithm, key_data): + signing_input = b'.'.join([encoded_header, encoded_claims]) + try: + key = jwk.construct(key_data, algorithm) + signature = key.sign(signing_input) + except Exception as e: + raise JWSError(e) + + encoded_signature = base64url_encode(signature) + + encoded_string = b'.'.join([encoded_header, encoded_claims, encoded_signature]) + + return encoded_string.decode('utf-8') + + +def _load(jwt): + if isinstance(jwt, six.text_type): + jwt = jwt.encode('utf-8') + try: + signing_input, crypto_segment = jwt.rsplit(b'.', 1) + header_segment, claims_segment = signing_input.split(b'.', 1) + header_data = base64url_decode(header_segment) + except ValueError: + raise JWSError('Not enough segments') + except (TypeError, binascii.Error): + raise JWSError('Invalid header padding') + + try: + header = json.loads(header_data.decode('utf-8')) + except ValueError as e: + raise JWSError('Invalid header string: %s' % e) + + if not isinstance(header, Mapping): + raise JWSError('Invalid header string: must be a json object') + + try: + payload = base64url_decode(claims_segment) + except (TypeError, binascii.Error): + raise JWSError('Invalid payload padding') + + try: + signature = base64url_decode(crypto_segment) + except (TypeError, binascii.Error): + raise JWSError('Invalid crypto padding') + + return (header, payload, signing_input, signature) + + +def _sig_matches_keys(keys, signing_input, signature, alg): + for key in keys: + key = jwk.construct(key, alg) + try: + if key.verify(signing_input, signature): + return True + except Exception: + pass + return False + + +def _get_keys(key): + + try: + key = json.loads(key) + except Exception: + pass + + # JWK Set per RFC 7517 + if 'keys' in key: + return key['keys'] + + # Individual JWK per RFC 7517 + elif 'kty' in key: + return (key,) + + # Some other mapping. Firebase uses just dict of kid, cert pairs + elif isinstance(key, Mapping): + values = key.values() + if values: + return values + return (key,) + + # Iterable but not text or mapping => list- or tuple-like + elif (isinstance(key, Iterable) and + not (isinstance(key, six.string_types) or isinstance(key, Mapping))): + return key + + # Scalar value, wrap in tuple. + else: + return (key,) + + +def _verify_signature(signing_input, header, signature, key='', algorithms=None): + + alg = header.get('alg') + if not alg: + raise JWSError('No algorithm was specified in the JWS header.') + + if algorithms is not None and alg not in algorithms: + raise JWSError('The specified alg value is not allowed') + + keys = _get_keys(key) + try: + if not _sig_matches_keys(keys, signing_input, signature, alg): + raise JWSSignatureError() + except JWSSignatureError: + raise JWSError('Signature verification failed.') + except JWSError: + raise JWSError('Invalid or unsupported algorithm: %s' % alg) diff --git a/jose/jwt.py b/jose/jwt.py new file mode 100644 index 0000000..ee3b98d --- /dev/null +++ b/jose/jwt.py @@ -0,0 +1,507 @@ + +import json + +from calendar import timegm +try: + from collections.abc import Mapping # Python3 +except ImportError: + from collections import Mapping # Python2, will be deprecated in Python 3.8 +from datetime import datetime +from datetime import timedelta +from six import string_types + +from jose import jws + +from .exceptions import JWSError +from .exceptions import JWTClaimsError +from .exceptions import JWTError +from .exceptions import ExpiredSignatureError +from .constants import ALGORITHMS +from .utils import timedelta_total_seconds, calculate_at_hash + + +def encode(claims, key, algorithm=ALGORITHMS.HS256, headers=None, access_token=None): + """Encodes a claims set and returns a JWT string. + + JWTs are JWS signed objects with a few reserved claims. + + Args: + claims (dict): A claims set to sign + key (str or dict): The key to use for signing the claim set. Can be + individual JWK or JWK set. + algorithm (str, optional): The algorithm to use for signing the + the claims. Defaults to HS256. + headers (dict, optional): A set of headers that will be added to + the default headers. Any headers that are added as additional + headers will override the default headers. + access_token (str, optional): If present, the 'at_hash' claim will + be calculated and added to the claims present in the 'claims' + parameter. + + Returns: + str: The string representation of the header, claims, and signature. + + Raises: + JWTError: If there is an error encoding the claims. + + Examples: + + >>> jwt.encode({'a': 'b'}, 'secret', algorithm='HS256') + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhIjoiYiJ9.jiMyrsmD8AoHWeQgmxZ5yq8z0lXS67_QGs52AzC8Ru8' + + """ + + for time_claim in ['exp', 'iat', 'nbf']: + + # Convert datetime to a intDate value in known time-format claims + if isinstance(claims.get(time_claim), datetime): + claims[time_claim] = timegm(claims[time_claim].utctimetuple()) + + if access_token: + claims['at_hash'] = calculate_at_hash(access_token, + ALGORITHMS.HASHES[algorithm]) + + return jws.sign(claims, key, headers=headers, algorithm=algorithm) + + +def decode(token, key, algorithms=None, options=None, audience=None, + issuer=None, subject=None, access_token=None): + """Verifies a JWT string's signature and validates reserved claims. + + Args: + token (str): A signed JWS to be verified. + key (str or dict): A key to attempt to verify the payload with. Can be + individual JWK or JWK set. + algorithms (str or list): Valid algorithms that should be used to verify the JWS. + audience (str): The intended audience of the token. If the "aud" claim is + included in the claim set, then the audience must be included and must equal + the provided claim. + issuer (str or iterable): Acceptable value(s) for the issuer of the token. + If the "iss" claim is included in the claim set, then the issuer must be + given and the claim in the token must be among the acceptable values. + subject (str): The subject of the token. If the "sub" claim is + included in the claim set, then the subject must be included and must equal + the provided claim. + access_token (str): An access token string. If the "at_hash" claim is included in the + claim set, then the access_token must be included, and it must match + the "at_hash" claim. + options (dict): A dictionary of options for skipping validation steps. + + defaults = { + 'verify_signature': True, + 'verify_aud': True, + 'verify_iat': True, + 'verify_exp': True, + 'verify_nbf': True, + 'verify_iss': True, + 'verify_sub': True, + 'verify_jti': True, + 'verify_at_hash': True, + 'require_aud': False, + 'require_iat': False, + 'require_exp': False, + 'require_nbf': False, + 'require_iss': False, + 'require_sub': False, + 'require_jti': False, + 'require_at_hash': False, + 'leeway': 0, + } + + Returns: + dict: The dict representation of the claims set, assuming the signature is valid + and all requested data validation passes. + + Raises: + JWTError: If the signature is invalid in any way. + ExpiredSignatureError: If the signature has expired. + JWTClaimsError: If any claim is invalid in any way. + + Examples: + + >>> payload = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhIjoiYiJ9.jiMyrsmD8AoHWeQgmxZ5yq8z0lXS67_QGs52AzC8Ru8' + >>> jwt.decode(payload, 'secret', algorithms='HS256') + + """ + + defaults = { + 'verify_signature': True, + 'verify_aud': True, + 'verify_iat': True, + 'verify_exp': True, + 'verify_nbf': True, + 'verify_iss': True, + 'verify_sub': True, + 'verify_jti': True, + 'verify_at_hash': True, + 'require_aud': False, + 'require_iat': False, + 'require_exp': False, + 'require_nbf': False, + 'require_iss': False, + 'require_sub': False, + 'require_jti': False, + 'require_at_hash': False, + 'leeway': 0, + } + + if options: + defaults.update(options) + + verify_signature = defaults.get('verify_signature', True) + + try: + payload = jws.verify(token, key, algorithms, verify=verify_signature) + except JWSError as e: + raise JWTError(e) + + # Needed for at_hash verification + algorithm = jws.get_unverified_header(token)['alg'] + + try: + claims = json.loads(payload.decode('utf-8')) + except ValueError as e: + raise JWTError('Invalid payload string: %s' % e) + + if not isinstance(claims, Mapping): + raise JWTError('Invalid payload string: must be a json object') + + _validate_claims(claims, audience=audience, issuer=issuer, + subject=subject, algorithm=algorithm, + access_token=access_token, + options=defaults) + + return claims + + +def get_unverified_header(token): + """Returns the decoded headers without verification of any kind. + + Args: + token (str): A signed JWT to decode the headers from. + + Returns: + dict: The dict representation of the token headers. + + Raises: + JWTError: If there is an exception decoding the token. + """ + try: + headers = jws.get_unverified_headers(token) + except Exception: + raise JWTError('Error decoding token headers.') + + return headers + + +def get_unverified_headers(token): + """Returns the decoded headers without verification of any kind. + + This is simply a wrapper of get_unverified_header() for backwards + compatibility. + + Args: + token (str): A signed JWT to decode the headers from. + + Returns: + dict: The dict representation of the token headers. + + Raises: + JWTError: If there is an exception decoding the token. + """ + return get_unverified_header(token) + + +def get_unverified_claims(token): + """Returns the decoded claims without verification of any kind. + + Args: + token (str): A signed JWT to decode the headers from. + + Returns: + dict: The dict representation of the token claims. + + Raises: + JWTError: If there is an exception decoding the token. + """ + try: + claims = jws.get_unverified_claims(token) + except Exception: + raise JWTError('Error decoding token claims.') + + try: + claims = json.loads(claims.decode('utf-8')) + except ValueError as e: + raise JWTError('Invalid claims string: %s' % e) + + if not isinstance(claims, Mapping): + raise JWTError('Invalid claims string: must be a json object') + + return claims + + +def _validate_iat(claims): + """Validates that the 'iat' claim is valid. + + The "iat" (issued at) claim identifies the time at which the JWT was + issued. This claim can be used to determine the age of the JWT. Its + value MUST be a number containing a NumericDate value. Use of this + claim is OPTIONAL. + + Args: + claims (dict): The claims dictionary to validate. + """ + + if 'iat' not in claims: + return + + try: + int(claims['iat']) + except ValueError: + raise JWTClaimsError('Issued At claim (iat) must be an integer.') + + +def _validate_nbf(claims, leeway=0): + """Validates that the 'nbf' claim is valid. + + The "nbf" (not before) claim identifies the time before which the JWT + MUST NOT be accepted for processing. The processing of the "nbf" + claim requires that the current date/time MUST be after or equal to + the not-before date/time listed in the "nbf" claim. Implementers MAY + provide for some small leeway, usually no more than a few minutes, to + account for clock skew. Its value MUST be a number containing a + NumericDate value. Use of this claim is OPTIONAL. + + Args: + claims (dict): The claims dictionary to validate. + leeway (int): The number of seconds of skew that is allowed. + """ + + if 'nbf' not in claims: + return + + try: + nbf = int(claims['nbf']) + except ValueError: + raise JWTClaimsError('Not Before claim (nbf) must be an integer.') + + now = timegm(datetime.utcnow().utctimetuple()) + + if nbf > (now + leeway): + raise JWTClaimsError('The token is not yet valid (nbf)') + + +def _validate_exp(claims, leeway=0): + """Validates that the 'exp' claim is valid. + + The "exp" (expiration time) claim identifies the expiration time on + or after which the JWT MUST NOT be accepted for processing. The + processing of the "exp" claim requires that the current date/time + MUST be before the expiration date/time listed in the "exp" claim. + Implementers MAY provide for some small leeway, usually no more than + a few minutes, to account for clock skew. Its value MUST be a number + containing a NumericDate value. Use of this claim is OPTIONAL. + + Args: + claims (dict): The claims dictionary to validate. + leeway (int): The number of seconds of skew that is allowed. + """ + + if 'exp' not in claims: + return + + try: + exp = int(claims['exp']) + except ValueError: + raise JWTClaimsError('Expiration Time claim (exp) must be an integer.') + + now = timegm(datetime.utcnow().utctimetuple()) + + if exp < (now - leeway): + raise ExpiredSignatureError('Signature has expired.') + + +def _validate_aud(claims, audience=None): + """Validates that the 'aud' claim is valid. + + The "aud" (audience) claim identifies the recipients that the JWT is + intended for. Each principal intended to process the JWT MUST + identify itself with a value in the audience claim. If the principal + processing the claim does not identify itself with a value in the + "aud" claim when this claim is present, then the JWT MUST be + rejected. In the general case, the "aud" value is an array of case- + sensitive strings, each containing a StringOrURI value. In the + special case when the JWT has one audience, the "aud" value MAY be a + single case-sensitive string containing a StringOrURI value. The + interpretation of audience values is generally application specific. + Use of this claim is OPTIONAL. + + Args: + claims (dict): The claims dictionary to validate. + audience (str): The audience that is verifying the token. + """ + + if 'aud' not in claims: + # if audience: + # raise JWTError('Audience claim expected, but not in claims') + return + + audience_claims = claims['aud'] + if isinstance(audience_claims, string_types): + audience_claims = [audience_claims] + if not isinstance(audience_claims, list): + raise JWTClaimsError('Invalid claim format in token') + if any(not isinstance(c, string_types) for c in audience_claims): + raise JWTClaimsError('Invalid claim format in token') + if audience not in audience_claims: + raise JWTClaimsError('Invalid audience') + + +def _validate_iss(claims, issuer=None): + """Validates that the 'iss' claim is valid. + + The "iss" (issuer) claim identifies the principal that issued the + JWT. The processing of this claim is generally application specific. + The "iss" value is a case-sensitive string containing a StringOrURI + value. Use of this claim is OPTIONAL. + + Args: + claims (dict): The claims dictionary to validate. + issuer (str or iterable): Acceptable value(s) for the issuer that + signed the token. + """ + + if issuer is not None: + if isinstance(issuer, string_types): + issuer = (issuer,) + if claims.get('iss') not in issuer: + raise JWTClaimsError('Invalid issuer') + + +def _validate_sub(claims, subject=None): + """Validates that the 'sub' claim is valid. + + The "sub" (subject) claim identifies the principal that is the + subject of the JWT. The claims in a JWT are normally statements + about the subject. The subject value MUST either be scoped to be + locally unique in the context of the issuer or be globally unique. + The processing of this claim is generally application specific. The + "sub" value is a case-sensitive string containing a StringOrURI + value. Use of this claim is OPTIONAL. + + Args: + claims (dict): The claims dictionary to validate. + subject (str): The subject of the token. + """ + + if 'sub' not in claims: + return + + if not isinstance(claims['sub'], string_types): + raise JWTClaimsError('Subject must be a string.') + + if subject is not None: + if claims.get('sub') != subject: + raise JWTClaimsError('Invalid subject') + + +def _validate_jti(claims): + """Validates that the 'jti' claim is valid. + + The "jti" (JWT ID) claim provides a unique identifier for the JWT. + The identifier value MUST be assigned in a manner that ensures that + there is a negligible probability that the same value will be + accidentally assigned to a different data object; if the application + uses multiple issuers, collisions MUST be prevented among values + produced by different issuers as well. The "jti" claim can be used + to prevent the JWT from being replayed. The "jti" value is a case- + sensitive string. Use of this claim is OPTIONAL. + + Args: + claims (dict): The claims dictionary to validate. + """ + if 'jti' not in claims: + return + + if not isinstance(claims['jti'], string_types): + raise JWTClaimsError('JWT ID must be a string.') + + +def _validate_at_hash(claims, access_token, algorithm): + """ + Validates that the 'at_hash' is valid. + + Its value is the base64url encoding of the left-most half of the hash + of the octets of the ASCII representation of the access_token value, + where the hash algorithm used is the hash algorithm used in the alg + Header Parameter of the ID Token's JOSE Header. For instance, if the + alg is RS256, hash the access_token value with SHA-256, then take the + left-most 128 bits and base64url encode them. The at_hash value is a + case sensitive string. Use of this claim is OPTIONAL. + + Args: + claims (dict): The claims dictionary to validate. + access_token (str): The access token returned by the OpenID Provider. + algorithm (str): The algorithm used to sign the JWT, as specified by + the token headers. + """ + if 'at_hash' not in claims: + return + + if not access_token: + msg = 'No access_token provided to compare against at_hash claim.' + raise JWTClaimsError(msg) + + try: + expected_hash = calculate_at_hash(access_token, + ALGORITHMS.HASHES[algorithm]) + except (TypeError, ValueError): + msg = 'Unable to calculate at_hash to verify against token claims.' + raise JWTClaimsError(msg) + + if claims['at_hash'] != expected_hash: + raise JWTClaimsError('at_hash claim does not match access_token.') + + +def _validate_claims(claims, audience=None, issuer=None, subject=None, + algorithm=None, access_token=None, options=None): + + leeway = options.get('leeway', 0) + + if isinstance(leeway, timedelta): + leeway = timedelta_total_seconds(leeway) + + for require_claim in [ + e[len("require_"):] for e in options.keys() if e.startswith("require_") and options[e] + ]: + if require_claim not in claims: + raise JWTError('missing required key "%s" among claims' % require_claim) + else: + options['verify_' + require_claim] = True # override verify when required + + if not isinstance(audience, (string_types, type(None))): + raise JWTError('audience must be a string or None') + + if options.get('verify_iat'): + _validate_iat(claims) + + if options.get('verify_nbf'): + _validate_nbf(claims, leeway=leeway) + + if options.get('verify_exp'): + _validate_exp(claims, leeway=leeway) + + if options.get('verify_aud'): + _validate_aud(claims, audience=audience) + + if options.get('verify_iss'): + _validate_iss(claims, issuer=issuer) + + if options.get('verify_sub'): + _validate_sub(claims, subject=subject) + + if options.get('verify_jti'): + _validate_jti(claims) + + if options.get('verify_at_hash'): + _validate_at_hash(claims, access_token, algorithm) diff --git a/jose/utils.py b/jose/utils.py new file mode 100644 index 0000000..2b98472 --- /dev/null +++ b/jose/utils.py @@ -0,0 +1,134 @@ + +import base64 +import hmac +import six +import struct +import sys + +if sys.version_info > (3,): + # Deal with integer compatibilities between Python 2 and 3. + # Using `from builtins import int` is not supported on AppEngine. + long = int + + +# Piggyback of the backends implementation of the function that converts a long +# to a bytes stream. Some plumbing is necessary to have the signatures match. +try: + from Crypto.Util.number import long_to_bytes +except ImportError: + try: + from cryptography.utils import int_to_bytes as _long_to_bytes + + def long_to_bytes(n, blocksize=0): + return _long_to_bytes(n, blocksize or None) + + except ImportError: + from ecdsa.ecdsa import int_to_string as _long_to_bytes + + def long_to_bytes(n, blocksize=0): + ret = _long_to_bytes(n) + if blocksize == 0: + return ret + else: + assert len(ret) <= blocksize + padding = blocksize - len(ret) + return b'\x00' * padding + ret + + +def long_to_base64(data, size=0): + return base64.urlsafe_b64encode(long_to_bytes(data, size)).strip(b'=') + + +def int_arr_to_long(arr): + return long(''.join(["%02x" % byte for byte in arr]), 16) + + +def base64_to_long(data): + if isinstance(data, six.text_type): + data = data.encode("ascii") + + # urlsafe_b64decode will happily convert b64encoded data + _d = base64.urlsafe_b64decode(bytes(data) + b'==') + return int_arr_to_long(struct.unpack('%sB' % len(_d), _d)) + + +def calculate_at_hash(access_token, hash_alg): + """Helper method for calculating an access token + hash, as described in http://openid.net/specs/openid-connect-core-1_0.html#CodeIDToken + + Its value is the base64url encoding of the left-most half of the hash of the octets + of the ASCII representation of the access_token value, where the hash algorithm + used is the hash algorithm used in the alg Header Parameter of the ID Token's JOSE + Header. For instance, if the alg is RS256, hash the access_token value with SHA-256, + then take the left-most 128 bits and base64url encode them. The at_hash value is a + case sensitive string. + + Args: + access_token (str): An access token string. + hash_alg (callable): A callable returning a hash object, e.g. hashlib.sha256 + + """ + hash_digest = hash_alg(access_token.encode('utf-8')).digest() + cut_at = int(len(hash_digest) / 2) + truncated = hash_digest[:cut_at] + at_hash = base64url_encode(truncated) + return at_hash.decode('utf-8') + + +def base64url_decode(input): + """Helper method to base64url_decode a string. + + Args: + input (str): A base64url_encoded string to decode. + + """ + rem = len(input) % 4 + + if rem > 0: + input += b'=' * (4 - rem) + + return base64.urlsafe_b64decode(input) + + +def base64url_encode(input): + """Helper method to base64url_encode a string. + + Args: + input (str): A base64url_encoded string to encode. + + """ + return base64.urlsafe_b64encode(input).replace(b'=', b'') + + +def timedelta_total_seconds(delta): + """Helper method to determine the total number of seconds + from a timedelta. + + Args: + delta (timedelta): A timedelta to convert to seconds. + """ + return delta.days * 24 * 60 * 60 + delta.seconds + + +def constant_time_string_compare(a, b): + """Helper for comparing string in constant time, independent + of the python version being used. + + Args: + a (str): A string to compare + b (str): A string to compare + """ + + try: + return hmac.compare_digest(a, b) + except AttributeError: + + if len(a) != len(b): + return False + + result = 0 + + for x, y in zip(a, b): + result |= ord(x) ^ ord(y) + + return result == 0 diff --git a/python_jose.egg-info/PKG-INFO b/python_jose.egg-info/PKG-INFO new file mode 100644 index 0000000..0f4788b --- /dev/null +++ b/python_jose.egg-info/PKG-INFO @@ -0,0 +1,136 @@ +Metadata-Version: 1.1 +Name: python-jose +Version: 3.1.0 +Summary: JOSE implementation in Python +Home-page: http://github.com/mpdavis/python-jose +Author: Michael Davis +Author-email: mike.philip.davis@gmail.com +License: MIT +Description: python-jose + =========== + + A JOSE implementation in Python + + |Build Status| |Coverage Status| |Docs| + + Docs are available on ReadTheDocs_. + + The JavaScript Object Signing and Encryption (JOSE) technologies - JSON + Web Signature (JWS), JSON Web Encryption (JWE), JSON Web Key (JWK), and + JSON Web Algorithms (JWA) - collectively can be used to encrypt and/or + sign content using a variety of algorithms. While the full set of + permutations is extremely large, and might be daunting to some, it is + expected that most applications will only use a small set of algorithms + to meet their needs. + + + Installation + ------------ + + :: + + $ pip install python-jose[cryptography] + + + Cryptographic Backends + ---------------------- + + As of 3.1.0, python-jose implements four different cryptographic backends. + The backend must be selected as an extra when installing python-jose. + If you do not select a backend, the native-python backend will be installed. + + Unless otherwise noted, all backends support all operations. + + Due to complexities with setuptools, the native-python backend is always installed, + even if you select a different backend on install. + We recommend that you remove unnecessary dependencies in production. + + #. cryptography + + * This backend uses `pyca/cryptography`_ for all cryptographic operations. + This is the recommended backend and is selected over all other backends if any others are present. + * Installation: ``pip install python-jose[cryptography]`` + * Unused dependencies: + + * ``rsa`` + * ``ecdsa`` + * ``pyasn1`` + + #. pycryptodome + + * This backend uses `pycryptodome`_ for all cryptographic operations. + * Installation: ``pip install python-jose[pycryptodome]`` + * Unused dependencies: + + * ``rsa`` + + #. native-python + + * This backend uses `python-rsa`_ and `python-ecdsa`_ for all cryptographic operations. + This backend is always installed but any other backend will take precedence if one is installed. + * Installation: ``pip install python-jose`` + + .. note:: + + The native-python backend cannot process certificates. + + #. pycrypto + + * This backend uses `pycrypto`_ for all cryptographic operations. + * Installation: ``pip install python-jose[pycrypto]`` + * Unused dependencies: + + * ``rsa`` + + .. warning:: + + The `pycrypto`_ project has not been maintained since 2013. + This backend is maintained for legacy compatibility purposes only. + Do not use this backend unless you cannot use any of the others. + + Usage + ----- + + .. code-block:: python + + >>> from jose import jwt + >>> token = jwt.encode({'key': 'value'}, 'secret', algorithm='HS256') + u'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJ2YWx1ZSJ9.FG-8UppwHaFp1LgRYQQeS6EDQF7_6-bMFegNucHjmWg' + + >>> jwt.decode(token, 'secret', algorithms=['HS256']) + {u'key': u'value'} + + + Thanks + ------ + + This library was originally based heavily on the work of the folks over at PyJWT_. + + .. |Build Status| image:: https://travis-ci.org/mpdavis/python-jose.svg?branch=master + :target: https://travis-ci.org/mpdavis/python-jose + .. |Coverage Status| image:: http://codecov.io/github/mpdavis/python-jose/coverage.svg?branch=master + :target: http://codecov.io/github/mpdavis/python-jose?branch=master + .. |Docs| image:: https://readthedocs.org/projects/python-jose/badge/ + :target: https://python-jose.readthedocs.org/en/latest/ + .. _ReadTheDocs: https://python-jose.readthedocs.org/en/latest/ + .. _PyJWT: https://github.com/jpadilla/pyjwt + .. _pyca/cryptography: http://cryptography.io/ + .. _pycryptodome: https://pycryptodome.readthedocs.io/en/latest/ + .. _pycrypto: https://www.dlitz.net/software/pycrypto/ + .. _python-ecdsa: https://github.com/warner/python-ecdsa + .. _python-rsa: https://stuvel.eu/rsa + +Keywords: jose jws jwe jwt json web token security signing +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Natural Language :: English +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Utilities diff --git a/python_jose.egg-info/SOURCES.txt b/python_jose.egg-info/SOURCES.txt new file mode 100644 index 0000000..4456b12 --- /dev/null +++ b/python_jose.egg-info/SOURCES.txt @@ -0,0 +1,103 @@ +LICENSE +MANIFEST.in +README.rst +requirements-dev.txt +requirements-rtd.txt +requirements.txt +setup.cfg +setup.py +tox.ini +docs/Makefile +docs/conf.py +docs/index.rst +docs/make.bat +docs/jwk/api.rst +docs/jwk/index.rst +docs/jws/api.rst +docs/jws/index.rst +docs/jwt/api.rst +docs/jwt/index.rst +jose/__init__.py +jose/constants.py +jose/exceptions.py +jose/jwk.py +jose/jws.py +jose/jwt.py +jose/utils.py +jose/backends/__init__.py +jose/backends/_asn1.py +jose/backends/base.py +jose/backends/cryptography_backend.py +jose/backends/ecdsa_backend.py +jose/backends/pycrypto_backend.py +jose/backends/rsa_backend.py +python_jose.egg-info/PKG-INFO +python_jose.egg-info/SOURCES.txt +python_jose.egg-info/dependency_links.txt +python_jose.egg-info/requires.txt +python_jose.egg-info/top_level.txt +tests/__init__.py +tests/__init__.pyc +tests/test_asn1.py +tests/test_backends.py +tests/test_firebase.py +tests/test_jwk.py +tests/test_jws.py +tests/test_jwt.py +tests/test_utils.py +tests/__pycache__/__init__.cpython-34.pyc +tests/__pycache__/__init__.cpython-36.pyc +tests/__pycache__/test_firebase.cpython-27-PYTEST.pyc +tests/__pycache__/test_firebase.cpython-34-PYTEST.pyc +tests/__pycache__/test_firebase.cpython-36-PYTEST.pyc +tests/__pycache__/test_jwk.cpython-27-PYTEST.pyc +tests/__pycache__/test_jwk.cpython-34-PYTEST.pyc +tests/__pycache__/test_jwk.cpython-36-PYTEST.pyc +tests/__pycache__/test_jws.cpython-27-PYTEST.pyc +tests/__pycache__/test_jws.cpython-34-PYTEST.pyc +tests/__pycache__/test_jws.cpython-36-PYTEST.pyc +tests/__pycache__/test_jwt.cpython-27-PYTEST.pyc +tests/__pycache__/test_jwt.cpython-34-PYTEST.pyc +tests/__pycache__/test_jwt.cpython-36-PYTEST.pyc +tests/__pycache__/test_tmp.cpython-36-PYTEST.pyc +tests/__pycache__/test_utils.cpython-27-PYTEST.pyc +tests/__pycache__/test_utils.cpython-34-PYTEST.pyc +tests/__pycache__/test_utils.cpython-36-PYTEST.pyc +tests/algorithms/__init__.py +tests/algorithms/__init__.pyc +tests/algorithms/test_EC.py +tests/algorithms/test_EC_compat.py +tests/algorithms/test_HMAC.py +tests/algorithms/test_RSA.py +tests/algorithms/test_RSA_compat.py +tests/algorithms/test_base.py +tests/algorithms/__pycache__/__init__.cpython-34.pyc +tests/algorithms/__pycache__/__init__.cpython-36.pyc +tests/algorithms/__pycache__/test_EC.cpython-27-PYTEST.pyc +tests/algorithms/__pycache__/test_EC.cpython-34-PYTEST.pyc +tests/algorithms/__pycache__/test_EC.cpython-36-PYTEST.pyc +tests/algorithms/__pycache__/test_Ed25519.cpython-27-PYTEST.pyc +tests/algorithms/__pycache__/test_Ed25519.cpython-34-PYTEST.pyc +tests/algorithms/__pycache__/test_Ed25519.cpython-36-PYTEST.pyc +tests/algorithms/__pycache__/test_HMAC.cpython-27-PYTEST.pyc +tests/algorithms/__pycache__/test_HMAC.cpython-34-PYTEST.pyc +tests/algorithms/__pycache__/test_HMAC.cpython-36-PYTEST.pyc +tests/algorithms/__pycache__/test_RSA.cpython-27-PYTEST.pyc +tests/algorithms/__pycache__/test_RSA.cpython-34-PYTEST.pyc +tests/algorithms/__pycache__/test_RSA.cpython-36-PYTEST.pyc +tests/algorithms/__pycache__/test_base.cpython-27-PYTEST.pyc +tests/algorithms/__pycache__/test_base.cpython-34-PYTEST.pyc +tests/algorithms/__pycache__/test_base.cpython-36-PYTEST.pyc +tests/rfc/__init__.py +tests/rfc/__init__.pyc +tests/rfc/test_rfc7520.py +tests/rfc/test_rfc8037.py +tests/rfc/test_rfc8410.py +tests/rfc/__pycache__/__init__.cpython-34.pyc +tests/rfc/__pycache__/__init__.cpython-36.pyc +tests/rfc/__pycache__/test_rfc7520.cpython-27-PYTEST.pyc +tests/rfc/__pycache__/test_rfc7520.cpython-34-PYTEST.pyc +tests/rfc/__pycache__/test_rfc7520.cpython-36-PYTEST.pyc +tests/rfc/__pycache__/test_rfc8037.cpython-27-PYTEST.pyc +tests/rfc/__pycache__/test_rfc8037.cpython-34-PYTEST.pyc +tests/rfc/__pycache__/test_rfc8037.cpython-36-PYTEST.pyc \ No newline at end of file diff --git a/python_jose.egg-info/dependency_links.txt b/python_jose.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/python_jose.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/python_jose.egg-info/requires.txt b/python_jose.egg-info/requires.txt new file mode 100644 index 0000000..3c30d39 --- /dev/null +++ b/python_jose.egg-info/requires.txt @@ -0,0 +1,15 @@ +six <2.0 +ecdsa <1.0 +rsa +pyasn1 + +[cryptography] +cryptography + +[pycrypto] +pycrypto >=2.6.0, <2.7.0 +pyasn1 + +[pycryptodome] +pycryptodome >=3.3.1, <4.0.0 +pyasn1 diff --git a/python_jose.egg-info/top_level.txt b/python_jose.egg-info/top_level.txt new file mode 100644 index 0000000..3ac440a --- /dev/null +++ b/python_jose.egg-info/top_level.txt @@ -0,0 +1,2 @@ +jose +jose/backends diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..495b2d3 --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,14 @@ +PyYAML==3.11 +cov-core==1.15.0 +coverage==4.4 +coveralls==1.5.1 +cryptography==2.4.2 +docopt==0.6.2 +nose==1.3.6 +py==1.5.4 +pytest==4.1.1 +pytest-cov==2.6.1 +# wsgiref is included in python standard library in Python 3, and will fail to install. +wsgiref==0.1.2; python_version < "3.0" +-r requirements.txt +-r requirements-rtd.txt diff --git a/requirements-rtd.txt b/requirements-rtd.txt new file mode 100644 index 0000000..36792c2 --- /dev/null +++ b/requirements-rtd.txt @@ -0,0 +1 @@ +sphinxcontrib-napoleon==0.3.4 \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..04dd5d1 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +pycryptodome +six +rsa +ecdsa +pyasn1 diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..156e77e --- /dev/null +++ b/setup.cfg @@ -0,0 +1,19 @@ +[flake8] +max-line-length = 119 + +[wheel] +universal = 1 + +[aliases] +test = pytest + +[tool:pytest] +addopts = --cov-report term-missing --cov jose +testpaths = tests +python_files = test_*.py + +[egg_info] +tag_build = +tag_date = 0 +tag_svn_revision = 0 + diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..ed196b8 --- /dev/null +++ b/setup.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +import os +import platform + +import jose + +from setuptools import setup + + +with open(os.path.join(os.path.dirname(__file__), 'README.rst')) as readme: + long_description = readme.read() + + +def get_packages(package): + """ + Return root package and all sub-packages. + """ + return [ + dirpath + for dirpath, dirnames, filenames in os.walk(package) + if os.path.exists(os.path.join(dirpath, '__init__.py')) + ] + + +def _cryptography_version(): + # pyca/cryptography dropped support for PyPy < 5.4 in 2.5 + # https://cryptography.io/en/latest/changelog/#v2-5 + if platform.python_implementation() == 'PyPy' and platform.python_version() < '5.4': + return 'cryptography < 2.5' + + return 'cryptography' + + +pyasn1 = ['pyasn1'] +extras_require = { + 'cryptography': [_cryptography_version()], + 'pycrypto': ['pycrypto >=2.6.0, <2.7.0'] + pyasn1, + 'pycryptodome': ['pycryptodome >=3.3.1, <4.0.0'] + pyasn1, +} +legacy_backend_requires = ['ecdsa <1.0', 'rsa'] + pyasn1 +install_requires = ['six <2.0'] + +# TODO: work this into the extras selection instead. +install_requires += legacy_backend_requires + + +setup( + name='python-jose', + version=jose.__version__, + author='Michael Davis', + author_email='mike.philip.davis@gmail.com', + description='JOSE implementation in Python', + license='MIT', + keywords='jose jws jwe jwt json web token security signing', + url='http://github.com/mpdavis/python-jose', + packages=get_packages('jose'), + long_description=long_description, + classifiers=[ + 'Development Status :: 5 - Production/Stable', + 'Intended Audience :: Developers', + 'Natural Language :: English', + 'License :: OSI Approved :: MIT License', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: Implementation :: PyPy', + 'Topic :: Utilities', + ], + extras_require=extras_require, + setup_requires=['pytest-runner'], + tests_require=[ + 'six', + 'ecdsa', + 'pytest', + 'pytest-cov', + 'pytest-runner', + ], + install_requires=install_requires +) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/__init__.pyc b/tests/__init__.pyc new file mode 100644 index 0000000..d34bd92 Binary files /dev/null and b/tests/__init__.pyc differ diff --git a/tests/__pycache__/__init__.cpython-34.pyc b/tests/__pycache__/__init__.cpython-34.pyc new file mode 100644 index 0000000..16b0b3b Binary files /dev/null and b/tests/__pycache__/__init__.cpython-34.pyc differ diff --git a/tests/__pycache__/__init__.cpython-36.pyc b/tests/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000..5ef2520 Binary files /dev/null and b/tests/__pycache__/__init__.cpython-36.pyc differ diff --git a/tests/__pycache__/test_firebase.cpython-27-PYTEST.pyc b/tests/__pycache__/test_firebase.cpython-27-PYTEST.pyc new file mode 100644 index 0000000..0395c13 Binary files /dev/null and b/tests/__pycache__/test_firebase.cpython-27-PYTEST.pyc differ diff --git a/tests/__pycache__/test_firebase.cpython-34-PYTEST.pyc b/tests/__pycache__/test_firebase.cpython-34-PYTEST.pyc new file mode 100644 index 0000000..619e418 Binary files /dev/null and b/tests/__pycache__/test_firebase.cpython-34-PYTEST.pyc differ diff --git a/tests/__pycache__/test_firebase.cpython-36-PYTEST.pyc b/tests/__pycache__/test_firebase.cpython-36-PYTEST.pyc new file mode 100644 index 0000000..72c0de7 Binary files /dev/null and b/tests/__pycache__/test_firebase.cpython-36-PYTEST.pyc differ diff --git a/tests/__pycache__/test_jwk.cpython-27-PYTEST.pyc b/tests/__pycache__/test_jwk.cpython-27-PYTEST.pyc new file mode 100644 index 0000000..9f0b61e Binary files /dev/null and b/tests/__pycache__/test_jwk.cpython-27-PYTEST.pyc differ diff --git a/tests/__pycache__/test_jwk.cpython-34-PYTEST.pyc b/tests/__pycache__/test_jwk.cpython-34-PYTEST.pyc new file mode 100644 index 0000000..b0698ca Binary files /dev/null and b/tests/__pycache__/test_jwk.cpython-34-PYTEST.pyc differ diff --git a/tests/__pycache__/test_jwk.cpython-36-PYTEST.pyc b/tests/__pycache__/test_jwk.cpython-36-PYTEST.pyc new file mode 100644 index 0000000..9bdd09e Binary files /dev/null and b/tests/__pycache__/test_jwk.cpython-36-PYTEST.pyc differ diff --git a/tests/__pycache__/test_jws.cpython-27-PYTEST.pyc b/tests/__pycache__/test_jws.cpython-27-PYTEST.pyc new file mode 100644 index 0000000..8c58381 Binary files /dev/null and b/tests/__pycache__/test_jws.cpython-27-PYTEST.pyc differ diff --git a/tests/__pycache__/test_jws.cpython-34-PYTEST.pyc b/tests/__pycache__/test_jws.cpython-34-PYTEST.pyc new file mode 100644 index 0000000..466bd91 Binary files /dev/null and b/tests/__pycache__/test_jws.cpython-34-PYTEST.pyc differ diff --git a/tests/__pycache__/test_jws.cpython-36-PYTEST.pyc b/tests/__pycache__/test_jws.cpython-36-PYTEST.pyc new file mode 100644 index 0000000..d81f49a Binary files /dev/null and b/tests/__pycache__/test_jws.cpython-36-PYTEST.pyc differ diff --git a/tests/__pycache__/test_jwt.cpython-27-PYTEST.pyc b/tests/__pycache__/test_jwt.cpython-27-PYTEST.pyc new file mode 100644 index 0000000..0e59a37 Binary files /dev/null and b/tests/__pycache__/test_jwt.cpython-27-PYTEST.pyc differ diff --git a/tests/__pycache__/test_jwt.cpython-34-PYTEST.pyc b/tests/__pycache__/test_jwt.cpython-34-PYTEST.pyc new file mode 100644 index 0000000..c74a003 Binary files /dev/null and b/tests/__pycache__/test_jwt.cpython-34-PYTEST.pyc differ diff --git a/tests/__pycache__/test_jwt.cpython-36-PYTEST.pyc b/tests/__pycache__/test_jwt.cpython-36-PYTEST.pyc new file mode 100644 index 0000000..c3c4e2f Binary files /dev/null and b/tests/__pycache__/test_jwt.cpython-36-PYTEST.pyc differ diff --git a/tests/__pycache__/test_tmp.cpython-36-PYTEST.pyc b/tests/__pycache__/test_tmp.cpython-36-PYTEST.pyc new file mode 100644 index 0000000..c90e07b Binary files /dev/null and b/tests/__pycache__/test_tmp.cpython-36-PYTEST.pyc differ diff --git a/tests/__pycache__/test_utils.cpython-27-PYTEST.pyc b/tests/__pycache__/test_utils.cpython-27-PYTEST.pyc new file mode 100644 index 0000000..1c8958b Binary files /dev/null and b/tests/__pycache__/test_utils.cpython-27-PYTEST.pyc differ diff --git a/tests/__pycache__/test_utils.cpython-34-PYTEST.pyc b/tests/__pycache__/test_utils.cpython-34-PYTEST.pyc new file mode 100644 index 0000000..7305868 Binary files /dev/null and b/tests/__pycache__/test_utils.cpython-34-PYTEST.pyc differ diff --git a/tests/__pycache__/test_utils.cpython-36-PYTEST.pyc b/tests/__pycache__/test_utils.cpython-36-PYTEST.pyc new file mode 100644 index 0000000..abcec0f Binary files /dev/null and b/tests/__pycache__/test_utils.cpython-36-PYTEST.pyc differ diff --git a/tests/algorithms/__init__.py b/tests/algorithms/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/algorithms/__init__.pyc b/tests/algorithms/__init__.pyc new file mode 100644 index 0000000..0fa9dbd Binary files /dev/null and b/tests/algorithms/__init__.pyc differ diff --git a/tests/algorithms/__pycache__/__init__.cpython-34.pyc b/tests/algorithms/__pycache__/__init__.cpython-34.pyc new file mode 100644 index 0000000..af1a9e4 Binary files /dev/null and b/tests/algorithms/__pycache__/__init__.cpython-34.pyc differ diff --git a/tests/algorithms/__pycache__/__init__.cpython-36.pyc b/tests/algorithms/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000..31b3fb9 Binary files /dev/null and b/tests/algorithms/__pycache__/__init__.cpython-36.pyc differ diff --git a/tests/algorithms/__pycache__/test_EC.cpython-27-PYTEST.pyc b/tests/algorithms/__pycache__/test_EC.cpython-27-PYTEST.pyc new file mode 100644 index 0000000..94115e5 Binary files /dev/null and b/tests/algorithms/__pycache__/test_EC.cpython-27-PYTEST.pyc differ diff --git a/tests/algorithms/__pycache__/test_EC.cpython-34-PYTEST.pyc b/tests/algorithms/__pycache__/test_EC.cpython-34-PYTEST.pyc new file mode 100644 index 0000000..bcbc34b Binary files /dev/null and b/tests/algorithms/__pycache__/test_EC.cpython-34-PYTEST.pyc differ diff --git a/tests/algorithms/__pycache__/test_EC.cpython-36-PYTEST.pyc b/tests/algorithms/__pycache__/test_EC.cpython-36-PYTEST.pyc new file mode 100644 index 0000000..bbac5f9 Binary files /dev/null and b/tests/algorithms/__pycache__/test_EC.cpython-36-PYTEST.pyc differ diff --git a/tests/algorithms/__pycache__/test_Ed25519.cpython-27-PYTEST.pyc b/tests/algorithms/__pycache__/test_Ed25519.cpython-27-PYTEST.pyc new file mode 100644 index 0000000..f9e55a9 Binary files /dev/null and b/tests/algorithms/__pycache__/test_Ed25519.cpython-27-PYTEST.pyc differ diff --git a/tests/algorithms/__pycache__/test_Ed25519.cpython-34-PYTEST.pyc b/tests/algorithms/__pycache__/test_Ed25519.cpython-34-PYTEST.pyc new file mode 100644 index 0000000..f3e7a4f Binary files /dev/null and b/tests/algorithms/__pycache__/test_Ed25519.cpython-34-PYTEST.pyc differ diff --git a/tests/algorithms/__pycache__/test_Ed25519.cpython-36-PYTEST.pyc b/tests/algorithms/__pycache__/test_Ed25519.cpython-36-PYTEST.pyc new file mode 100644 index 0000000..2f1adcf Binary files /dev/null and b/tests/algorithms/__pycache__/test_Ed25519.cpython-36-PYTEST.pyc differ diff --git a/tests/algorithms/__pycache__/test_HMAC.cpython-27-PYTEST.pyc b/tests/algorithms/__pycache__/test_HMAC.cpython-27-PYTEST.pyc new file mode 100644 index 0000000..69da1b6 Binary files /dev/null and b/tests/algorithms/__pycache__/test_HMAC.cpython-27-PYTEST.pyc differ diff --git a/tests/algorithms/__pycache__/test_HMAC.cpython-34-PYTEST.pyc b/tests/algorithms/__pycache__/test_HMAC.cpython-34-PYTEST.pyc new file mode 100644 index 0000000..2e1a3ee Binary files /dev/null and b/tests/algorithms/__pycache__/test_HMAC.cpython-34-PYTEST.pyc differ diff --git a/tests/algorithms/__pycache__/test_HMAC.cpython-36-PYTEST.pyc b/tests/algorithms/__pycache__/test_HMAC.cpython-36-PYTEST.pyc new file mode 100644 index 0000000..8923400 Binary files /dev/null and b/tests/algorithms/__pycache__/test_HMAC.cpython-36-PYTEST.pyc differ diff --git a/tests/algorithms/__pycache__/test_RSA.cpython-27-PYTEST.pyc b/tests/algorithms/__pycache__/test_RSA.cpython-27-PYTEST.pyc new file mode 100644 index 0000000..17c57f6 Binary files /dev/null and b/tests/algorithms/__pycache__/test_RSA.cpython-27-PYTEST.pyc differ diff --git a/tests/algorithms/__pycache__/test_RSA.cpython-34-PYTEST.pyc b/tests/algorithms/__pycache__/test_RSA.cpython-34-PYTEST.pyc new file mode 100644 index 0000000..6833a7d Binary files /dev/null and b/tests/algorithms/__pycache__/test_RSA.cpython-34-PYTEST.pyc differ diff --git a/tests/algorithms/__pycache__/test_RSA.cpython-36-PYTEST.pyc b/tests/algorithms/__pycache__/test_RSA.cpython-36-PYTEST.pyc new file mode 100644 index 0000000..f2ede44 Binary files /dev/null and b/tests/algorithms/__pycache__/test_RSA.cpython-36-PYTEST.pyc differ diff --git a/tests/algorithms/__pycache__/test_base.cpython-27-PYTEST.pyc b/tests/algorithms/__pycache__/test_base.cpython-27-PYTEST.pyc new file mode 100644 index 0000000..0ca6e9b Binary files /dev/null and b/tests/algorithms/__pycache__/test_base.cpython-27-PYTEST.pyc differ diff --git a/tests/algorithms/__pycache__/test_base.cpython-34-PYTEST.pyc b/tests/algorithms/__pycache__/test_base.cpython-34-PYTEST.pyc new file mode 100644 index 0000000..85b8c68 Binary files /dev/null and b/tests/algorithms/__pycache__/test_base.cpython-34-PYTEST.pyc differ diff --git a/tests/algorithms/__pycache__/test_base.cpython-36-PYTEST.pyc b/tests/algorithms/__pycache__/test_base.cpython-36-PYTEST.pyc new file mode 100644 index 0000000..7fa239b Binary files /dev/null and b/tests/algorithms/__pycache__/test_base.cpython-36-PYTEST.pyc differ diff --git a/tests/algorithms/test_EC.py b/tests/algorithms/test_EC.py new file mode 100644 index 0000000..7f012af --- /dev/null +++ b/tests/algorithms/test_EC.py @@ -0,0 +1,200 @@ + +from jose.constants import ALGORITHMS +from jose.exceptions import JOSEError, JWKError + +from jose.backends import ECKey +try: + from jose.backends.ecdsa_backend import ECDSAECKey + import ecdsa +except ImportError: + ECDSAECKey = ecdsa = None + +try: + from jose.backends.cryptography_backend import CryptographyECKey + from cryptography.hazmat.primitives.asymmetric import ec as CryptographyEc + from cryptography.hazmat.backends import default_backend as CryptographyBackend +except ImportError: + CryptographyECKey = CryptographyEc = CryptographyBackend = None + +import pytest + +private_key = """-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIOiSs10XnBlfykk5zsJRmzYybKdMlGniSJcssDvUcF6DoAoGCCqGSM49 +AwEHoUQDQgAE7gb4edKJ7ul9IgomCdcOebQTZ8qktqtBfRKboa71CfEKzBruUi+D +WkG0HJWIORlPbvXME+DRh6G/yVOKnTm88Q== +-----END EC PRIVATE KEY-----""" + +# Private key generated using NIST256p curve +TOO_SHORT_PRIVATE_KEY = b"""\ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIMlUyYGOpjV4bbW0C9FKS2zkspD0L/5vJLnr6sJoLdc+oAoGCCqGSM49 +AwEHoUQDQgAE6TDUNj5QXl+RKdZvBV+cg7Td6cJRB+Ta8XAhIuCAzonq0Ix//1+C +pNSsy11sIKmMl61YJzxvZ6WkNluBmkDPCQ== +-----END EC PRIVATE KEY----- +""" + +# ES256 signatures generated to test conversion logic +DER_SIGNATURE = ( + b"0F\x02!\x00\x89yG\x81W\x01\x11\x9b0\x08\xa4\xd0\xe3g([\x07\xb5\x01\xb3" + b"\x9d\xdf \xd1\xbc\xedK\x01\x87:}\xf2\x02!\x00\xb2shTA\x00\x1a\x13~\xba" + b"J\xdb\xeem\x12\x1e\xfeMO\x04\xb2[\x86A\xbd\xc6hu\x953X\x1e" +) +RAW_SIGNATURE = ( + b"\x89yG\x81W\x01\x11\x9b0\x08\xa4\xd0\xe3g([\x07\xb5\x01\xb3\x9d\xdf " + b"\xd1\xbc\xedK\x01\x87:}\xf2\xb2shTA\x00\x1a\x13~\xbaJ\xdb\xeem\x12\x1e" + b"\xfeMO\x04\xb2[\x86A\xbd\xc6hu\x953X\x1e" +) + + +def _backend_exception_types(): + """Build the backend exception types based on available backends.""" + if None not in (ECDSAECKey, ecdsa): + yield ECDSAECKey, ecdsa.BadDigestError + + if CryptographyECKey is not None: + yield CryptographyECKey, TypeError + + +@pytest.mark.ecdsa +@pytest.mark.skipif( + None in (ECDSAECKey, ecdsa), + reason="python-ecdsa backend not available" +) +def test_key_from_ecdsa(): + key = ecdsa.SigningKey.from_pem(private_key) + assert not ECKey(key, ALGORITHMS.ES256).is_public() + + +@pytest.mark.cryptography +@pytest.mark.skipif(CryptographyECKey is None, reason="pyca/cryptography backend not available") +@pytest.mark.parametrize("algorithm, expected_length", ( + (ALGORITHMS.ES256, 32), + (ALGORITHMS.ES384, 48), + (ALGORITHMS.ES512, 66) +)) +def test_cryptography_sig_component_length(algorithm, expected_length): + # Put mapping inside here to avoid more complex handling for test runs that do not have pyca/cryptography + mapping = { + ALGORITHMS.ES256: CryptographyEc.SECP256R1, + ALGORITHMS.ES384: CryptographyEc.SECP384R1, + ALGORITHMS.ES512: CryptographyEc.SECP521R1, + } + key = CryptographyECKey( + CryptographyEc.generate_private_key(mapping[algorithm](), backend=CryptographyBackend()), + algorithm + ) + assert key._sig_component_length() == expected_length + + +@pytest.mark.cryptography +@pytest.mark.skipif(CryptographyECKey is None, reason="pyca/cryptography backend not available") +def test_cryptograhy_der_to_raw(): + key = CryptographyECKey(private_key, ALGORITHMS.ES256) + assert key._der_to_raw(DER_SIGNATURE) == RAW_SIGNATURE + + +@pytest.mark.cryptography +@pytest.mark.skipif(CryptographyECKey is None, reason="pyca/cryptography backend not available") +def test_cryptograhy_raw_to_der(): + key = CryptographyECKey(private_key, ALGORITHMS.ES256) + assert key._raw_to_der(RAW_SIGNATURE) == DER_SIGNATURE + + +class TestECAlgorithm: + + def test_key_from_pem(self): + assert not ECKey(private_key, ALGORITHMS.ES256).is_public() + + def test_to_pem(self): + key = ECKey(private_key, ALGORITHMS.ES256) + assert not key.is_public() + assert key.to_pem().strip() == private_key.strip().encode('utf-8') + + public_pem = key.public_key().to_pem() + assert ECKey(public_pem, ALGORITHMS.ES256).is_public() + + @pytest.mark.parametrize("Backend,ExceptionType", _backend_exception_types()) + def test_key_too_short(self, Backend, ExceptionType): + key = Backend(TOO_SHORT_PRIVATE_KEY, ALGORITHMS.ES512) + with pytest.raises(ExceptionType): + key.sign(b'foo') + + def test_get_public_key(self): + key = ECKey(private_key, ALGORITHMS.ES256) + pubkey = key.public_key() + pubkey2 = pubkey.public_key() + assert pubkey == pubkey2 + + def test_string_secret(self): + key = 'secret' + with pytest.raises(JOSEError): + ECKey(key, ALGORITHMS.ES256) + + def test_object(self): + key = object() + with pytest.raises(JOSEError): + ECKey(key, ALGORITHMS.ES256) + + def test_invalid_algorithm(self): + with pytest.raises(JWKError): + ECKey(private_key, 'nonexistent') + + with pytest.raises(JWKError): + ECKey({'kty': 'bla'}, ALGORITHMS.ES256) + + def test_EC_jwk(self): + key = { + "kty": "EC", + "kid": "bilbo.baggins@hobbiton.example", + "use": "sig", + "crv": "P-521", + "x": "AHKZLLOsCOzz5cY97ewNUajB957y-C-U88c3v13nmGZx6sYl_oJXu9A5RkTKqjqvjyekWF-7ytDyRXYgCF5cj0Kt", + "y": "AdymlHvOiLxXkEhayXQnNCvDX4h9htZaCJN34kfmC6pV5OhQHiraVySsUdaQkAgDPrwQrJmbnX9cwlGfP-HqHZR1", + "d": "AAhRON2r9cqXX1hg-RoI6R1tX5p2rUAYdmpHZoC1XNM56KtscrX6zbKipQrCW9CGZH3T4ubpnoTKLDYJ_fF3_rJt", + } + + assert not ECKey(key, ALGORITHMS.ES512).is_public() + + del key['d'] + + # We are now dealing with a public key. + assert ECKey(key, ALGORITHMS.ES512).is_public() + + del key['x'] + + # This key is missing a required parameter. + with pytest.raises(JWKError): + ECKey(key, ALGORITHMS.ES512) + + def test_verify(self): + key = ECKey(private_key, ALGORITHMS.ES256) + msg = b'test' + signature = key.sign(msg) + public_key = key.public_key() + + assert bool(public_key.verify(msg, signature)) + assert not bool(public_key.verify(msg, b'not a signature')) + + def assert_parameters(self, as_dict, private): + assert isinstance(as_dict, dict) + + # Public parameters should always be there. + assert 'x' in as_dict + assert 'y' in as_dict + assert 'crv' in as_dict + + assert 'kty' in as_dict + assert as_dict['kty'] == 'EC' + + if private: + # Private parameters as well + assert 'd' in as_dict + + else: + # Private parameters should be absent + assert 'd' not in as_dict + + def test_to_dict(self): + key = ECKey(private_key, ALGORITHMS.ES256) + self.assert_parameters(key.to_dict(), private=True) + self.assert_parameters(key.public_key().to_dict(), private=False) diff --git a/tests/algorithms/test_EC_compat.py b/tests/algorithms/test_EC_compat.py new file mode 100644 index 0000000..1fc6dab --- /dev/null +++ b/tests/algorithms/test_EC_compat.py @@ -0,0 +1,72 @@ +import pytest + +try: + from jose.backends.ecdsa_backend import ECDSAECKey + from jose.backends.cryptography_backend import CryptographyECKey +except ImportError: + ECDSAECKey = CryptographyECKey = None +from jose.constants import ALGORITHMS + +from .test_EC import private_key + + +@pytest.mark.backend_compatibility +@pytest.mark.skipif( + None in (ECDSAECKey, CryptographyECKey), + reason="Multiple crypto backends not available for backend compatibility tests" +) +class TestBackendEcdsaCompatibility(object): + + @pytest.mark.parametrize("BackendSign", [ECDSAECKey, CryptographyECKey]) + @pytest.mark.parametrize("BackendVerify", [ECDSAECKey, CryptographyECKey]) + def test_signing_parity(self, BackendSign, BackendVerify): + key_sign = BackendSign(private_key, ALGORITHMS.ES256) + key_verify = BackendVerify(private_key, ALGORITHMS.ES256).public_key() + + msg = b'test' + sig = key_sign.sign(msg) + + # valid signature + assert key_verify.verify(msg, sig) + + # invalid signature + assert not key_verify.verify(msg, b'n' * 64) + + @pytest.mark.parametrize("BackendFrom", [ECDSAECKey, CryptographyECKey]) + @pytest.mark.parametrize("BackendTo", [ECDSAECKey, CryptographyECKey]) + def test_public_key_to_pem(self, BackendFrom, BackendTo): + key = BackendFrom(private_key, ALGORITHMS.ES256) + key2 = BackendTo(private_key, ALGORITHMS.ES256) + + assert key.public_key().to_pem().strip() == key2.public_key().to_pem().strip() + + @pytest.mark.parametrize("BackendFrom", [ECDSAECKey, CryptographyECKey]) + @pytest.mark.parametrize("BackendTo", [ECDSAECKey, CryptographyECKey]) + def test_private_key_to_pem(self, BackendFrom, BackendTo): + key = BackendFrom(private_key, ALGORITHMS.ES256) + key2 = BackendTo(private_key, ALGORITHMS.ES256) + + assert key.to_pem().strip() == key2.to_pem().strip() + + @pytest.mark.parametrize("BackendFrom", [ECDSAECKey, CryptographyECKey]) + @pytest.mark.parametrize("BackendTo", [ECDSAECKey, CryptographyECKey]) + def test_public_key_load_cycle(self, BackendFrom, BackendTo): + key = BackendFrom(private_key, ALGORITHMS.ES256) + pubkey = key.public_key() + + pub_pem_source = pubkey.to_pem().strip() + + pub_target = BackendTo(pub_pem_source, ALGORITHMS.ES256) + + assert pub_pem_source == pub_target.to_pem().strip() + + @pytest.mark.parametrize("BackendFrom", [ECDSAECKey, CryptographyECKey]) + @pytest.mark.parametrize("BackendTo", [ECDSAECKey, CryptographyECKey]) + def test_private_key_load_cycle(self, BackendFrom, BackendTo): + key = BackendFrom(private_key, ALGORITHMS.ES256) + + pem_source = key.to_pem().strip() + + target = BackendTo(pem_source, ALGORITHMS.ES256) + + assert pem_source == target.to_pem().strip() diff --git a/tests/algorithms/test_HMAC.py b/tests/algorithms/test_HMAC.py new file mode 100644 index 0000000..e84c2c0 --- /dev/null +++ b/tests/algorithms/test_HMAC.py @@ -0,0 +1,45 @@ + +from jose.constants import ALGORITHMS +from jose.exceptions import JOSEError +from jose.jwk import HMACKey + +import pytest + + +class TestHMACAlgorithm: + + def test_non_string_key(self): + with pytest.raises(JOSEError): + HMACKey(object(), ALGORITHMS.HS256) + + def test_RSA_key(self): + key = "-----BEGIN PUBLIC KEY-----" + with pytest.raises(JOSEError): + HMACKey(key, ALGORITHMS.HS256) + + key = "-----BEGIN RSA PUBLIC KEY-----" + with pytest.raises(JOSEError): + HMACKey(key, ALGORITHMS.HS256) + + key = "-----BEGIN CERTIFICATE-----" + with pytest.raises(JOSEError): + HMACKey(key, ALGORITHMS.HS256) + + key = "ssh-rsa" + with pytest.raises(JOSEError): + HMACKey(key, ALGORITHMS.HS256) + + def test_to_dict(self): + passphrase = 'The quick brown fox jumps over the lazy dog' + encoded = b'VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZw' + key = HMACKey(passphrase, ALGORITHMS.HS256) + + as_dict = key.to_dict() + assert 'alg' in as_dict + assert as_dict['alg'] == ALGORITHMS.HS256 + + assert 'kty' in as_dict + assert as_dict['kty'] == 'oct' + + assert 'k' in as_dict + assert as_dict['k'] == encoded diff --git a/tests/algorithms/test_RSA.py b/tests/algorithms/test_RSA.py new file mode 100644 index 0000000..97aeb20 --- /dev/null +++ b/tests/algorithms/test_RSA.py @@ -0,0 +1,385 @@ +import base64 +import sys + +try: + from jose.backends.rsa_backend import RSAKey as PurePythonRSAKey + from jose.backends import rsa_backend +except ImportError: + PurePythonRSAKey = rsa_backend = None + +try: + from Crypto.PublicKey import RSA as PyCryptoRSA + from jose.backends.pycrypto_backend import RSAKey as PyCryptoRSAKey +except ImportError: + PyCryptoRSA = PyCryptoRSAKey = None + +try: + from cryptography.hazmat.backends import default_backend + from cryptography.hazmat.primitives.asymmetric import rsa as pyca_rsa + from jose.backends.cryptography_backend import CryptographyRSAKey +except ImportError: + default_backend = pyca_rsa = CryptographyRSAKey = None + +from jose.backends import RSAKey +from jose.constants import ALGORITHMS +from jose.exceptions import JOSEError, JWKError + +import pytest + +# Deal with integer compatibilities between Python 2 and 3. +# Using `from builtins import int` is not supported on AppEngine. +if sys.version_info > (3,): + long = int + +private_key_4096_pkcs1 = b"""-----BEGIN RSA PRIVATE KEY----- +MIIJKwIBAAKCAgEAtSKfSeI0fukRIX38AHlKB1YPpX8PUYN2JdvfM+XjNmLfU1M7 +4N0VmdzIX95sneQGO9kC2xMIE+AIlt52Yf/KgBZggAlS9Y0Vx8DsSL2HvOjguAdX +ir3vYLvAyyHin/mUisJOqccFKChHKjnk0uXy/38+1r17/cYTp76brKpU1I4kM20M +//dbvLBWjfzyw9ehufr74aVwr+0xJfsBVr2oaQFww/XHGz69Q7yHK6DbxYO4w4q2 +sIfcC4pT8XTPHo4JZ2M733Ea8a7HxtZS563/mhhRZLU5aynQpwaVv2U++CL6EvGt +8TlNZOkeRv8wz+Rt8B70jzoRpVK36rR+pHKlXhMGT619v82LneTdsqA25Wi2Ld/c +0niuul24A6+aaj2u9SWbxA9LmVtFntvNbRaHXE1SLpLPoIp8uppGF02Nz2v3ld8g +CnTTWfq/BQ80Qy8e0coRRABECZrjIMzHEg6MloRDy4na0pRQv61VogqRKDU2r3/V +ezFPQDb3ciYsZjWBr3HpNOkUjTrvLmFyOE9Q5R/qQGmc6BYtfk5rn7iIfXlkJAZH +XhBy+ElBuiBM+YSkFM7dH92sSIoZ05V4MP09Xcppx7kdwsJy72Sust9Hnd9B7V35 +YnVF6W791lVHnenhCJOziRmkH4xLLbPkaST2Ks3IHH7tVltM6NsRk3jNdVMCAwEA +AQKCAgEArx+0JXigDHtFZr4pYEPjwMgCBJ2dr8+L8PptB/4g+LoK9MKqR7M4aTO+ +PoILPXPyWvZq/meeDakyZLrcdc8ad1ArKF7baDBpeGEbkRA9JfV5HjNq/ea4gyvD +MCGou8ZPSQCnkRmr8LFQbJDgnM5Za5AYrwEv2aEh67IrTHq53W83rMioIumCNiG+ +7TQ7egEGiYsQ745GLrECLZhKKRTgt/T+k1cSk1LLJawme5XgJUw+3D9GddJEepvY +oL+wZ/gnO2ADyPnPdQ7oc2NPcFMXpmIQf29+/g7FflatfQhkIv+eC6bB51DhdMi1 +zyp2hOhzKg6jn74ixVX+Hts2/cMiAPu0NaWmU9n8g7HmXWc4+uSO/fssGjI3DLYK +d5xnhrq4a3ZO5oJLeMO9U71+Ykctg23PTHwNAGrsPYdjGcBnJEdtbXa31agI5PAG +6rgGUY3iSoWqHLgBTxrX04TWVvLQi8wbxh7BEF0yasOeZKxdE2IWYg75zGsjluyH +lOnpRa5lSf6KZ6thh9eczFHYtS4DvYBcZ9hZW/g87ie28SkBFxxl0brYt9uKNYJv +uajVG8kT80AC7Wzg2q7Wmnoww3JNJUbNths5dqKyUSlMFMIB/vOePFHLrA6qDfAn +sQHgUb9WHhUrYsH20XKpqR2OjmWU05bV4pSMW/JwG37o+px1yKECggEBANnwx0d7 +ksEMvJjeN5plDy3eMLifBI+6SL/o5TXDoFM6rJxF+0UP70uouYJq2dI+DCSA6c/E +sn7WAOirY177adKcBV8biwAtmKHnFnCs/kwAZq8lMvQPtNPJ/vq2n40kO48h8fxb +eGcmyAqFPZ4YKSxrPA4cdbHIuFSt9WyaUcVFmzdTFHVlRP70EXdmXHt84byWNB4C +Heq8zmrNxPNAi65nEkUks7iBQMtuvyV2+aXjDOTBMCd66IhIh2iZq1O7kXUwgh1O +H9hCa7oriHyAdgkKdKCWocmbPPENOETgjraA9wRIXwOYTDb1X5hMvi1mCHo8xjMj +u4szD03xJVi7WrsCggEBANTEblCkxEyhJqaMZF3U3df2Yr/ZtHqsrTr4lwB/MOKk +zmuSrROxheEkKIsxbiV+AxTvtPR1FQrlqbhTJRwy+pw4KPJ7P4fq2R/YBqvXSNBC +amTt6l2XdXqnAk3A++cOEZ2lU9ubfgdeN2Ih8rgdn1LWeOSjCWfExmkoU61/Xe6x +AMeXKQSlHKSnX9voxuE2xINHeU6ZAKy1kGmrJtEiWnI8b8C4s8fTyDtXJ1Lasys0 +iHO2Tz2jUhf4IJwb87Lk7Ize2MrI+oPzVDXlmkbjkB4tYyoiRTj8rk8pwBW/HVv0 +02pjOLTa4kz1kQ3lsZ/3As4zfNi7mWEhadmEsAIfYkkCggEBANO39r/Yqj5kUyrm +ZXnVxyM2AHq58EJ4I4hbhZ/vRWbVTy4ZRfpXeo4zgNPTXXvCzyT/HyS53vUcjJF7 +PfPdpXX2H7m/Fg+8O9S8m64mQHwwv5BSQOecAnzkdJG2q9T/Z+Sqg1w2uAbtQ9QE +kFFvA0ClhBfpSeTGK1wICq3QVLOh5SGf0fYhxR8wl284v4svTFRaTpMAV3Pcq2JS +N4xgHdH1S2hkOTt6RSnbklGg/PFMWxA3JMKVwiPy4aiZ8DhNtQb1ctFpPcJm9CRN +ejAI06IAyD/hVZZ2+oLp5snypHFjY5SDgdoKL7AMOyvHEdEkmAO32ot/oQefOLTt +GOzURVUCggEBALSx5iYi6HtT2SlUzeBKaeWBYDgiwf31LGGKwWMwoem5oX0GYmr5 +NwQP20brQeohbKiZMwrxbF+G0G60Xi3mtaN6pnvYZAogTymWI4RJH5OO9CCnVYUK +nkD+GRzDqqt97UP/Joq5MX08bLiwsBvhPG/zqVQzikdQfFjOYNJV+wY92LWpELLb +Lso/Q0/WDyExjA8Z4lH36vTCddTn/91Y2Ytu/FGmCzjICaMrzz+0cLlesgvjZsSo +MY4dskQiEQN7G9I/Z8pAiVEKlBf52N4fYUPfs/oShMty/O5KPNG7L0nrUKlnfr9J +rStC2l/9FK8P7pgEbiD6obY11FlhMMF8udECggEBAIKhvOFtipD1jqDOpjOoR9sK +/lRR5bVVWQfamMDN1AwmjJbVHS8hhtYUM/4sh2p12P6RgoO8fODf1vEcWFh3xxNZ +E1pPCPaICD9i5U+NRvPz2vC900HcraLRrUFaRzwhqOOknYJSBrGzW+Cx3YSeaOCg +nKyI8B5gw4C0G0iL1dSsz2bR1O4GNOVfT3R6joZEXATFo/Kc2L0YAvApBNUYvY0k +bjJ/JfTO5060SsWftf4iw3jrhSn9RwTTYdq/kErGFWvDGJn2MiuhMe2onNfVzIGR +mdUxHwi1ulkspAn/fmY7f0hZpskDwcHyZmbKZuk+NU/FJ8IAcmvk9y7m25nSSc8= +-----END RSA PRIVATE KEY-----""" +private_key_2048_pkcs1 = b"""-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAom6GcUPchmHxBuV3zJ60EPC7y30WiiVxn1WXSPHmfqaj0q2U +xS03YugkYmX9lB/EQ6Z5bOY9VuL1oMudL6Dkb9aYYEBZHVgejV7vtYuYT19QMesn +AsmGq8etie7XyWHzfWTxljbF53yvxXJMixcFzebAov9pUiV9Hmy3hYVLw3J1NXVg +gPZpUT2oF+qAayhPsOi2b0CrIE3FvioDx7IiRXKFpV/1gah3NRSKxCrsxV6V+UGO ++trP1ViWiu4oXB5j25kZmkgI0lXG60p58DUUeCOnEemvurltf9T9IEs7LGBEzUYm +itGSY4ZOY3MabPypRfFRRotZEDyZjshq4xfXAwIDAQABAoIBAFclRzoTd4gdmevi +RwDgEKmaDpchGGurpScgC5eWONywWOpaOJwFI1cMRyEHqSHEXU8STMkxSa2I/NF1 +DHMWNhkOoBfbzjPhKBse2Sqkp2XGNEdj6z0ik/8rlR6QpvMjezhGZRr7bfhBPCiJ +pylkg7exWp7Yu0/YTyV4nImlNz23GvrYHFtzDzTtn9gW4fe46wI08s4PqH/TyBh8 +QkwkTwOKTk6n/xz2hND/shUOGjaoS0o6y4+8v3O1JYUWa7YZaIFofvF/dHR0yieg +2gQjc0c6+VeBm8dEbn3he+KnIBwQbWsiCuWL6Jq4XPtMbqutfovIYf9lRB+3q2PI +VSh3mwECgYEAzhOhG+usoxjJGk2wVJH5wnHL0zyH8gWF4SnnxwwdBOF4kdLB2eva +SJsi8rJQMT0TC4wZ6TsD2fJXGazIyM6OnD+52AViiUsLVS5MR7qEMNitdkWEtDx9 +Xve50NF9XkTrn6+cgqvfJ9ezE4cOaiD3Eov1u/HbHRx3K2Qf9IzvGoMCgYEAycgk +yOSYR0A3xKcfCQyOU0THEZWBBzd6zBAQ6VQNbcWHh1E8Ve0td6cvCxfydW1pIzXE +7b7J/BgMoC9oSQPfqQJQF2O0XESrdNgXjscfFpVgPfzbFQNgf7d0DSq4b/A5n5QR +HVMmWzVQoRQUwqTNeVxs0NpY6W6Meqv3i/KJqYECgYA/KyMyhM55fCqA5pmLgueV +Y/5/tMlTNcAxIgBLMnpeuaKUyI7ldveFVBClZmVQgpEo8/wpUw6+Kxvp4d32N+Ld +IGeeQSBQR3Gk3blCL3k/49tgKrUf7n7bsoIB8YVFdUjovRLzty2DcAoTjU2s2IgD +5mUgBGYPCV+6LEnjU6QjcwKBgGg+0FJBVzKoSKd+N5hzNixqwfWhqXFTBkvamQIS +fIWToTsVivhRekXwx2sRyh9EkSaxprW09aEZw5wWIehm6evk1//dcNaiW3oYEcOf +t73xGjGsKnsmrXoOCxSqV3LtRrfcxSLDTHOejbNKLpeIkOb8CvOzem/OvyC5K0DP +4rMBAoGBAJStRo5xQ2F9cyZW8vLd4eR3FHXxF/7Moxr6AyV3RLUjMhkwB8ZcFLUQ +dXI4NN9leDeIpNaGU6ozr+At3f50GtCWxdUppy9FDh5qDamBV8K4/+uNqFPiKFQ9 +uwNcJ8daMgVZ0QBrD3CBcSZQrfC484BlV6spJ3C16qDVSQPt7sAI +-----END RSA PRIVATE KEY-----""" +PRIVATE_KEYS = ( + pytest.param(private_key_2048_pkcs1, id="RSA_2048_PKCS1"), + pytest.param(private_key_4096_pkcs1, id="RSA_4096_PKCS1") +) + +LEGACY_INVALID_PRIVATE_KEY_PKCS8_PEM = b"""-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFADCCCSsCAQACggIBALUin0niNH7pESF9/AB5 +SgdWD6V/D1GDdiXb3zPl4zZi31NTO+DdFZncyF/ebJ3kBjvZAtsTCBPgCJbedmH/ +yoAWYIAJUvWNFcfA7Ei9h7zo4LgHV4q972C7wMsh4p/5lIrCTqnHBSgoRyo55NLl +8v9/Pta9e/3GE6e+m6yqVNSOJDNtDP/3W7ywVo388sPXobn6++GlcK/tMSX7AVa9 +qGkBcMP1xxs+vUO8hyug28WDuMOKtrCH3AuKU/F0zx6OCWdjO99xGvGux8bWUuet +/5oYUWS1OWsp0KcGlb9lPvgi+hLxrfE5TWTpHkb/MM/kbfAe9I86EaVSt+q0fqRy +pV4TBk+tfb/Ni53k3bKgNuVoti3f3NJ4rrpduAOvmmo9rvUlm8QPS5lbRZ7bzW0W +h1xNUi6Sz6CKfLqaRhdNjc9r95XfIAp001n6vwUPNEMvHtHKEUQARAma4yDMxxIO +jJaEQ8uJ2tKUUL+tVaIKkSg1Nq9/1XsxT0A293ImLGY1ga9x6TTpFI067y5hcjhP +UOUf6kBpnOgWLX5Oa5+4iH15ZCQGR14QcvhJQbogTPmEpBTO3R/drEiKGdOVeDD9 +PV3Kace5HcLCcu9krrLfR53fQe1d+WJ1Relu/dZVR53p4QiTs4kZpB+MSy2z5Gkk +9irNyBx+7VZbTOjbEZN4zXVTAgMBAAECggIBAK8ftCV4oAx7RWa+KWBD48DIAgSd +na/Pi/D6bQf+IPi6CvTCqkezOGkzvj6CCz1z8lr2av5nng2pMmS63HXPGndQKyhe +22gwaXhhG5EQPSX1eR4zav3muIMrwzAhqLvGT0kAp5EZq/CxUGyQ4JzOWWuQGK8B +L9mhIeuyK0x6ud1vN6zIqCLpgjYhvu00O3oBBomLEO+ORi6xAi2YSikU4Lf0/pNX +EpNSyyWsJnuV4CVMPtw/RnXSRHqb2KC/sGf4JztgA8j5z3UO6HNjT3BTF6ZiEH9v +fv4OxX5WrX0IZCL/ngumwedQ4XTItc8qdoTocyoOo5++IsVV/h7bNv3DIgD7tDWl +plPZ/IOx5l1nOPrkjv37LBoyNwy2CnecZ4a6uGt2TuaCS3jDvVO9fmJHLYNtz0x8 +DQBq7D2HYxnAZyRHbW12t9WoCOTwBuq4BlGN4kqFqhy4AU8a19OE1lby0IvMG8Ye +wRBdMmrDnmSsXRNiFmIO+cxrI5bsh5Tp6UWuZUn+imerYYfXnMxR2LUuA72AXGfY +WVv4PO4ntvEpARccZdG62LfbijWCb7mo1RvJE/NAAu1s4Nqu1pp6MMNyTSVGzbYb +OXaislEpTBTCAf7znjxRy6wOqg3wJ7EB4FG/Vh4VK2LB9tFyqakdjo5llNOW1eKU +jFvycBt+6PqcdcihAoIBAQDZ8MdHe5LBDLyY3jeaZQ8t3jC4nwSPuki/6OU1w6BT +OqycRftFD+9LqLmCatnSPgwkgOnPxLJ+1gDoq2Ne+2nSnAVfG4sALZih5xZwrP5M +AGavJTL0D7TTyf76tp+NJDuPIfH8W3hnJsgKhT2eGCksazwOHHWxyLhUrfVsmlHF +RZs3UxR1ZUT+9BF3Zlx7fOG8ljQeAh3qvM5qzcTzQIuuZxJFJLO4gUDLbr8ldvml +4wzkwTAneuiISIdomatTu5F1MIIdTh/YQmu6K4h8gHYJCnSglqHJmzzxDThE4I62 +gPcESF8DmEw29V+YTL4tZgh6PMYzI7uLMw9N8SVYu1q7AoIBAQDUxG5QpMRMoSam +jGRd1N3X9mK/2bR6rK06+JcAfzDipM5rkq0TsYXhJCiLMW4lfgMU77T0dRUK5am4 +UyUcMvqcOCjyez+H6tkf2Aar10jQQmpk7epdl3V6pwJNwPvnDhGdpVPbm34HXjdi +IfK4HZ9S1njkowlnxMZpKFOtf13usQDHlykEpRykp1/b6MbhNsSDR3lOmQCstZBp +qybRIlpyPG/AuLPH08g7VydS2rMrNIhztk89o1IX+CCcG/Oy5OyM3tjKyPqD81Q1 +5ZpG45AeLWMqIkU4/K5PKcAVvx1b9NNqYzi02uJM9ZEN5bGf9wLOM3zYu5lhIWnZ +hLACH2JJAoIBAQDTt/a/2Ko+ZFMq5mV51ccjNgB6ufBCeCOIW4Wf70Vm1U8uGUX6 +V3qOM4DT0117ws8k/x8kud71HIyRez3z3aV19h+5vxYPvDvUvJuuJkB8ML+QUkDn +nAJ85HSRtqvU/2fkqoNcNrgG7UPUBJBRbwNApYQX6UnkxitcCAqt0FSzoeUhn9H2 +IcUfMJdvOL+LL0xUWk6TAFdz3KtiUjeMYB3R9UtoZDk7ekUp25JRoPzxTFsQNyTC +lcIj8uGomfA4TbUG9XLRaT3CZvQkTXowCNOiAMg/4VWWdvqC6ebJ8qRxY2OUg4Ha +Ci+wDDsrxxHRJJgDt9qLf6EHnzi07Rjs1EVVAoIBAQC0seYmIuh7U9kpVM3gSmnl +gWA4IsH99SxhisFjMKHpuaF9BmJq+TcED9tG60HqIWyomTMK8WxfhtButF4t5rWj +eqZ72GQKIE8pliOESR+TjvQgp1WFCp5A/hkcw6qrfe1D/yaKuTF9PGy4sLAb4Txv +86lUM4pHUHxYzmDSVfsGPdi1qRCy2y7KP0NP1g8hMYwPGeJR9+r0wnXU5//dWNmL +bvxRpgs4yAmjK88/tHC5XrIL42bEqDGOHbJEIhEDexvSP2fKQIlRCpQX+djeH2FD +37P6EoTLcvzuSjzRuy9J61CpZ36/Sa0rQtpf/RSvD+6YBG4g+qG2NdRZYTDBfLnR +AoIBAQCCobzhbYqQ9Y6gzqYzqEfbCv5UUeW1VVkH2pjAzdQMJoyW1R0vIYbWFDP+ +LIdqddj+kYKDvHzg39bxHFhYd8cTWRNaTwj2iAg/YuVPjUbz89rwvdNB3K2i0a1B +Wkc8IajjpJ2CUgaxs1vgsd2EnmjgoJysiPAeYMOAtBtIi9XUrM9m0dTuBjTlX090 +eo6GRFwExaPynNi9GALwKQTVGL2NJG4yfyX0zudOtErFn7X+IsN464Up/UcE02Ha +v5BKxhVrwxiZ9jIroTHtqJzX1cyBkZnVMR8ItbpZLKQJ/35mO39IWabJA8HB8mZm +ymbpPjVPxSfCAHJr5Pcu5tuZ0knP +-----END PRIVATE KEY----- +""" + + +def _legacy_invalid_private_key_pkcs8_der(): + legacy_key = LEGACY_INVALID_PRIVATE_KEY_PKCS8_PEM.strip() + legacy_key = legacy_key[legacy_key.index(b"\n"):legacy_key.rindex(b"\n")] + return base64.b64decode(legacy_key) + + +def _actually_invalid_private_key_pkcs8_der(): + legacy_key = _legacy_invalid_private_key_pkcs8_der() + invalid_key = legacy_key[:len(rsa_backend.LEGACY_INVALID_PKCS8_RSA_HEADER)] + invalid_key += b"\x00" + invalid_key += legacy_key[len(rsa_backend.LEGACY_INVALID_PKCS8_RSA_HEADER):] + return invalid_key + + +def _actually_invalid_private_key_pkcs8_pem(): + invalid_key = b"-----BEGIN PRIVATE KEY-----\n" + invalid_key += base64.b64encode(_actually_invalid_private_key_pkcs8_der()) + invalid_key += b"\n-----END PRIVATE KEY-----\n" + return invalid_key + + +@pytest.mark.skipif(PurePythonRSAKey is None, reason="python-rsa backend not available") +class TestPurePythonRsa(object): + + def test_python_rsa_legacy_pem_read(self): + key = PurePythonRSAKey(LEGACY_INVALID_PRIVATE_KEY_PKCS8_PEM, ALGORITHMS.RS256) + new_pem = key.to_pem(pem_format="PKCS8") + assert new_pem != LEGACY_INVALID_PRIVATE_KEY_PKCS8_PEM + + def test_python_rsa_legacy_pem_invalid(self): + with pytest.raises(JWKError) as excinfo: + PurePythonRSAKey(_actually_invalid_private_key_pkcs8_pem(), ALGORITHMS.RS256) + + excinfo.match("Invalid private key encoding") + + def test_python_rsa_legacy_private_key_pkcs8_to_pkcs1(self): + legacy_key = _legacy_invalid_private_key_pkcs8_der() + legacy_pkcs1 = legacy_key[len(rsa_backend.LEGACY_INVALID_PKCS8_RSA_HEADER):] + + assert rsa_backend._legacy_private_key_pkcs8_to_pkcs1(legacy_key) == legacy_pkcs1 + + def test_python_rsa_legacy_private_key_pkcs8_to_pkcs1_invalid(self): + invalid_key = _actually_invalid_private_key_pkcs8_der() + + with pytest.raises(ValueError) as excinfo: + rsa_backend._legacy_private_key_pkcs8_to_pkcs1(invalid_key) + + excinfo.match("Invalid private key encoding") + + +@pytest.mark.pycrypto +@pytest.mark.pycryptodome +@pytest.mark.skipif(None in (PyCryptoRSA, PyCryptoRSAKey), reason="Pycrypto/dome backend not available") +def test_pycrypto_RSA_key_instance(): + key = PyCryptoRSA.construct((long( + 26057131595212989515105618545799160306093557851986992545257129318694524535510983041068168825614868056510242030438003863929818932202262132630250203397069801217463517914103389095129323580576852108653940669240896817348477800490303630912852266209307160550655497615975529276169196271699168537716821419779900117025818140018436554173242441334827711966499484119233207097432165756707507563413323850255548329534279691658369466534587631102538061857114141268972476680597988266772849780811214198186940677291891818952682545840788356616771009013059992237747149380197028452160324144544057074406611859615973035412993832273216732343819), + long(65537))) + PyCryptoRSAKey(key, ALGORITHMS.RS256) + + +# TODO: Unclear why this test was marked as only for pycrypto +@pytest.mark.pycrypto +@pytest.mark.pycryptodome +@pytest.mark.parametrize("private_key", PRIVATE_KEYS) +@pytest.mark.skipif(None in (PyCryptoRSA, PyCryptoRSAKey), reason="Pycrypto/dome backend not available") +def test_pycrypto_unencoded_cleartext(private_key): + key = PyCryptoRSAKey(private_key, ALGORITHMS.RS256) + msg = b'test' + signature = key.sign(msg) + public_key = key.public_key() + + assert bool(public_key.verify(msg, signature)) + assert not bool(public_key.verify(msg, 1)) + + +@pytest.mark.cryptography +@pytest.mark.skipif( + None in (default_backend, pyca_rsa, CryptographyRSAKey), + reason="Cryptography backend not available" +) +def test_cryptography_RSA_key_instance(): + + key = pyca_rsa.RSAPublicNumbers( + long(65537), + long(26057131595212989515105618545799160306093557851986992545257129318694524535510983041068168825614868056510242030438003863929818932202262132630250203397069801217463517914103389095129323580576852108653940669240896817348477800490303630912852266209307160550655497615975529276169196271699168537716821419779900117025818140018436554173242441334827711966499484119233207097432165756707507563413323850255548329534279691658369466534587631102538061857114141268972476680597988266772849780811214198186940677291891818952682545840788356616771009013059992237747149380197028452160324144544057074406611859615973035412993832273216732343819), + ).public_key(default_backend()) + + pubkey = CryptographyRSAKey(key, ALGORITHMS.RS256) + assert pubkey.is_public() + + pem = pubkey.to_pem() + assert pem.startswith(b'-----BEGIN PUBLIC KEY-----') + + +class TestRSAAlgorithm: + def test_RSA_key(self): + assert not RSAKey(private_key_4096_pkcs1, ALGORITHMS.RS256).is_public() + + def test_string_secret(self): + key = 'secret' + with pytest.raises(JOSEError): + RSAKey(key, ALGORITHMS.RS256) + + def test_object(self): + key = object() + with pytest.raises(JOSEError): + RSAKey(key, ALGORITHMS.RS256) + + def test_bad_cert(self,): + key = '-----BEGIN CERTIFICATE-----' + with pytest.raises(JOSEError): + RSAKey(key, ALGORITHMS.RS256) + + def test_invalid_algorithm(self): + with pytest.raises(JWKError): + RSAKey(private_key_4096_pkcs1, ALGORITHMS.ES256) + + with pytest.raises(JWKError): + RSAKey({'kty': 'bla'}, ALGORITHMS.RS256) + + def test_RSA_jwk(self): + key = { + "kty": "RSA", + "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", + "e": "AQAB", + } + assert RSAKey(key, ALGORITHMS.RS256).is_public() + + key = { + "kty": "RSA", + "kid": "bilbo.baggins@hobbiton.example", + "use": "sig", + "n": "n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw", + "e": "AQAB", + "d": "bWUC9B-EFRIo8kpGfh0ZuyGPvMNKvYWNtB_ikiH9k20eT-O1q_I78eiZkpXxXQ0UTEs2LsNRS-8uJbvQ-A1irkwMSMkK1J3XTGgdrhCku9gRldY7sNA_AKZGh-Q661_42rINLRCe8W-nZ34ui_qOfkLnK9QWDDqpaIsA-bMwWWSDFu2MUBYwkHTMEzLYGqOe04noqeq1hExBTHBOBdkMXiuFhUq1BU6l-DqEiWxqg82sXt2h-LMnT3046AOYJoRioz75tSUQfGCshWTBnP5uDjd18kKhyv07lhfSJdrPdM5Plyl21hsFf4L_mHCuoFau7gdsPfHPxxjVOcOpBrQzwQ", + "p": "3Slxg_DwTXJcb6095RoXygQCAZ5RnAvZlno1yhHtnUex_fp7AZ_9nRaO7HX_-SFfGQeutao2TDjDAWU4Vupk8rw9JR0AzZ0N2fvuIAmr_WCsmGpeNqQnev1T7IyEsnh8UMt-n5CafhkikzhEsrmndH6LxOrvRJlsPp6Zv8bUq0k", + "q": "uKE2dh-cTf6ERF4k4e_jy78GfPYUIaUyoSSJuBzp3Cubk3OCqs6grT8bR_cu0Dm1MZwWmtdqDyI95HrUeq3MP15vMMON8lHTeZu2lmKvwqW7anV5UzhM1iZ7z4yMkuUwFWoBvyY898EXvRD-hdqRxHlSqAZ192zB3pVFJ0s7pFc", + "dp": "B8PVvXkvJrj2L-GYQ7v3y9r6Kw5g9SahXBwsWUzp19TVlgI-YV85q1NIb1rxQtD-IsXXR3-TanevuRPRt5OBOdiMGQp8pbt26gljYfKU_E9xn-RULHz0-ed9E9gXLKD4VGngpz-PfQ_q29pk5xWHoJp009Qf1HvChixRX 59ehik", + "dq": "CLDmDGduhylc9o7r84rEUVn7pzQ6PF83Y-iBZx5NT-TpnOZKF1pErAMVeKzFEl41DlHHqqBLSM0W1sOFbwTxYWZDm6sI6og5iTbwQGIC3gnJKbi_7k_vJgGHwHxgPaX2PnvP-zyEkDERuf-ry4c_Z11Cq9AqC2yeL6kdKT1cYF8", + "qi": "3PiqvXQN0zwMeE-sBvZgi289XP9XCQF3VWqPzMKnIgQp7_Tugo6-NZBKCQsMf3HaEGBjTVJs_jcK8-TRXvaKe-7ZMaQj8VfBdYkssbu0NKDDhjJ-GtiseaDVWt7dcH0cfwxgFUHpQh7FoCrjFJ6h6ZEpMF6xmujs4qMpPz8aaI4" + } + assert not RSAKey(key, ALGORITHMS.RS256).is_public() + + del key['p'] + + # Some but not all extra parameters are present + with pytest.raises(JWKError): + RSAKey(key, ALGORITHMS.RS256) + + del key['q'] + del key['dp'] + del key['dq'] + del key['qi'] + + # None of the extra parameters are present, but 'key' is still private. + assert not RSAKey(key, ALGORITHMS.RS256).is_public() + + @pytest.mark.parametrize("private_key", PRIVATE_KEYS) + def test_get_public_key(self, private_key): + key = RSAKey(private_key, ALGORITHMS.RS256) + public_key = key.public_key() + public_key2 = public_key.public_key() + assert public_key.is_public() + assert public_key2.is_public() + assert public_key == public_key2 + + @pytest.mark.parametrize("pkey", PRIVATE_KEYS) + def test_to_pem(self, pkey): + key = RSAKey(pkey, ALGORITHMS.RS256) + assert key.to_pem(pem_format='PKCS1').strip() == pkey.strip() + + pkcs8 = key.to_pem(pem_format='PKCS8').strip() + assert pkcs8 != pkey.strip() + + newkey = RSAKey(pkcs8, ALGORITHMS.RS256) + assert newkey.to_pem(pem_format='PKCS1').strip() == pkey.strip() + + def assert_parameters(self, as_dict, private): + assert isinstance(as_dict, dict) + + # Public parameters should always be there. + assert 'n' in as_dict + assert 'e' in as_dict + + if private: + # Private parameters as well + assert 'd' in as_dict + assert 'p' in as_dict + assert 'q' in as_dict + assert 'dp' in as_dict + assert 'dq' in as_dict + assert 'qi' in as_dict + else: + # Private parameters should be absent + assert 'd' not in as_dict + assert 'p' not in as_dict + assert 'q' not in as_dict + assert 'dp' not in as_dict + assert 'dq' not in as_dict + assert 'qi' not in as_dict + + def assert_roundtrip(self, key): + assert RSAKey( + key.to_dict(), + ALGORITHMS.RS256 + ).to_dict() == key.to_dict() + + @pytest.mark.parametrize("private_key", PRIVATE_KEYS) + def test_to_dict(self, private_key): + key = RSAKey(private_key, ALGORITHMS.RS256) + self.assert_parameters(key.to_dict(), private=True) + self.assert_parameters(key.public_key().to_dict(), private=False) + self.assert_roundtrip(key) + self.assert_roundtrip(key.public_key()) diff --git a/tests/algorithms/test_RSA_compat.py b/tests/algorithms/test_RSA_compat.py new file mode 100644 index 0000000..0da03d5 --- /dev/null +++ b/tests/algorithms/test_RSA_compat.py @@ -0,0 +1,102 @@ +import pytest + +try: + from jose.backends.rsa_backend import RSAKey as PurePythonRSAKey + from jose.backends.cryptography_backend import CryptographyRSAKey + from jose.backends.pycrypto_backend import RSAKey as PyCryptoRSAKey +except ImportError: + PurePythonRSAKey = CryptographyRSAKey = PyCryptoRSAKey = None +from jose.constants import ALGORITHMS + +from .test_RSA import PRIVATE_KEYS + +CRYPTO_BACKENDS = ( + pytest.param(PurePythonRSAKey, id="python_rsa"), + pytest.param(CryptographyRSAKey, id="pyca/cryptography"), + pytest.param(PyCryptoRSAKey, id="pycrypto/dome") +) +ENCODINGS = ("PKCS1", "PKCS8") + + +@pytest.mark.backend_compatibility +@pytest.mark.skipif( + None in (PurePythonRSAKey, CryptographyRSAKey, PyCryptoRSAKey), + reason="Multiple crypto backends not available for backend compatibility tests" +) +class TestBackendRsaCompatibility(object): + + @pytest.mark.parametrize("BackendSign", CRYPTO_BACKENDS) + @pytest.mark.parametrize("BackendVerify", CRYPTO_BACKENDS) + @pytest.mark.parametrize("private_key", PRIVATE_KEYS) + def test_signing_parity(self, BackendSign, BackendVerify, private_key): + key_sign = BackendSign(private_key, ALGORITHMS.RS256) + key_verify = BackendVerify(private_key, ALGORITHMS.RS256).public_key() + + msg = b'test' + sig = key_sign.sign(msg) + + # valid signature + assert key_verify.verify(msg, sig) + + # invalid signature + assert not key_verify.verify(msg, b'n' * 64) + + @pytest.mark.parametrize("encoding", ENCODINGS) + @pytest.mark.parametrize("BackendFrom", CRYPTO_BACKENDS) + @pytest.mark.parametrize("BackendTo", CRYPTO_BACKENDS) + @pytest.mark.parametrize("private_key", PRIVATE_KEYS) + def test_public_key_to_pem(self, BackendFrom, BackendTo, encoding, private_key): + key = BackendFrom(private_key, ALGORITHMS.RS256) + key2 = BackendTo(private_key, ALGORITHMS.RS256) + + key1_pem = key.public_key().to_pem(pem_format=encoding).strip() + key2_pem = key2.public_key().to_pem(pem_format=encoding).strip() + assert key1_pem == key2_pem + + @pytest.mark.parametrize("encoding", ENCODINGS) + @pytest.mark.parametrize("BackendFrom", CRYPTO_BACKENDS) + @pytest.mark.parametrize("BackendTo", CRYPTO_BACKENDS) + @pytest.mark.parametrize("private_key", PRIVATE_KEYS) + def test_private_key_to_pem(self, BackendFrom, BackendTo, encoding, private_key): + key = BackendFrom(private_key, ALGORITHMS.RS256) + key2 = BackendTo(private_key, ALGORITHMS.RS256) + + key1_pem = key.to_pem(pem_format=encoding).strip() + key2_pem = key2.to_pem(pem_format=encoding).strip() + + import base64 + a = base64.b64decode(key1_pem[key1_pem.index(b"\n"):key1_pem.rindex(b"\n")]) + b = base64.b64decode(key2_pem[key2_pem.index(b"\n"):key2_pem.rindex(b"\n")]) + assert a == b + + assert key1_pem == key2_pem + + @pytest.mark.parametrize("encoding_save", ENCODINGS) + @pytest.mark.parametrize("encoding_load", ENCODINGS) + @pytest.mark.parametrize("BackendFrom", CRYPTO_BACKENDS) + @pytest.mark.parametrize("BackendTo", CRYPTO_BACKENDS) + @pytest.mark.parametrize("private_key", PRIVATE_KEYS) + def test_public_key_load_cycle(self, BackendFrom, BackendTo, encoding_save, encoding_load, private_key): + key = BackendFrom(private_key, ALGORITHMS.RS256) + + pem_pub_reference = key.public_key().to_pem(pem_format=encoding_save).strip() + pem_pub_load = key.public_key().to_pem(pem_format=encoding_load).strip() + + pubkey_2 = BackendTo(pem_pub_load, ALGORITHMS.RS256) + + assert pem_pub_reference == pubkey_2.to_pem(encoding_save).strip() + + @pytest.mark.parametrize("encoding_save", ENCODINGS) + @pytest.mark.parametrize("encoding_load", ENCODINGS) + @pytest.mark.parametrize("BackendFrom", CRYPTO_BACKENDS) + @pytest.mark.parametrize("BackendTo", CRYPTO_BACKENDS) + @pytest.mark.parametrize("private_key", PRIVATE_KEYS) + def test_private_key_load_cycle(self, BackendFrom, BackendTo, encoding_save, encoding_load, private_key): + key = BackendFrom(private_key, ALGORITHMS.RS256) + + pem_reference = key.to_pem(pem_format=encoding_save).strip() + pem_load = key.to_pem(pem_format=encoding_load).strip() + + key_2 = BackendTo(pem_load, ALGORITHMS.RS256) + + assert pem_reference == key_2.to_pem(encoding_save).strip() diff --git a/tests/algorithms/test_base.py b/tests/algorithms/test_base.py new file mode 100644 index 0000000..6f7fcb7 --- /dev/null +++ b/tests/algorithms/test_base.py @@ -0,0 +1,19 @@ +from jose.jwk import Key + +import pytest + + +@pytest.fixture +def alg(): + return Key("key", "ALG") + + +class TestBaseAlgorithm: + + def test_sign_is_interface(self, alg): + with pytest.raises(NotImplementedError): + alg.sign('msg') + + def test_verify_is_interface(self, alg): + with pytest.raises(NotImplementedError): + alg.verify('msg', 'sig') diff --git a/tests/rfc/__init__.py b/tests/rfc/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/rfc/__init__.pyc b/tests/rfc/__init__.pyc new file mode 100644 index 0000000..be005fd Binary files /dev/null and b/tests/rfc/__init__.pyc differ diff --git a/tests/rfc/__pycache__/__init__.cpython-34.pyc b/tests/rfc/__pycache__/__init__.cpython-34.pyc new file mode 100644 index 0000000..f300c5e Binary files /dev/null and b/tests/rfc/__pycache__/__init__.cpython-34.pyc differ diff --git a/tests/rfc/__pycache__/__init__.cpython-36.pyc b/tests/rfc/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000..128b029 Binary files /dev/null and b/tests/rfc/__pycache__/__init__.cpython-36.pyc differ diff --git a/tests/rfc/__pycache__/test_rfc7520.cpython-27-PYTEST.pyc b/tests/rfc/__pycache__/test_rfc7520.cpython-27-PYTEST.pyc new file mode 100644 index 0000000..53e9da6 Binary files /dev/null and b/tests/rfc/__pycache__/test_rfc7520.cpython-27-PYTEST.pyc differ diff --git a/tests/rfc/__pycache__/test_rfc7520.cpython-34-PYTEST.pyc b/tests/rfc/__pycache__/test_rfc7520.cpython-34-PYTEST.pyc new file mode 100644 index 0000000..11160d2 Binary files /dev/null and b/tests/rfc/__pycache__/test_rfc7520.cpython-34-PYTEST.pyc differ diff --git a/tests/rfc/__pycache__/test_rfc7520.cpython-36-PYTEST.pyc b/tests/rfc/__pycache__/test_rfc7520.cpython-36-PYTEST.pyc new file mode 100644 index 0000000..cf45a4f Binary files /dev/null and b/tests/rfc/__pycache__/test_rfc7520.cpython-36-PYTEST.pyc differ diff --git a/tests/rfc/__pycache__/test_rfc8037.cpython-27-PYTEST.pyc b/tests/rfc/__pycache__/test_rfc8037.cpython-27-PYTEST.pyc new file mode 100644 index 0000000..fdf37ba Binary files /dev/null and b/tests/rfc/__pycache__/test_rfc8037.cpython-27-PYTEST.pyc differ diff --git a/tests/rfc/__pycache__/test_rfc8037.cpython-34-PYTEST.pyc b/tests/rfc/__pycache__/test_rfc8037.cpython-34-PYTEST.pyc new file mode 100644 index 0000000..c9ba795 Binary files /dev/null and b/tests/rfc/__pycache__/test_rfc8037.cpython-34-PYTEST.pyc differ diff --git a/tests/rfc/__pycache__/test_rfc8037.cpython-36-PYTEST.pyc b/tests/rfc/__pycache__/test_rfc8037.cpython-36-PYTEST.pyc new file mode 100644 index 0000000..88cf2dc Binary files /dev/null and b/tests/rfc/__pycache__/test_rfc8037.cpython-36-PYTEST.pyc differ diff --git a/tests/rfc/test_rfc7520.py b/tests/rfc/test_rfc7520.py new file mode 100644 index 0000000..568e692 --- /dev/null +++ b/tests/rfc/test_rfc7520.py @@ -0,0 +1,6783 @@ + +# Disable flake8 reporting +# flake8: noqa + +from jose.jws import verify + +expected_payload = b"It\xe2\x80\x99s a dangerous business, Frodo, going out your door. You step onto the road, and if you don't keep your feet, there\xe2\x80\x99s no knowing where you might be swept off to." + + +# [Docs] [txt|pdf] [draft-ietf-jose-c...] + + + +# Internet Engineering Task Force (IETF) M. Miller +# Request for Comments: 7520 Cisco Systems, Inc. +# Category: Informational May 2015 +# ISSN: 2070-1721 + + +# Examples of Protecting Content Using +# JSON Object Signing and Encryption (JOSE) + +# Abstract + +# This document contains a set of examples using JSON Object Signing +# and Encryption (JOSE) technology to protect data. These examples +# present a representative sampling of JSON Web Key (JWK) objects as +# well as various JSON Web Signature (JWS) and JSON Web Encryption +# (JWE) results given similar inputs. + +# Status of This Memo + +# This document is not an Internet Standards Track specification; it is +# published for informational purposes. + +# This document is a product of the Internet Engineering Task Force +# (IETF). It represents the consensus of the IETF community. It has +# received public review and has been approved for publication by the +# Internet Engineering Steering Group (IESG). Not all documents +# approved by the IESG are a candidate for any level of Internet +# Standard; see Section 2 of RFC 5741. + +# Information about the current status of this document, any errata, +# and how to provide feedback on it may be obtained at +# http://www.rfc-editor.org/info/rfc7520. + +# Copyright Notice + +# Copyright (c) 2015 IETF Trust and the persons identified as the +# document authors. All rights reserved. + +# This document is subject to BCP 78 and the IETF Trust's Legal +# Provisions Relating to IETF Documents +# (http://trustee.ietf.org/license-info) in effect on the date of +# publication of this document. Please review these documents +# carefully, as they describe your rights and restrictions with respect +# to this document. Code Components extracted from this document must +# include Simplified BSD License text as described in Section 4.e of +# the Trust Legal Provisions and are provided without warranty as +# described in the Simplified BSD License. + + + + +# Miller Informational [Page 1] + +# RFC 7520 JOSE Cookbook May 2015 + + +# Table of Contents + +# 1. Introduction ....................................................5 +# 1.1. Conventions Used in This Document ..........................5 +# 2. Terminology .....................................................6 +# 3. JSON Web Key Examples ...........................................6 +# 3.1. EC Public Key ..............................................6 +# 3.2. EC Private Key .............................................7 +# 3.3. RSA Public Key .............................................8 +# 3.4. RSA Private Key ............................................8 +# 3.5. Symmetric Key (MAC Computation) ...........................10 +# 3.6. Symmetric Key (Encryption) ................................11 +# 4. JSON Web Signature Examples ....................................11 +# 4.1. RSA v1.5 Signature ........................................12 +# 4.1.1. Input Factors ......................................12 +# 4.1.2. Signing Operation ..................................12 +# 4.1.3. Output Results .....................................13 +# 4.2. RSA-PSS Signature .........................................15 +# 4.2.1. Input Factors ......................................15 +# 4.2.2. Signing Operation ..................................16 +# 4.2.3. Output Results .....................................17 +# 4.3. ECDSA Signature ...........................................19 +# 4.3.1. Input Factors ......................................19 +# 4.3.2. Signing Operation ..................................19 +# 4.3.3. Output Results .....................................20 +# 4.4. HMAC-SHA2 Integrity Protection ............................21 +# 4.4.1. Input Factors ......................................22 +# 4.4.2. Signing Operation ..................................22 +# 4.4.3. Output Results .....................................23 +# 4.5. Signature with Detached Content ...........................24 +# 4.5.1. Input Factors ......................................25 +# 4.5.2. Signing Operation ..................................25 +# 4.5.3. Output Results .....................................26 +# 4.6. Protecting Specific Header Fields .........................27 +# 4.6.1. Input Factors ......................................27 +# 4.6.2. Signing Operation ..................................27 +# 4.6.3. Output Results .....................................28 +# 4.7. Protecting Content Only ...................................29 +# 4.7.1. Input Factors ......................................30 +# 4.7.2. Signing Operation ..................................30 +# 4.7.3. Output Results .....................................31 +# 4.8. Multiple Signatures .......................................32 +# 4.8.1. Input Factors ......................................32 +# 4.8.2. First Signing Operation ............................33 +# 4.8.3. Second Signing Operation ...........................34 +# 4.8.4. Third Signing Operation ............................36 +# 4.8.5. Output Results .....................................37 +# 5. JSON Web Encryption Examples ...................................39 + + + +# Miller Informational [Page 2] + +# RFC 7520 JOSE Cookbook May 2015 + + +# 5.1. Key Encryption Using RSA v1.5 and AES-HMAC-SHA2 ...........39 +# 5.1.1. Input Factors ......................................39 +# 5.1.2. Generated Factors ..................................41 +# 5.1.3. Encrypting the Key .................................41 +# 5.1.4. Encrypting the Content .............................42 +# 5.1.5. Output Results .....................................43 +# 5.2. Key Encryption Using RSA-OAEP with AES-GCM ................45 +# 5.2.1. Input Factors ......................................46 +# 5.2.2. Generated Factors ..................................47 +# 5.2.3. Encrypting the Key .................................48 +# 5.2.4. Encrypting the Content .............................48 +# 5.2.5. Output Results .....................................49 +# 5.3. Key Wrap Using PBES2-AES-KeyWrap with AES-CBC-HMAC-SHA2 ...52 +# 5.3.1. Input Factors ......................................53 +# 5.3.2. Generated Factors ..................................54 +# 5.3.3. Encrypting the Key .................................54 +# 5.3.4. Encrypting the Content .............................55 +# 5.3.5. Output Results .....................................56 +# 5.4. Key Agreement with Key Wrapping Using ECDH-ES and +# AES-KeyWrap with AES-GCM ..................................59 +# 5.4.1. Input Factors ......................................59 +# 5.4.2. Generated Factors ..................................60 +# 5.4.3. Encrypting the Key .................................60 +# 5.4.4. Encrypting the Content .............................61 +# 5.4.5. Output Results .....................................63 +# 5.5. Key Agreement Using ECDH-ES with AES-CBC-HMAC-SHA2 ........65 +# 5.5.1. Input Factors ......................................66 +# 5.5.2. Generated Factors ..................................66 +# 5.5.3. Key Agreement ......................................67 +# 5.5.4. Encrypting the Content .............................67 +# 5.5.5. Output Results .....................................68 +# 5.6. Direct Encryption Using AES-GCM ...........................70 +# 5.6.1. Input Factors ......................................70 +# 5.6.2. Generated Factors ..................................70 +# 5.6.3. Encrypting the Content .............................71 +# 5.6.4. Output Results .....................................72 +# 5.7. Key Wrap Using AES-GCM KeyWrap with AES-CBC-HMAC-SHA2 .....73 +# 5.7.1. Input Factors ......................................73 +# 5.7.2. Generated Factors ..................................74 +# 5.7.3. Encrypting the Key .................................74 +# 5.7.4. Encrypting the Content .............................75 +# 5.7.5. Output Results .....................................77 +# 5.8. Key Wrap Using AES-KeyWrap with AES-GCM ...................79 +# 5.8.1. Input Factors ......................................79 +# 5.8.2. Generated Factors ..................................80 +# 5.8.3. Encrypting the Key .................................80 +# 5.8.4. Encrypting the Content .............................80 +# 5.8.5. Output Results .....................................82 + + + +# Miller Informational [Page 3] + +# RFC 7520 JOSE Cookbook May 2015 + + +# 5.9. Compressed Content ........................................84 +# 5.9.1. Input Factors ......................................84 +# 5.9.2. Generated Factors ..................................84 +# 5.9.3. Encrypting the Key .................................85 +# 5.9.4. Encrypting the Content .............................85 +# 5.9.5. Output Results .....................................86 +# 5.10. Including Additional Authenticated Data ..................88 +# 5.10.1. Input Factors .....................................88 +# 5.10.2. Generated Factors .................................89 +# 5.10.3. Encrypting the Key ................................90 +# 5.10.4. Encrypting the Content ............................90 +# 5.10.5. Output Results ....................................91 +# 5.11. Protecting Specific Header Fields ........................93 +# 5.11.1. Input Factors .....................................93 +# 5.11.2. Generated Factors .................................94 +# 5.11.3. Encrypting the Key ................................94 +# 5.11.4. Encrypting the Content ............................94 +# 5.11.5. Output Results ....................................95 +# 5.12. Protecting Content Only ..................................97 +# 5.12.1. Input Factors .....................................97 +# 5.12.2. Generated Factors .................................98 +# 5.12.3. Encrypting the Key ................................98 +# 5.12.4. Encrypting the Content ............................98 +# 5.12.5. Output Results ....................................99 +# 5.13. Encrypting to Multiple Recipients .......................101 +# 5.13.1. Input Factors ....................................101 +# 5.13.2. Generated Factors ................................101 +# 5.13.3. Encrypting the Key to the First Recipient ........102 +# 5.13.4. Encrypting the Key to the Second Recipient .......103 +# 5.13.5. Encrypting the Key to the Third Recipient ........105 +# 5.13.6. Encrypting the Content ...........................106 +# 5.13.7. Output Results ...................................108 +# 6. Nesting Signatures and Encryption .............................110 +# 6.1. Signing Input Factors ....................................110 +# 6.2. Signing Operation ........................................112 +# 6.3. Signing Output ...........................................112 +# 6.4. Encryption Input Factors .................................113 +# 6.5. Encryption Generated Factors .............................113 +# 6.6. Encrypting the Key .......................................114 +# 6.7. Encrypting the Content ...................................114 +# 6.8. Encryption Output ........................................115 +# 7. Security Considerations .......................................119 +# 8. References ....................................................119 +# 8.1. Normative References .....................................119 +# 8.2. Informative References ...................................120 +# Acknowledgements .................................................120 +# Author's Address .................................................120 + + + + +# Miller Informational [Page 4] + +# RFC 7520 JOSE Cookbook May 2015 + + +# 1. Introduction + +# The JSON Object Signing and Encryption (JOSE) technologies -- JSON +# Web Signature [JWS], JSON Web Encryption [JWE], JSON Web Key [JWK], +# and JSON Web Algorithms [JWA] -- can be used collectively to encrypt +# and/or sign content using a variety of algorithms. While the full +# set of permutations is extremely large, and might be daunting to +# some, it is expected that most applications will only use a small set +# of algorithms to meet their needs. + +# This document provides a number of examples of signing or encrypting +# content using JOSE. While not exhaustive, it does compile a +# representative sampling of JOSE features. As much as possible, the +# same signature payload or encryption plaintext content is used to +# illustrate differences in various signing and encryption results. + +# This document also provides a number of example JWK objects. These +# examples illustrate the distinguishing properties of various key +# types and emphasize important characteristics. Most of the JWK +# examples are then used in the signature or encryption examples that +# follow. + +# All of the examples contained herein are available in a machine- +# readable format at . + +# 1.1. Conventions Used in This Document + +# This document separates data that are expected to be input to an +# implementation of JOSE from data that are expected to be generated by +# an implementation of JOSE. Each example, wherever possible, provides +# enough information both to replicate the results of this document and +# to validate the results by running its inverse operation (e.g., +# signature results can be validated by performing the JWS verify). +# However, some algorithms inherently use random data; therefore, +# computations employing them cannot be exactly replicated. Such cases +# are explicitly stated in the relevant sections. + +# All instances of binary octet strings are represented using base64url +# [RFC4648] encoding. + +# Wherever possible and unless otherwise noted, the examples include +# the JWS or JWE Compact Serialization, general JWS or JWE JSON +# Serialization, and flattened JWS or JWE JSON Serialization. + +# All of the examples in this document have whitespace added to improve +# formatting and readability. Except for JWE Plaintext or JWS Payload +# content, whitespace is not part of the cryptographic operations nor +# the exchange results. + + + +# Miller Informational [Page 5] + +# RFC 7520 JOSE Cookbook May 2015 + + +# Unless otherwise noted, the JWE Plaintext or JWS Payload content does +# include " " (U+0020 SPACE) characters. Line breaks (U+000A LINE +# FEED) replace some " " (U+0020 SPACE) characters to improve +# readability but are not present in the JWE Plaintext or JWS Payload. + +# 2. Terminology + +# This document inherits terminology regarding JSON Web Signature (JWS) +# technology from [JWS], terminology regarding JSON Web Encryption +# (JWE) technology from [JWE], terminology regarding JSON Web Key (JWK) +# technology from [JWK], and terminology regarding algorithms from +# [JWA]. + +# 3. JSON Web Key Examples + +# The following sections demonstrate how to represent various JWK and +# JWK Set objects. + +# 3.1. EC Public Key + +# This example illustrates an Elliptic Curve (EC) public key. This +# example is the public key corresponding to the private key in +# Figure 2. + +# Note that whitespace is added for readability as described in +# Section 1.1. + +# { +# "kty": "EC", +# "kid": "bilbo.baggins@hobbiton.example", +# "use": "sig", +# "crv": "P-521", +# "x": "AHKZLLOsCOzz5cY97ewNUajB957y-C-U88c3v13nmGZx6sYl_oJXu9 +# A5RkTKqjqvjyekWF-7ytDyRXYgCF5cj0Kt", +# "y": "AdymlHvOiLxXkEhayXQnNCvDX4h9htZaCJN34kfmC6pV5OhQHiraVy +# SsUdaQkAgDPrwQrJmbnX9cwlGfP-HqHZR1" +# } + +ec_public_key = { + "kty": "EC", + "kid": "bilbo.baggins@hobbiton.example", + "use": "sig", + "crv": "P-521", + "x": "AHKZLLOsCOzz5cY97ewNUajB957y-C-U88c3v13nmGZx6sYl_oJXu9A5RkTKqjqvjyekWF-7ytDyRXYgCF5cj0Kt", + "y": "AdymlHvOiLxXkEhayXQnNCvDX4h9htZaCJN34kfmC6pV5OhQHiraVySsUdaQkAgDPrwQrJmbnX9cwlGfP-HqHZR1" +} + +# Figure 1: Elliptic Curve P-521 Public Key + +# The field "kty" value of "EC" identifies this as an Elliptic Curve +# key. The field "crv" identifies the curve, which is curve P-521 for +# this example. The values of the fields "x" and "y" are the +# base64url-encoded X and Y coordinates (respectively). + + + + + + + +# Miller Informational [Page 6] + +# RFC 7520 JOSE Cookbook May 2015 + + +# The values of the fields "x" and "y" decoded are the octets necessary +# to represent each full coordinate to the order of the curve. For a +# key over curve P-521, the values of the fields "x" and "y" are +# exactly 66 octets in length when decoded, padded with leading zero +# (0x00) octets to reach the expected length. + +# 3.2. EC Private Key + +# This example illustrates an Elliptic Curve private key. This example +# is the private key corresponding to the public key in Figure 1. + +# Note that whitespace is added for readability as described in +# Section 1.1. + +# { +# "kty": "EC", +# "kid": "bilbo.baggins@hobbiton.example", +# "use": "sig", +# "crv": "P-521", +# "x": "AHKZLLOsCOzz5cY97ewNUajB957y-C-U88c3v13nmGZx6sYl_oJXu9 +# A5RkTKqjqvjyekWF-7ytDyRXYgCF5cj0Kt", +# "y": "AdymlHvOiLxXkEhayXQnNCvDX4h9htZaCJN34kfmC6pV5OhQHiraVy +# SsUdaQkAgDPrwQrJmbnX9cwlGfP-HqHZR1", +# "d": "AAhRON2r9cqXX1hg-RoI6R1tX5p2rUAYdmpHZoC1XNM56KtscrX6zb +# KipQrCW9CGZH3T4ubpnoTKLDYJ_fF3_rJt" +# } + +# Figure 2: Elliptic Curve P-521 Private Key + +# The field "kty" value of "EC" identifies this as an Elliptic Curve +# key. The field "crv" identifies the curve, which is curve P-521 +# (also known as SECG curve secp521r1) for this example. The values of +# the fields "x" and "y" are the base64url-encoded X and Y coordinates +# (respectively). The field "d" value is the base64url-encoded private +# key. + +# The values of the fields "d", "x", and "y" decoded are the octets +# necessary to represent the private key or each full coordinate +# (respectively) to the order of the curve. For a key over curve +# P-521, the values of the "d", "x", and "y" fields are each exactly 66 +# octets in length when decoded, padded with leading zero (0x00) octets +# to reach the expected length. + + + + + + + + + +# Miller Informational [Page 7] + +# RFC 7520 JOSE Cookbook May 2015 + + +# 3.3. RSA Public Key + +# This example illustrates an RSA public key. This example is the +# public key corresponding to the private key in Figure 4. + +# Note that whitespace is added for readability as described in +# Section 1.1. + +# { +# "kty": "RSA", +# "kid": "bilbo.baggins@hobbiton.example", +# "use": "sig", +# "n": "n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT +# -O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqV +# wGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj- +# oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde +# 3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuC +# LqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5g +# HdrNP5zw", +# "e": "AQAB" +# } + +rsa_public_jwk = { + "kty": "RSA", + "kid": "bilbo.baggins@hobbiton.example", + "use": "sig", + "n": "n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw", + "e": "AQAB" +} + +# Figure 3: RSA 2048-Bit Public Key + +# The field "kty" value of "RSA" identifies this as an RSA key. The +# fields "n" and "e" values are the modulus and (public) exponent +# (respectively) using the minimum octets necessary. + +# For a 2048-bit key, the field "n" value is 256 octets in length when +# decoded. + +# 3.4. RSA Private Key + +# This example illustrates an RSA private key. This example is the +# private key corresponding to the public key in Figure 3. + +# Note that whitespace is added for readability as described in +# Section 1.1. + + + + + + + + + + + + + +# Miller Informational [Page 8] + +# RFC 7520 JOSE Cookbook May 2015 + + +# { +# "kty": "RSA", +# "kid": "bilbo.baggins@hobbiton.example", +# "use": "sig", +# "n": "n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT +# -O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqV +# wGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj- +# oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde +# 3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuC +# LqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5g +# HdrNP5zw", +# "e": "AQAB", +# "d": "bWUC9B-EFRIo8kpGfh0ZuyGPvMNKvYWNtB_ikiH9k20eT-O1q_I78e +# iZkpXxXQ0UTEs2LsNRS-8uJbvQ-A1irkwMSMkK1J3XTGgdrhCku9gRld +# Y7sNA_AKZGh-Q661_42rINLRCe8W-nZ34ui_qOfkLnK9QWDDqpaIsA-b +# MwWWSDFu2MUBYwkHTMEzLYGqOe04noqeq1hExBTHBOBdkMXiuFhUq1BU +# 6l-DqEiWxqg82sXt2h-LMnT3046AOYJoRioz75tSUQfGCshWTBnP5uDj +# d18kKhyv07lhfSJdrPdM5Plyl21hsFf4L_mHCuoFau7gdsPfHPxxjVOc +# OpBrQzwQ", +# "p": "3Slxg_DwTXJcb6095RoXygQCAZ5RnAvZlno1yhHtnUex_fp7AZ_9nR +# aO7HX_-SFfGQeutao2TDjDAWU4Vupk8rw9JR0AzZ0N2fvuIAmr_WCsmG +# peNqQnev1T7IyEsnh8UMt-n5CafhkikzhEsrmndH6LxOrvRJlsPp6Zv8 +# bUq0k", +# "q": "uKE2dh-cTf6ERF4k4e_jy78GfPYUIaUyoSSJuBzp3Cubk3OCqs6grT +# 8bR_cu0Dm1MZwWmtdqDyI95HrUeq3MP15vMMON8lHTeZu2lmKvwqW7an +# V5UzhM1iZ7z4yMkuUwFWoBvyY898EXvRD-hdqRxHlSqAZ192zB3pVFJ0 +# s7pFc", +# "dp": "B8PVvXkvJrj2L-GYQ7v3y9r6Kw5g9SahXBwsWUzp19TVlgI-YV85q +# 1NIb1rxQtD-IsXXR3-TanevuRPRt5OBOdiMGQp8pbt26gljYfKU_E9xn +# -RULHz0-ed9E9gXLKD4VGngpz-PfQ_q29pk5xWHoJp009Qf1HvChixRX +# 59ehik", +# "dq": "CLDmDGduhylc9o7r84rEUVn7pzQ6PF83Y-iBZx5NT-TpnOZKF1pEr +# AMVeKzFEl41DlHHqqBLSM0W1sOFbwTxYWZDm6sI6og5iTbwQGIC3gnJK +# bi_7k_vJgGHwHxgPaX2PnvP-zyEkDERuf-ry4c_Z11Cq9AqC2yeL6kdK +# T1cYF8", +# "qi": "3PiqvXQN0zwMeE-sBvZgi289XP9XCQF3VWqPzMKnIgQp7_Tugo6-N +# ZBKCQsMf3HaEGBjTVJs_jcK8-TRXvaKe-7ZMaQj8VfBdYkssbu0NKDDh +# jJ-GtiseaDVWt7dcH0cfwxgFUHpQh7FoCrjFJ6h6ZEpMF6xmujs4qMpP +# z8aaI4" +# } + +# Figure 4: RSA 2048-Bit Private Key + + + + + + + + + +# Miller Informational [Page 9] + +# RFC 7520 JOSE Cookbook May 2015 + + +# The field "kty" value of "RSA" identifies this as an RSA key. The +# fields "n" and "e" values are the base64url-encoded modulus and +# (public) exponent (respectively) using the minimum number of octets +# necessary. The field "d" value is the base64url-encoded private +# exponent using the minimum number of octets necessary. The fields +# "p", "q", "dp", "dq", and "qi" are the base64url-encoded additional +# private information using the minimum number of octets necessary. + +# For a 2048-bit key, the field "n" is 256 octets in length when +# decoded, and the field "d" is not longer than 256 octets in length +# when decoded. + +# 3.5. Symmetric Key (MAC Computation) + +# This example illustrates a symmetric key used for computing Message +# Authentication Codes (MACs). + +# Note that whitespace is added for readability as described in +# Section 1.1. + +# { +# "kty": "oct", +# "kid": "018c0ae5-4d9b-471b-bfd6-eef314bc7037", +# "use": "sig", +# "alg": "HS256", +# "k": "hJtXIZ2uSN5kbQfbtTNWbpdmhkV8FJG-Onbc6mxCcYg" +# } + +hmac_key = { + "kty": "oct", + "kid": "018c0ae5-4d9b-471b-bfd6-eef314bc7037", + "use": "sig", + "alg": "HS256", + "k": "hJtXIZ2uSN5kbQfbtTNWbpdmhkV8FJG-Onbc6mxCcYg" +} + +# Figure 5: HMAC SHA-256 Symmetric Key + +# The field "kty" value of "oct" identifies this as a symmetric key. +# The field "k" value is the symmetric key. + +# When used for the signing algorithm "HS256" (HMAC-SHA256), the field +# "k" value is 32 octets (or more) in length when decoded, padded with +# leading zero (0x00) octets to reach the minimum expected length. + + + + + + + + + + + + + + + +# Miller Informational [Page 10] + +# RFC 7520 JOSE Cookbook May 2015 + + +# 3.6. Symmetric Key (Encryption) + +# This example illustrates a symmetric key used for encryption. + +# Note that whitespace is added for readability as described in +# Section 1.1. + +# { +# "kty": "oct", +# "kid": "1e571774-2e08-40da-8308-e8d68773842d", +# "use": "enc", +# "alg": "A256GCM", +# "k": "AAPapAv4LbFbiVawEjagUBluYqN5rhna-8nuldDvOx8" +# } + +# Figure 6: AES 256-Bit Symmetric Encryption Key + +# The field "kty" value of "oct" identifies this as a symmetric key. +# The field "k" value is the symmetric key. + +# For the content encryption algorithm "A256GCM", the field "k" value +# is exactly 32 octets in length when decoded, padded with leading zero +# (0x00) octets to reach the expected length. + +# 4. JSON Web Signature Examples + +# The following sections demonstrate how to generate various JWS +# objects. + +# All of the signature examples use the following payload content (an +# abridged quote from "The Fellowship of the Ring" [LOTR-FELLOWSHIP]), +# serialized as UTF-8. The payload is presented here as a series of +# quoted strings that are concatenated to produce the JWS Payload. The +# sequence "\xe2\x80\x99" is substituted for (U+2019 RIGHT SINGLE +# QUOTATION MARK), and quotation marks (U+0022 QUOTATION MARK) are +# added for readability but are not present in the JWS Payload. + +# "It\xe2\x80\x99s a dangerous business, Frodo, going out your " +# "door. You step onto the road, and if you don't keep your feet, " +# "there\xe2\x80\x99s no knowing where you might be swept off " +# "to." + +# Figure 7: Payload Content Plaintext + + + + + + + + +# Miller Informational [Page 11] + +# RFC 7520 JOSE Cookbook May 2015 + + +# The payload -- with the sequence "\xe2\x80\x99" replaced with (U+2019 +# RIGHT SINGLE QUOTATION MARK) and quotations marks (U+0022 QUOTATION +# MARK) are removed -- is encoded as UTF-8 and then as base64url +# [RFC4648]: + +# SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IH +# lvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBk +# b24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcm +# UgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4 + +# Figure 8: Payload Content, base64url-encoded + +# 4.1. RSA v1.5 Signature + +# This example illustrates signing content using the "RS256" (RSASSA- +# PKCS1-v1_5 with SHA-256) algorithm. + +# Note that whitespace is added for readability as described in +# Section 1.1. + +# 4.1.1. Input Factors + +# The following are supplied before beginning the signing operation: + +# o Payload content; this example uses the content from Figure 7, +# encoded using base64url [RFC4648] to produce Figure 8. + +# o RSA private key; this example uses the key from Figure 4. + +# o "alg" parameter of "RS256". + +# 4.1.2. Signing Operation + +# The following is generated to complete the signing operation: + +# o JWS Protected Header; this example uses the header from Figure 9, +# encoded using base64url [RFC4648] to produce Figure 10. + +# { +# "alg": "RS256", +# "kid": "bilbo.baggins@hobbiton.example" +# } + +# Figure 9: JWS Protected Header JSON + + + + + + + +# Miller Informational [Page 12] + +# RFC 7520 JOSE Cookbook May 2015 + + +# eyJhbGciOiJSUzI1NiIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZX +# hhbXBsZSJ9 + +# Figure 10: JWS Protected Header, base64url-encoded + +# The JWS Protected Header (Figure 10) and JWS Payload (Figure 8) are +# combined as described in Section 5.1 of [JWS] to produce the JWS +# Signing Input (Figure 11). + +# eyJhbGciOiJSUzI1NiIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZX +# hhbXBsZSJ9 +# . +# SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IH +# lvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBk +# b24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcm +# UgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4 + +# Figure 11: JWS Signing Input + +# Performing the signature operation over the JWS Signing Input +# (Figure 11) produces the JWS Signature (Figure 12). + +# MRjdkly7_-oTPTS3AXP41iQIGKa80A0ZmTuV5MEaHoxnW2e5CZ5NlKtainoFmK +# ZopdHM1O2U4mwzJdQx996ivp83xuglII7PNDi84wnB-BDkoBwA78185hX-Es4J +# IwmDLJK3lfWRa-XtL0RnltuYv746iYTh_qHRD68BNt1uSNCrUCTJDt5aAE6x8w +# W1Kt9eRo4QPocSadnHXFxnt8Is9UzpERV0ePPQdLuW3IS_de3xyIrDaLGdjluP +# xUAhb6L2aXic1U12podGU0KLUQSE_oI-ZnmKJ3F4uOZDnd6QZWJushZ41Axf_f +# cIe8u9ipH84ogoree7vjbU5y18kDquDg + +# Figure 12: JWS Signature, base64url-encoded + +# 4.1.3. Output Results + +# The following compose the resulting JWS object: + +# o JWS Protected Header (Figure 9) + +# o Payload content (Figure 8) + +# o Signature (Figure 12) + + + + + + + + + + + +# Miller Informational [Page 13] + +# RFC 7520 JOSE Cookbook May 2015 + + +# The resulting JWS object using the JWS Compact Serialization: + +# eyJhbGciOiJSUzI1NiIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZX +# hhbXBsZSJ9 +# . +# SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IH +# lvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBk +# b24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcm +# UgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4 +# . +# MRjdkly7_-oTPTS3AXP41iQIGKa80A0ZmTuV5MEaHoxnW2e5CZ5NlKtainoFmK +# ZopdHM1O2U4mwzJdQx996ivp83xuglII7PNDi84wnB-BDkoBwA78185hX-Es4J +# IwmDLJK3lfWRa-XtL0RnltuYv746iYTh_qHRD68BNt1uSNCrUCTJDt5aAE6x8w +# W1Kt9eRo4QPocSadnHXFxnt8Is9UzpERV0ePPQdLuW3IS_de3xyIrDaLGdjluP +# xUAhb6L2aXic1U12podGU0KLUQSE_oI-ZnmKJ3F4uOZDnd6QZWJushZ41Axf_f +# cIe8u9ipH84ogoree7vjbU5y18kDquDg + +# Figure 13: JWS Compact Serialization + +class TestFourOneThree: + + token = "eyJhbGciOiJSUzI1NiIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZXhhbXBsZSJ9.SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4.MRjdkly7_-oTPTS3AXP41iQIGKa80A0ZmTuV5MEaHoxnW2e5CZ5NlKtainoFmKZopdHM1O2U4mwzJdQx996ivp83xuglII7PNDi84wnB-BDkoBwA78185hX-Es4JIwmDLJK3lfWRa-XtL0RnltuYv746iYTh_qHRD68BNt1uSNCrUCTJDt5aAE6x8wW1Kt9eRo4QPocSadnHXFxnt8Is9UzpERV0ePPQdLuW3IS_de3xyIrDaLGdjluPxUAhb6L2aXic1U12podGU0KLUQSE_oI-ZnmKJ3F4uOZDnd6QZWJushZ41Axf_fcIe8u9ipH84ogoree7vjbU5y18kDquDg" + + def test_signature(self): + + payload = verify(self.token, rsa_public_jwk, 'RS256') + assert payload == expected_payload + +# The resulting JWS object using the general JWS JSON Serialization: + +# { +# "payload": "SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywg +# Z29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9h +# ZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXi +# gJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9m +# ZiB0by4", +# "signatures": [ +# { +# "protected": "eyJhbGciOiJSUzI1NiIsImtpZCI6ImJpbGJvLmJhZ2 +# dpbnNAaG9iYml0b24uZXhhbXBsZSJ9", +# "signature": "MRjdkly7_-oTPTS3AXP41iQIGKa80A0ZmTuV5MEaHo +# xnW2e5CZ5NlKtainoFmKZopdHM1O2U4mwzJdQx996ivp83xuglII +# 7PNDi84wnB-BDkoBwA78185hX-Es4JIwmDLJK3lfWRa-XtL0Rnlt +# uYv746iYTh_qHRD68BNt1uSNCrUCTJDt5aAE6x8wW1Kt9eRo4QPo +# cSadnHXFxnt8Is9UzpERV0ePPQdLuW3IS_de3xyIrDaLGdjluPxU +# Ahb6L2aXic1U12podGU0KLUQSE_oI-ZnmKJ3F4uOZDnd6QZWJush +# Z41Axf_fcIe8u9ipH84ogoree7vjbU5y18kDquDg" +# } +# ] +# } + +# Figure 14: General JWS JSON Serialization + + + + + + + + +# Miller Informational [Page 14] + +# RFC 7520 JOSE Cookbook May 2015 + + +# The resulting JWS object using the flattened JWS JSON Serialization: + +# { +# "payload": "SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywg +# Z29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9h +# ZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXi +# gJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9m +# ZiB0by4", +# "protected": "eyJhbGciOiJSUzI1NiIsImtpZCI6ImJpbGJvLmJhZ2dpbn +# NAaG9iYml0b24uZXhhbXBsZSJ9", +# "signature": "MRjdkly7_-oTPTS3AXP41iQIGKa80A0ZmTuV5MEaHoxnW2 +# e5CZ5NlKtainoFmKZopdHM1O2U4mwzJdQx996ivp83xuglII7PNDi84w +# nB-BDkoBwA78185hX-Es4JIwmDLJK3lfWRa-XtL0RnltuYv746iYTh_q +# HRD68BNt1uSNCrUCTJDt5aAE6x8wW1Kt9eRo4QPocSadnHXFxnt8Is9U +# zpERV0ePPQdLuW3IS_de3xyIrDaLGdjluPxUAhb6L2aXic1U12podGU0 +# KLUQSE_oI-ZnmKJ3F4uOZDnd6QZWJushZ41Axf_fcIe8u9ipH84ogore +# e7vjbU5y18kDquDg" +# } + +# Figure 15: Flattened JWS JSON Serialization + +# 4.2. RSA-PSS Signature + +# This example illustrates signing content using the "PS384" (RSASSA- +# PSS with SHA-384) algorithm. + +# Note that RSASSA-PSS uses random data to generate the signature; it +# might not be possible to exactly replicate the results in this +# section. + +# Note that whitespace is added for readability as described in +# Section 1.1. + +# 4.2.1. Input Factors + +# The following are supplied before beginning the signing operation: + +# o Payload content; this example uses the content from Figure 7, +# encoded using base64url [RFC4648] to produce Figure 8. + +# o RSA private key; this example uses the key from Figure 4. + +# o "alg" parameter of "PS384". + + + + + + + + +# Miller Informational [Page 15] + +# RFC 7520 JOSE Cookbook May 2015 + + +# 4.2.2. Signing Operation + +# The following is generated to complete the signing operation: + +# o JWS Protected Header; this example uses the header from Figure 16, +# encoded using base64url [RFC4648] to produce Figure 17. + +# { +# "alg": "PS384", +# "kid": "bilbo.baggins@hobbiton.example" +# } + +# Figure 16: JWS Protected Header JSON + +# eyJhbGciOiJQUzM4NCIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZX +# hhbXBsZSJ9 + +# Figure 17: JWS Protected Header, base64url-encoded + +# The JWS Protected Header (Figure 17) and JWS Payload (Figure 8) are +# combined as described in [JWS] to produce the JWS Signing Input +# (Figure 18). + +# eyJhbGciOiJQUzM4NCIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZX +# hhbXBsZSJ9 +# . +# SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IH +# lvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBk +# b24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcm +# UgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4 + +# Figure 18: JWS Signing Input + +# Performing the signature operation over the JWS Signing Input +# (Figure 18) produces the JWS Signature (Figure 19). + +# cu22eBqkYDKgIlTpzDXGvaFfz6WGoz7fUDcfT0kkOy42miAh2qyBzk1xEsnk2I +# pN6-tPid6VrklHkqsGqDqHCdP6O8TTB5dDDItllVo6_1OLPpcbUrhiUSMxbbXU +# vdvWXzg-UD8biiReQFlfz28zGWVsdiNAUf8ZnyPEgVFn442ZdNqiVJRmBqrYRX +# e8P_ijQ7p8Vdz0TTrxUeT3lm8d9shnr2lfJT8ImUjvAA2Xez2Mlp8cBE5awDzT +# 0qI0n6uiP1aCN_2_jLAeQTlqRHtfa64QQSUmFAAjVKPbByi7xho0uTOcbH510a +# 6GYmJUAfmWjwZ6oD4ifKo8DYM-X72Eaw + +# Figure 19: JWS Signature, base64url-encoded + + + + + + + +# Miller Informational [Page 16] + +# RFC 7520 JOSE Cookbook May 2015 + + +# 4.2.3. Output Results + +# The following compose the resulting JWS object: + +# o JWS Protected Header (Figure 17) + +# o Payload content (Figure 8) + +# o Signature (Figure 19) + +# The resulting JWS object using the JWS Compact Serialization: + +# eyJhbGciOiJQUzM4NCIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZX +# hhbXBsZSJ9 +# . +# SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IH +# lvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBk +# b24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcm +# UgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4 +# . +# cu22eBqkYDKgIlTpzDXGvaFfz6WGoz7fUDcfT0kkOy42miAh2qyBzk1xEsnk2I +# pN6-tPid6VrklHkqsGqDqHCdP6O8TTB5dDDItllVo6_1OLPpcbUrhiUSMxbbXU +# vdvWXzg-UD8biiReQFlfz28zGWVsdiNAUf8ZnyPEgVFn442ZdNqiVJRmBqrYRX +# e8P_ijQ7p8Vdz0TTrxUeT3lm8d9shnr2lfJT8ImUjvAA2Xez2Mlp8cBE5awDzT +# 0qI0n6uiP1aCN_2_jLAeQTlqRHtfa64QQSUmFAAjVKPbByi7xho0uTOcbH510a +# 6GYmJUAfmWjwZ6oD4ifKo8DYM-X72Eaw + +# Figure 20: JWS Compact Serialization + + + + + + + + + + + + + + + + + + + + + + + +# Miller Informational [Page 17] + +# RFC 7520 JOSE Cookbook May 2015 + + +# The resulting JWS object using the general JWS JSON Serialization: + +# { +# "payload": "SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywg +# Z29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9h +# ZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXi +# gJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9m +# ZiB0by4", +# "signatures": [ +# { +# "protected": "eyJhbGciOiJQUzM4NCIsImtpZCI6ImJpbGJvLmJhZ2 +# dpbnNAaG9iYml0b24uZXhhbXBsZSJ9", +# "signature": "cu22eBqkYDKgIlTpzDXGvaFfz6WGoz7fUDcfT0kkOy +# 42miAh2qyBzk1xEsnk2IpN6-tPid6VrklHkqsGqDqHCdP6O8TTB5 +# dDDItllVo6_1OLPpcbUrhiUSMxbbXUvdvWXzg-UD8biiReQFlfz2 +# 8zGWVsdiNAUf8ZnyPEgVFn442ZdNqiVJRmBqrYRXe8P_ijQ7p8Vd +# z0TTrxUeT3lm8d9shnr2lfJT8ImUjvAA2Xez2Mlp8cBE5awDzT0q +# I0n6uiP1aCN_2_jLAeQTlqRHtfa64QQSUmFAAjVKPbByi7xho0uT +# OcbH510a6GYmJUAfmWjwZ6oD4ifKo8DYM-X72Eaw" +# } +# ] +# } + +# Figure 21: General JWS JSON Serialization + +# The resulting JWS object using the flattened JWS JSON Serialization: + +# { +# "payload": "SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywg +# Z29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9h +# ZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXi +# gJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9m +# ZiB0by4", +# "protected": "eyJhbGciOiJQUzM4NCIsImtpZCI6ImJpbGJvLmJhZ2dpbn +# NAaG9iYml0b24uZXhhbXBsZSJ9", +# "signature": "cu22eBqkYDKgIlTpzDXGvaFfz6WGoz7fUDcfT0kkOy42mi +# Ah2qyBzk1xEsnk2IpN6-tPid6VrklHkqsGqDqHCdP6O8TTB5dDDItllV +# o6_1OLPpcbUrhiUSMxbbXUvdvWXzg-UD8biiReQFlfz28zGWVsdiNAUf +# 8ZnyPEgVFn442ZdNqiVJRmBqrYRXe8P_ijQ7p8Vdz0TTrxUeT3lm8d9s +# hnr2lfJT8ImUjvAA2Xez2Mlp8cBE5awDzT0qI0n6uiP1aCN_2_jLAeQT +# lqRHtfa64QQSUmFAAjVKPbByi7xho0uTOcbH510a6GYmJUAfmWjwZ6oD +# 4ifKo8DYM-X72Eaw" +# } + +# Figure 22: Flattened JWS JSON Serialization + + + + + + +# Miller Informational [Page 18] + +# RFC 7520 JOSE Cookbook May 2015 + + +# 4.3. ECDSA Signature + +# This example illustrates signing content using the "ES512" (Elliptic +# Curve Digital Signature Algorithm (ECDSA) with curve P-521 and SHA- +# 512) algorithm. + +# Note that ECDSA uses random data to generate the signature; it might +# not be possible to exactly replicate the results in this section. + +# Note that whitespace is added for readability as described in +# Section 1.1. + +# 4.3.1. Input Factors + +# The following are supplied before beginning the signing operation: + +# o Payload content; this example uses the content from Figure 7, +# encoded using base64url [RFC4648] to produce Figure 8. + +# o EC private key on the curve P-521; this example uses the key from +# Figure 2. + +# o "alg" parameter of "ES512". + +# 4.3.2. Signing Operation + +# The following is generated before beginning the signature process: + +# o JWS Protected Header; this example uses the header from Figure 23, +# encoded using base64url [RFC4648] to produce Figure 24. + +# { +# "alg": "ES512", +# "kid": "bilbo.baggins@hobbiton.example" +# } + +# Figure 23: JWS Protected Header JSON + +# eyJhbGciOiJFUzUxMiIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZX +# hhbXBsZSJ9 + +# Figure 24: JWS Protected Header, base64url-encoded + + + + + + + + + +# Miller Informational [Page 19] + +# RFC 7520 JOSE Cookbook May 2015 + + +# The JWS Protected Header (Figure 24) and JWS Payload (Figure 8) are +# combined as described in [JWS] to produce the JWS Signing Input +# (Figure 25). + +# eyJhbGciOiJFUzUxMiIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZX +# hhbXBsZSJ9 +# . +# SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IH +# lvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBk +# b24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcm +# UgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4 + +# Figure 25: JWS Signing Input + +# Performing the signature operation over the JWS Signing Input +# (Figure 25) produces the JWS Signature (Figure 26). + +# AE_R_YZCChjn4791jSQCrdPZCNYqHXCTZH0-JZGYNlaAjP2kqaluUIIUnC9qvb +# u9Plon7KRTzoNEuT4Va2cmL1eJAQy3mtPBu_u_sDDyYjnAMDxXPn7XrT0lw-kv +# AD890jl8e2puQens_IEKBpHABlsbEPX6sFY8OcGDqoRuBomu9xQ2 + +# Figure 26: JWS Signature, base64url-encoded + +# 4.3.3. Output Results + +# The following compose the resulting JWS object: + +# o JWS Protected Header (Figure 24) + +# o Payload content (Figure 8) + +# o Signature (Figure 26) + +# The resulting JWS object using the JWS Compact Serialization: + +# eyJhbGciOiJFUzUxMiIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZX +# hhbXBsZSJ9 +# . +# SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IH +# lvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBk +# b24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcm +# UgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4 +# . +# AE_R_YZCChjn4791jSQCrdPZCNYqHXCTZH0-JZGYNlaAjP2kqaluUIIUnC9qvb +# u9Plon7KRTzoNEuT4Va2cmL1eJAQy3mtPBu_u_sDDyYjnAMDxXPn7XrT0lw-kv +# AD890jl8e2puQens_IEKBpHABlsbEPX6sFY8OcGDqoRuBomu9xQ2 + +# Figure 27: JWS Compact Serialization + + +class TestFourThreeThree: + + token = "eyJhbGciOiJFUzUxMiIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZXhhbXBsZSJ9.SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4.AE_R_YZCChjn4791jSQCrdPZCNYqHXCTZH0-JZGYNlaAjP2kqaluUIIUnC9qvbu9Plon7KRTzoNEuT4Va2cmL1eJAQy3mtPBu_u_sDDyYjnAMDxXPn7XrT0lw-kvAD890jl8e2puQens_IEKBpHABlsbEPX6sFY8OcGDqoRuBomu9xQ2" + + def test_signature(self): + + payload = verify(self.token, ec_public_key, 'ES512') + assert payload == expected_payload + + + +# Miller Informational [Page 20] + +# RFC 7520 JOSE Cookbook May 2015 + + +# The resulting JWS object using the general JWS JSON Serialization: + +# { +# "payload": "SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywg +# Z29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9h +# ZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXi +# gJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9m +# ZiB0by4", +# "signatures": [ +# { +# "protected": "eyJhbGciOiJFUzUxMiIsImtpZCI6ImJpbGJvLmJhZ2 +# dpbnNAaG9iYml0b24uZXhhbXBsZSJ9", +# "signature": "AE_R_YZCChjn4791jSQCrdPZCNYqHXCTZH0-JZGYNl +# aAjP2kqaluUIIUnC9qvbu9Plon7KRTzoNEuT4Va2cmL1eJAQy3mt +# PBu_u_sDDyYjnAMDxXPn7XrT0lw-kvAD890jl8e2puQens_IEKBp +# HABlsbEPX6sFY8OcGDqoRuBomu9xQ2" +# } +# ] +# } + +# Figure 28: General JWS JSON Serialization + +# The resulting JWS object using the flattened JWS JSON Serialization: + +# { +# "payload": "SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywg +# Z29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9h +# ZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXi +# gJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9m +# ZiB0by4", +# "protected": "eyJhbGciOiJFUzUxMiIsImtpZCI6ImJpbGJvLmJhZ2dpbn +# NAaG9iYml0b24uZXhhbXBsZSJ9", +# "signature": "AE_R_YZCChjn4791jSQCrdPZCNYqHXCTZH0-JZGYNlaAjP +# 2kqaluUIIUnC9qvbu9Plon7KRTzoNEuT4Va2cmL1eJAQy3mtPBu_u_sD +# DyYjnAMDxXPn7XrT0lw-kvAD890jl8e2puQens_IEKBpHABlsbEPX6sF +# Y8OcGDqoRuBomu9xQ2" +# } + +# Figure 29: Flattened JWS JSON Serialization + +# 4.4. HMAC-SHA2 Integrity Protection + +# This example illustrates integrity protecting content using the +# "HS256" (HMAC-SHA-256) algorithm. + +# Note that whitespace is added for readability as described in +# Section 1.1. + + + + +# Miller Informational [Page 21] + +# RFC 7520 JOSE Cookbook May 2015 + + +# 4.4.1. Input Factors + +# The following are supplied before beginning the signing operation: + +# o Payload content; this example uses the content from Figure 7, +# encoded using base64url [RFC4648] to produce Figure 8. + +# o HMAC symmetric key; this example uses the key from Figure 5. + +# o "alg" parameter of "HS256". + +# 4.4.2. Signing Operation + +# The following is generated before completing the signing operation: + +# o JWS Protected Header; this example uses the header from Figure 30, +# encoded using base64url [RFC4648] to produce Figure 31. + +# { +# "alg": "HS256", +# "kid": "018c0ae5-4d9b-471b-bfd6-eef314bc7037" +# } + +# Figure 30: JWS Protected Header JSON + +# eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LTRkOWItNDcxYi1iZmQ2LW +# VlZjMxNGJjNzAzNyJ9 + +# Figure 31: JWS Protected Header, base64url-encoded + +# The JWS Protected Header (Figure 31) and JWS Payload (Figure 8) are +# combined as described in [JWS] to produce the JWS Signing Input +# (Figure 32). + +# eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LTRkOWItNDcxYi1iZmQ2LW +# VlZjMxNGJjNzAzNyJ9 +# . +# SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IH +# lvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBk +# b24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcm +# UgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4 + +# Figure 32: JWS Signing Input + + + + + + + + +# Miller Informational [Page 22] + +# RFC 7520 JOSE Cookbook May 2015 + + +# Performing the signature operation over the JWS Signing Input +# (Figure 32) produces the JWS Signature (Figure 33). + +# s0h6KThzkfBBBkLspW1h84VsJZFTsPPqMDA7g1Md7p0 + +# Figure 33: JWS Signature, base64url-encoded + +# 4.4.3. Output Results + +# The following compose the resulting JWS object: + +# o JWS Protected Header (Figure 31) + +# o Payload content (Figure 8) + +# o Signature (Figure 33) + +# The resulting JWS object using the JWS Compact Serialization: + +# eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LTRkOWItNDcxYi1iZmQ2LW +# VlZjMxNGJjNzAzNyJ9 +# . +# SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IH +# lvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBk +# b24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcm +# UgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4 +# . +# s0h6KThzkfBBBkLspW1h84VsJZFTsPPqMDA7g1Md7p0 + +# Figure 34: JWS Compact Serialization + + +class TestFourFourThree: + + token = "eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LTRkOWItNDcxYi1iZmQ2LWVlZjMxNGJjNzAzNyJ9.SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4.s0h6KThzkfBBBkLspW1h84VsJZFTsPPqMDA7g1Md7p0" + + def test_signature(self): + + payload = verify(self.token, hmac_key, 'HS256') + assert payload == expected_payload + + + + + + + + + + + + + + + + + + +# Miller Informational [Page 23] + +# RFC 7520 JOSE Cookbook May 2015 + + +# The resulting JWS object using the general JWS JSON Serialization: + +# { +# "payload": "SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywg +# Z29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9h +# ZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXi +# gJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9m +# ZiB0by4", +# "signatures": [ +# { +# "protected": "eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LT +# RkOWItNDcxYi1iZmQ2LWVlZjMxNGJjNzAzNyJ9", +# "signature": "s0h6KThzkfBBBkLspW1h84VsJZFTsPPqMDA7g1Md7p +# 0" +# } +# ] +# } + +# Figure 35: General JWS JSON Serialization + +# The resulting JWS object using the flattened JWS JSON Serialization: + +# { +# "payload": "SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywg +# Z29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9h +# ZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXi +# gJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9m +# ZiB0by4", +# "protected": "eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LTRkOW +# ItNDcxYi1iZmQ2LWVlZjMxNGJjNzAzNyJ9", +# "signature": "s0h6KThzkfBBBkLspW1h84VsJZFTsPPqMDA7g1Md7p0" +# } + +# Figure 36: Flattened JWS JSON Serialization + +# 4.5. Signature with Detached Content + +# This example illustrates a signature with detached content. This +# example is identical to other examples in Section 4, except the +# resulting JWS objects do not include the JWS Payload field. Instead, +# the application is expected to locate it elsewhere. For example, the +# signature might be in a metadata section, with the payload being the +# content. + +# Note that whitespace is added for readability as described in +# Section 1.1. + + + + + +# Miller Informational [Page 24] + +# RFC 7520 JOSE Cookbook May 2015 + + +# 4.5.1. Input Factors + +# The following are supplied before beginning the signing operation: + +# o Payload content; this example uses the content from Figure 7, +# encoded using base64url [RFC4648] to produce Figure 8. + +# o Signing key; this example uses the AES symmetric key from +# Figure 5. + +# o Signing algorithm; this example uses "HS256". + +# 4.5.2. Signing Operation + +# The following is generated before completing the signing operation: + +# o JWS Protected Header; this example uses the header from Figure 37, +# encoded using base64url [RFC4648] to produce Figure 38. + +# { +# "alg": "HS256", +# "kid": "018c0ae5-4d9b-471b-bfd6-eef314bc7037" +# } + +# Figure 37: JWS Protected Header JSON + +# eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LTRkOWItNDcxYi1iZmQ2LW +# VlZjMxNGJjNzAzNyJ9 + +# Figure 38: JWS Protected Header, base64url-encoded + +# The JWS Protected Header (Figure 38) and JWS Payload (Figure 8) are +# combined as described in [JWS] to produce the JWS Signing Input +# (Figure 39). + +# eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LTRkOWItNDcxYi1iZmQ2LW +# VlZjMxNGJjNzAzNyJ9 +# . +# SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IH +# lvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBk +# b24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcm +# UgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4 + +# Figure 39: JWS Signing Input + + + + + + + +# Miller Informational [Page 25] + +# RFC 7520 JOSE Cookbook May 2015 + + +# Performing the signature operation over the JWS Signing Input +# (Figure 39) produces the JWS Signature (Figure 40). + +# s0h6KThzkfBBBkLspW1h84VsJZFTsPPqMDA7g1Md7p0 + +# Figure 40: JWS Signature, base64url-encoded + +# 4.5.3. Output Results + +# The following compose the resulting JWS object: + +# o JWS Protected Header (Figure 38) + +# o Signature (Figure 40) + +# The resulting JWS object using the JWS Compact Serialization: + +# eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LTRkOWItNDcxYi1iZmQ2LW +# VlZjMxNGJjNzAzNyJ9 +# . +# . +# s0h6KThzkfBBBkLspW1h84VsJZFTsPPqMDA7g1Md7p0 + +# Figure 41: General JWS JSON Serialization + +# The resulting JWS object using the general JWS JSON Serialization: + +# { +# "signatures": [ +# { +# "protected": "eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LT +# RkOWItNDcxYi1iZmQ2LWVlZjMxNGJjNzAzNyJ9", +# "signature": "s0h6KThzkfBBBkLspW1h84VsJZFTsPPqMDA7g1Md7p +# 0" +# } +# ] +# } + +# Figure 42: General JWS JSON Serialization + + + + + + + + + + + + +# Miller Informational [Page 26] + +# RFC 7520 JOSE Cookbook May 2015 + + +# The resulting JWS object using the flattened JWS JSON Serialization: + +# { +# "protected": "eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LTRkOW +# ItNDcxYi1iZmQ2LWVlZjMxNGJjNzAzNyJ9", +# "signature": "s0h6KThzkfBBBkLspW1h84VsJZFTsPPqMDA7g1Md7p0" +# } + +# Figure 43: Flattened JWS JSON Serialization + +# 4.6. Protecting Specific Header Fields + +# This example illustrates a signature where only certain Header +# Parameters are protected. Since this example contains both +# unprotected and protected Header Parameters, only the general JWS +# JSON Serialization and flattened JWS JSON Serialization are possible. + +# Note that whitespace is added for readability as described in +# Section 1.1. + +# 4.6.1. Input Factors + +# The following are supplied before beginning the signing operation: + +# o Payload content; this example uses the content from Figure 7, +# encoded using base64url [RFC4648] to produce Figure 8. + +# o Signing key; this example uses the AES symmetric key from +# Figure 5. + +# o Signing algorithm; this example uses "HS256". + +# 4.6.2. Signing Operation + +# The following are generated before completing the signing operation: + +# o JWS Protected Header; this example uses the header from Figure 44, +# encoded using base64url [RFC4648] to produce Figure 45. + +# o JWS Unprotected Header; this example uses the header from +# Figure 46. + +# { +# "alg": "HS256" +# } + +# Figure 44: JWS Protected Header JSON + + + + +# Miller Informational [Page 27] + +# RFC 7520 JOSE Cookbook May 2015 + + +# eyJhbGciOiJIUzI1NiJ9 + +# Figure 45: JWS Protected Header, base64url-encoded + +# { +# "kid": "018c0ae5-4d9b-471b-bfd6-eef314bc7037" +# } + +# Figure 46: JWS Unprotected Header JSON + +# The JWS Protected Header (Figure 45) and JWS Payload (Figure 8) are +# combined as described in [JWS] to produce the JWS Signing Input +# (Figure 47). + +# eyJhbGciOiJIUzI1NiJ9 +# . +# SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IH +# lvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBk +# b24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcm +# UgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4 + +# Figure 47: JWS Signing Input + +# Performing the signature operation over the JWS Signing Input +# (Figure 47) produces the JWS Signature (Figure 48). + +# bWUSVaxorn7bEF1djytBd0kHv70Ly5pvbomzMWSOr20 + +# Figure 48: JWS Signature, base64url-encoded + +# 4.6.3. Output Results + +# The following compose the resulting JWS object: + +# o JWS Protected Header (Figure 45) + +# o JWS Unprotected Header (Figure 46) + +# o Payload content (Figure 8) + +# o Signature (Figure 48) + +# The JWS Compact Serialization is not presented because it does not +# support this use case. + + + + + + + +# Miller Informational [Page 28] + +# RFC 7520 JOSE Cookbook May 2015 + + +# The resulting JWS object using the general JWS JSON Serialization: + +# { +# "payload": "SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywg +# Z29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9h +# ZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXi +# gJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9m +# ZiB0by4", +# "signatures": [ +# { +# "protected": "eyJhbGciOiJIUzI1NiJ9", +# "header": { +# "kid": "018c0ae5-4d9b-471b-bfd6-eef314bc7037" +# }, +# "signature": "bWUSVaxorn7bEF1djytBd0kHv70Ly5pvbomzMWSOr2 +# 0" +# } +# ] +# } + +# Figure 49: General JWS JSON Serialization + +# The resulting JWS object using the flattened JWS JSON Serialization: + +# { +# "payload": "SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywg +# Z29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9h +# ZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXi +# gJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9m +# ZiB0by4", +# "protected": "eyJhbGciOiJIUzI1NiJ9", +# "header": { +# "kid": "018c0ae5-4d9b-471b-bfd6-eef314bc7037" +# }, +# "signature": "bWUSVaxorn7bEF1djytBd0kHv70Ly5pvbomzMWSOr20" +# } + +# Figure 50: Flattened JWS JSON Serialization + +# 4.7. Protecting Content Only + +# This example illustrates a signature where none of the Header +# Parameters are protected. Since this example contains only +# unprotected Header Parameters, only the general JWS JSON +# Serialization and flattened JWS JSON Serialization are possible. + +# Note that whitespace is added for readability as described in +# Section 1.1. + + + +# Miller Informational [Page 29] + +# RFC 7520 JOSE Cookbook May 2015 + + +# 4.7.1. Input Factors + +# The following are supplied before beginning the signing operation: + +# o Payload content; this example uses the content from Figure 7, +# encoded using base64url [RFC4648] to produce Figure 8. + +# o Signing key; this example uses the AES symmetric key from +# Figure 5. + +# o Signing algorithm; this example uses "HS256". + +# 4.7.2. Signing Operation + +# The following is generated before completing the signing operation: + +# o JWS Unprotected Header; this example uses the header from +# Figure 51. + +# { +# "alg": "HS256", +# "kid": "018c0ae5-4d9b-471b-bfd6-eef314bc7037" +# } + +# Figure 51: JWS Unprotected Header JSON + +# The empty string (as there is no JWS Protected Header) and JWS +# Payload (Figure 8) are combined as described in [JWS] to produce the +# JWS Signing Input (Figure 52). + +# . +# SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IH +# lvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBk +# b24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcm +# UgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4 + +# Figure 52: JWS Signing Input + +# Performing the signature operation over the JWS Signing Input +# (Figure 52) produces the JWS Signature (Figure 53). + +# xuLifqLGiblpv9zBpuZczWhNj1gARaLV3UxvxhJxZuk + +# Figure 53: JWS Signature, base64url-encoded + + + + + + + +# Miller Informational [Page 30] + +# RFC 7520 JOSE Cookbook May 2015 + + +# 4.7.3. Output Results + +# The following compose the resulting JWS object: + +# o JWS Unprotected Header (Figure 51) + +# o Payload content (Figure 8) + +# o Signature (Figure 53) + +# The JWS Compact Serialization is not presented because it does not +# support this use case. + +# The resulting JWS object using the general JWS JSON Serialization: + +# { +# "payload": "SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywg +# Z29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9h +# ZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXi +# gJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9m +# ZiB0by4", +# "signatures": [ +# { +# "header": { +# "alg": "HS256", +# "kid": "018c0ae5-4d9b-471b-bfd6-eef314bc7037" +# }, +# "signature": "xuLifqLGiblpv9zBpuZczWhNj1gARaLV3UxvxhJxZu +# k" +# } +# ] +# } + +# Figure 54: General JWS JSON Serialization + + + + + + + + + + + + + + + + + +# Miller Informational [Page 31] + +# RFC 7520 JOSE Cookbook May 2015 + + +# The resulting JWS object using the flattened JWS JSON Serialization: + +# { +# "payload": "SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywg +# Z29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9h +# ZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXi +# gJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9m +# ZiB0by4", +# "header": { +# "alg": "HS256", +# "kid": "018c0ae5-4d9b-471b-bfd6-eef314bc7037" +# }, +# "signature": "xuLifqLGiblpv9zBpuZczWhNj1gARaLV3UxvxhJxZuk" +# } + +# Figure 55: Flattened JWS JSON Serialization + +# 4.8. Multiple Signatures + +# This example illustrates multiple signatures applied to the same +# payload. Since this example contains more than one signature, only +# the JSON General Serialization is possible. + +# Note that whitespace is added for readability as described in +# Section 1.1. + +# 4.8.1. Input Factors + +# The following are supplied before beginning the signing operation: + +# o Payload content; this example uses the content from Figure 7, +# encoded using base64url [RFC4648] to produce Figure 8. + +# o Signing keys; this example uses the following: + +# * RSA private key from Figure 4 for the first signature + +# * EC private key from Figure 2 for the second signature + +# * AES symmetric key from Figure 5 for the third signature + +# o Signing algorithms; this example uses the following: + +# * "RS256" for the first signature + +# * "ES512" for the second signature + +# * "HS256" for the third signature + + + +# Miller Informational [Page 32] + +# RFC 7520 JOSE Cookbook May 2015 + + +# 4.8.2. First Signing Operation + +# The following are generated before completing the first signing +# operation: + +# o JWS Protected Header; this example uses the header from Figure 56, +# encoded using base64url [RFC4648] to produce Figure 57. + +# o JWS Unprotected Header; this example uses the header from +# Figure 58. + +# { +# "alg": "RS256" +# } + +# Figure 56: Signature #1 JWS Protected Header JSON + +# eyJhbGciOiJSUzI1NiJ9 + +# Figure 57: Signature #1 JWS Protected Header, base64url-encoded + +# { +# "kid": "bilbo.baggins@hobbiton.example" +# } + +# Figure 58: Signature #1 JWS Unprotected Header JSON + +# The JWS Protected Header (Figure 57) and JWS Payload (Figure 8) are +# combined as described in [JWS] to produce the JWS Signing Input +# (Figure 59). + +# eyJhbGciOiJSUzI1NiJ9 +# . +# SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IH +# lvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBk +# b24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcm +# UgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4 + +# Figure 59: JWS Signing Input + + + + + + + + + + + + +# Miller Informational [Page 33] + +# RFC 7520 JOSE Cookbook May 2015 + + +# Performing the signature operation over the JWS Signing Input +# (Figure 59) produces the JWS Signature (Figure 60). + +# MIsjqtVlOpa71KE-Mss8_Nq2YH4FGhiocsqrgi5NvyG53uoimic1tcMdSg-qpt +# rzZc7CG6Svw2Y13TDIqHzTUrL_lR2ZFcryNFiHkSw129EghGpwkpxaTn_THJTC +# glNbADko1MZBCdwzJxwqZc-1RlpO2HibUYyXSwO97BSe0_evZKdjvvKSgsIqjy +# tKSeAMbhMBdMma622_BG5t4sdbuCHtFjp9iJmkio47AIwqkZV1aIZsv33uPUqB +# BCXbYoQJwt7mxPftHmNlGoOSMxR_3thmXTCm4US-xiNOyhbm8afKK64jU6_TPt +# QHiJeQJxz9G3Tx-083B745_AfYOnlC9w + +# Figure 60: JWS Signature #1, base64url-encoded + +# The following is the assembled first signature serialized as JSON: + +# { +# "protected": "eyJhbGciOiJSUzI1NiJ9", +# "header": { +# "kid": "bilbo.baggins@hobbiton.example" +# }, +# "signature": "MIsjqtVlOpa71KE-Mss8_Nq2YH4FGhiocsqrgi5NvyG53u +# oimic1tcMdSg-qptrzZc7CG6Svw2Y13TDIqHzTUrL_lR2ZFcryNFiHkS +# w129EghGpwkpxaTn_THJTCglNbADko1MZBCdwzJxwqZc-1RlpO2HibUY +# yXSwO97BSe0_evZKdjvvKSgsIqjytKSeAMbhMBdMma622_BG5t4sdbuC +# HtFjp9iJmkio47AIwqkZV1aIZsv33uPUqBBCXbYoQJwt7mxPftHmNlGo +# OSMxR_3thmXTCm4US-xiNOyhbm8afKK64jU6_TPtQHiJeQJxz9G3Tx-0 +# 83B745_AfYOnlC9w" +# } + +# Figure 61: Signature #1 JSON + +# 4.8.3. Second Signing Operation + +# The following is generated before completing the second signing +# operation: + +# o JWS Unprotected Header; this example uses the header from +# Figure 62. + +# { +# "alg": "ES512", +# "kid": "bilbo.baggins@hobbiton.example" +# } + +# Figure 62: Signature #2 JWS Unprotected Header JSON + + + + + + + +# Miller Informational [Page 34] + +# RFC 7520 JOSE Cookbook May 2015 + + +# The empty string (as there is no JWS Protected Header) and JWS +# Payload (Figure 8) are combined as described in [JWS] to produce the +# JWS Signing Input (Figure 63). + +# . +# SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IH +# lvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBk +# b24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcm +# UgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4 + +# Figure 63: JWS Signing Input + +# Performing the signature operation over the JWS Signing Input +# (Figure 63) produces the JWS Signature (Figure 64). + +# ARcVLnaJJaUWG8fG-8t5BREVAuTY8n8YHjwDO1muhcdCoFZFFjfISu0Cdkn9Yb +# dlmi54ho0x924DUz8sK7ZXkhc7AFM8ObLfTvNCrqcI3Jkl2U5IX3utNhODH6v7 +# xgy1Qahsn0fyb4zSAkje8bAWz4vIfj5pCMYxxm4fgV3q7ZYhm5eD + +# Figure 64: JWS Signature #2, base64url-encoded + +# The following is the assembled second signature serialized as JSON: + +# { +# "header": { +# "alg": "ES512", +# "kid": "bilbo.baggins@hobbiton.example" +# }, +# "signature": "ARcVLnaJJaUWG8fG-8t5BREVAuTY8n8YHjwDO1muhcdCoF +# ZFFjfISu0Cdkn9Ybdlmi54ho0x924DUz8sK7ZXkhc7AFM8ObLfTvNCrq +# cI3Jkl2U5IX3utNhODH6v7xgy1Qahsn0fyb4zSAkje8bAWz4vIfj5pCM +# Yxxm4fgV3q7ZYhm5eD" +# } + +# Figure 65: Signature #2 JSON + + + + + + + + + + + + + + + + +# Miller Informational [Page 35] + +# RFC 7520 JOSE Cookbook May 2015 + + +# 4.8.4. Third Signing Operation + +# The following is generated before completing the third signing +# operation: + +# o JWS Protected Header; this example uses the header from Figure 66, +# encoded using base64url [RFC4648] to produce Figure 67. + +# { +# "alg": "HS256", +# "kid": "018c0ae5-4d9b-471b-bfd6-eef314bc7037" +# } + +# Figure 66: Signature #3 JWS Protected Header JSON + +# eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LTRkOWItNDcxYi1iZmQ2LW +# VlZjMxNGJjNzAzNyJ9 + +# Figure 67: Signature #3 JWS Protected Header, base64url-encoded + +# The JWS Protected Header (Figure 67) and JWS Payload (Figure 8) are +# combined as described in [JWS] to produce the JWS Signing Input +# (Figure 68). + +# eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LTRkOWItNDcxYi1iZmQ2LW +# VlZjMxNGJjNzAzNyJ9 +# . +# SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IH +# lvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBk +# b24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcm +# UgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4 + +# Figure 68: JWS Signing Input + +# Performing the signature operation over the JWS Signing Input +# (Figure 68) produces the JWS Signature (Figure 69). + +# s0h6KThzkfBBBkLspW1h84VsJZFTsPPqMDA7g1Md7p0 + +# Figure 69: JWS Signature #3, base64url-encoded + + + + + + + + + + + +# Miller Informational [Page 36] + +# RFC 7520 JOSE Cookbook May 2015 + + +# The following is the assembled third signature serialized as JSON: + +# { +# "protected": "eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LTRkOW +# ItNDcxYi1iZmQ2LWVlZjMxNGJjNzAzNyJ9", +# "signature": "s0h6KThzkfBBBkLspW1h84VsJZFTsPPqMDA7g1Md7p0" +# } + +# Figure 70: Signature #3 JSON + +# 4.8.5. Output Results + +# The following compose the resulting JWS object: + +# o Payload content (Figure 8) + +# o Signature #1 JSON (Figure 61) + +# o Signature #2 JSON (Figure 65) + +# o Signature #3 JSON (Figure 70) + +# The JWS Compact Serialization is not presented because it does not +# support this use case; the flattened JWS JSON Serialization is not +# presented because there is more than one signature. + + + + + + + + + + + + + + + + + + + + + + + + + + +# Miller Informational [Page 37] + +# RFC 7520 JOSE Cookbook May 2015 + + +# The resulting JWS object using the general JWS JSON Serialization: + +# { +# "payload": "SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywg +# Z29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9h +# ZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXi +# gJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9m +# ZiB0by4", +# "signatures": [ +# { +# "protected": "eyJhbGciOiJSUzI1NiJ9", +# "header": { +# "kid": "bilbo.baggins@hobbiton.example" +# }, +# "signature": "MIsjqtVlOpa71KE-Mss8_Nq2YH4FGhiocsqrgi5Nvy +# G53uoimic1tcMdSg-qptrzZc7CG6Svw2Y13TDIqHzTUrL_lR2ZFc +# ryNFiHkSw129EghGpwkpxaTn_THJTCglNbADko1MZBCdwzJxwqZc +# -1RlpO2HibUYyXSwO97BSe0_evZKdjvvKSgsIqjytKSeAMbhMBdM +# ma622_BG5t4sdbuCHtFjp9iJmkio47AIwqkZV1aIZsv33uPUqBBC +# XbYoQJwt7mxPftHmNlGoOSMxR_3thmXTCm4US-xiNOyhbm8afKK6 +# 4jU6_TPtQHiJeQJxz9G3Tx-083B745_AfYOnlC9w" +# }, +# { +# "header": { +# "alg": "ES512", +# "kid": "bilbo.baggins@hobbiton.example" +# }, +# "signature": "ARcVLnaJJaUWG8fG-8t5BREVAuTY8n8YHjwDO1muhc +# dCoFZFFjfISu0Cdkn9Ybdlmi54ho0x924DUz8sK7ZXkhc7AFM8Ob +# LfTvNCrqcI3Jkl2U5IX3utNhODH6v7xgy1Qahsn0fyb4zSAkje8b +# AWz4vIfj5pCMYxxm4fgV3q7ZYhm5eD" +# }, +# { +# "protected": "eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LT +# RkOWItNDcxYi1iZmQ2LWVlZjMxNGJjNzAzNyJ9", +# "signature": "s0h6KThzkfBBBkLspW1h84VsJZFTsPPqMDA7g1Md7p +# 0" +# } +# ] +# } + +# Figure 71: General JWS JSON Serialization + + + + + + + + + +# Miller Informational [Page 38] + +# RFC 7520 JOSE Cookbook May 2015 + + +# 5. JSON Web Encryption Examples + +# The following sections demonstrate how to generate various JWE +# objects. + +# All of the encryption examples (unless otherwise noted) use the +# following Plaintext content (an abridged quote from "The Fellowship +# of the Ring" [LOTR-FELLOWSHIP]), serialized as UTF-8. The Plaintext +# is presented here as a series of quoted strings that are concatenated +# to produce the JWE Plaintext. The sequence "\xe2\x80\x93" is +# substituted for (U+2013 EN DASH), and quotation marks (U+0022 +# QUOTATION MARK) are added for readability but are not present in the +# JWE Plaintext. + +# "You can trust us to stick with you through thick and " +# "thin\xe2\x80\x93to the bitter end. And you can trust us to " +# "keep any secret of yours\xe2\x80\x93closer than you keep it " +# "yourself. But you cannot trust us to let you face trouble " +# "alone, and go off without a word. We are your friends, Frodo." + +# Figure 72: Plaintext Content + +# 5.1. Key Encryption Using RSA v1.5 and AES-HMAC-SHA2 + +# This example illustrates encrypting content using the "RSA1_5" +# (RSAES-PKCS1-v1_5) key encryption algorithm and the "A128CBC-HS256" +# (AES-128-CBC-HMAC-SHA-256) content encryption algorithm. + +# Note that RSAES-PKCS1-v1_5 uses random data to generate the +# ciphertext; it might not be possible to exactly replicate the results +# in this section. + +# Note that only the RSA public key is necessary to perform the +# encryption. However, the example includes the RSA private key to +# allow readers to validate the output. + +# Note that whitespace is added for readability as described in +# Section 1.1. + +# 5.1.1. Input Factors + +# The following are supplied before beginning the encryption process: + +# o Plaintext content; this example uses the content from Figure 72. + +# o RSA public key; this example uses the key from Figure 73. + + + + + +# Miller Informational [Page 39] + +# RFC 7520 JOSE Cookbook May 2015 + + +# o "alg" parameter of "RSA1_5". + +# o "enc" parameter of "A128CBC-HS256". + +# { +# "kty": "RSA", +# "kid": "frodo.baggins@hobbiton.example", +# "use": "enc", +# "n": "maxhbsmBtdQ3CNrKvprUE6n9lYcregDMLYNeTAWcLj8NnPU9XIYegT +# HVHQjxKDSHP2l-F5jS7sppG1wgdAqZyhnWvXhYNvcM7RfgKxqNx_xAHx +# 6f3yy7s-M9PSNCwPC2lh6UAkR4I00EhV9lrypM9Pi4lBUop9t5fS9W5U +# NwaAllhrd-osQGPjIeI1deHTwx-ZTHu3C60Pu_LJIl6hKn9wbwaUmA4c +# R5Bd2pgbaY7ASgsjCUbtYJaNIHSoHXprUdJZKUMAzV0WOKPfA6OPI4oy +# pBadjvMZ4ZAj3BnXaSYsEZhaueTXvZB4eZOAjIyh2e_VOIKVMsnDrJYA +# VotGlvMQ", +# "e": "AQAB", +# "d": "Kn9tgoHfiTVi8uPu5b9TnwyHwG5dK6RE0uFdlpCGnJN7ZEi963R7wy +# bQ1PLAHmpIbNTztfrheoAniRV1NCIqXaW_qS461xiDTp4ntEPnqcKsyO +# 5jMAji7-CL8vhpYYowNFvIesgMoVaPRYMYT9TW63hNM0aWs7USZ_hLg6 +# Oe1mY0vHTI3FucjSM86Nff4oIENt43r2fspgEPGRrdE6fpLc9Oaq-qeP +# 1GFULimrRdndm-P8q8kvN3KHlNAtEgrQAgTTgz80S-3VD0FgWfgnb1PN +# miuPUxO8OpI9KDIfu_acc6fg14nsNaJqXe6RESvhGPH2afjHqSy_Fd2v +# pzj85bQQ", +# "p": "2DwQmZ43FoTnQ8IkUj3BmKRf5Eh2mizZA5xEJ2MinUE3sdTYKSLtaE +# oekX9vbBZuWxHdVhM6UnKCJ_2iNk8Z0ayLYHL0_G21aXf9-unynEpUsH +# 7HHTklLpYAzOOx1ZgVljoxAdWNn3hiEFrjZLZGS7lOH-a3QQlDDQoJOJ +# 2VFmU", +# "q": "te8LY4-W7IyaqH1ExujjMqkTAlTeRbv0VLQnfLY2xINnrWdwiQ93_V +# F099aP1ESeLja2nw-6iKIe-qT7mtCPozKfVtUYfz5HrJ_XY2kfexJINb +# 9lhZHMv5p1skZpeIS-GPHCC6gRlKo1q-idn_qxyusfWv7WAxlSVfQfk8 +# d6Et0", +# "dp": "UfYKcL_or492vVc0PzwLSplbg4L3-Z5wL48mwiswbpzOyIgd2xHTH +# QmjJpFAIZ8q-zf9RmgJXkDrFs9rkdxPtAsL1WYdeCT5c125Fkdg317JV +# RDo1inX7x2Kdh8ERCreW8_4zXItuTl_KiXZNU5lvMQjWbIw2eTx1lpsf +# lo0rYU", +# "dq": "iEgcO-QfpepdH8FWd7mUFyrXdnOkXJBCogChY6YKuIHGc_p8Le9Mb +# pFKESzEaLlN1Ehf3B6oGBl5Iz_ayUlZj2IoQZ82znoUrpa9fVYNot87A +# CfzIG7q9Mv7RiPAderZi03tkVXAdaBau_9vs5rS-7HMtxkVrxSUvJY14 +# TkXlHE", +# "qi": "kC-lzZOqoFaZCr5l0tOVtREKoVqaAYhQiqIRGL-MzS4sCmRkxm5vZ +# lXYx6RtE1n_AagjqajlkjieGlxTTThHD8Iga6foGBMaAr5uR1hGQpSc7 +# Gl7CF1DZkBJMTQN6EshYzZfxW08mIO8M6Rzuh0beL6fG9mkDcIyPrBXx +# 2bQ_mM" +# } + +# Figure 73: RSA 2048-Bit Key, in JWK Format + + + + + +# Miller Informational [Page 40] + +# RFC 7520 JOSE Cookbook May 2015 + + +# (NOTE: While the key includes the private parameters, only the public +# parameters "e" and "n" are necessary for the encryption operation.) + +# 5.1.2. Generated Factors + +# The following are generated before encrypting: + +# o AES symmetric key as the Content Encryption Key (CEK); this +# example uses the key from Figure 74. + +# o Initialization Vector; this example uses the Initialization Vector +# from Figure 75. + +# 3qyTVhIWt5juqZUCpfRqpvauwB956MEJL2Rt-8qXKSo + +# Figure 74: Content Encryption Key, base64url-encoded + +# bbd5sTkYwhAIqfHsx8DayA + +# Figure 75: Initialization Vector, base64url-encoded + +# 5.1.3. Encrypting the Key + +# Performing the key encryption operation over the CEK (Figure 74) with +# the RSA key (Figure 73) results in the following Encrypted Key: + +# laLxI0j-nLH-_BgLOXMozKxmy9gffy2gTdvqzfTihJBuuzxg0V7yk1WClnQePF +# vG2K-pvSlWc9BRIazDrn50RcRai__3TDON395H3c62tIouJJ4XaRvYHFjZTZ2G +# Xfz8YAImcc91Tfk0WXC2F5Xbb71ClQ1DDH151tlpH77f2ff7xiSxh9oSewYrcG +# TSLUeeCt36r1Kt3OSj7EyBQXoZlN7IxbyhMAfgIe7Mv1rOTOI5I8NQqeXXW8Vl +# zNmoxaGMny3YnGir5Wf6Qt2nBq4qDaPdnaAuuGUGEecelIO1wx1BpyIfgvfjOh +# MBs9M8XL223Fg47xlGsMXdfuY-4jaqVw + +# Figure 76: Encrypted Key, base64url-encoded + + + + + + + + + + + + + + + + + +# Miller Informational [Page 41] + +# RFC 7520 JOSE Cookbook May 2015 + + +# 5.1.4. Encrypting the Content + +# The following is generated before encrypting the Plaintext: + +# o JWE Protected Header; this example uses the header from Figure 77, +# encoded using base64url [RFC4648] to produce Figure 78. + +# { +# "alg": "RSA1_5", +# "kid": "frodo.baggins@hobbiton.example", +# "enc": "A128CBC-HS256" +# } + +# Figure 77: JWE Protected Header JSON + +# eyJhbGciOiJSU0ExXzUiLCJraWQiOiJmcm9kby5iYWdnaW5zQGhvYmJpdG9uLm +# V4YW1wbGUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0 + +# Figure 78: JWE Protected Header, base64url-encoded + +# Performing the content encryption operation on the Plaintext +# (Figure 72) using the following: + +# o CEK (Figure 74); + +# o Initialization Vector (Figure 75); and + +# o JWE Protected Header (Figure 77) as authenticated data + +# produces the following: + +# o Ciphertext from Figure 79. + +# o Authentication Tag from Figure 80. + +# 0fys_TY_na7f8dwSfXLiYdHaA2DxUjD67ieF7fcVbIR62JhJvGZ4_FNVSiGc_r +# aa0HnLQ6s1P2sv3Xzl1p1l_o5wR_RsSzrS8Z-wnI3Jvo0mkpEEnlDmZvDu_k8O +# WzJv7eZVEqiWKdyVzFhPpiyQU28GLOpRc2VbVbK4dQKPdNTjPPEmRqcaGeTWZV +# yeSUvf5k59yJZxRuSvWFf6KrNtmRdZ8R4mDOjHSrM_s8uwIFcqt4r5GX8TKaI0 +# zT5CbL5Qlw3sRc7u_hg0yKVOiRytEAEs3vZkcfLkP6nbXdC_PkMdNS-ohP78T2 +# O6_7uInMGhFeX4ctHG7VelHGiT93JfWDEQi5_V9UN1rhXNrYu-0fVMkZAKX3VW +# i7lzA6BP430m + +# Figure 79: Ciphertext, base64url-encoded + +# kvKuFBXHe5mQr4lqgobAUg + +# Figure 80: Authentication Tag, base64url-encoded + + + +# Miller Informational [Page 42] + +# RFC 7520 JOSE Cookbook May 2015 + + +# 5.1.5. Output Results + +# The following compose the resulting JWE object: + +# o JWE Protected Header (Figure 78) + +# o Encrypted Key (Figure 76) + +# o Initialization Vector (Figure 75) + +# o Ciphertext (Figure 79) + +# o Authentication Tag (Figure 80) + +# The resulting JWE object using the JWE Compact Serialization: + +# eyJhbGciOiJSU0ExXzUiLCJraWQiOiJmcm9kby5iYWdnaW5zQGhvYmJpdG9uLm +# V4YW1wbGUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0 +# . +# laLxI0j-nLH-_BgLOXMozKxmy9gffy2gTdvqzfTihJBuuzxg0V7yk1WClnQePF +# vG2K-pvSlWc9BRIazDrn50RcRai__3TDON395H3c62tIouJJ4XaRvYHFjZTZ2G +# Xfz8YAImcc91Tfk0WXC2F5Xbb71ClQ1DDH151tlpH77f2ff7xiSxh9oSewYrcG +# TSLUeeCt36r1Kt3OSj7EyBQXoZlN7IxbyhMAfgIe7Mv1rOTOI5I8NQqeXXW8Vl +# zNmoxaGMny3YnGir5Wf6Qt2nBq4qDaPdnaAuuGUGEecelIO1wx1BpyIfgvfjOh +# MBs9M8XL223Fg47xlGsMXdfuY-4jaqVw +# . +# bbd5sTkYwhAIqfHsx8DayA +# . +# 0fys_TY_na7f8dwSfXLiYdHaA2DxUjD67ieF7fcVbIR62JhJvGZ4_FNVSiGc_r +# aa0HnLQ6s1P2sv3Xzl1p1l_o5wR_RsSzrS8Z-wnI3Jvo0mkpEEnlDmZvDu_k8O +# WzJv7eZVEqiWKdyVzFhPpiyQU28GLOpRc2VbVbK4dQKPdNTjPPEmRqcaGeTWZV +# yeSUvf5k59yJZxRuSvWFf6KrNtmRdZ8R4mDOjHSrM_s8uwIFcqt4r5GX8TKaI0 +# zT5CbL5Qlw3sRc7u_hg0yKVOiRytEAEs3vZkcfLkP6nbXdC_PkMdNS-ohP78T2 +# O6_7uInMGhFeX4ctHG7VelHGiT93JfWDEQi5_V9UN1rhXNrYu-0fVMkZAKX3VW +# i7lzA6BP430m +# . +# kvKuFBXHe5mQr4lqgobAUg + +# Figure 81: JWE Compact Serialization + + + + + + + + + + + + +# Miller Informational [Page 43] + +# RFC 7520 JOSE Cookbook May 2015 + + +# The resulting JWE object using the general JWE JSON Serialization: + +# { +# "recipients": [ +# { +# "encrypted_key": "laLxI0j-nLH-_BgLOXMozKxmy9gffy2gTdvqzf +# TihJBuuzxg0V7yk1WClnQePFvG2K-pvSlWc9BRIazDrn50RcRai_ +# _3TDON395H3c62tIouJJ4XaRvYHFjZTZ2GXfz8YAImcc91Tfk0WX +# C2F5Xbb71ClQ1DDH151tlpH77f2ff7xiSxh9oSewYrcGTSLUeeCt +# 36r1Kt3OSj7EyBQXoZlN7IxbyhMAfgIe7Mv1rOTOI5I8NQqeXXW8 +# VlzNmoxaGMny3YnGir5Wf6Qt2nBq4qDaPdnaAuuGUGEecelIO1wx +# 1BpyIfgvfjOhMBs9M8XL223Fg47xlGsMXdfuY-4jaqVw" +# } +# ], +# "protected": "eyJhbGciOiJSU0ExXzUiLCJraWQiOiJmcm9kby5iYWdnaW +# 5zQGhvYmJpdG9uLmV4YW1wbGUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In +# 0", +# "iv": "bbd5sTkYwhAIqfHsx8DayA", +# "ciphertext": "0fys_TY_na7f8dwSfXLiYdHaA2DxUjD67ieF7fcVbIR62 +# JhJvGZ4_FNVSiGc_raa0HnLQ6s1P2sv3Xzl1p1l_o5wR_RsSzrS8Z-wn +# I3Jvo0mkpEEnlDmZvDu_k8OWzJv7eZVEqiWKdyVzFhPpiyQU28GLOpRc +# 2VbVbK4dQKPdNTjPPEmRqcaGeTWZVyeSUvf5k59yJZxRuSvWFf6KrNtm +# RdZ8R4mDOjHSrM_s8uwIFcqt4r5GX8TKaI0zT5CbL5Qlw3sRc7u_hg0y +# KVOiRytEAEs3vZkcfLkP6nbXdC_PkMdNS-ohP78T2O6_7uInMGhFeX4c +# tHG7VelHGiT93JfWDEQi5_V9UN1rhXNrYu-0fVMkZAKX3VWi7lzA6BP4 +# 30m", +# "tag": "kvKuFBXHe5mQr4lqgobAUg" +# } + +# Figure 82: General JWE JSON Serialization + + + + + + + + + + + + + + + + + + + + + +# Miller Informational [Page 44] + +# RFC 7520 JOSE Cookbook May 2015 + + +# The resulting JWE object using the flattened JWE JSON Serialization: + +# { +# "protected": "eyJhbGciOiJSU0ExXzUiLCJraWQiOiJmcm9kby5iYWdnaW +# 5zQGhvYmJpdG9uLmV4YW1wbGUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In +# 0", +# "encrypted_key": "laLxI0j-nLH-_BgLOXMozKxmy9gffy2gTdvqzfTihJ +# Buuzxg0V7yk1WClnQePFvG2K-pvSlWc9BRIazDrn50RcRai__3TDON39 +# 5H3c62tIouJJ4XaRvYHFjZTZ2GXfz8YAImcc91Tfk0WXC2F5Xbb71ClQ +# 1DDH151tlpH77f2ff7xiSxh9oSewYrcGTSLUeeCt36r1Kt3OSj7EyBQX +# oZlN7IxbyhMAfgIe7Mv1rOTOI5I8NQqeXXW8VlzNmoxaGMny3YnGir5W +# f6Qt2nBq4qDaPdnaAuuGUGEecelIO1wx1BpyIfgvfjOhMBs9M8XL223F +# g47xlGsMXdfuY-4jaqVw", +# "iv": "bbd5sTkYwhAIqfHsx8DayA", +# "ciphertext": "0fys_TY_na7f8dwSfXLiYdHaA2DxUjD67ieF7fcVbIR62 +# JhJvGZ4_FNVSiGc_raa0HnLQ6s1P2sv3Xzl1p1l_o5wR_RsSzrS8Z-wn +# I3Jvo0mkpEEnlDmZvDu_k8OWzJv7eZVEqiWKdyVzFhPpiyQU28GLOpRc +# 2VbVbK4dQKPdNTjPPEmRqcaGeTWZVyeSUvf5k59yJZxRuSvWFf6KrNtm +# RdZ8R4mDOjHSrM_s8uwIFcqt4r5GX8TKaI0zT5CbL5Qlw3sRc7u_hg0y +# KVOiRytEAEs3vZkcfLkP6nbXdC_PkMdNS-ohP78T2O6_7uInMGhFeX4c +# tHG7VelHGiT93JfWDEQi5_V9UN1rhXNrYu-0fVMkZAKX3VWi7lzA6BP4 +# 30m", +# "tag": "kvKuFBXHe5mQr4lqgobAUg" +# } + +# Figure 83: Flattened JWE JSON Serialization + +# 5.2. Key Encryption Using RSA-OAEP with AES-GCM + +# This example illustrates encrypting content using the "RSA-OAEP" +# (RSAES-OAEP) key encryption algorithm and the "A256GCM" (AES-GCM) +# content encryption algorithm. + +# Note that RSAES-OAEP uses random data to generate the ciphertext; it +# might not be possible to exactly replicate the results in this +# section. + +# Note that only the RSA public key is necessary to perform the +# encryption. However, the example includes the RSA private key to +# allow readers to validate the output. + +# Note that whitespace is added for readability as described in +# Section 1.1. + + + + + + + + +# Miller Informational [Page 45] + +# RFC 7520 JOSE Cookbook May 2015 + + +# 5.2.1. Input Factors + +# The following are supplied before beginning the encryption process: + +# o Plaintext content; this example uses the Plaintext from Figure 72. + +# o RSA public key; this example uses the key from Figure 84. + +# o "alg" parameter of "RSA-OAEP". + +# o "enc" parameter of "A256GCM". + +# { +# "kty": "RSA", +# "kid": "samwise.gamgee@hobbiton.example", +# "use": "enc", +# "n": "wbdxI55VaanZXPY29Lg5hdmv2XhvqAhoxUkanfzf2-5zVUxa6prHRr +# I4pP1AhoqJRlZfYtWWd5mmHRG2pAHIlh0ySJ9wi0BioZBl1XP2e-C-Fy +# XJGcTy0HdKQWlrfhTm42EW7Vv04r4gfao6uxjLGwfpGrZLarohiWCPnk +# Nrg71S2CuNZSQBIPGjXfkmIy2tl_VWgGnL22GplyXj5YlBLdxXp3XeSt +# sqo571utNfoUTU8E4qdzJ3U1DItoVkPGsMwlmmnJiwA7sXRItBCivR4M +# 5qnZtdw-7v4WuR4779ubDuJ5nalMv2S66-RPcnFAzWSKxtBDnFJJDGIU +# e7Tzizjg1nms0Xq_yPub_UOlWn0ec85FCft1hACpWG8schrOBeNqHBOD +# FskYpUc2LC5JA2TaPF2dA67dg1TTsC_FupfQ2kNGcE1LgprxKHcVWYQb +# 86B-HozjHZcqtauBzFNV5tbTuB-TpkcvJfNcFLlH3b8mb-H_ox35FjqB +# SAjLKyoeqfKTpVjvXhd09knwgJf6VKq6UC418_TOljMVfFTWXUxlnfhO +# OnzW6HSSzD1c9WrCuVzsUMv54szidQ9wf1cYWf3g5qFDxDQKis99gcDa +# iCAwM3yEBIzuNeeCa5dartHDb1xEB_HcHSeYbghbMjGfasvKn0aZRsnT +# yC0xhWBlsolZE", +# "e": "AQAB", +# "alg": "RSA-OAEP", +# "d": "n7fzJc3_WG59VEOBTkayzuSMM780OJQuZjN_KbH8lOZG25ZoA7T4Bx +# cc0xQn5oZE5uSCIwg91oCt0JvxPcpmqzaJZg1nirjcWZ-oBtVk7gCAWq +# -B3qhfF3izlbkosrzjHajIcY33HBhsy4_WerrXg4MDNE4HYojy68TcxT +# 2LYQRxUOCf5TtJXvM8olexlSGtVnQnDRutxEUCwiewfmmrfveEogLx9E +# A-KMgAjTiISXxqIXQhWUQX1G7v_mV_Hr2YuImYcNcHkRvp9E7ook0876 +# DhkO8v4UOZLwA1OlUX98mkoqwc58A_Y2lBYbVx1_s5lpPsEqbbH-nqIj +# h1fL0gdNfihLxnclWtW7pCztLnImZAyeCWAG7ZIfv-Rn9fLIv9jZ6r7r +# -MSH9sqbuziHN2grGjD_jfRluMHa0l84fFKl6bcqN1JWxPVhzNZo01yD +# F-1LiQnqUYSepPf6X3a2SOdkqBRiquE6EvLuSYIDpJq3jDIsgoL8Mo1L +# oomgiJxUwL_GWEOGu28gplyzm-9Q0U0nyhEf1uhSR8aJAQWAiFImWH5W +# _IQT9I7-yrindr_2fWQ_i1UgMsGzA7aOGzZfPljRy6z-tY_KuBG00-28 +# S_aWvjyUc-Alp8AUyKjBZ-7CWH32fGWK48j1t-zomrwjL_mnhsPbGs0c +# 9WsWgRzI-K8gE", +# "p": "7_2v3OQZzlPFcHyYfLABQ3XP85Es4hCdwCkbDeltaUXgVy9l9etKgh +# vM4hRkOvbb01kYVuLFmxIkCDtpi-zLCYAdXKrAK3PtSbtzld_XZ9nlsY +# a_QZWpXB_IrtFjVfdKUdMz94pHUhFGFj7nr6NNxfpiHSHWFE1zD_AC3m +# Y46J961Y2LRnreVwAGNw53p07Db8yD_92pDa97vqcZOdgtybH9q6uma- + + + +# Miller Informational [Page 46] + +# RFC 7520 JOSE Cookbook May 2015 + + +# RFNhO1AoiJhYZj69hjmMRXx-x56HO9cnXNbmzNSCFCKnQmn4GQLmRj9s +# fbZRqL94bbtE4_e0Zrpo8RNo8vxRLqQNwIy85fc6BRgBJomt8QdQvIgP +# gWCv5HoQ", +# "q": "zqOHk1P6WN_rHuM7ZF1cXH0x6RuOHq67WuHiSknqQeefGBA9PWs6Zy +# KQCO-O6mKXtcgE8_Q_hA2kMRcKOcvHil1hqMCNSXlflM7WPRPZu2qCDc +# qssd_uMbP-DqYthH_EzwL9KnYoH7JQFxxmcv5An8oXUtTwk4knKjkIYG +# RuUwfQTus0w1NfjFAyxOOiAQ37ussIcE6C6ZSsM3n41UlbJ7TCqewzVJ +# aPJN5cxjySPZPD3Vp01a9YgAD6a3IIaKJdIxJS1ImnfPevSJQBE79-EX +# e2kSwVgOzvt-gsmM29QQ8veHy4uAqca5dZzMs7hkkHtw1z0jHV90epQJ +# JlXXnH8Q", +# "dp": "19oDkBh1AXelMIxQFm2zZTqUhAzCIr4xNIGEPNoDt1jK83_FJA-xn +# x5kA7-1erdHdms_Ef67HsONNv5A60JaR7w8LHnDiBGnjdaUmmuO8XAxQ +# J_ia5mxjxNjS6E2yD44USo2JmHvzeeNczq25elqbTPLhUpGo1IZuG72F +# ZQ5gTjXoTXC2-xtCDEUZfaUNh4IeAipfLugbpe0JAFlFfrTDAMUFpC3i +# XjxqzbEanflwPvj6V9iDSgjj8SozSM0dLtxvu0LIeIQAeEgT_yXcrKGm +# pKdSO08kLBx8VUjkbv_3Pn20Gyu2YEuwpFlM_H1NikuxJNKFGmnAq9Lc +# nwwT0jvoQ", +# "dq": "S6p59KrlmzGzaQYQM3o0XfHCGvfqHLYjCO557HYQf72O9kLMCfd_1 +# VBEqeD-1jjwELKDjck8kOBl5UvohK1oDfSP1DleAy-cnmL29DqWmhgwM +# 1ip0CCNmkmsmDSlqkUXDi6sAaZuntyukyflI-qSQ3C_BafPyFaKrt1fg +# dyEwYa08pESKwwWisy7KnmoUvaJ3SaHmohFS78TJ25cfc10wZ9hQNOrI +# ChZlkiOdFCtxDqdmCqNacnhgE3bZQjGp3n83ODSz9zwJcSUvODlXBPc2 +# AycH6Ci5yjbxt4Ppox_5pjm6xnQkiPgj01GpsUssMmBN7iHVsrE7N2iz +# nBNCeOUIQ", +# "qi": "FZhClBMywVVjnuUud-05qd5CYU0dK79akAgy9oX6RX6I3IIIPckCc +# iRrokxglZn-omAY5CnCe4KdrnjFOT5YUZE7G_Pg44XgCXaarLQf4hl80 +# oPEf6-jJ5Iy6wPRx7G2e8qLxnh9cOdf-kRqgOS3F48Ucvw3ma5V6KGMw +# QqWFeV31XtZ8l5cVI-I3NzBS7qltpUVgz2Ju021eyc7IlqgzR98qKONl +# 27DuEES0aK0WE97jnsyO27Yp88Wa2RiBrEocM89QZI1seJiGDizHRUP4 +# UZxw9zsXww46wy0P6f9grnYp7t8LkyDDk8eoI4KX6SNMNVcyVS9IWjlq +# 8EzqZEKIA" +# } + +# Figure 84: RSA 4096-Bit Key + +# (NOTE: While the key includes the private parameters, only the public +# parameters "e" and "n" are necessary for the encryption operation.) + +# 5.2.2. Generated Factors + +# The following are generated before encrypting: + +# o AES symmetric key as the Content Encryption Key (CEK); this +# example uses the key from Figure 85. + +# o Initialization Vector; this example uses the Initialization Vector +# from Figure 86. + + + + +# Miller Informational [Page 47] + +# RFC 7520 JOSE Cookbook May 2015 + + +# mYMfsggkTAm0TbvtlFh2hyoXnbEzJQjMxmgLN3d8xXA + +# Figure 85: Content Encryption Key, base64url-encoded + +# -nBoKLH0YkLZPSI9 + +# Figure 86: Initialization Vector, base64url-encoded + +# 5.2.3. Encrypting the Key + +# Performing the key encryption operation over the CEK (Figure 85) with +# the RSA key (Figure 84) produces the following Encrypted Key: + +# rT99rwrBTbTI7IJM8fU3Eli7226HEB7IchCxNuh7lCiud48LxeolRdtFF4nzQi +# beYOl5S_PJsAXZwSXtDePz9hk-BbtsTBqC2UsPOdwjC9NhNupNNu9uHIVftDyu +# cvI6hvALeZ6OGnhNV4v1zx2k7O1D89mAzfw-_kT3tkuorpDU-CpBENfIHX1Q58 +# -Aad3FzMuo3Fn9buEP2yXakLXYa15BUXQsupM4A1GD4_H4Bd7V3u9h8Gkg8Bpx +# KdUV9ScfJQTcYm6eJEBz3aSwIaK4T3-dwWpuBOhROQXBosJzS1asnuHtVMt2pK +# IIfux5BC6huIvmY7kzV7W7aIUrpYm_3H4zYvyMeq5pGqFmW2k8zpO878TRlZx7 +# pZfPYDSXZyS0CfKKkMozT_qiCwZTSz4duYnt8hS4Z9sGthXn9uDqd6wycMagnQ +# fOTs_lycTWmY-aqWVDKhjYNRf03NiwRtb5BE-tOdFwCASQj3uuAgPGrO2AWBe3 +# 8UjQb0lvXn1SpyvYZ3WFc7WOJYaTa7A8DRn6MC6T-xDmMuxC0G7S2rscw5lQQU +# 06MvZTlFOt0UvfuKBa03cxA_nIBIhLMjY2kOTxQMmpDPTr6Cbo8aKaOnx6ASE5 +# Jx9paBpnNmOOKH35j_QlrQhDWUN6A2Gg8iFayJ69xDEdHAVCGRzN3woEI2ozDR +# s + +# Figure 87: Encrypted Key, base64url-encoded + +# 5.2.4. Encrypting the Content + +# The following is generated before encrypting the Plaintext: + +# o JWE Protected Header; this example uses the header from Figure 88, +# encoded using base64url [RFC4648] to produce Figure 89. + +# { +# "alg": "RSA-OAEP", +# "kid": "samwise.gamgee@hobbiton.example", +# "enc": "A256GCM" +# } + +# Figure 88: JWE Protected Header JSON + +# eyJhbGciOiJSU0EtT0FFUCIsImtpZCI6InNhbXdpc2UuZ2FtZ2VlQGhvYmJpdG +# 9uLmV4YW1wbGUiLCJlbmMiOiJBMjU2R0NNIn0 + +# Figure 89: JWE Protected Header, base64url-encoded + + + + +# Miller Informational [Page 48] + +# RFC 7520 JOSE Cookbook May 2015 + + +# Performing the content encryption operation over the Plaintext +# (Figure 72) with the following: + +# o CEK (Figure 85); + +# o Initialization Vector (Figure 86); and + +# o JWE Protected Header (Figure 89) as authenticated data + +# produces the following: + +# o Ciphertext from Figure 90. + +# o Authentication Tag from Figure 91. + +# o4k2cnGN8rSSw3IDo1YuySkqeS_t2m1GXklSgqBdpACm6UJuJowOHC5ytjqYgR +# L-I-soPlwqMUf4UgRWWeaOGNw6vGW-xyM01lTYxrXfVzIIaRdhYtEMRBvBWbEw +# P7ua1DRfvaOjgZv6Ifa3brcAM64d8p5lhhNcizPersuhw5f-pGYzseva-TUaL8 +# iWnctc-sSwy7SQmRkfhDjwbz0fz6kFovEgj64X1I5s7E6GLp5fnbYGLa1QUiML +# 7Cc2GxgvI7zqWo0YIEc7aCflLG1-8BboVWFdZKLK9vNoycrYHumwzKluLWEbSV +# maPpOslY2n525DxDfWaVFUfKQxMF56vn4B9QMpWAbnypNimbM8zVOw + +# Figure 90: Ciphertext, base64url-encoded + +# UCGiqJxhBI3IFVdPalHHvA + +# Figure 91: Authentication Tag, base64url-encoded + +# 5.2.5. Output Results + +# The following compose the resulting JWE object: + +# o JWE Protected Header (Figure 89) + +# o Encrypted Key (Figure 87) + +# o Initialization Vector (Figure 86) + +# o Ciphertext (Figure 90) + +# o Authentication Tag (Figure 91) + + + + + + + + + + +# Miller Informational [Page 49] + +# RFC 7520 JOSE Cookbook May 2015 + + +# The resulting JWE object using the JWE Compact Serialization: + +# eyJhbGciOiJSU0EtT0FFUCIsImtpZCI6InNhbXdpc2UuZ2FtZ2VlQGhvYmJpdG +# 9uLmV4YW1wbGUiLCJlbmMiOiJBMjU2R0NNIn0 +# . +# rT99rwrBTbTI7IJM8fU3Eli7226HEB7IchCxNuh7lCiud48LxeolRdtFF4nzQi +# beYOl5S_PJsAXZwSXtDePz9hk-BbtsTBqC2UsPOdwjC9NhNupNNu9uHIVftDyu +# cvI6hvALeZ6OGnhNV4v1zx2k7O1D89mAzfw-_kT3tkuorpDU-CpBENfIHX1Q58 +# -Aad3FzMuo3Fn9buEP2yXakLXYa15BUXQsupM4A1GD4_H4Bd7V3u9h8Gkg8Bpx +# KdUV9ScfJQTcYm6eJEBz3aSwIaK4T3-dwWpuBOhROQXBosJzS1asnuHtVMt2pK +# IIfux5BC6huIvmY7kzV7W7aIUrpYm_3H4zYvyMeq5pGqFmW2k8zpO878TRlZx7 +# pZfPYDSXZyS0CfKKkMozT_qiCwZTSz4duYnt8hS4Z9sGthXn9uDqd6wycMagnQ +# fOTs_lycTWmY-aqWVDKhjYNRf03NiwRtb5BE-tOdFwCASQj3uuAgPGrO2AWBe3 +# 8UjQb0lvXn1SpyvYZ3WFc7WOJYaTa7A8DRn6MC6T-xDmMuxC0G7S2rscw5lQQU +# 06MvZTlFOt0UvfuKBa03cxA_nIBIhLMjY2kOTxQMmpDPTr6Cbo8aKaOnx6ASE5 +# Jx9paBpnNmOOKH35j_QlrQhDWUN6A2Gg8iFayJ69xDEdHAVCGRzN3woEI2ozDR +# s +# . +# -nBoKLH0YkLZPSI9 +# . +# o4k2cnGN8rSSw3IDo1YuySkqeS_t2m1GXklSgqBdpACm6UJuJowOHC5ytjqYgR +# L-I-soPlwqMUf4UgRWWeaOGNw6vGW-xyM01lTYxrXfVzIIaRdhYtEMRBvBWbEw +# P7ua1DRfvaOjgZv6Ifa3brcAM64d8p5lhhNcizPersuhw5f-pGYzseva-TUaL8 +# iWnctc-sSwy7SQmRkfhDjwbz0fz6kFovEgj64X1I5s7E6GLp5fnbYGLa1QUiML +# 7Cc2GxgvI7zqWo0YIEc7aCflLG1-8BboVWFdZKLK9vNoycrYHumwzKluLWEbSV +# maPpOslY2n525DxDfWaVFUfKQxMF56vn4B9QMpWAbnypNimbM8zVOw +# . +# UCGiqJxhBI3IFVdPalHHvA + +# Figure 92: JWE Compact Serialization + + + + + + + + + + + + + + + + + + + + + +# Miller Informational [Page 50] + +# RFC 7520 JOSE Cookbook May 2015 + + +# The resulting JWE object using the general JWE JSON Serialization: + +# { +# "recipients": [ +# { +# "encrypted_key": "rT99rwrBTbTI7IJM8fU3Eli7226HEB7IchCxNu +# h7lCiud48LxeolRdtFF4nzQibeYOl5S_PJsAXZwSXtDePz9hk-Bb +# tsTBqC2UsPOdwjC9NhNupNNu9uHIVftDyucvI6hvALeZ6OGnhNV4 +# v1zx2k7O1D89mAzfw-_kT3tkuorpDU-CpBENfIHX1Q58-Aad3FzM +# uo3Fn9buEP2yXakLXYa15BUXQsupM4A1GD4_H4Bd7V3u9h8Gkg8B +# pxKdUV9ScfJQTcYm6eJEBz3aSwIaK4T3-dwWpuBOhROQXBosJzS1 +# asnuHtVMt2pKIIfux5BC6huIvmY7kzV7W7aIUrpYm_3H4zYvyMeq +# 5pGqFmW2k8zpO878TRlZx7pZfPYDSXZyS0CfKKkMozT_qiCwZTSz +# 4duYnt8hS4Z9sGthXn9uDqd6wycMagnQfOTs_lycTWmY-aqWVDKh +# jYNRf03NiwRtb5BE-tOdFwCASQj3uuAgPGrO2AWBe38UjQb0lvXn +# 1SpyvYZ3WFc7WOJYaTa7A8DRn6MC6T-xDmMuxC0G7S2rscw5lQQU +# 06MvZTlFOt0UvfuKBa03cxA_nIBIhLMjY2kOTxQMmpDPTr6Cbo8a +# KaOnx6ASE5Jx9paBpnNmOOKH35j_QlrQhDWUN6A2Gg8iFayJ69xD +# EdHAVCGRzN3woEI2ozDRs" +# } +# ], +# "protected": "eyJhbGciOiJSU0EtT0FFUCIsImtpZCI6InNhbXdpc2UuZ2 +# FtZ2VlQGhvYmJpdG9uLmV4YW1wbGUiLCJlbmMiOiJBMjU2R0NNIn0", +# "iv": "-nBoKLH0YkLZPSI9", +# "ciphertext": "o4k2cnGN8rSSw3IDo1YuySkqeS_t2m1GXklSgqBdpACm6 +# UJuJowOHC5ytjqYgRL-I-soPlwqMUf4UgRWWeaOGNw6vGW-xyM01lTYx +# rXfVzIIaRdhYtEMRBvBWbEwP7ua1DRfvaOjgZv6Ifa3brcAM64d8p5lh +# hNcizPersuhw5f-pGYzseva-TUaL8iWnctc-sSwy7SQmRkfhDjwbz0fz +# 6kFovEgj64X1I5s7E6GLp5fnbYGLa1QUiML7Cc2GxgvI7zqWo0YIEc7a +# CflLG1-8BboVWFdZKLK9vNoycrYHumwzKluLWEbSVmaPpOslY2n525Dx +# DfWaVFUfKQxMF56vn4B9QMpWAbnypNimbM8zVOw", +# "tag": "UCGiqJxhBI3IFVdPalHHvA" +# } + +# Figure 93: General JWE JSON Serialization + + + + + + + + + + + + + + + + +# Miller Informational [Page 51] + +# RFC 7520 JOSE Cookbook May 2015 + + +# The resulting JWE object using the flattened JWE JSON Serialization: + +# { +# "protected": "eyJhbGciOiJSU0EtT0FFUCIsImtpZCI6InNhbXdpc2UuZ2 +# FtZ2VlQGhvYmJpdG9uLmV4YW1wbGUiLCJlbmMiOiJBMjU2R0NNIn0", +# "encrypted_key": "rT99rwrBTbTI7IJM8fU3Eli7226HEB7IchCxNuh7lC +# iud48LxeolRdtFF4nzQibeYOl5S_PJsAXZwSXtDePz9hk-BbtsTBqC2U +# sPOdwjC9NhNupNNu9uHIVftDyucvI6hvALeZ6OGnhNV4v1zx2k7O1D89 +# mAzfw-_kT3tkuorpDU-CpBENfIHX1Q58-Aad3FzMuo3Fn9buEP2yXakL +# XYa15BUXQsupM4A1GD4_H4Bd7V3u9h8Gkg8BpxKdUV9ScfJQTcYm6eJE +# Bz3aSwIaK4T3-dwWpuBOhROQXBosJzS1asnuHtVMt2pKIIfux5BC6huI +# vmY7kzV7W7aIUrpYm_3H4zYvyMeq5pGqFmW2k8zpO878TRlZx7pZfPYD +# SXZyS0CfKKkMozT_qiCwZTSz4duYnt8hS4Z9sGthXn9uDqd6wycMagnQ +# fOTs_lycTWmY-aqWVDKhjYNRf03NiwRtb5BE-tOdFwCASQj3uuAgPGrO +# 2AWBe38UjQb0lvXn1SpyvYZ3WFc7WOJYaTa7A8DRn6MC6T-xDmMuxC0G +# 7S2rscw5lQQU06MvZTlFOt0UvfuKBa03cxA_nIBIhLMjY2kOTxQMmpDP +# Tr6Cbo8aKaOnx6ASE5Jx9paBpnNmOOKH35j_QlrQhDWUN6A2Gg8iFayJ +# 69xDEdHAVCGRzN3woEI2ozDRs", +# "iv": "-nBoKLH0YkLZPSI9", +# "ciphertext": "o4k2cnGN8rSSw3IDo1YuySkqeS_t2m1GXklSgqBdpACm6 +# UJuJowOHC5ytjqYgRL-I-soPlwqMUf4UgRWWeaOGNw6vGW-xyM01lTYx +# rXfVzIIaRdhYtEMRBvBWbEwP7ua1DRfvaOjgZv6Ifa3brcAM64d8p5lh +# hNcizPersuhw5f-pGYzseva-TUaL8iWnctc-sSwy7SQmRkfhDjwbz0fz +# 6kFovEgj64X1I5s7E6GLp5fnbYGLa1QUiML7Cc2GxgvI7zqWo0YIEc7a +# CflLG1-8BboVWFdZKLK9vNoycrYHumwzKluLWEbSVmaPpOslY2n525Dx +# DfWaVFUfKQxMF56vn4B9QMpWAbnypNimbM8zVOw", +# "tag": "UCGiqJxhBI3IFVdPalHHvA" +# } + +# Figure 94: Flattened JWE JSON Serialization + +# 5.3. Key Wrap Using PBES2-AES-KeyWrap with AES-CBC-HMAC-SHA2 + +# The example illustrates encrypting content using the +# "PBES2-HS512+A256KW" (PBES2 Password-based Encryption using HMAC- +# SHA-512 and AES-256-KeyWrap) key encryption algorithm with the +# "A128CBC-HS256" (AES-128-CBC-HMAC-SHA-256) content encryption +# algorithm. + +# A common use of password-based encryption is the import/export of +# keys. Therefore, this example uses a JWK Set for the Plaintext +# content instead of the Plaintext from Figure 72. + + + + + + + + + +# Miller Informational [Page 52] + +# RFC 7520 JOSE Cookbook May 2015 + + +# Note that if password-based encryption is used for multiple +# recipients, it is expected that each recipient use different values +# for the PBES2 parameters "p2s" and "p2c". + +# Note that whitespace is added for readability as described in +# Section 1.1. + +# 5.3.1. Input Factors + +# The following are supplied before beginning the encryption process: + +# o Plaintext content; this example uses the Plaintext from Figure 95 +# (NOTE: All whitespace was added for readability). + +# o Password; this example uses the password from Figure 96 -- with +# the sequence "\xe2\x80\x93" replaced with (U+2013 EN DASH). + +# o "alg" parameter of "PBES2-HS512+A256KW". + +# o "enc" parameter of "A128CBC-HS256". + +# { +# "keys": [ +# { +# "kty": "oct", +# "kid": "77c7e2b8-6e13-45cf-8672-617b5b45243a", +# "use": "enc", +# "alg": "A128GCM", +# "k": "XctOhJAkA-pD9Lh7ZgW_2A" +# }, +# { +# "kty": "oct", +# "kid": "81b20965-8332-43d9-a468-82160ad91ac8", +# "use": "enc", +# "alg": "A128KW", +# "k": "GZy6sIZ6wl9NJOKB-jnmVQ" +# }, +# { +# "kty": "oct", +# "kid": "18ec08e1-bfa9-4d95-b205-2b4dd1d4321d", +# "use": "enc", +# "alg": "A256GCMKW", +# "k": "qC57l_uxcm7Nm3K-ct4GFjx8tM1U8CZ0NLBvdQstiS8" +# } +# ] +# } + +# Figure 95: Plaintext Content + + + +# Miller Informational [Page 53] + +# RFC 7520 JOSE Cookbook May 2015 + + +# entrap_o\xe2\x80\x93peter_long\xe2\x80\x93credit_tun + +# Figure 96: Password + +# 5.3.2. Generated Factors + +# The following are generated before encrypting: + +# o AES symmetric key as the Content Encryption Key (CEK); this +# example uses the key from Figure 97. + +# o Initialization Vector; this example uses the Initialization Vector +# from Figure 98. + +# uwsjJXaBK407Qaf0_zpcpmr1Cs0CC50hIUEyGNEt3m0 + +# Figure 97: Content Encryption Key, base64url-encoded + +# VBiCzVHNoLiR3F4V82uoTQ + +# Figure 98: Initialization Vector, base64url-encoded + +# 5.3.3. Encrypting the Key + +# The following are generated before encrypting the CEK: + +# o Salt input; this example uses the salt input from Figure 99. + +# o Iteration count; this example uses the iteration count 8192. + +# 8Q1SzinasR3xchYz6ZZcHA + +# Figure 99: Salt Input, base64url-encoded + +# Performing the key encryption operation over the CEK (Figure 97) with +# the following: + +# o Password (Figure 96); + +# o Salt input (Figure 99), encoded as an octet string; and + +# o Iteration count (8192) + +# produces the following Encrypted Key: + +# d3qNhUWfqheyPp4H8sjOWsDYajoej4c5Je6rlUtFPWdgtURtmeDV1g + +# Figure 100: Encrypted Key, base64url-encoded + + + +# Miller Informational [Page 54] + +# RFC 7520 JOSE Cookbook May 2015 + + +# 5.3.4. Encrypting the Content + +# The following is generated before encrypting the content: + +# o JWE Protected Header; this example uses the header from +# Figure 101, encoded using base64url [RFC4648] to produce +# Figure 102. + +# { +# "alg": "PBES2-HS512+A256KW", +# "p2s": "8Q1SzinasR3xchYz6ZZcHA", +# "p2c": 8192, +# "cty": "jwk-set+json", +# "enc": "A128CBC-HS256" +# } + +# Figure 101: JWE Protected Header JSON + +# eyJhbGciOiJQQkVTMi1IUzUxMitBMjU2S1ciLCJwMnMiOiI4UTFTemluYXNSM3 +# hjaFl6NlpaY0hBIiwicDJjIjo4MTkyLCJjdHkiOiJqd2stc2V0K2pzb24iLCJl +# bmMiOiJBMTI4Q0JDLUhTMjU2In0 + +# Figure 102: JWE Protected Header, base64url-encoded + +# Performing the content encryption operation over the Plaintext +# (Figure 95) with the following: + +# o CEK (Figure 97); + +# o Initialization Vector (Figure 98); and + +# o JWE Protected Header (Figure 102) as authenticated data + +# produces the following: + +# o Ciphertext from Figure 103. + +# o Authentication Tag from Figure 104. + + + + + + + + + + + + + +# Miller Informational [Page 55] + +# RFC 7520 JOSE Cookbook May 2015 + + +# 23i-Tb1AV4n0WKVSSgcQrdg6GRqsUKxjruHXYsTHAJLZ2nsnGIX86vMXqIi6IR +# sfywCRFzLxEcZBRnTvG3nhzPk0GDD7FMyXhUHpDjEYCNA_XOmzg8yZR9oyjo6l +# TF6si4q9FZ2EhzgFQCLO_6h5EVg3vR75_hkBsnuoqoM3dwejXBtIodN84PeqMb +# 6asmas_dpSsz7H10fC5ni9xIz424givB1YLldF6exVmL93R3fOoOJbmk2GBQZL +# _SEGllv2cQsBgeprARsaQ7Bq99tT80coH8ItBjgV08AtzXFFsx9qKvC982KLKd +# PQMTlVJKkqtV4Ru5LEVpBZXBnZrtViSOgyg6AiuwaS-rCrcD_ePOGSuxvgtrok +# AKYPqmXUeRdjFJwafkYEkiuDCV9vWGAi1DH2xTafhJwcmywIyzi4BqRpmdn_N- +# zl5tuJYyuvKhjKv6ihbsV_k1hJGPGAxJ6wUpmwC4PTQ2izEm0TuSE8oMKdTw8V +# 3kobXZ77ulMwDs4p + +# Figure 103: Ciphertext, base64url-encoded + +# 0HlwodAhOCILG5SQ2LQ9dg + +# Figure 104: Authentication Tag, base64url-encoded + +# 5.3.5. Output Results + +# The following compose the resulting JWE object: + +# o JWE Protected Header (Figure 102) + +# o Encrypted Key (Figure 100) + +# o Initialization Vector (Figure 98) + +# o Ciphertext (Figure 103) + +# o Authentication Tag (Figure 104) + + + + + + + + + + + + + + + + + + + + + + +# Miller Informational [Page 56] + +# RFC 7520 JOSE Cookbook May 2015 + + +# The resulting JWE object using the JWE Compact Serialization: + +# eyJhbGciOiJQQkVTMi1IUzUxMitBMjU2S1ciLCJwMnMiOiI4UTFTemluYXNSM3 +# hjaFl6NlpaY0hBIiwicDJjIjo4MTkyLCJjdHkiOiJqd2stc2V0K2pzb24iLCJl +# bmMiOiJBMTI4Q0JDLUhTMjU2In0 +# . +# d3qNhUWfqheyPp4H8sjOWsDYajoej4c5Je6rlUtFPWdgtURtmeDV1g +# . +# VBiCzVHNoLiR3F4V82uoTQ +# . +# 23i-Tb1AV4n0WKVSSgcQrdg6GRqsUKxjruHXYsTHAJLZ2nsnGIX86vMXqIi6IR +# sfywCRFzLxEcZBRnTvG3nhzPk0GDD7FMyXhUHpDjEYCNA_XOmzg8yZR9oyjo6l +# TF6si4q9FZ2EhzgFQCLO_6h5EVg3vR75_hkBsnuoqoM3dwejXBtIodN84PeqMb +# 6asmas_dpSsz7H10fC5ni9xIz424givB1YLldF6exVmL93R3fOoOJbmk2GBQZL +# _SEGllv2cQsBgeprARsaQ7Bq99tT80coH8ItBjgV08AtzXFFsx9qKvC982KLKd +# PQMTlVJKkqtV4Ru5LEVpBZXBnZrtViSOgyg6AiuwaS-rCrcD_ePOGSuxvgtrok +# AKYPqmXUeRdjFJwafkYEkiuDCV9vWGAi1DH2xTafhJwcmywIyzi4BqRpmdn_N- +# zl5tuJYyuvKhjKv6ihbsV_k1hJGPGAxJ6wUpmwC4PTQ2izEm0TuSE8oMKdTw8V +# 3kobXZ77ulMwDs4p +# . +# 0HlwodAhOCILG5SQ2LQ9dg + +# Figure 105: JWE Compact Serialization + + + + + + + + + + + + + + + + + + + + + + + + + + + + +# Miller Informational [Page 57] + +# RFC 7520 JOSE Cookbook May 2015 + + +# The resulting JWE object using the general JWE JSON Serialization: + +# { +# "recipients": [ +# { +# "encrypted_key": "d3qNhUWfqheyPp4H8sjOWsDYajoej4c5Je6rlU +# tFPWdgtURtmeDV1g" +# } +# ], +# "protected": "eyJhbGciOiJQQkVTMi1IUzUxMitBMjU2S1ciLCJwMnMiOi +# I4UTFTemluYXNSM3hjaFl6NlpaY0hBIiwicDJjIjo4MTkyLCJjdHkiOi +# Jqd2stc2V0K2pzb24iLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0", +# "iv": "VBiCzVHNoLiR3F4V82uoTQ", +# "ciphertext": "23i-Tb1AV4n0WKVSSgcQrdg6GRqsUKxjruHXYsTHAJLZ2 +# nsnGIX86vMXqIi6IRsfywCRFzLxEcZBRnTvG3nhzPk0GDD7FMyXhUHpD +# jEYCNA_XOmzg8yZR9oyjo6lTF6si4q9FZ2EhzgFQCLO_6h5EVg3vR75_ +# hkBsnuoqoM3dwejXBtIodN84PeqMb6asmas_dpSsz7H10fC5ni9xIz42 +# 4givB1YLldF6exVmL93R3fOoOJbmk2GBQZL_SEGllv2cQsBgeprARsaQ +# 7Bq99tT80coH8ItBjgV08AtzXFFsx9qKvC982KLKdPQMTlVJKkqtV4Ru +# 5LEVpBZXBnZrtViSOgyg6AiuwaS-rCrcD_ePOGSuxvgtrokAKYPqmXUe +# RdjFJwafkYEkiuDCV9vWGAi1DH2xTafhJwcmywIyzi4BqRpmdn_N-zl5 +# tuJYyuvKhjKv6ihbsV_k1hJGPGAxJ6wUpmwC4PTQ2izEm0TuSE8oMKdT +# w8V3kobXZ77ulMwDs4p", +# "tag": "0HlwodAhOCILG5SQ2LQ9dg" +# } + +# Figure 106: General JWE JSON Serialization + + + + + + + + + + + + + + + + + + + + + + + + +# Miller Informational [Page 58] + +# RFC 7520 JOSE Cookbook May 2015 + + +# The resulting JWE object using the flattened JWE JSON Serialization: + +# { +# "protected": "eyJhbGciOiJQQkVTMi1IUzUxMitBMjU2S1ciLCJwMnMiOi +# I4UTFTemluYXNSM3hjaFl6NlpaY0hBIiwicDJjIjo4MTkyLCJjdHkiOi +# Jqd2stc2V0K2pzb24iLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0", +# "encrypted_key": "d3qNhUWfqheyPp4H8sjOWsDYajoej4c5Je6rlUtFPW +# dgtURtmeDV1g", +# "iv": "VBiCzVHNoLiR3F4V82uoTQ", +# "ciphertext": "23i-Tb1AV4n0WKVSSgcQrdg6GRqsUKxjruHXYsTHAJLZ2 +# nsnGIX86vMXqIi6IRsfywCRFzLxEcZBRnTvG3nhzPk0GDD7FMyXhUHpD +# jEYCNA_XOmzg8yZR9oyjo6lTF6si4q9FZ2EhzgFQCLO_6h5EVg3vR75_ +# hkBsnuoqoM3dwejXBtIodN84PeqMb6asmas_dpSsz7H10fC5ni9xIz42 +# 4givB1YLldF6exVmL93R3fOoOJbmk2GBQZL_SEGllv2cQsBgeprARsaQ +# 7Bq99tT80coH8ItBjgV08AtzXFFsx9qKvC982KLKdPQMTlVJKkqtV4Ru +# 5LEVpBZXBnZrtViSOgyg6AiuwaS-rCrcD_ePOGSuxvgtrokAKYPqmXUe +# RdjFJwafkYEkiuDCV9vWGAi1DH2xTafhJwcmywIyzi4BqRpmdn_N-zl5 +# tuJYyuvKhjKv6ihbsV_k1hJGPGAxJ6wUpmwC4PTQ2izEm0TuSE8oMKdT +# w8V3kobXZ77ulMwDs4p", +# "tag": "0HlwodAhOCILG5SQ2LQ9dg" +# } + +# Figure 107: Flattened JWE JSON Serialization + +# 5.4. Key Agreement with Key Wrapping Using ECDH-ES and AES-KeyWrap with +# AES-GCM + +# This example illustrates encrypting content using the "ECDH- +# ES+A128KW" (Elliptic Curve Diffie-Hellman Ephemeral-Static with AES- +# 128-KeyWrap) key encryption algorithm and the "A128GCM" (AES-GCM) +# content encryption algorithm. + +# Note that only the EC public key is necessary to perform the key +# agreement. However, the example includes the EC private key to allow +# readers to validate the output. + +# Note that whitespace is added for readability as described in +# Section 1.1. + +# 5.4.1. Input Factors + +# The following are supplied before beginning the encryption process: + +# o Plaintext content; this example uses the content from Figure 72. + +# o EC public key; this example uses the public key from Figure 108. + + + + + +# Miller Informational [Page 59] + +# RFC 7520 JOSE Cookbook May 2015 + + +# o "alg" parameter of "ECDH-ES+A128KW". + +# o "enc" parameter of "A128GCM". + +# { +# "kty": "EC", +# "kid": "peregrin.took@tuckborough.example", +# "use": "enc", +# "crv": "P-384", +# "x": "YU4rRUzdmVqmRtWOs2OpDE_T5fsNIodcG8G5FWPrTPMyxpzsSOGaQL +# pe2FpxBmu2", +# "y": "A8-yxCHxkfBz3hKZfI1jUYMjUhsEveZ9THuwFjH2sCNdtksRJU7D5- +# SkgaFL1ETP", +# "d": "iTx2pk7wW-GqJkHcEkFQb2EFyYcO7RugmaW3mRrQVAOUiPommT0Idn +# YK2xDlZh-j" +# } + +# Figure 108: Elliptic Curve P-384 Key, in JWK Format + +# (NOTE: While the key includes the private parameters, only the public +# parameters "crv", "x", and "y" are necessary for the encryption +# operation.) + +# 5.4.2. Generated Factors + +# The following are generated before encrypting: + +# o AES symmetric key as the Content Encryption Key (CEK); this +# example uses the key from Figure 109. + +# o Initialization Vector; this example uses the Initialization Vector +# from Figure 110. + +# Nou2ueKlP70ZXDbq9UrRwg + +# Figure 109: Content Encryption Key, base64url-encoded + +# mH-G2zVqgztUtnW_ + +# Figure 110: Initialization Vector, base64url-encoded + +# 5.4.3. Encrypting the Key + +# To encrypt the Content Encryption Key, the following is generated: + +# o Ephemeral EC private key on the same curve as the EC public key; +# this example uses the private key from Figure 111. + + + + +# Miller Informational [Page 60] + +# RFC 7520 JOSE Cookbook May 2015 + + +# { +# "kty": "EC", +# "crv": "P-384", +# "x": "uBo4kHPw6kbjx5l0xowrd_oYzBmaz-GKFZu4xAFFkbYiWgutEK6iuE +# DsQ6wNdNg3", +# "y": "sp3p5SGhZVC2faXumI-e9JU2Mo8KpoYrFDr5yPNVtW4PgEwZOyQTA- +# JdaY8tb7E0", +# "d": "D5H4Y_5PSKZvhfVFbcCYJOtcGZygRgfZkpsBr59Icmmhe9sW6nkZ8W +# fwhinUfWJg" +# } + +# Figure 111: Ephemeral Elliptic Curve P-384 Key, in JWK Format + +# Performing the key encryption operation over the CEK (Figure 109) +# with the following: + +# o The static Elliptic Curve public key (Figure 108); and + +# o The ephemeral Elliptic Curve private key (Figure 111) + +# produces the following JWE Encrypted Key: + +# 0DJjBXri_kBcC46IkU5_Jk9BqaQeHdv2 + +# Figure 112: Encrypted Key, base64url-encoded + +# 5.4.4. Encrypting the Content + +# The following is generated before encrypting the content: + +# o JWE Protected Header; this example uses the header from +# Figure 113, encoded to base64url [RFC4648] as Figure 114. + +# { +# "alg": "ECDH-ES+A128KW", +# "kid": "peregrin.took@tuckborough.example", +# "epk": { +# "kty": "EC", +# "crv": "P-384", +# "x": "uBo4kHPw6kbjx5l0xowrd_oYzBmaz-GKFZu4xAFFkbYiWgutEK6i +# uEDsQ6wNdNg3", +# "y": "sp3p5SGhZVC2faXumI-e9JU2Mo8KpoYrFDr5yPNVtW4PgEwZOyQT +# A-JdaY8tb7E0" +# }, +# "enc": "A128GCM" +# } + +# Figure 113: JWE Protected Header JSON + + + +# Miller Informational [Page 61] + +# RFC 7520 JOSE Cookbook May 2015 + + +# eyJhbGciOiJFQ0RILUVTK0ExMjhLVyIsImtpZCI6InBlcmVncmluLnRvb2tAdH +# Vja2Jvcm91Z2guZXhhbXBsZSIsImVwayI6eyJrdHkiOiJFQyIsImNydiI6IlAt +# Mzg0IiwieCI6InVCbzRrSFB3Nmtiang1bDB4b3dyZF9vWXpCbWF6LUdLRlp1NH +# hBRkZrYllpV2d1dEVLNml1RURzUTZ3TmROZzMiLCJ5Ijoic3AzcDVTR2haVkMy +# ZmFYdW1JLWU5SlUyTW84S3BvWXJGRHI1eVBOVnRXNFBnRXdaT3lRVEEtSmRhWT +# h0YjdFMCJ9LCJlbmMiOiJBMTI4R0NNIn0 + +# Figure 114: JWE Protected Header, base64url-encoded + +# Performing the content encryption operation on the Plaintext +# (Figure 72) using the following: + +# o CEK (Figure 109); + +# o Initialization Vector (Figure 110); and + +# o JWE Protected Header (Figure 114) as authenticated data + +# produces the following: + +# o Ciphertext from Figure 115. + +# o Authentication Tag from Figure 116. + +# tkZuOO9h95OgHJmkkrfLBisku8rGf6nzVxhRM3sVOhXgz5NJ76oID7lpnAi_cP +# WJRCjSpAaUZ5dOR3Spy7QuEkmKx8-3RCMhSYMzsXaEwDdXta9Mn5B7cCBoJKB0 +# IgEnj_qfo1hIi-uEkUpOZ8aLTZGHfpl05jMwbKkTe2yK3mjF6SBAsgicQDVCkc +# Y9BLluzx1RmC3ORXaM0JaHPB93YcdSDGgpgBWMVrNU1ErkjcMqMoT_wtCex3w0 +# 3XdLkjXIuEr2hWgeP-nkUZTPU9EoGSPj6fAS-bSz87RCPrxZdj_iVyC6QWcqAu +# 07WNhjzJEPc4jVntRJ6K53NgPQ5p99l3Z408OUqj4ioYezbS6vTPlQ + +# Figure 115: Ciphertext, base64url-encoded + +# WuGzxmcreYjpHGJoa17EBg + +# Figure 116: Authentication Tag, base64url-encoded + + + + + + + + + + + + + + + +# Miller Informational [Page 62] + +# RFC 7520 JOSE Cookbook May 2015 + + +# 5.4.5. Output Results + +# The following compose the resulting JWE object: + +# o JWE Protected Header (Figure 114) + +# o Encrypted Key (Figure 112) + +# o Initialization Vector (Figure 110) + +# o Ciphertext (Figure 115) + +# o Authentication Tag (Figure 116) + +# The resulting JWE object using the JWE Compact Serialization: + +# eyJhbGciOiJFQ0RILUVTK0ExMjhLVyIsImtpZCI6InBlcmVncmluLnRvb2tAdH +# Vja2Jvcm91Z2guZXhhbXBsZSIsImVwayI6eyJrdHkiOiJFQyIsImNydiI6IlAt +# Mzg0IiwieCI6InVCbzRrSFB3Nmtiang1bDB4b3dyZF9vWXpCbWF6LUdLRlp1NH +# hBRkZrYllpV2d1dEVLNml1RURzUTZ3TmROZzMiLCJ5Ijoic3AzcDVTR2haVkMy +# ZmFYdW1JLWU5SlUyTW84S3BvWXJGRHI1eVBOVnRXNFBnRXdaT3lRVEEtSmRhWT +# h0YjdFMCJ9LCJlbmMiOiJBMTI4R0NNIn0 +# . +# 0DJjBXri_kBcC46IkU5_Jk9BqaQeHdv2 +# . +# mH-G2zVqgztUtnW_ +# . +# tkZuOO9h95OgHJmkkrfLBisku8rGf6nzVxhRM3sVOhXgz5NJ76oID7lpnAi_cP +# WJRCjSpAaUZ5dOR3Spy7QuEkmKx8-3RCMhSYMzsXaEwDdXta9Mn5B7cCBoJKB0 +# IgEnj_qfo1hIi-uEkUpOZ8aLTZGHfpl05jMwbKkTe2yK3mjF6SBAsgicQDVCkc +# Y9BLluzx1RmC3ORXaM0JaHPB93YcdSDGgpgBWMVrNU1ErkjcMqMoT_wtCex3w0 +# 3XdLkjXIuEr2hWgeP-nkUZTPU9EoGSPj6fAS-bSz87RCPrxZdj_iVyC6QWcqAu +# 07WNhjzJEPc4jVntRJ6K53NgPQ5p99l3Z408OUqj4ioYezbS6vTPlQ +# . +# WuGzxmcreYjpHGJoa17EBg + +# Figure 117: JWE Compact Serialization + + + + + + + + + + + + + + +# Miller Informational [Page 63] + +# RFC 7520 JOSE Cookbook May 2015 + + +# The resulting JWE object using the general JWE JSON Serialization: + +# { +# "recipients": [ +# { +# "encrypted_key": "0DJjBXri_kBcC46IkU5_Jk9BqaQeHdv2" +# } +# ], +# "protected": "eyJhbGciOiJFQ0RILUVTK0ExMjhLVyIsImtpZCI6InBlcm +# VncmluLnRvb2tAdHVja2Jvcm91Z2guZXhhbXBsZSIsImVwayI6eyJrdH +# kiOiJFQyIsImNydiI6IlAtMzg0IiwieCI6InVCbzRrSFB3Nmtiang1bD +# B4b3dyZF9vWXpCbWF6LUdLRlp1NHhBRkZrYllpV2d1dEVLNml1RURzUT +# Z3TmROZzMiLCJ5Ijoic3AzcDVTR2haVkMyZmFYdW1JLWU5SlUyTW84S3 +# BvWXJGRHI1eVBOVnRXNFBnRXdaT3lRVEEtSmRhWTh0YjdFMCJ9LCJlbm +# MiOiJBMTI4R0NNIn0", +# "iv": "mH-G2zVqgztUtnW_", +# "ciphertext": "tkZuOO9h95OgHJmkkrfLBisku8rGf6nzVxhRM3sVOhXgz +# 5NJ76oID7lpnAi_cPWJRCjSpAaUZ5dOR3Spy7QuEkmKx8-3RCMhSYMzs +# XaEwDdXta9Mn5B7cCBoJKB0IgEnj_qfo1hIi-uEkUpOZ8aLTZGHfpl05 +# jMwbKkTe2yK3mjF6SBAsgicQDVCkcY9BLluzx1RmC3ORXaM0JaHPB93Y +# cdSDGgpgBWMVrNU1ErkjcMqMoT_wtCex3w03XdLkjXIuEr2hWgeP-nkU +# ZTPU9EoGSPj6fAS-bSz87RCPrxZdj_iVyC6QWcqAu07WNhjzJEPc4jVn +# tRJ6K53NgPQ5p99l3Z408OUqj4ioYezbS6vTPlQ", +# "tag": "WuGzxmcreYjpHGJoa17EBg" +# } + +# Figure 118: General JWE JSON Serialization + + + + + + + + + + + + + + + + + + + + + + + + +# Miller Informational [Page 64] + +# RFC 7520 JOSE Cookbook May 2015 + + +# The resulting JWE object using the flattened JWE JSON Serialization: + +# { +# "protected": "eyJhbGciOiJFQ0RILUVTK0ExMjhLVyIsImtpZCI6InBlcm +# VncmluLnRvb2tAdHVja2Jvcm91Z2guZXhhbXBsZSIsImVwayI6eyJrdH +# kiOiJFQyIsImNydiI6IlAtMzg0IiwieCI6InVCbzRrSFB3Nmtiang1bD +# B4b3dyZF9vWXpCbWF6LUdLRlp1NHhBRkZrYllpV2d1dEVLNml1RURzUT +# Z3TmROZzMiLCJ5Ijoic3AzcDVTR2haVkMyZmFYdW1JLWU5SlUyTW84S3 +# BvWXJGRHI1eVBOVnRXNFBnRXdaT3lRVEEtSmRhWTh0YjdFMCJ9LCJlbm +# MiOiJBMTI4R0NNIn0", +# "encrypted_key": "0DJjBXri_kBcC46IkU5_Jk9BqaQeHdv2", +# "iv": "mH-G2zVqgztUtnW_", +# "ciphertext": "tkZuOO9h95OgHJmkkrfLBisku8rGf6nzVxhRM3sVOhXgz +# 5NJ76oID7lpnAi_cPWJRCjSpAaUZ5dOR3Spy7QuEkmKx8-3RCMhSYMzs +# XaEwDdXta9Mn5B7cCBoJKB0IgEnj_qfo1hIi-uEkUpOZ8aLTZGHfpl05 +# jMwbKkTe2yK3mjF6SBAsgicQDVCkcY9BLluzx1RmC3ORXaM0JaHPB93Y +# cdSDGgpgBWMVrNU1ErkjcMqMoT_wtCex3w03XdLkjXIuEr2hWgeP-nkU +# ZTPU9EoGSPj6fAS-bSz87RCPrxZdj_iVyC6QWcqAu07WNhjzJEPc4jVn +# tRJ6K53NgPQ5p99l3Z408OUqj4ioYezbS6vTPlQ", +# "tag": "WuGzxmcreYjpHGJoa17EBg" +# } + +# Figure 119: Flattened JWE JSON Serialization + +# 5.5. Key Agreement Using ECDH-ES with AES-CBC-HMAC-SHA2 + +# This example illustrates encrypting content using the "ECDH-ES" +# (Elliptic Curve Diffie-Hellman Ephemeral-Static) key agreement +# algorithm and the "A128CBC-HS256" (AES-128-CBC-HMAC-SHA-256) content +# encryption algorithm. + +# Note that only the EC public key is necessary to perform the key +# agreement. However, the example includes the EC private key to allow +# readers to validate the output. + +# Note that whitespace is added for readability as described in +# Section 1.1. + + + + + + + + + + + + + + +# Miller Informational [Page 65] + +# RFC 7520 JOSE Cookbook May 2015 + + +# 5.5.1. Input Factors + +# The following are supplied before beginning the encryption process: + +# o Plaintext content; this example uses the content from Figure 72. + +# o EC public key; this example uses the public key from Figure 120. + +# o "alg" parameter of "ECDH-ES". + +# o "enc" parameter of "A128CBC-HS256". + +# { +# "kty": "EC", +# "kid": "meriadoc.brandybuck@buckland.example", +# "use": "enc", +# "crv": "P-256", +# "x": "Ze2loSV3wrroKUN_4zhwGhCqo3Xhu1td4QjeQ5wIVR0", +# "y": "HlLtdXARY_f55A3fnzQbPcm6hgr34Mp8p-nuzQCE0Zw", +# "d": "r_kHyZ-a06rmxM3yESK84r1otSg-aQcVStkRhA-iCM8" +# } + +# Figure 120: Elliptic Curve P-256 Key + +# (NOTE: While the key includes the private parameters, only the public +# parameters "crv", "x", and "y" are necessary for the encryption +# operation.) + +# 5.5.2. Generated Factors + +# The following is generated before encrypting: + +# o Initialization Vector; this example uses the Initialization Vector +# from Figure 121. + +# yc9N8v5sYyv3iGQT926IUg + +# Figure 121: Initialization Vector, base64url-encoded + +# NOTE: The Content Encryption Key (CEK) is not randomly generated; +# instead, it is determined using ECDH-ES key agreement. + + + + + + + + + + +# Miller Informational [Page 66] + +# RFC 7520 JOSE Cookbook May 2015 + + +# 5.5.3. Key Agreement + +# The following is generated to agree on a CEK: + +# o Ephemeral private key; this example uses the private key from +# Figure 122. + +# { +# "kty": "EC", +# "crv": "P-256", +# "x": "mPUKT_bAWGHIhg0TpjjqVsP1rXWQu_vwVOHHtNkdYoA", +# "y": "8BQAsImGeAS46fyWw5MhYfGTT0IjBpFw2SS34Dv4Irs", +# "d": "AtH35vJsQ9SGjYfOsjUxYXQKrPH3FjZHmEtSKoSN8cM" +# } + +# Figure 122: Ephemeral Private Key, in JWK Format + +# Performing the ECDH operation using the static EC public key +# (Figure 120) over the ephemeral private key (Figure 122) produces the +# following CEK: + +# hzHdlfQIAEehb8Hrd_mFRhKsKLEzPfshfXs9l6areCc + +# Figure 123: Agreed-to Content Encryption Key, base64url-encoded + +# 5.5.4. Encrypting the Content + +# The following is generated before encrypting the content: + +# o JWE Protected Header; this example uses the header from +# Figure 124, encoded to base64url [RFC4648] as Figure 125. + +# { +# "alg": "ECDH-ES", +# "kid": "meriadoc.brandybuck@buckland.example", +# "epk": { +# "kty": "EC", +# "crv": "P-256", +# "x": "mPUKT_bAWGHIhg0TpjjqVsP1rXWQu_vwVOHHtNkdYoA", +# "y": "8BQAsImGeAS46fyWw5MhYfGTT0IjBpFw2SS34Dv4Irs" +# }, +# "enc": "A128CBC-HS256" +# } + +# Figure 124: JWE Protected Header JSON + + + + + + +# Miller Informational [Page 67] + +# RFC 7520 JOSE Cookbook May 2015 + + +# eyJhbGciOiJFQ0RILUVTIiwia2lkIjoibWVyaWFkb2MuYnJhbmR5YnVja0BidW +# NrbGFuZC5leGFtcGxlIiwiZXBrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYi +# LCJ4IjoibVBVS1RfYkFXR0hJaGcwVHBqanFWc1AxclhXUXVfdndWT0hIdE5rZF +# lvQSIsInkiOiI4QlFBc0ltR2VBUzQ2ZnlXdzVNaFlmR1RUMElqQnBGdzJTUzM0 +# RHY0SXJzIn0sImVuYyI6IkExMjhDQkMtSFMyNTYifQ + +# Figure 125: JWE Protected Header, base64url-encoded + +# Performing the content encryption operation on the Plaintext +# (Figure 72) using the following: + +# o CEK (Figure 123); + +# o Initialization Vector (Figure 121); and + +# o JWE Protected Header (Figure 125) as authenticated data + +# produces the following: + +# o Ciphertext from Figure 126. + +# o Authentication Tag from Figure 127. + +# BoDlwPnTypYq-ivjmQvAYJLb5Q6l-F3LIgQomlz87yW4OPKbWE1zSTEFjDfhU9 +# IPIOSA9Bml4m7iDFwA-1ZXvHteLDtw4R1XRGMEsDIqAYtskTTmzmzNa-_q4F_e +# vAPUmwlO-ZG45Mnq4uhM1fm_D9rBtWolqZSF3xGNNkpOMQKF1Cl8i8wjzRli7- +# IXgyirlKQsbhhqRzkv8IcY6aHl24j03C-AR2le1r7URUhArM79BY8soZU0lzwI +# -sD5PZ3l4NDCCei9XkoIAfsXJWmySPoeRb2Ni5UZL4mYpvKDiwmyzGd65KqVw7 +# MsFfI_K767G9C9Azp73gKZD0DyUn1mn0WW5LmyX_yJ-3AROq8p1WZBfG-ZyJ61 +# 95_JGG2m9Csg + +# Figure 126: Ciphertext, base64url-encoded + +# WCCkNa-x4BeB9hIDIfFuhg + +# Figure 127: Authentication Tag, base64url-encoded + +# 5.5.5. Output Results + +# The following compose the resulting JWE object: + +# o JWE Protected Header (Figure 114) + +# o Initialization Vector (Figure 110) + +# o Ciphertext (Figure 115) + +# o Authentication Tag (Figure 116) + + + +# Miller Informational [Page 68] + +# RFC 7520 JOSE Cookbook May 2015 + + +# Only the general JWE JSON Serialization is presented because the +# flattened JWE JSON Serialization is identical. + +# The resulting JWE object using the JWE Compact Serialization: + +# eyJhbGciOiJFQ0RILUVTIiwia2lkIjoibWVyaWFkb2MuYnJhbmR5YnVja0BidW +# NrbGFuZC5leGFtcGxlIiwiZXBrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYi +# LCJ4IjoibVBVS1RfYkFXR0hJaGcwVHBqanFWc1AxclhXUXVfdndWT0hIdE5rZF +# lvQSIsInkiOiI4QlFBc0ltR2VBUzQ2ZnlXdzVNaFlmR1RUMElqQnBGdzJTUzM0 +# RHY0SXJzIn0sImVuYyI6IkExMjhDQkMtSFMyNTYifQ +# . +# . +# yc9N8v5sYyv3iGQT926IUg +# . +# BoDlwPnTypYq-ivjmQvAYJLb5Q6l-F3LIgQomlz87yW4OPKbWE1zSTEFjDfhU9 +# IPIOSA9Bml4m7iDFwA-1ZXvHteLDtw4R1XRGMEsDIqAYtskTTmzmzNa-_q4F_e +# vAPUmwlO-ZG45Mnq4uhM1fm_D9rBtWolqZSF3xGNNkpOMQKF1Cl8i8wjzRli7- +# IXgyirlKQsbhhqRzkv8IcY6aHl24j03C-AR2le1r7URUhArM79BY8soZU0lzwI +# -sD5PZ3l4NDCCei9XkoIAfsXJWmySPoeRb2Ni5UZL4mYpvKDiwmyzGd65KqVw7 +# MsFfI_K767G9C9Azp73gKZD0DyUn1mn0WW5LmyX_yJ-3AROq8p1WZBfG-ZyJ61 +# 95_JGG2m9Csg +# . +# WCCkNa-x4BeB9hIDIfFuhg + +# Figure 128: JWE Compact Serialization + +# The resulting JWE object using the general JWE JSON Serialization: + +# { +# "protected": "eyJhbGciOiJFQ0RILUVTIiwia2lkIjoibWVyaWFkb2MuYn +# JhbmR5YnVja0BidWNrbGFuZC5leGFtcGxlIiwiZXBrIjp7Imt0eSI6Ik +# VDIiwiY3J2IjoiUC0yNTYiLCJ4IjoibVBVS1RfYkFXR0hJaGcwVHBqan +# FWc1AxclhXUXVfdndWT0hIdE5rZFlvQSIsInkiOiI4QlFBc0ltR2VBUz +# Q2ZnlXdzVNaFlmR1RUMElqQnBGdzJTUzM0RHY0SXJzIn0sImVuYyI6Ik +# ExMjhDQkMtSFMyNTYifQ", +# "iv": "yc9N8v5sYyv3iGQT926IUg", +# "ciphertext": "BoDlwPnTypYq-ivjmQvAYJLb5Q6l-F3LIgQomlz87yW4O +# PKbWE1zSTEFjDfhU9IPIOSA9Bml4m7iDFwA-1ZXvHteLDtw4R1XRGMEs +# DIqAYtskTTmzmzNa-_q4F_evAPUmwlO-ZG45Mnq4uhM1fm_D9rBtWolq +# ZSF3xGNNkpOMQKF1Cl8i8wjzRli7-IXgyirlKQsbhhqRzkv8IcY6aHl2 +# 4j03C-AR2le1r7URUhArM79BY8soZU0lzwI-sD5PZ3l4NDCCei9XkoIA +# fsXJWmySPoeRb2Ni5UZL4mYpvKDiwmyzGd65KqVw7MsFfI_K767G9C9A +# zp73gKZD0DyUn1mn0WW5LmyX_yJ-3AROq8p1WZBfG-ZyJ6195_JGG2m9 +# Csg", +# "tag": "WCCkNa-x4BeB9hIDIfFuhg" +# } + +# Figure 129: General JWE JSON Serialization + + + +# Miller Informational [Page 69] + +# RFC 7520 JOSE Cookbook May 2015 + + +# 5.6. Direct Encryption Using AES-GCM + +# This example illustrates encrypting content using a previously +# exchanged key directly and the "A128GCM" (AES-GCM) content encryption +# algorithm. + +# Note that whitespace is added for readability as described in +# Section 1.1. + +# 5.6.1. Input Factors + +# The following are supplied before beginning the encryption process: + +# o Plaintext content; this example uses the content from Figure 72. + +# o AES symmetric key as the Content Encryption Key (CEK); this +# example uses the key from Figure 130. + +# o "alg" parameter of "dir". + +# o "enc" parameter of "A128GCM". + +# { +# "kty": "oct", +# "kid": "77c7e2b8-6e13-45cf-8672-617b5b45243a", +# "use": "enc", +# "alg": "A128GCM", +# "k": "XctOhJAkA-pD9Lh7ZgW_2A" +# } + +# Figure 130: AES 128-Bit Key, in JWK Format + +# 5.6.2. Generated Factors + +# The following is generated before encrypting: + +# o Initialization Vector; this example uses the Initialization Vector +# from Figure 131. + +# refa467QzzKx6QAB + +# Figure 131: Initialization Vector, base64url-encoded + + + + + + + + + +# Miller Informational [Page 70] + +# RFC 7520 JOSE Cookbook May 2015 + + +# 5.6.3. Encrypting the Content + +# The following is generated before encrypting the content: + +# o JWE Protected Header; this example uses the header from +# Figure 132, encoded as base64url [RFC4648] to produce Figure 133. + +# { +# "alg": "dir", +# "kid": "77c7e2b8-6e13-45cf-8672-617b5b45243a", +# "enc": "A128GCM" +# } + +# Figure 132: JWE Protected Header JSON + +# eyJhbGciOiJkaXIiLCJraWQiOiI3N2M3ZTJiOC02ZTEzLTQ1Y2YtODY3Mi02MT +# diNWI0NTI0M2EiLCJlbmMiOiJBMTI4R0NNIn0 + +# Figure 133: JWE Protected Header, base64url-encoded + +# Performing the encryption operation on the Plaintext (Figure 72) +# using the following: + +# o CEK (Figure 130); + +# o Initialization Vector (Figure 131); and + +# o JWE Protected Header (Figure 133) as authenticated data + +# produces the following: + +# o Ciphertext from Figure 134. + +# o Authentication Tag from Figure 135. + +# JW_i_f52hww_ELQPGaYyeAB6HYGcR559l9TYnSovc23XJoBcW29rHP8yZOZG7Y +# hLpT1bjFuvZPjQS-m0IFtVcXkZXdH_lr_FrdYt9HRUYkshtrMmIUAyGmUnd9zM +# DB2n0cRDIHAzFVeJUDxkUwVAE7_YGRPdcqMyiBoCO-FBdE-Nceb4h3-FtBP-c_ +# BIwCPTjb9o0SbdcdREEMJMyZBH8ySWMVi1gPD9yxi-aQpGbSv_F9N4IZAxscj5 +# g-NJsUPbjk29-s7LJAGb15wEBtXphVCgyy53CoIKLHHeJHXex45Uz9aKZSRSIn +# ZI-wjsY0yu3cT4_aQ3i1o-tiE-F8Ios61EKgyIQ4CWao8PFMj8TTnp + +# Figure 134: Ciphertext, base64url-encoded + +# vbb32Xvllea2OtmHAdccRQ + +# Figure 135: Authentication Tag, base64url-encoded + + + + +# Miller Informational [Page 71] + +# RFC 7520 JOSE Cookbook May 2015 + + +# 5.6.4. Output Results + +# The following compose the resulting JWE object: + +# o JWE Protected Header (Figure 133) + +# o Initialization Vector (Figure 131) + +# o Ciphertext (Figure 134) + +# o Authentication Tag (Figure 135) + +# Only the general JWE JSON Serialization is presented because the +# flattened JWE JSON Serialization is identical. + +# The resulting JWE object using the JWE Compact Serialization: + +# eyJhbGciOiJkaXIiLCJraWQiOiI3N2M3ZTJiOC02ZTEzLTQ1Y2YtODY3Mi02MT +# diNWI0NTI0M2EiLCJlbmMiOiJBMTI4R0NNIn0 +# . +# . +# refa467QzzKx6QAB +# . +# JW_i_f52hww_ELQPGaYyeAB6HYGcR559l9TYnSovc23XJoBcW29rHP8yZOZG7Y +# hLpT1bjFuvZPjQS-m0IFtVcXkZXdH_lr_FrdYt9HRUYkshtrMmIUAyGmUnd9zM +# DB2n0cRDIHAzFVeJUDxkUwVAE7_YGRPdcqMyiBoCO-FBdE-Nceb4h3-FtBP-c_ +# BIwCPTjb9o0SbdcdREEMJMyZBH8ySWMVi1gPD9yxi-aQpGbSv_F9N4IZAxscj5 +# g-NJsUPbjk29-s7LJAGb15wEBtXphVCgyy53CoIKLHHeJHXex45Uz9aKZSRSIn +# ZI-wjsY0yu3cT4_aQ3i1o-tiE-F8Ios61EKgyIQ4CWao8PFMj8TTnp +# . +# vbb32Xvllea2OtmHAdccRQ + +# Figure 136: JWE Compact Serialization + + + + + + + + + + + + + + + + + + +# Miller Informational [Page 72] + +# RFC 7520 JOSE Cookbook May 2015 + + +# The resulting JWE object using the general JWE JSON Serialization: + +# { +# "protected": "eyJhbGciOiJkaXIiLCJraWQiOiI3N2M3ZTJiOC02ZTEzLT +# Q1Y2YtODY3Mi02MTdiNWI0NTI0M2EiLCJlbmMiOiJBMTI4R0NNIn0", +# "iv": "refa467QzzKx6QAB", +# "ciphertext": "JW_i_f52hww_ELQPGaYyeAB6HYGcR559l9TYnSovc23XJ +# oBcW29rHP8yZOZG7YhLpT1bjFuvZPjQS-m0IFtVcXkZXdH_lr_FrdYt9 +# HRUYkshtrMmIUAyGmUnd9zMDB2n0cRDIHAzFVeJUDxkUwVAE7_YGRPdc +# qMyiBoCO-FBdE-Nceb4h3-FtBP-c_BIwCPTjb9o0SbdcdREEMJMyZBH8 +# ySWMVi1gPD9yxi-aQpGbSv_F9N4IZAxscj5g-NJsUPbjk29-s7LJAGb1 +# 5wEBtXphVCgyy53CoIKLHHeJHXex45Uz9aKZSRSInZI-wjsY0yu3cT4_ +# aQ3i1o-tiE-F8Ios61EKgyIQ4CWao8PFMj8TTnp", +# "tag": "vbb32Xvllea2OtmHAdccRQ" +# } + +# Figure 137: General JWE JSON Serialization + +# 5.7. Key Wrap Using AES-GCM KeyWrap with AES-CBC-HMAC-SHA2 + +# This example illustrates encrypting content using the "A256GCMKW" +# (AES-256-GCM-KeyWrap) key encryption algorithm with the "A128CBC- +# HS256" (AES-128-CBC-HMAC-SHA-256) content encryption algorithm. + +# Note that whitespace is added for readability as described in +# Section 1.1. + +# 5.7.1. Input Factors + +# The following are supplied before beginning the encryption process: + +# o Plaintext content; this example uses the content from Figure 72. + +# o AES symmetric key; this example uses the key from Figure 138. + +# o "alg" parameter of "A256GCMKW". + +# o "enc" parameter of "A128CBC-HS256". + + + + + + + + + + + + + +# Miller Informational [Page 73] + +# RFC 7520 JOSE Cookbook May 2015 + + +# { +# "kty": "oct", +# "kid": "18ec08e1-bfa9-4d95-b205-2b4dd1d4321d", +# "use": "enc", +# "alg": "A256GCMKW", +# "k": "qC57l_uxcm7Nm3K-ct4GFjx8tM1U8CZ0NLBvdQstiS8" +# } + +# Figure 138: AES 256-Bit Key + +# 5.7.2. Generated Factors + +# The following are generated before encrypting: + +# o AES symmetric key as the Content Encryption Key (CEK); this +# example uses the key from Figure 139. + +# o Initialization Vector for content encryption; this example uses +# the Initialization Vector from Figure 140. + +# UWxARpat23nL9ReIj4WG3D1ee9I4r-Mv5QLuFXdy_rE + +# Figure 139: Content Encryption Key, base64url-encoded + +# gz6NjyEFNm_vm8Gj6FwoFQ + +# Figure 140: Initialization Vector, base64url-encoded + +# 5.7.3. Encrypting the Key + +# The following is generated before encrypting the CEK: + +# o Initialization Vector for key wrapping; this example uses the +# Initialization Vector from Figure 141. + +# KkYT0GX_2jHlfqN_ + +# Figure 141: Initialization Vector for Key Wrapping, base64url-encoded + + + + + + + + + + + + + +# Miller Informational [Page 74] + +# RFC 7520 JOSE Cookbook May 2015 + + +# Performing the key encryption operation over the CEK (Figure 139) +# with the following: + +# o AES symmetric key (Figure 138); + +# o Initialization Vector (Figure 141); and + +# o The empty string as authenticated data + +# produces the following: + +# o Encrypted Key from Figure 142. + +# o Authentication Tag from Figure 143. + +# lJf3HbOApxMEBkCMOoTnnABxs_CvTWUmZQ2ElLvYNok + +# Figure 142: Encrypted Key, base64url-encoded + +# kfPduVQ3T3H6vnewt--ksw + +# Figure 143: Authentication Tag from Key Wrapping, base64url-encoded + +# 5.7.4. Encrypting the Content + +# The following is generated before encrypting the content: + +# o JWE Protected Header; this example uses the header from +# Figure 144, encoded to base64url [RFC4648] as Figure 145. + +# { +# "alg": "A256GCMKW", +# "kid": "18ec08e1-bfa9-4d95-b205-2b4dd1d4321d", +# "tag": "kfPduVQ3T3H6vnewt--ksw", +# "iv": "KkYT0GX_2jHlfqN_", +# "enc": "A128CBC-HS256" +# } + +# Figure 144: JWE Protected Header JSON + + + + + + + + + + + + +# Miller Informational [Page 75] + +# RFC 7520 JOSE Cookbook May 2015 + + +# eyJhbGciOiJBMjU2R0NNS1ciLCJraWQiOiIxOGVjMDhlMS1iZmE5LTRkOTUtYj +# IwNS0yYjRkZDFkNDMyMWQiLCJ0YWciOiJrZlBkdVZRM1QzSDZ2bmV3dC0ta3N3 +# IiwiaXYiOiJLa1lUMEdYXzJqSGxmcU5fIiwiZW5jIjoiQTEyOENCQy1IUzI1Ni +# J9 + +# Figure 145: JWE Protected Header, base64url-encoded + +# Performing the content encryption operation over the Plaintext +# (Figure 72) with the following: + +# o CEK (Figure 139); + +# o Initialization Vector (Figure 140); and + +# o JWE Protected Header (Figure 145) as authenticated data + +# produces the following: + +# o Ciphertext from Figure 146. + +# o Authentication Tag from Figure 147. + +# Jf5p9-ZhJlJy_IQ_byKFmI0Ro7w7G1QiaZpI8OaiVgD8EqoDZHyFKFBupS8iaE +# eVIgMqWmsuJKuoVgzR3YfzoMd3GxEm3VxNhzWyWtZKX0gxKdy6HgLvqoGNbZCz +# LjqcpDiF8q2_62EVAbr2uSc2oaxFmFuIQHLcqAHxy51449xkjZ7ewzZaGV3eFq +# hpco8o4DijXaG5_7kp3h2cajRfDgymuxUbWgLqaeNQaJtvJmSMFuEOSAzw9Hde +# b6yhdTynCRmu-kqtO5Dec4lT2OMZKpnxc_F1_4yDJFcqb5CiDSmA-psB2k0Jtj +# xAj4UPI61oONK7zzFIu4gBfjJCndsZfdvG7h8wGjV98QhrKEnR7xKZ3KCr0_qR +# 1B-gxpNk3xWU + +# Figure 146: Ciphertext, base64url-encoded + +# DKW7jrb4WaRSNfbXVPlT5g + +# Figure 147: Authentication Tag, base64url-encoded + + + + + + + + + + + + + + + + +# Miller Informational [Page 76] + +# RFC 7520 JOSE Cookbook May 2015 + + +# 5.7.5. Output Results + +# The following compose the resulting JWE object: + +# o JWE Protected Header (Figure 145) + +# o Encrypted Key (Figure 142) + +# o Initialization Vector (Figure 140) + +# o Ciphertext (Figure 146) + +# o Authentication Tag (Figure 147) + +# The resulting JWE object using the JWE Compact Serialization: + +# eyJhbGciOiJBMjU2R0NNS1ciLCJraWQiOiIxOGVjMDhlMS1iZmE5LTRkOTUtYj +# IwNS0yYjRkZDFkNDMyMWQiLCJ0YWciOiJrZlBkdVZRM1QzSDZ2bmV3dC0ta3N3 +# IiwiaXYiOiJLa1lUMEdYXzJqSGxmcU5fIiwiZW5jIjoiQTEyOENCQy1IUzI1Ni +# J9 +# . +# lJf3HbOApxMEBkCMOoTnnABxs_CvTWUmZQ2ElLvYNok +# . +# gz6NjyEFNm_vm8Gj6FwoFQ +# . +# Jf5p9-ZhJlJy_IQ_byKFmI0Ro7w7G1QiaZpI8OaiVgD8EqoDZHyFKFBupS8iaE +# eVIgMqWmsuJKuoVgzR3YfzoMd3GxEm3VxNhzWyWtZKX0gxKdy6HgLvqoGNbZCz +# LjqcpDiF8q2_62EVAbr2uSc2oaxFmFuIQHLcqAHxy51449xkjZ7ewzZaGV3eFq +# hpco8o4DijXaG5_7kp3h2cajRfDgymuxUbWgLqaeNQaJtvJmSMFuEOSAzw9Hde +# b6yhdTynCRmu-kqtO5Dec4lT2OMZKpnxc_F1_4yDJFcqb5CiDSmA-psB2k0Jtj +# xAj4UPI61oONK7zzFIu4gBfjJCndsZfdvG7h8wGjV98QhrKEnR7xKZ3KCr0_qR +# 1B-gxpNk3xWU +# . +# DKW7jrb4WaRSNfbXVPlT5g + +# Figure 148: JWE Compact Serialization + + + + + + + + + + + + + + + +# Miller Informational [Page 77] + +# RFC 7520 JOSE Cookbook May 2015 + + +# The resulting JWE object using the general JWE JSON Serialization: + +# { +# "recipients": [ +# { +# "encrypted_key": "lJf3HbOApxMEBkCMOoTnnABxs_CvTWUmZQ2ElL +# vYNok" +# } +# ], +# "protected": "eyJhbGciOiJBMjU2R0NNS1ciLCJraWQiOiIxOGVjMDhlMS +# 1iZmE5LTRkOTUtYjIwNS0yYjRkZDFkNDMyMWQiLCJ0YWciOiJrZlBkdV +# ZRM1QzSDZ2bmV3dC0ta3N3IiwiaXYiOiJLa1lUMEdYXzJqSGxmcU5fIi +# wiZW5jIjoiQTEyOENCQy1IUzI1NiJ9", +# "iv": "gz6NjyEFNm_vm8Gj6FwoFQ", +# "ciphertext": "Jf5p9-ZhJlJy_IQ_byKFmI0Ro7w7G1QiaZpI8OaiVgD8E +# qoDZHyFKFBupS8iaEeVIgMqWmsuJKuoVgzR3YfzoMd3GxEm3VxNhzWyW +# tZKX0gxKdy6HgLvqoGNbZCzLjqcpDiF8q2_62EVAbr2uSc2oaxFmFuIQ +# HLcqAHxy51449xkjZ7ewzZaGV3eFqhpco8o4DijXaG5_7kp3h2cajRfD +# gymuxUbWgLqaeNQaJtvJmSMFuEOSAzw9Hdeb6yhdTynCRmu-kqtO5Dec +# 4lT2OMZKpnxc_F1_4yDJFcqb5CiDSmA-psB2k0JtjxAj4UPI61oONK7z +# zFIu4gBfjJCndsZfdvG7h8wGjV98QhrKEnR7xKZ3KCr0_qR1B-gxpNk3 +# xWU", +# "tag": "DKW7jrb4WaRSNfbXVPlT5g" +# } + +# Figure 149: General JWE JSON Serialization + + + + + + + + + + + + + + + + + + + + + + + + + +# Miller Informational [Page 78] + +# RFC 7520 JOSE Cookbook May 2015 + + +# The resulting JWE object using the flattened JWE JSON Serialization: + +# { +# "protected": "eyJhbGciOiJBMjU2R0NNS1ciLCJpdiI6IktrWVQwR1hfMm +# pIbGZxTl8iLCJraWQiOiIxOGVjMDhlMS1iZmE5LTRkOTUtYjIwNS0yYj +# RkZDFkNDMyMWQiLCJ0YWciOiJrZlBkdVZRM1QzSDZ2bmV3dC0ta3N3Ii +# wiZW5jIjoiQTEyOENCQy1IUzI1NiJ9", +# "encrypted_key": "lJf3HbOApxMEBkCMOoTnnABxs_CvTWUmZQ2ElLvYNo +# k", +# "iv": "gz6NjyEFNm_vm8Gj6FwoFQ", +# "ciphertext": "Jf5p9-ZhJlJy_IQ_byKFmI0Ro7w7G1QiaZpI8OaiVgD8E +# qoDZHyFKFBupS8iaEeVIgMqWmsuJKuoVgzR3YfzoMd3GxEm3VxNhzWyW +# tZKX0gxKdy6HgLvqoGNbZCzLjqcpDiF8q2_62EVAbr2uSc2oaxFmFuIQ +# HLcqAHxy51449xkjZ7ewzZaGV3eFqhpco8o4DijXaG5_7kp3h2cajRfD +# gymuxUbWgLqaeNQaJtvJmSMFuEOSAzw9Hdeb6yhdTynCRmu-kqtO5Dec +# 4lT2OMZKpnxc_F1_4yDJFcqb5CiDSmA-psB2k0JtjxAj4UPI61oONK7z +# zFIu4gBfjJCndsZfdvG7h8wGjV98QhrKEnR7xKZ3KCr0_qR1B-gxpNk3 +# xWU", +# "tag": "NvBveHr_vonkvflfnUrmBQ" +# } + +# Figure 150: Flattened JWE JSON Serialization + +# 5.8. Key Wrap Using AES-KeyWrap with AES-GCM + +# The following example illustrates content encryption using the +# "A128KW" (AES-128-KeyWrap) key encryption algorithm and the "A128GCM" +# (AES-128-GCM) content encryption algorithm. + +# Note that whitespace is added for readability as described in +# Section 1.1. + +# 5.8.1. Input Factors + +# The following are supplied before beginning the encryption process: + +# o Plaintext content; this example uses the content from Figure 72. + +# o AES symmetric key; this example uses the key from Figure 151. + +# o "alg" parameter of "A128KW". + +# o "enc" parameter of "A128GCM". + + + + + + + + +# Miller Informational [Page 79] + +# RFC 7520 JOSE Cookbook May 2015 + + +# { +# "kty": "oct", +# "kid": "81b20965-8332-43d9-a468-82160ad91ac8", +# "use": "enc", +# "alg": "A128KW", +# "k": "GZy6sIZ6wl9NJOKB-jnmVQ" +# } + +# Figure 151: AES 128-Bit Key + +# 5.8.2. Generated Factors + +# The following are generated before encrypting: + +# o AES symmetric key as the Content Encryption Key; this example uses +# the key from Figure 152. + +# o Initialization Vector; this example uses the Initialization Vector +# from Figure 153. + +# aY5_Ghmk9KxWPBLu_glx1w + +# Figure 152: Content Encryption Key, base64url-encoded + +# Qx0pmsDa8KnJc9Jo + +# Figure 153: Initialization Vector, base64url-encoded + +# 5.8.3. Encrypting the Key + +# Performing the key encryption operation over the CEK (Figure 152) +# with the AES symmetric key (Figure 151) produces the following +# Encrypted Key: + +# CBI6oDw8MydIx1IBntf_lQcw2MmJKIQx + +# Figure 154: Encrypted Key, base64url-encoded + +# 5.8.4. Encrypting the Content + +# The following is generated before encrypting the content: + +# o JWE Protected Header; this example uses the header from +# Figure 155, encoded to base64url [RFC4648] as Figure 156. + + + + + + + +# Miller Informational [Page 80] + +# RFC 7520 JOSE Cookbook May 2015 + + +# { +# "alg": "A128KW", +# "kid": "81b20965-8332-43d9-a468-82160ad91ac8", +# "enc": "A128GCM" +# } + +# Figure 155: JWE Protected Header JSON + +# eyJhbGciOiJBMTI4S1ciLCJraWQiOiI4MWIyMDk2NS04MzMyLTQzZDktYTQ2OC +# 04MjE2MGFkOTFhYzgiLCJlbmMiOiJBMTI4R0NNIn0 + +# Figure 156: JWE Protected Header, base64url-encoded + +# Performing the content encryption over the Plaintext (Figure 72) with +# the following: + +# o CEK (Figure 152); + +# o Initialization Vector (Figure 153); and + +# o JWE Protected Header (Figure 156) as authenticated data + +# produces the following: + +# o Ciphertext from Figure 157. + +# o Authentication Tag from Figure 158. + +# AwliP-KmWgsZ37BvzCefNen6VTbRK3QMA4TkvRkH0tP1bTdhtFJgJxeVmJkLD6 +# 1A1hnWGetdg11c9ADsnWgL56NyxwSYjU1ZEHcGkd3EkU0vjHi9gTlb90qSYFfe +# F0LwkcTtjbYKCsiNJQkcIp1yeM03OmuiYSoYJVSpf7ej6zaYcMv3WwdxDFl8RE +# wOhNImk2Xld2JXq6BR53TSFkyT7PwVLuq-1GwtGHlQeg7gDT6xW0JqHDPn_H-p +# uQsmthc9Zg0ojmJfqqFvETUxLAF-KjcBTS5dNy6egwkYtOt8EIHK-oEsKYtZRa +# a8Z7MOZ7UGxGIMvEmxrGCPeJa14slv2-gaqK0kEThkaSqdYw0FkQZF + +# Figure 157: Ciphertext, base64url-encoded + +# ER7MWJZ1FBI_NKvn7Zb1Lw + +# Figure 158: Authentication Tag, base64url-encoded + + + + + + + + + + + +# Miller Informational [Page 81] + +# RFC 7520 JOSE Cookbook May 2015 + + +# 5.8.5. Output Results + +# The following compose the resulting JWE object: + +# o JWE Protected Header (Figure 156) + +# o Encrypted Key (Figure 154) + +# o Initialization Vector (Figure 153) + +# o Ciphertext (Figure 157) + +# o Authentication Tag (Figure 158) + +# The resulting JWE object using the JWE Compact Serialization: + +# eyJhbGciOiJBMTI4S1ciLCJraWQiOiI4MWIyMDk2NS04MzMyLTQzZDktYTQ2OC +# 04MjE2MGFkOTFhYzgiLCJlbmMiOiJBMTI4R0NNIn0 +# . +# CBI6oDw8MydIx1IBntf_lQcw2MmJKIQx +# . +# Qx0pmsDa8KnJc9Jo +# . +# AwliP-KmWgsZ37BvzCefNen6VTbRK3QMA4TkvRkH0tP1bTdhtFJgJxeVmJkLD6 +# 1A1hnWGetdg11c9ADsnWgL56NyxwSYjU1ZEHcGkd3EkU0vjHi9gTlb90qSYFfe +# F0LwkcTtjbYKCsiNJQkcIp1yeM03OmuiYSoYJVSpf7ej6zaYcMv3WwdxDFl8RE +# wOhNImk2Xld2JXq6BR53TSFkyT7PwVLuq-1GwtGHlQeg7gDT6xW0JqHDPn_H-p +# uQsmthc9Zg0ojmJfqqFvETUxLAF-KjcBTS5dNy6egwkYtOt8EIHK-oEsKYtZRa +# a8Z7MOZ7UGxGIMvEmxrGCPeJa14slv2-gaqK0kEThkaSqdYw0FkQZF +# . +# ER7MWJZ1FBI_NKvn7Zb1Lw + +# Figure 159: JWE Compact Serialization + + + + + + + + + + + + + + + + + + +# Miller Informational [Page 82] + +# RFC 7520 JOSE Cookbook May 2015 + + +# The resulting JWE object using the general JWE JSON Serialization: + +# { +# "recipients": [ +# { +# "encrypted_key": "CBI6oDw8MydIx1IBntf_lQcw2MmJKIQx" +# } +# ], +# "protected": "eyJhbGciOiJBMTI4S1ciLCJraWQiOiI4MWIyMDk2NS04Mz +# MyLTQzZDktYTQ2OC04MjE2MGFkOTFhYzgiLCJlbmMiOiJBMTI4R0NNIn +# 0", +# "iv": "Qx0pmsDa8KnJc9Jo", +# "ciphertext": "AwliP-KmWgsZ37BvzCefNen6VTbRK3QMA4TkvRkH0tP1b +# TdhtFJgJxeVmJkLD61A1hnWGetdg11c9ADsnWgL56NyxwSYjU1ZEHcGk +# d3EkU0vjHi9gTlb90qSYFfeF0LwkcTtjbYKCsiNJQkcIp1yeM03OmuiY +# SoYJVSpf7ej6zaYcMv3WwdxDFl8REwOhNImk2Xld2JXq6BR53TSFkyT7 +# PwVLuq-1GwtGHlQeg7gDT6xW0JqHDPn_H-puQsmthc9Zg0ojmJfqqFvE +# TUxLAF-KjcBTS5dNy6egwkYtOt8EIHK-oEsKYtZRaa8Z7MOZ7UGxGIMv +# EmxrGCPeJa14slv2-gaqK0kEThkaSqdYw0FkQZF", +# "tag": "ER7MWJZ1FBI_NKvn7Zb1Lw" +# } + +# Figure 160: General JWE JSON Serialization + +# The resulting JWE object using the flattened JWE JSON Serialization: + +# { +# "protected": "eyJhbGciOiJBMTI4S1ciLCJraWQiOiI4MWIyMDk2NS04Mz +# MyLTQzZDktYTQ2OC04MjE2MGFkOTFhYzgiLCJlbmMiOiJBMTI4R0NNIn +# 0", +# "encrypted_key": "CBI6oDw8MydIx1IBntf_lQcw2MmJKIQx", +# "iv": "Qx0pmsDa8KnJc9Jo", +# "ciphertext": "AwliP-KmWgsZ37BvzCefNen6VTbRK3QMA4TkvRkH0tP1b +# TdhtFJgJxeVmJkLD61A1hnWGetdg11c9ADsnWgL56NyxwSYjU1ZEHcGk +# d3EkU0vjHi9gTlb90qSYFfeF0LwkcTtjbYKCsiNJQkcIp1yeM03OmuiY +# SoYJVSpf7ej6zaYcMv3WwdxDFl8REwOhNImk2Xld2JXq6BR53TSFkyT7 +# PwVLuq-1GwtGHlQeg7gDT6xW0JqHDPn_H-puQsmthc9Zg0ojmJfqqFvE +# TUxLAF-KjcBTS5dNy6egwkYtOt8EIHK-oEsKYtZRaa8Z7MOZ7UGxGIMv +# EmxrGCPeJa14slv2-gaqK0kEThkaSqdYw0FkQZF", +# "tag": "ER7MWJZ1FBI_NKvn7Zb1Lw" +# } + +# Figure 161: Flattened JWE JSON Serialization + + + + + + + + +# Miller Informational [Page 83] + +# RFC 7520 JOSE Cookbook May 2015 + + +# 5.9. Compressed Content + +# This example illustrates encrypting content that is first compressed. +# It reuses the AES symmetric key, key encryption algorithm, and +# content encryption algorithm from Section 5.8. + +# Note that whitespace is added for readability as described in +# Section 1.1. + +# 5.9.1. Input Factors + +# The following are supplied before beginning the encryption process: + +# o Plaintext content; this example uses the content from Figure 72. + +# o Recipient encryption key; this example uses the key from +# Figure 151. + +# o Key encryption algorithm; this example uses "A128KW". + +# o Content encryption algorithm; this example uses "A128GCM". + +# o "zip" parameter of "DEF". + +# 5.9.2. Generated Factors + +# The following are generated before encrypting: + +# o Compressed Plaintext from the original Plaintext content; +# compressing Figure 72 using the DEFLATE [RFC1951] algorithm +# produces the compressed Plaintext from Figure 162. + +# o AES symmetric key as the Content Encryption Key (CEK); this +# example uses the key from Figure 163. + +# o Initialization Vector; this example uses the Initialization Vector +# from Figure 164. + +# bY_BDcIwDEVX-QNU3QEOrIA4pqlDokYxchxVvbEDGzIJbioOSJwc-f___HPjBu +# 8KVFpVtAplVE1-wZo0YjNZo3C7R5v72pV5f5X382VWjYQpqZKAyjziZOr2B7kQ +# PSy6oZIXUnDYbVKN4jNXi2u0yB7t1qSHTjmMODf9QgvrDzfTIQXnyQRuUya4zI +# WG3vTOdir0v7BRHFYWq3k1k1A_gSDJqtcBF-GZxw8 + +# Figure 162: Compressed Plaintext, base64url-encoded + + + + + + + +# Miller Informational [Page 84] + +# RFC 7520 JOSE Cookbook May 2015 + + +# hC-MpLZSuwWv8sexS6ydfw + +# Figure 163: Content Encryption Key, base64url-encoded + +# p9pUq6XHY0jfEZIl + +# Figure 164: Initialization Vector, base64url-encoded + +# 5.9.3. Encrypting the Key + +# Performing the key encryption operation over the CEK (Figure 163) +# with the AES symmetric key (Figure 151) produces the following +# Encrypted Key: + +# 5vUT2WOtQxKWcekM_IzVQwkGgzlFDwPi + +# Figure 165: Encrypted Key, base64url-encoded + +# 5.9.4. Encrypting the Content + +# The following is generated before encrypting the content: + +# o JWE Protected Header; this example uses the header from +# Figure 166, encoded to base64url [RFC4648] as Figure 167. + +# { +# "alg": "A128KW", +# "kid": "81b20965-8332-43d9-a468-82160ad91ac8", +# "enc": "A128GCM", +# "zip": "DEF" +# } + +# Figure 166: JWE Protected Header JSON + +# eyJhbGciOiJBMTI4S1ciLCJraWQiOiI4MWIyMDk2NS04MzMyLTQzZDktYTQ2OC +# 04MjE2MGFkOTFhYzgiLCJlbmMiOiJBMTI4R0NNIiwiemlwIjoiREVGIn0 + +# Figure 167: JWE Protected Header, base64url-encoded + + + + + + + + + + + + + +# Miller Informational [Page 85] + +# RFC 7520 JOSE Cookbook May 2015 + + +# Performing the content encryption operation over the compressed +# Plaintext (Figure 162, encoded as an octet string) with the +# following: + +# o CEK (Figure 163); + +# o Initialization Vector (Figure 164); and + +# o JWE Protected Header (Figure 167) as authenticated data + +# produces the following: + +# o Ciphertext from Figure 168. + +# o Authentication Tag from Figure 169. + +# HbDtOsdai1oYziSx25KEeTxmwnh8L8jKMFNc1k3zmMI6VB8hry57tDZ61jXyez +# SPt0fdLVfe6Jf5y5-JaCap_JQBcb5opbmT60uWGml8blyiMQmOn9J--XhhlYg0 +# m-BHaqfDO5iTOWxPxFMUedx7WCy8mxgDHj0aBMG6152PsM-w5E_o2B3jDbrYBK +# hpYA7qi3AyijnCJ7BP9rr3U8kxExCpG3mK420TjOw + +# Figure 168: Ciphertext, base64url-encoded + +# VILuUwuIxaLVmh5X-T7kmA + +# Figure 169: Authentication Tag, base64url-encoded + +# 5.9.5. Output Results + +# The following compose the resulting JWE object: + +# o JWE Protected Header (Figure 167) + +# o Encrypted Key (Figure 165) + +# o Initialization Vector (Figure 164) + +# o Ciphertext (Figure 168) + +# o Authentication Tag (Figure 169) + + + + + + + + + + + +# Miller Informational [Page 86] + +# RFC 7520 JOSE Cookbook May 2015 + + +# The resulting JWE object using the JWE Compact Serialization: + +# eyJhbGciOiJBMTI4S1ciLCJraWQiOiI4MWIyMDk2NS04MzMyLTQzZDktYTQ2OC +# 04MjE2MGFkOTFhYzgiLCJlbmMiOiJBMTI4R0NNIiwiemlwIjoiREVGIn0 +# . +# 5vUT2WOtQxKWcekM_IzVQwkGgzlFDwPi +# . +# p9pUq6XHY0jfEZIl +# . +# HbDtOsdai1oYziSx25KEeTxmwnh8L8jKMFNc1k3zmMI6VB8hry57tDZ61jXyez +# SPt0fdLVfe6Jf5y5-JaCap_JQBcb5opbmT60uWGml8blyiMQmOn9J--XhhlYg0 +# m-BHaqfDO5iTOWxPxFMUedx7WCy8mxgDHj0aBMG6152PsM-w5E_o2B3jDbrYBK +# hpYA7qi3AyijnCJ7BP9rr3U8kxExCpG3mK420TjOw +# . +# VILuUwuIxaLVmh5X-T7kmA + +# Figure 170: JWE Compact Serialization + +# The resulting JWE object using the general JWE JSON Serialization: + +# { +# "recipients": [ +# { +# "encrypted_key": "5vUT2WOtQxKWcekM_IzVQwkGgzlFDwPi" +# } +# ], +# "protected": "eyJhbGciOiJBMTI4S1ciLCJraWQiOiI4MWIyMDk2NS04Mz +# MyLTQzZDktYTQ2OC04MjE2MGFkOTFhYzgiLCJlbmMiOiJBMTI4R0NNIi +# wiemlwIjoiREVGIn0", +# "iv": "p9pUq6XHY0jfEZIl", +# "ciphertext": "HbDtOsdai1oYziSx25KEeTxmwnh8L8jKMFNc1k3zmMI6V +# B8hry57tDZ61jXyezSPt0fdLVfe6Jf5y5-JaCap_JQBcb5opbmT60uWG +# ml8blyiMQmOn9J--XhhlYg0m-BHaqfDO5iTOWxPxFMUedx7WCy8mxgDH +# j0aBMG6152PsM-w5E_o2B3jDbrYBKhpYA7qi3AyijnCJ7BP9rr3U8kxE +# xCpG3mK420TjOw", +# "tag": "VILuUwuIxaLVmh5X-T7kmA" +# } + +# Figure 171: General JWE JSON Serialization + + + + + + + + + + + + +# Miller Informational [Page 87] + +# RFC 7520 JOSE Cookbook May 2015 + + +# The resulting JWE object using the flattened JWE JSON Serialization: + +# { +# "protected": "eyJhbGciOiJBMTI4S1ciLCJraWQiOiI4MWIyMDk2NS04Mz +# MyLTQzZDktYTQ2OC04MjE2MGFkOTFhYzgiLCJlbmMiOiJBMTI4R0NNIi +# wiemlwIjoiREVGIn0", +# "encrypted_key": "5vUT2WOtQxKWcekM_IzVQwkGgzlFDwPi", +# "iv": "p9pUq6XHY0jfEZIl", +# "ciphertext": "HbDtOsdai1oYziSx25KEeTxmwnh8L8jKMFNc1k3zmMI6V +# B8hry57tDZ61jXyezSPt0fdLVfe6Jf5y5-JaCap_JQBcb5opbmT60uWG +# ml8blyiMQmOn9J--XhhlYg0m-BHaqfDO5iTOWxPxFMUedx7WCy8mxgDH +# j0aBMG6152PsM-w5E_o2B3jDbrYBKhpYA7qi3AyijnCJ7BP9rr3U8kxE +# xCpG3mK420TjOw", +# "tag": "VILuUwuIxaLVmh5X-T7kmA" +# } + +# Figure 172: Flattened JWE JSON Serialization + +# 5.10. Including Additional Authenticated Data + +# This example illustrates encrypting content that includes additional +# authenticated data. As this example includes an additional top-level +# property not present in the JWE Compact Serialization, only the +# flattened JWE JSON Serialization and general JWE JSON Serialization +# are possible. + +# Note that whitespace is added for readability as described in +# Section 1.1. + +# 5.10.1. Input Factors + +# The following are supplied before beginning the encryption process: + +# o Plaintext content; this example uses the content from Figure 72. + +# o Recipient encryption key; this example uses the key from +# Figure 151. + +# o Key encryption algorithm; this example uses "A128KW". + +# o Content encryption algorithm; this example uses "A128GCM". + +# o Additional Authenticated Data; this example uses a vCard [RFC7095] +# from Figure 173, serialized to UTF-8. + + + + + + + +# Miller Informational [Page 88] + +# RFC 7520 JOSE Cookbook May 2015 + + +# [ +# "vcard", +# [ +# [ "version", {}, "text", "4.0" ], +# [ "fn", {}, "text", "Meriadoc Brandybuck" ], +# [ "n", {}, +# "text", [ +# "Brandybuck", "Meriadoc", "Mr.", "" +# ] +# ], +# [ "bday", {}, "text", "TA 2982" ], +# [ "gender", {}, "text", "M" ] +# ] +# ] + +# Figure 173: Additional Authenticated Data, in JSON Format + +# NOTE: Whitespace between JSON values was added for readability. + +# 5.10.2. Generated Factors + +# The following are generated before encrypting: + +# o AES symmetric key as the Content Encryption Key (CEK); this +# example uses the key from Figure 174. + +# o Initialization Vector; this example uses the Initialization Vector +# from Figure 175. + +# o Encoded Additional Authenticated Data (AAD); this example uses the +# Additional Authenticated Data from Figure 173, encoded to +# base64url [RFC4648] as Figure 176. + +# 75m1ALsYv10pZTKPWrsqdg + +# Figure 174: Content Encryption Key, base64url-encoded + +# veCx9ece2orS7c_N + +# Figure 175: Initialization Vector, base64url-encoded + +# WyJ2Y2FyZCIsW1sidmVyc2lvbiIse30sInRleHQiLCI0LjAiXSxbImZuIix7fS +# widGV4dCIsIk1lcmlhZG9jIEJyYW5keWJ1Y2siXSxbIm4iLHt9LCJ0ZXh0Iixb +# IkJyYW5keWJ1Y2siLCJNZXJpYWRvYyIsIk1yLiIsIiJdXSxbImJkYXkiLHt9LC +# J0ZXh0IiwiVEEgMjk4MiJdLFsiZ2VuZGVyIix7fSwidGV4dCIsIk0iXV1d + +# Figure 176: Additional Authenticated Data, base64url-encoded + + + + +# Miller Informational [Page 89] + +# RFC 7520 JOSE Cookbook May 2015 + + +# 5.10.3. Encrypting the Key + +# Performing the key encryption operation over the CEK (Figure 174) +# with the AES symmetric key (Figure 151) produces the following +# Encrypted Key: + +# 4YiiQ_ZzH76TaIkJmYfRFgOV9MIpnx4X + +# Figure 177: Encrypted Key, base64url-encoded + +# 5.10.4. Encrypting the Content + +# The following is generated before encrypting the content: + +# o JWE Protected Header; this example uses the header from +# Figure 178, encoded to base64url [RFC4648] as Figure 179. + +# { +# "alg": "A128KW", +# "kid": "81b20965-8332-43d9-a468-82160ad91ac8", +# "enc": "A128GCM" +# } + +# Figure 178: JWE Protected Header JSON + +# eyJhbGciOiJBMTI4S1ciLCJraWQiOiI4MWIyMDk2NS04MzMyLTQzZDktYTQ2OC +# 04MjE2MGFkOTFhYzgiLCJlbmMiOiJBMTI4R0NNIn0 + +# Figure 179: JWE Protected Header, base64url-encoded + +# Performing the content encryption operation over the Plaintext with +# the following: + +# o CEK (Figure 174); + +# o Initialization Vector (Figure 175); and + +# o Concatenation of the JWE Protected Header (Figure 179), ".", and +# the base64url [RFC4648] encoding of Figure 173 as authenticated +# data + +# produces the following: + +# o Ciphertext from Figure 180. + +# o Authentication Tag from Figure 181. + + + + + +# Miller Informational [Page 90] + +# RFC 7520 JOSE Cookbook May 2015 + + +# Z_3cbr0k3bVM6N3oSNmHz7Lyf3iPppGf3Pj17wNZqteJ0Ui8p74SchQP8xygM1 +# oFRWCNzeIa6s6BcEtp8qEFiqTUEyiNkOWDNoF14T_4NFqF-p2Mx8zkbKxI7oPK +# 8KNarFbyxIDvICNqBLba-v3uzXBdB89fzOI-Lv4PjOFAQGHrgv1rjXAmKbgkft +# 9cB4WeyZw8MldbBhc-V_KWZslrsLNygon_JJWd_ek6LQn5NRehvApqf9ZrxB4a +# q3FXBxOxCys35PhCdaggy2kfUfl2OkwKnWUbgXVD1C6HxLIlqHhCwXDG59weHr +# RDQeHyMRoBljoV3X_bUTJDnKBFOod7nLz-cj48JMx3SnCZTpbQAkFV + +# Figure 180: Ciphertext, base64url-encoded + +# vOaH_Rajnpy_3hOtqvZHRA + +# Figure 181: Authentication Tag, base64url-encoded + +# 5.10.5. Output Results + +# The following compose the resulting JWE object: + +# o JWE Protected Header (Figure 179) + +# o Encrypted Key (Figure 177) + +# o Initialization Vector (Figure 175) + +# o Additional Authenticated Data (Figure 176) + +# o Ciphertext (Figure 180) + +# o Authentication Tag (Figure 181) + +# The JWE Compact Serialization is not presented because it does not +# support this use case. + + + + + + + + + + + + + + + + + + + + +# Miller Informational [Page 91] + +# RFC 7520 JOSE Cookbook May 2015 + + +# The resulting JWE object using the general JWE JSON Serialization: + +# { +# "recipients": [ +# { +# "encrypted_key": "4YiiQ_ZzH76TaIkJmYfRFgOV9MIpnx4X" +# } +# ], +# "protected": "eyJhbGciOiJBMTI4S1ciLCJraWQiOiI4MWIyMDk2NS04Mz +# MyLTQzZDktYTQ2OC04MjE2MGFkOTFhYzgiLCJlbmMiOiJBMTI4R0NNIn +# 0", +# "iv": "veCx9ece2orS7c_N", +# "aad": "WyJ2Y2FyZCIsW1sidmVyc2lvbiIse30sInRleHQiLCI0LjAiXSxb +# ImZuIix7fSwidGV4dCIsIk1lcmlhZG9jIEJyYW5keWJ1Y2siXSxbIm4i +# LHt9LCJ0ZXh0IixbIkJyYW5keWJ1Y2siLCJNZXJpYWRvYyIsIk1yLiIs +# IiJdXSxbImJkYXkiLHt9LCJ0ZXh0IiwiVEEgMjk4MiJdLFsiZ2VuZGVy +# Iix7fSwidGV4dCIsIk0iXV1d", +# "ciphertext": "Z_3cbr0k3bVM6N3oSNmHz7Lyf3iPppGf3Pj17wNZqteJ0 +# Ui8p74SchQP8xygM1oFRWCNzeIa6s6BcEtp8qEFiqTUEyiNkOWDNoF14 +# T_4NFqF-p2Mx8zkbKxI7oPK8KNarFbyxIDvICNqBLba-v3uzXBdB89fz +# OI-Lv4PjOFAQGHrgv1rjXAmKbgkft9cB4WeyZw8MldbBhc-V_KWZslrs +# LNygon_JJWd_ek6LQn5NRehvApqf9ZrxB4aq3FXBxOxCys35PhCdaggy +# 2kfUfl2OkwKnWUbgXVD1C6HxLIlqHhCwXDG59weHrRDQeHyMRoBljoV3 +# X_bUTJDnKBFOod7nLz-cj48JMx3SnCZTpbQAkFV", +# "tag": "vOaH_Rajnpy_3hOtqvZHRA" +# } + +# Figure 182: General JWE JSON Serialization + + + + + + + + + + + + + + + + + + + + + + + +# Miller Informational [Page 92] + +# RFC 7520 JOSE Cookbook May 2015 + + +# The resulting JWE object using the flattened JWE JSON Serialization: + +# { +# "protected": "eyJhbGciOiJBMTI4S1ciLCJraWQiOiI4MWIyMDk2NS04Mz +# MyLTQzZDktYTQ2OC04MjE2MGFkOTFhYzgiLCJlbmMiOiJBMTI4R0NNIn +# 0", +# "encrypted_key": "4YiiQ_ZzH76TaIkJmYfRFgOV9MIpnx4X", +# "aad": "WyJ2Y2FyZCIsW1sidmVyc2lvbiIse30sInRleHQiLCI0LjAiXSxb +# ImZuIix7fSwidGV4dCIsIk1lcmlhZG9jIEJyYW5keWJ1Y2siXSxbIm4i +# LHt9LCJ0ZXh0IixbIkJyYW5keWJ1Y2siLCJNZXJpYWRvYyIsIk1yLiIs +# IiJdXSxbImJkYXkiLHt9LCJ0ZXh0IiwiVEEgMjk4MiJdLFsiZ2VuZGVy +# Iix7fSwidGV4dCIsIk0iXV1d", +# "iv": "veCx9ece2orS7c_N", +# "ciphertext": "Z_3cbr0k3bVM6N3oSNmHz7Lyf3iPppGf3Pj17wNZqteJ0 +# Ui8p74SchQP8xygM1oFRWCNzeIa6s6BcEtp8qEFiqTUEyiNkOWDNoF14 +# T_4NFqF-p2Mx8zkbKxI7oPK8KNarFbyxIDvICNqBLba-v3uzXBdB89fz +# OI-Lv4PjOFAQGHrgv1rjXAmKbgkft9cB4WeyZw8MldbBhc-V_KWZslrs +# LNygon_JJWd_ek6LQn5NRehvApqf9ZrxB4aq3FXBxOxCys35PhCdaggy +# 2kfUfl2OkwKnWUbgXVD1C6HxLIlqHhCwXDG59weHrRDQeHyMRoBljoV3 +# X_bUTJDnKBFOod7nLz-cj48JMx3SnCZTpbQAkFV", +# "tag": "vOaH_Rajnpy_3hOtqvZHRA" +# } + +# Figure 183: Flattened JWE JSON Serialization + +# 5.11. Protecting Specific Header Fields + +# This example illustrates encrypting content where only certain JOSE +# Header Parameters are protected. As this example includes parameters +# in the JWE Shared Unprotected Header, only the general JWE JSON +# Serialization and flattened JWE JSON Serialization are possible. + +# Note that whitespace is added for readability as described in +# Section 1.1. + +# 5.11.1. Input Factors + +# The following are supplied before beginning the encryption process: + +# o Plaintext content; this example uses the content from Figure 72. + +# o Recipient encryption key; this example uses the key from +# Figure 151. + +# o Key encryption algorithm; this example uses "A128KW". + +# o Content encryption algorithm; this example uses "A128GCM". + + + + +# Miller Informational [Page 93] + +# RFC 7520 JOSE Cookbook May 2015 + + +# 5.11.2. Generated Factors + +# The following are generated before encrypting: + +# o AES symmetric key as the Content Encryption Key (CEK); this +# example uses the key from Figure 184. + +# o Initialization Vector; this example uses the Initialization Vector +# from Figure 185. + +# WDgEptBmQs9ouUvArz6x6g + +# Figure 184: Content Encryption Key, base64url-encoded + +# WgEJsDS9bkoXQ3nR + +# Figure 185: Initialization Vector, base64url-encoded + +# 5.11.3. Encrypting the Key + +# Performing the key encryption operation over the CEK (Figure 184) +# with the AES symmetric key (Figure 151) produces the following +# Encrypted Key: + +# jJIcM9J-hbx3wnqhf5FlkEYos0sHsF0H + +# Figure 186: Encrypted Key, base64url-encoded + +# 5.11.4. Encrypting the Content + +# The following is generated before encrypting the content: + +# o JWE Protected Header; this example uses the header from +# Figure 187, encoded to base64url [RFC4648] as Figure 188. + +# { +# "enc": "A128GCM" +# } + +# Figure 187: JWE Protected Header JSON + +# eyJlbmMiOiJBMTI4R0NNIn0 + +# Figure 188: JWE Protected Header, base64url-encoded + + + + + + + +# Miller Informational [Page 94] + +# RFC 7520 JOSE Cookbook May 2015 + + +# Performing the content encryption operation over the Plaintext with +# the following: + +# o CEK (Figure 184); + +# o Initialization Vector (Figure 185); and + +# o JWE Protected Header (Figure 188) as authenticated data + +# produces the following: + +# o Ciphertext from Figure 189. + +# o Authentication Tag from Figure 190. + +# lIbCyRmRJxnB2yLQOTqjCDKV3H30ossOw3uD9DPsqLL2DM3swKkjOwQyZtWsFL +# YMj5YeLht_StAn21tHmQJuuNt64T8D4t6C7kC9OCCJ1IHAolUv4MyOt80MoPb8 +# fZYbNKqplzYJgIL58g8N2v46OgyG637d6uuKPwhAnTGm_zWhqc_srOvgiLkzyF +# XPq1hBAURbc3-8BqeRb48iR1-_5g5UjWVD3lgiLCN_P7AW8mIiFvUNXBPJK3nO +# WL4teUPS8yHLbWeL83olU4UAgL48x-8dDkH23JykibVSQju-f7e-1xreHWXzWL +# Hs1NqBbre0dEwK3HX_xM0LjUz77Krppgegoutpf5qaKg3l-_xMINmf + +# Figure 189: Ciphertext, base64url-encoded + +# fNYLqpUe84KD45lvDiaBAQ + +# Figure 190: Authentication Tag, base64url-encoded + +# 5.11.5. Output Results + +# The following compose the resulting JWE object: + +# o JWE Shared Unprotected Header (Figure 191) + +# o JWE Protected Header (Figure 188) + +# o Encrypted Key (Figure 186) + +# o Initialization Vector (Figure 185) + +# o Ciphertext (Figure 189) + +# o Authentication Tag (Figure 190) + +# The JWE Compact Serialization is not presented because it does not +# support this use case. + + + + + +# Miller Informational [Page 95] + +# RFC 7520 JOSE Cookbook May 2015 + + +# The following JWE Shared Unprotected Header is generated before +# assembling the output results: + +# { +# "alg": "A128KW", +# "kid": "81b20965-8332-43d9-a468-82160ad91ac8" +# } + +# Figure 191: JWE Shared Unprotected Header JSON + +# The resulting JWE object using the general JWE JSON Serialization: + +# { +# "recipients": [ +# { +# "encrypted_key": "jJIcM9J-hbx3wnqhf5FlkEYos0sHsF0H" +# } +# ], +# "unprotected": { +# "alg": "A128KW", +# "kid": "81b20965-8332-43d9-a468-82160ad91ac8" +# }, +# "protected": "eyJlbmMiOiJBMTI4R0NNIn0", +# "iv": "WgEJsDS9bkoXQ3nR", +# "ciphertext": "lIbCyRmRJxnB2yLQOTqjCDKV3H30ossOw3uD9DPsqLL2D +# M3swKkjOwQyZtWsFLYMj5YeLht_StAn21tHmQJuuNt64T8D4t6C7kC9O +# CCJ1IHAolUv4MyOt80MoPb8fZYbNKqplzYJgIL58g8N2v46OgyG637d6 +# uuKPwhAnTGm_zWhqc_srOvgiLkzyFXPq1hBAURbc3-8BqeRb48iR1-_5 +# g5UjWVD3lgiLCN_P7AW8mIiFvUNXBPJK3nOWL4teUPS8yHLbWeL83olU +# 4UAgL48x-8dDkH23JykibVSQju-f7e-1xreHWXzWLHs1NqBbre0dEwK3 +# HX_xM0LjUz77Krppgegoutpf5qaKg3l-_xMINmf", +# "tag": "fNYLqpUe84KD45lvDiaBAQ" +# } + +# Figure 192: General JWE JSON Serialization + + + + + + + + + + + + + + + + +# Miller Informational [Page 96] + +# RFC 7520 JOSE Cookbook May 2015 + + +# The resulting JWE object using the flattened JWE JSON Serialization: + +# { +# "protected": "eyJlbmMiOiJBMTI4R0NNIn0", +# "unprotected": { +# "alg": "A128KW", +# "kid": "81b20965-8332-43d9-a468-82160ad91ac8" +# }, +# "encrypted_key": "jJIcM9J-hbx3wnqhf5FlkEYos0sHsF0H", +# "iv": "WgEJsDS9bkoXQ3nR", +# "ciphertext": "lIbCyRmRJxnB2yLQOTqjCDKV3H30ossOw3uD9DPsqLL2D +# M3swKkjOwQyZtWsFLYMj5YeLht_StAn21tHmQJuuNt64T8D4t6C7kC9O +# CCJ1IHAolUv4MyOt80MoPb8fZYbNKqplzYJgIL58g8N2v46OgyG637d6 +# uuKPwhAnTGm_zWhqc_srOvgiLkzyFXPq1hBAURbc3-8BqeRb48iR1-_5 +# g5UjWVD3lgiLCN_P7AW8mIiFvUNXBPJK3nOWL4teUPS8yHLbWeL83olU +# 4UAgL48x-8dDkH23JykibVSQju-f7e-1xreHWXzWLHs1NqBbre0dEwK3 +# HX_xM0LjUz77Krppgegoutpf5qaKg3l-_xMINmf", +# "tag": "fNYLqpUe84KD45lvDiaBAQ" +# } + +# Figure 193: Flattened JWE JSON Serialization + +# 5.12. Protecting Content Only + +# This example illustrates encrypting content where none of the JOSE +# header parameters are protected. As this example includes parameters +# only in the JWE Shared Unprotected Header, only the flattened JWE +# JSON Serialization and general JWE JSON Serialization are possible. + +# Note that whitespace is added for readability as described in +# Section 1.1. + +# 5.12.1. Input Factors + +# The following are supplied before beginning the encryption process: + +# o Plaintext content; this example uses the content from Figure 72. + +# o Recipient encryption key; this example uses the key from +# Figure 151. + +# o Key encryption algorithm; this example uses "A128KW". + +# o Content encryption algorithm; this example uses "A128GCM". + + + + + + + +# Miller Informational [Page 97] + +# RFC 7520 JOSE Cookbook May 2015 + + +# 5.12.2. Generated Factors + +# The following are generated before encrypting: + +# o AES symmetric key as the Content Encryption Key; this example the +# key from Figure 194. + +# o Initialization Vector; this example uses the Initialization Vector +# from Figure 195. + +# KBooAFl30QPV3vkcZlXnzQ + +# Figure 194: Content Encryption Key, base64url-encoded + +# YihBoVOGsR1l7jCD + +# Figure 195: Initialization Vector, base64url-encoded + +# 5.12.3. Encrypting the Key + +# Performing the key encryption operation over the CEK (Figure 194) +# with the AES symmetric key (Figure 151) produces the following +# Encrypted Key: + +# 244YHfO_W7RMpQW81UjQrZcq5LSyqiPv + +# Figure 196: Encrypted Key, base64url-encoded + +# 5.12.4. Encrypting the Content + +# Performing the content encryption operation over the Plaintext +# (Figure 72) using the following: + +# o CEK (Figure 194); + +# o Initialization Vector (Figure 195); and + +# o Empty string as authenticated data + +# produces the following: + +# o Ciphertext from Figure 197. + +# o Authentication Tag from Figure 198. + + + + + + + +# Miller Informational [Page 98] + +# RFC 7520 JOSE Cookbook May 2015 + + +# qtPIMMaOBRgASL10dNQhOa7Gqrk7Eal1vwht7R4TT1uq-arsVCPaIeFwQfzrSS +# 6oEUWbBtxEasE0vC6r7sphyVziMCVJEuRJyoAHFSP3eqQPb4Ic1SDSqyXjw_L3 +# svybhHYUGyQuTmUQEDjgjJfBOifwHIsDsRPeBz1NomqeifVPq5GTCWFo5k_MNI +# QURR2Wj0AHC2k7JZfu2iWjUHLF8ExFZLZ4nlmsvJu_mvifMYiikfNfsZAudISO +# a6O73yPZtL04k_1FI7WDfrb2w7OqKLWDXzlpcxohPVOLQwpA3mFNRKdY-bQz4Z +# 4KX9lfz1cne31N4-8BKmojpw-OdQjKdLOGkC445Fb_K1tlDQXw2sBF + +# Figure 197: Ciphertext, base64url-encoded + +# e2m0Vm7JvjK2VpCKXS-kyg + +# Figure 198: Authentication Tag, base64url-encoded + +# 5.12.5. Output Results + +# The JWE Compact Serialization is not presented because it does not +# support this use case. + +# The following JWE Shared Unprotected Header is generated before +# assembling the output results: + +# { +# "alg": "A128KW", +# "kid": "81b20965-8332-43d9-a468-82160ad91ac8", +# "enc": "A128GCM" +# } + +# Figure 199: JWE Shared Unprotected Header JSON + +# The following compose the resulting JWE object: + +# o JWE Shared Unprotected Header (Figure 199) + +# o Encrypted Key (Figure 196) + +# o Initialization Vector (Figure 195) + +# o Ciphertext (Figure 197) + +# o Authentication Tag (Figure 198) + + + + + + + + + + + +# Miller Informational [Page 99] + +# RFC 7520 JOSE Cookbook May 2015 + + +# The resulting JWE object using the general JWE JSON Serialization: + +# { +# "recipients": [ +# { +# "encrypted_key": "244YHfO_W7RMpQW81UjQrZcq5LSyqiPv" +# } +# ], +# "unprotected": { +# "alg": "A128KW", +# "kid": "81b20965-8332-43d9-a468-82160ad91ac8", +# "enc": "A128GCM" +# }, +# "iv": "YihBoVOGsR1l7jCD", +# "ciphertext": "qtPIMMaOBRgASL10dNQhOa7Gqrk7Eal1vwht7R4TT1uq- +# arsVCPaIeFwQfzrSS6oEUWbBtxEasE0vC6r7sphyVziMCVJEuRJyoAHF +# SP3eqQPb4Ic1SDSqyXjw_L3svybhHYUGyQuTmUQEDjgjJfBOifwHIsDs +# RPeBz1NomqeifVPq5GTCWFo5k_MNIQURR2Wj0AHC2k7JZfu2iWjUHLF8 +# ExFZLZ4nlmsvJu_mvifMYiikfNfsZAudISOa6O73yPZtL04k_1FI7WDf +# rb2w7OqKLWDXzlpcxohPVOLQwpA3mFNRKdY-bQz4Z4KX9lfz1cne31N4 +# -8BKmojpw-OdQjKdLOGkC445Fb_K1tlDQXw2sBF", +# "tag": "e2m0Vm7JvjK2VpCKXS-kyg" +# } + +# Figure 200: General JWE JSON Serialization + +# The resulting JWE object using the flattened JWE JSON Serialization: + +# { +# "unprotected": { +# "alg": "A128KW", +# "kid": "81b20965-8332-43d9-a468-82160ad91ac8", +# "enc": "A128GCM" +# }, +# "encrypted_key": "244YHfO_W7RMpQW81UjQrZcq5LSyqiPv", +# "iv": "YihBoVOGsR1l7jCD", +# "ciphertext": "qtPIMMaOBRgASL10dNQhOa7Gqrk7Eal1vwht7R4TT1uq- +# arsVCPaIeFwQfzrSS6oEUWbBtxEasE0vC6r7sphyVziMCVJEuRJyoAHF +# SP3eqQPb4Ic1SDSqyXjw_L3svybhHYUGyQuTmUQEDjgjJfBOifwHIsDs +# RPeBz1NomqeifVPq5GTCWFo5k_MNIQURR2Wj0AHC2k7JZfu2iWjUHLF8 +# ExFZLZ4nlmsvJu_mvifMYiikfNfsZAudISOa6O73yPZtL04k_1FI7WDf +# rb2w7OqKLWDXzlpcxohPVOLQwpA3mFNRKdY-bQz4Z4KX9lfz1cne31N4 +# -8BKmojpw-OdQjKdLOGkC445Fb_K1tlDQXw2sBF", +# "tag": "e2m0Vm7JvjK2VpCKXS-kyg" +# } + +# Figure 201: Flattened JWE JSON Serialization + + + + +# Miller Informational [Page 100] + +# RFC 7520 JOSE Cookbook May 2015 + + +# 5.13. Encrypting to Multiple Recipients + +# This example illustrates encryption content for multiple recipients. +# As this example has multiple recipients, only the general JWE JSON +# Serialization is possible. + +# Note that RSAES-PKCS1-v1_5 uses random data to generate the +# ciphertext; it might not be possible to exactly replicate the results +# in this section. + +# Note that whitespace is added for readability as described in +# Section 1.1. + +# 5.13.1. Input Factors + +# The following are supplied before beginning the encryption process: + +# o Plaintext content; this example uses the Plaintext from Figure 72. + +# o Recipient keys; this example uses the following: + +# * The RSA public key from Figure 73 for the first recipient. + +# * The EC public key from Figure 108 for the second recipient. + +# * The AES symmetric key from Figure 138 for the third recipient. + +# o Key encryption algorithms; this example uses the following: + +# * "RSA1_5" for the first recipient. + +# * "ECDH-ES+A256KW" for the second recipient. + +# * "A256GCMKW" for the third recipient. + +# o Content encryption algorithm; this example uses "A128CBC-HS256". + +# 5.13.2. Generated Factors + +# The following are generated before encrypting: + +# o AES symmetric key as the Content Encryption Key (CEK); this +# example uses the key from Figure 202. + +# o Initialization Vector; this example uses the Initialization Vector +# from Figure 203. + + + + + +# Miller Informational [Page 101] + +# RFC 7520 JOSE Cookbook May 2015 + + +# zXayeJ4gvm8NJr3IUInyokTUO-LbQNKEhe_zWlYbdpQ + +# Figure 202: Content Encryption Key, base64url-encoded + +# VgEIHY20EnzUtZFl2RpB1g + +# Figure 203: Initialization Vector, base64url-encoded + +# 5.13.3. Encrypting the Key to the First Recipient + +# Performing the "RSA1_5" key encryption operation over the CEK +# (Figure 202) with the first recipient's RSA key (Figure 73) produces +# the following Encrypted Key: + +# dYOD28kab0Vvf4ODgxVAJXgHcSZICSOp8M51zjwj4w6Y5G4XJQsNNIBiqyvUUA +# OcpL7S7-cFe7Pio7gV_Q06WmCSa-vhW6me4bWrBf7cHwEQJdXihidAYWVajJIa +# KMXMvFRMV6iDlRr076DFthg2_AV0_tSiV6xSEIFqt1xnYPpmP91tc5WJDOGb-w +# qjw0-b-S1laS11QVbuP78dQ7Fa0zAVzzjHX-xvyM2wxj_otxr9clN1LnZMbeYS +# rRicJK5xodvWgkpIdkMHo4LvdhRRvzoKzlic89jFWPlnBq_V4n5trGuExtp_-d +# bHcGlihqc_wGgho9fLMK8JOArYLcMDNQ + +# Figure 204: Recipient #1 Encrypted Key, base64url-encoded + +# The following is generated after encrypting the CEK for the first +# recipient: + +# o Recipient JWE Unprotected Header from Figure 205. + +# { +# "alg": "RSA1_5", +# "kid": "frodo.baggins@hobbiton.example" +# } + +# Figure 205: Recipient #1 JWE Per-Recipient Unprotected Header JSON + + + + + + + + + + + + + + + + + +# Miller Informational [Page 102] + +# RFC 7520 JOSE Cookbook May 2015 + + +# The following is the assembled first recipient JSON: + +# { +# "encrypted_key": "dYOD28kab0Vvf4ODgxVAJXgHcSZICSOp8M51zjwj4w +# 6Y5G4XJQsNNIBiqyvUUAOcpL7S7-cFe7Pio7gV_Q06WmCSa-vhW6me4b +# WrBf7cHwEQJdXihidAYWVajJIaKMXMvFRMV6iDlRr076DFthg2_AV0_t +# SiV6xSEIFqt1xnYPpmP91tc5WJDOGb-wqjw0-b-S1laS11QVbuP78dQ7 +# Fa0zAVzzjHX-xvyM2wxj_otxr9clN1LnZMbeYSrRicJK5xodvWgkpIdk +# MHo4LvdhRRvzoKzlic89jFWPlnBq_V4n5trGuExtp_-dbHcGlihqc_wG +# gho9fLMK8JOArYLcMDNQ", +# "header": { +# "alg": "RSA1_5", +# "kid": "frodo.baggins@hobbiton.example" +# } +# } + +# Figure 206: Recipient #1 JSON + +# 5.13.4. Encrypting the Key to the Second Recipient + +# The following is generated before encrypting the CEK for the second +# recipient: + +# o Ephemeral EC private key on the same curve as the EC public key; +# this example uses the private key from Figure 207. + +# { +# "kty": "EC", +# "crv": "P-384", +# "x": "Uzdvk3pi5wKCRc1izp5_r0OjeqT-I68i8g2b8mva8diRhsE2xAn2Dt +# MRb25Ma2CX", +# "y": "VDrRyFJh-Kwd1EjAgmj5Eo-CTHAZ53MC7PjjpLioy3ylEjI1pOMbw9 +# 1fzZ84pbfm", +# "d": "1DKHfTv-PiifVw2VBHM_ZiVcwOMxkOyANS_lQHJcrDxVY3jhVCvZPw +# MxJKIE793C" +# } + +# Figure 207: Ephemeral Private Key for Recipient #2, in JWK Format + + + + + + + + + + + + + +# Miller Informational [Page 103] + +# RFC 7520 JOSE Cookbook May 2015 + + +# Performing the "ECDH-ES+A256KW" key encryption operation over the CEK +# (Figure 202) with the following: + +# o Static Elliptic Curve public key (Figure 108). + +# o Ephemeral Elliptic Curve private key (Figure 207). + +# produces the following Encrypted Key: + +# ExInT0io9BqBMYF6-maw5tZlgoZXThD1zWKsHixJuw_elY4gSSId_w + +# Figure 208: Recipient #2 Encrypted Key, base64url-encoded + +# The following is generated after encrypting the CEK for the second +# recipient: + +# o Recipient JWE Unprotected Header from Figure 209. + +# { +# "alg": "ECDH-ES+A256KW", +# "kid": "peregrin.took@tuckborough.example", +# "epk": { +# "kty": "EC", +# "crv": "P-384", +# "x": "Uzdvk3pi5wKCRc1izp5_r0OjeqT-I68i8g2b8mva8diRhsE2xAn2 +# DtMRb25Ma2CX", +# "y": "VDrRyFJh-Kwd1EjAgmj5Eo-CTHAZ53MC7PjjpLioy3ylEjI1pOMb +# w91fzZ84pbfm" +# } +# } + +# Figure 209: Recipient #2 JWE Per-Recipient Unprotected Header JSON + + + + + + + + + + + + + + + + + + + +# Miller Informational [Page 104] + +# RFC 7520 JOSE Cookbook May 2015 + + +# The following is the assembled second recipient JSON: + +# { +# "encrypted_key": "ExInT0io9BqBMYF6-maw5tZlgoZXThD1zWKsHixJuw +# _elY4gSSId_w", +# "header": { +# "alg": "ECDH-ES+A256KW", +# "kid": "peregrin.took@tuckborough.example", +# "epk": { +# "kty": "EC", +# "crv": "P-384", +# "x": "Uzdvk3pi5wKCRc1izp5_r0OjeqT-I68i8g2b8mva8diRhsE2xA +# n2DtMRb25Ma2CX", +# "y": "VDrRyFJh-Kwd1EjAgmj5Eo-CTHAZ53MC7PjjpLioy3ylEjI1pO +# Mbw91fzZ84pbfm" +# } +# } +# } + +# Figure 210: Recipient #2 JSON + +# 5.13.5. Encrypting the Key to the Third Recipient + +# The following is generated before encrypting the CEK for the third +# recipient: + +# o Initialization Vector for key wrapping; this example uses the +# Initialization Vector from Figure 211. + +# AvpeoPZ9Ncn9mkBn + +# Figure 211: Recipient #2 Initialization Vector for Key Wrapping, +# base64url-encoded + +# Performing the "A256GCMKW" key encryption operation over the CEK +# (Figure 202) with the following: + +# o AES symmetric key (Figure 138); and + +# o Initialization Vector (Figure 211) + +# produces the following: + +# o Encrypted Key from Figure 212. + +# o Authentication Tag from Figure 213. + + + + + +# Miller Informational [Page 105] + +# RFC 7520 JOSE Cookbook May 2015 + + +# a7CclAejo_7JSuPB8zeagxXRam8dwCfmkt9-WyTpS1E + +# Figure 212: Recipient #3 Encrypted Key, base64url-encoded + +# 59Nqh1LlYtVIhfD3pgRGvw + +# Figure 213: Recipient #3 Authentication Tag from Key Wrapping, +# base64url-encoded + +# The following is generated after encrypting the CEK for the third +# recipient: + +# o Recipient JWE Unprotected Header; this example uses the header +# from Figure 214. + +# { +# "alg": "A256GCMKW", +# "kid": "18ec08e1-bfa9-4d95-b205-2b4dd1d4321d", +# "tag": "59Nqh1LlYtVIhfD3pgRGvw", +# "iv": "AvpeoPZ9Ncn9mkBn" +# } + +# Figure 214: Recipient #3 JWE Per-Recipient Unprotected Header JSON + +# The following is the assembled third recipient JSON: + +# { +# "encrypted_key": "a7CclAejo_7JSuPB8zeagxXRam8dwCfmkt9-WyTpS1 +# E", +# "header": { +# "alg": "A256GCMKW", +# "kid": "18ec08e1-bfa9-4d95-b205-2b4dd1d4321d", +# "tag": "59Nqh1LlYtVIhfD3pgRGvw", +# "iv": "AvpeoPZ9Ncn9mkBn" +# } +# } + +# Figure 215: Recipient #3 JSON + +# 5.13.6. Encrypting the Content + +# The following is generated before encrypting the content: + +# o JWE Protected Header; this example uses the header from +# Figure 216, encoded to base64url [RFC4648] as Figure 217. + + + + + + +# Miller Informational [Page 106] + +# RFC 7520 JOSE Cookbook May 2015 + + +# { +# "enc": "A128CBC-HS256" +# } + +# Figure 216: JWE Protected Header JSON + +# eyJlbmMiOiJBMTI4Q0JDLUhTMjU2In0 + +# Figure 217: JWE Protected Header, base64url-encoded + +# Performing the content encryption operation over the Plaintext +# (Figure 72) with the following: + +# o CEK (Figure 202), + +# o Initialization Vector (Figure 203), and + +# o JWE Protected Header (Figure 217) as the authenticated data + +# produces the following: + +# o Ciphertext from Figure 218. + +# o Authentication Tag from Figure 219. + +# ajm2Q-OpPXCr7-MHXicknb1lsxLdXxK_yLds0KuhJzfWK04SjdxQeSw2L9mu3a +# _k1C55kCQ_3xlkcVKC5yr__Is48VOoK0k63_QRM9tBURMFqLByJ8vOYQX0oJW4 +# VUHJLmGhF-tVQWB7Kz8mr8zeE7txF0MSaP6ga7-siYxStR7_G07Thd1jh-zGT0 +# wxM5g-VRORtq0K6AXpLlwEqRp7pkt2zRM0ZAXqSpe1O6FJ7FHLDyEFnD-zDIZu +# kLpCbzhzMDLLw2-8I14FQrgi-iEuzHgIJFIJn2wh9Tj0cg_kOZy9BqMRZbmYXM +# Y9YQjorZ_P_JYG3ARAIF3OjDNqpdYe-K_5Q5crGJSDNyij_ygEiItR5jssQVH2 +# ofDQdLChtazE + +# Figure 218: Ciphertext, base64url-encoded + +# BESYyFN7T09KY7i8zKs5_g + +# Figure 219: Authentication Tag, base64url-encoded + + + + + + + + + + + + + +# Miller Informational [Page 107] + +# RFC 7520 JOSE Cookbook May 2015 + + +# The following is generated after encrypting the Plaintext: + +# o JWE Shared Unprotected Header parameters; this example uses the +# header from Figure 220. + +# { +# "cty": "text/plain" +# } + +# Figure 220: JWE Shared Unprotected Header JSON + +# 5.13.7. Output Results + +# The following compose the resulting JWE object: + +# o Recipient #1 JSON (Figure 206) + +# o Recipient #2 JSON (Figure 210) + +# o Recipient #3 JSON (Figure 215) + +# o Initialization Vector (Figure 203) + +# o Ciphertext (Figure 218) + +# o Authentication Tag (Figure 219) + +# The JWE Compact Serialization is not presented because it does not +# support this use case; the flattened JWE JSON Serialization is not +# presented because there is more than one recipient. + + + + + + + + + + + + + + + + + + + + + +# Miller Informational [Page 108] + +# RFC 7520 JOSE Cookbook May 2015 + + +# The resulting JWE object using the general JWE JSON Serialization: + +# { +# "recipients": [ +# { +# "encrypted_key": "dYOD28kab0Vvf4ODgxVAJXgHcSZICSOp8M51zj +# wj4w6Y5G4XJQsNNIBiqyvUUAOcpL7S7-cFe7Pio7gV_Q06WmCSa- +# vhW6me4bWrBf7cHwEQJdXihidAYWVajJIaKMXMvFRMV6iDlRr076 +# DFthg2_AV0_tSiV6xSEIFqt1xnYPpmP91tc5WJDOGb-wqjw0-b-S +# 1laS11QVbuP78dQ7Fa0zAVzzjHX-xvyM2wxj_otxr9clN1LnZMbe +# YSrRicJK5xodvWgkpIdkMHo4LvdhRRvzoKzlic89jFWPlnBq_V4n +# 5trGuExtp_-dbHcGlihqc_wGgho9fLMK8JOArYLcMDNQ", +# "header": { +# "alg": "RSA1_5", +# "kid": "frodo.baggins@hobbiton.example" +# } +# }, +# { +# "encrypted_key": "ExInT0io9BqBMYF6-maw5tZlgoZXThD1zWKsHi +# xJuw_elY4gSSId_w", +# "header": { +# "alg": "ECDH-ES+A256KW", +# "kid": "peregrin.took@tuckborough.example", +# "epk": { +# "kty": "EC", +# "crv": "P-384", +# "x": "Uzdvk3pi5wKCRc1izp5_r0OjeqT-I68i8g2b8mva8diRhs +# E2xAn2DtMRb25Ma2CX", +# "y": "VDrRyFJh-Kwd1EjAgmj5Eo-CTHAZ53MC7PjjpLioy3ylEj +# I1pOMbw91fzZ84pbfm" +# } +# } +# }, +# { +# "encrypted_key": "a7CclAejo_7JSuPB8zeagxXRam8dwCfmkt9-Wy +# TpS1E", +# "header": { +# "alg": "A256GCMKW", +# "kid": "18ec08e1-bfa9-4d95-b205-2b4dd1d4321d", +# "tag": "59Nqh1LlYtVIhfD3pgRGvw", +# "iv": "AvpeoPZ9Ncn9mkBn" +# } +# } +# ], +# "unprotected": { +# "cty": "text/plain" +# }, +# "protected": "eyJlbmMiOiJBMTI4Q0JDLUhTMjU2In0", + + + +# Miller Informational [Page 109] + +# RFC 7520 JOSE Cookbook May 2015 + + +# "iv": "VgEIHY20EnzUtZFl2RpB1g", +# "ciphertext": "ajm2Q-OpPXCr7-MHXicknb1lsxLdXxK_yLds0KuhJzfWK +# 04SjdxQeSw2L9mu3a_k1C55kCQ_3xlkcVKC5yr__Is48VOoK0k63_QRM +# 9tBURMFqLByJ8vOYQX0oJW4VUHJLmGhF-tVQWB7Kz8mr8zeE7txF0MSa +# P6ga7-siYxStR7_G07Thd1jh-zGT0wxM5g-VRORtq0K6AXpLlwEqRp7p +# kt2zRM0ZAXqSpe1O6FJ7FHLDyEFnD-zDIZukLpCbzhzMDLLw2-8I14FQ +# rgi-iEuzHgIJFIJn2wh9Tj0cg_kOZy9BqMRZbmYXMY9YQjorZ_P_JYG3 +# ARAIF3OjDNqpdYe-K_5Q5crGJSDNyij_ygEiItR5jssQVH2ofDQdLCht +# azE", +# "tag": "BESYyFN7T09KY7i8zKs5_g" +# } + +# Figure 221: General JWE JSON Serialization + +# 6. Nesting Signatures and Encryption + +# This example illustrates nesting a JSON Web Signature (JWS) structure +# within a JSON Web Encryption (JWE) structure. The signature uses the +# "PS256" (RSASSA-PSS) algorithm; the encryption uses the "RSA-OAEP" +# (RSAES-OAEP) key encryption algorithm and the "A128GCM" (AES-GCM) +# content encryption algorithm. + +# Note that RSASSA-PSS uses random data to generate the signature, and +# RSAES-OAEP uses random data to generate the ciphertext; it might not +# be possible to exactly replicate the results in this section. + +# Note that whitespace is added for readability as described in +# Section 1.1. + +# 6.1. Signing Input Factors + +# The following are supplied before beginning the signing operation: + +# o Payload content; this example uses the JSON Web Token [JWT] +# content from Figure 222, encoded as base64url [RFC4648] to produce +# Figure 223. + +# o RSA private key; this example uses the key from Figure 224. + +# o "alg" parameter of "PS256". + +# { +# "iss": "hobbiton.example", +# "exp": 1300819380, +# "http://example.com/is_root": true +# } + +# Figure 222: Payload Content, in JSON Format + + + +# Miller Informational [Page 110] + +# RFC 7520 JOSE Cookbook May 2015 + + +# eyJpc3MiOiJob2JiaXRvbi5leGFtcGxlIiwiZXhwIjoxMzAwODE5MzgwLCJodH +# RwOi8vZXhhbXBsZS5jb20vaXNfcm9vdCI6dHJ1ZX0 + +# Figure 223: Payload Content, base64url-encoded + +# { +# "kty": "RSA", +# "kid": "hobbiton.example", +# "use": "sig", +# "n": "kNrPIBDXMU6fcyv5i-QHQAQ-K8gsC3HJb7FYhYaw8hXbNJa-t8q0lD +# KwLZgQXYV-ffWxXJv5GGrlZE4GU52lfMEegTDzYTrRQ3tepgKFjMGg6I +# y6fkl1ZNsx2gEonsnlShfzA9GJwRTmtKPbk1s-hwx1IU5AT-AIelNqBg +# cF2vE5W25_SGGBoaROVdUYxqETDggM1z5cKV4ZjDZ8-lh4oVB07bkac6 +# LQdHpJUUySH_Er20DXx30Kyi97PciXKTS-QKXnmm8ivyRCmux22ZoPUi +# nd2BKC5OiG4MwALhaL2Z2k8CsRdfy-7dg7z41Rp6D0ZeEvtaUp4bX4aK +# raL4rTfw", +# "e": "AQAB", +# "d": "ZLe_TIxpE9-W_n2VBa-HWvuYPtjvxwVXClJFOpJsdea8g9RMx34qEO +# EtnoYc2un3CZ3LtJi-mju5RAT8YSc76YJds3ZVw0UiO8mMBeG6-iOnvg +# obobNx7K57-xjTJZU72EjOr9kB7z6ZKwDDq7HFyCDhUEcYcHFVc7iL_6 +# TibVhAhOFONWlqlJgEgwVYd0rybNGKifdnpEbwyHoMwY6HM1qvnEFgP7 +# iZ0YzHUT535x6jj4VKcdA7ZduFkhUauysySEW7mxZM6fj1vdjJIy9LD1 +# fIz30Xv4ckoqhKF5GONU6tNmMmNgAD6gIViyEle1PrIxl1tBhCI14bRW +# -zrpHgAQ", +# "p": "yKWYoNIAqwMRQlgIBOdT1NIcbDNUUs2Rh-pBaxD_mIkweMt4Mg-0-B +# 2iSYvMrs8horhonV7vxCQagcBAATGW-hAafUehWjxWSH-3KccRM8toL4 +# e0q7M-idRDOBXSoe7Z2-CV2x_ZCY3RP8qp642R13WgXqGDIM4MbUkZSj +# cY9-c", +# "q": "uND4o15V30KDzf8vFJw589p1vlQVQ3NEilrinRUPHkkxaAzDzccGgr +# WMWpGxGFFnNL3w5CqPLeU76-5IVYQq0HwYVl0hVXQHr7sgaGu-483Ad3 +# ENcL23FrOnF45m7_2ooAstJDe49MeLTTQKrSIBl_SKvqpYvfSPTczPcZ +# kh9Kk", +# "dp": "jmTnEoq2qqa8ouaymjhJSCnsveUXnMQC2gAneQJRQkFqQu-zV2PKP +# KNbPvKVyiF5b2-L3tM3OW2d2iNDyRUWXlT7V5l0KwPTABSTOnTqAmYCh +# Gi8kXXdlhcrtSvXldBakC6saxwI_TzGGY2MVXzc2ZnCvCXHV4qjSxOrf +# P3pHFU", +# "dq": "R9FUvU88OVzEkTkXl3-5-WusE4DjHmndeZIlu3rifBdfLpq_P-iWP +# BbGaq9wzQ1c-J7SzCdJqkEJDv5yd2C7rnZ6kpzwBh_nmL8zscAk1qsun +# nt9CJGAYz7-sGWy1JGShFazfP52ThB4rlCJ0YuEaQMrIzpY77_oLAhpm +# DA0hLk", +# "qi": "S8tC7ZknW6hPITkjcwttQOPLVmRfwirRlFAViuDb8NW9CrV_7F2Oq +# UZCqmzHTYAumwGFHI1WVRep7anleWaJjxC_1b3fq_al4qH3Pe-EKiHg6 +# IMazuRtZLUROcThrExDbF5dYbsciDnfRUWLErZ4N1Be0bnxYuPqxwKd9 +# QZwMo0" +# } + +# Figure 224: RSA 2048-Bit Private Key, in JWK Format + + + + +# Miller Informational [Page 111] + +# RFC 7520 JOSE Cookbook May 2015 + + +# 6.2. Signing Operation + +# The following is generated to complete the signing operation: + +# o JWS Protected Header; this example uses the header from +# Figure 225, encoded using base64url [RFC4648] to produce +# Figure 226. + +# { +# "alg": "PS256", +# "typ": "JWT" +# } + +# Figure 225: JWS Protected Header JSON + +# eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9 + +# Figure 226: JWS Protected Header, base64url-encoded + +# Performing the signature operation over the combined JWS Protected +# Header (Figure 226) and payload content (Figure 222) produces the +# following signature: + +# dPpMqwRZxFYi1UfcDAaf8M99o7kwUWtiXZ-ByvVuJih4MhJ_aZqciprz0OWaIA +# kIvn1qskChirjKvY9ESZNUCP4JjvfyPS-nqjJxYoA5ztWOyFk2cZNIPXjcJXSQ +# wXPO9tEe-v4VSqgD0aKHqPxYog4N6Cz1lKph1U1sYDSI67_bLL7elg_vkjfMp5 +# _W5l5LuUYGMeh6hxQIaIUXf9EwV2JmvTMuZ-vBOWy0Sniy1EFo72CRTvmtrIf5 +# AROo5MNliY3KtUxeP-SOmD-LEYwW9SlkohYzMVAZDDOrVbv7KVRHpeYNaK75KE +# QqdCEEkS_rskZS-Qtt_nlegTWh1mEYaA + +# Figure 227: JWS Signature, base64url-encoded + +# 6.3. Signing Output + +# The following compose the resulting JWS object: + +# o JWS Protected Header (Figure 226) + +# o Payload content (Figure 223) + +# o Signature (Figure 227) + + + + + + + + + + +# Miller Informational [Page 112] + +# RFC 7520 JOSE Cookbook May 2015 + + +# The resulting JWS object using the JWS Compact Serialization (which +# is the plaintext input to the following encryption operation): + +# eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9 +# . +# eyJpc3MiOiJob2JiaXRvbi5leGFtcGxlIiwiZXhwIjoxMzAwODE5MzgwLCJodH +# RwOi8vZXhhbXBsZS5jb20vaXNfcm9vdCI6dHJ1ZX0 +# . +# dPpMqwRZxFYi1UfcDAaf8M99o7kwUWtiXZ-ByvVuJih4MhJ_aZqciprz0OWaIA +# kIvn1qskChirjKvY9ESZNUCP4JjvfyPS-nqjJxYoA5ztWOyFk2cZNIPXjcJXSQ +# wXPO9tEe-v4VSqgD0aKHqPxYog4N6Cz1lKph1U1sYDSI67_bLL7elg_vkjfMp5 +# _W5l5LuUYGMeh6hxQIaIUXf9EwV2JmvTMuZ-vBOWy0Sniy1EFo72CRTvmtrIf5 +# AROo5MNliY3KtUxeP-SOmD-LEYwW9SlkohYzMVAZDDOrVbv7KVRHpeYNaK75KE +# QqdCEEkS_rskZS-Qtt_nlegTWh1mEYaA + +# Figure 228: JWS Compact Serialization + +# 6.4. Encryption Input Factors + +# The following are supplied before beginning the encryption process: + +# o Plaintext content; this example uses the content from Figure 228. + +# o RSA public key; this example uses the key from Figure 84. + +# o "alg" parameter of "RSA-OAEP". + +# o "enc" parameter of "A128GCM". + +# 6.5. Encryption Generated Factors + +# The following are generated before encrypting: + +# o AES symmetric key as the Content Encryption Key (CEK); this +# example uses the key from Figure 229. + +# o Initialization Vector; this example uses the Initialization Vector +# from Figure 230. + +# 0RHSNYwN-6-2QBGsYTZLSQ + +# Figure 229: Content Encryption Key, base64url-encoded + +# GbX1i9kXz0sxXPmA + +# Figure 230: Initialization Vector, base64url-encoded + + + + + +# Miller Informational [Page 113] + +# RFC 7520 JOSE Cookbook May 2015 + + +# 6.6. Encrypting the Key + +# Performing the key encryption operation over the CEK (Figure 229) +# with the RSA key (Figure 84) produces the following Encrypted Key: + +# a0JHRoITfpX4qRewImjlStn8m3CPxBV1ueYlVhjurCyrBg3I7YhCRYjphDOOS4 +# E7rXbr2Fn6NyQq-A-gqT0FXqNjVOGrG-bi13mwy7RoYhjTkBEC6P7sMYMXXx4g +# zMedpiJHQVeyI-zkZV7A9matpgevAJWrXzOUysYGTtwoSN6gtUVtlLaivjvb21 +# O0ul4YxSHV-ByK1kyeetRp_fuYJxHoKLQL9P424sKx2WGYb4zsBIPF4ssl_e5I +# R7nany-25_UmC2urosNkoFz9cQ82MypZP8gqbQJyPN-Fpp4Z-5o6yV64x6yzDU +# F_5JCIdl-Qv6H5dMVIY7q1eKpXcV1lWO_2FefEBqXxXvIjLeZivjNkzogCq3-I +# apSjVFnMjBxjpYLT8muaawo1yy1XXMuinIpNcOY3n4KKrXLrCcteX85m4IIHMZ +# a38s1Hpr56fPPseMA-Jltmt-a9iEDtOzhtxz8AXy9tsCAZV2XBWNG8c3kJusAa +# mBKOYwfk7JhLRDgOnJjlJLhn7TI4UxDp9dCmUXEN6z0v23W15qJIEXNJtqnblp +# ymooeWAHCT4e_Owbim1g0AEpTHUdA2iiLNs9WTX_H_TXuPC8yDDhi1smxS_X_x +# pkIHkiIHWDOLx03BpqDTivpKkBYwqP2UZkcxqX2Fo_GnVrNwlK7Lgxw6FSQvDO +# 0 + +# Figure 231: Encrypted Key, base64url-encoded + +# 6.7. Encrypting the Content + +# The following is generated before encrypting the Plaintext: + +# o JWE Protected Header; this example uses the header from +# Figure 232, encoded using base64url [RFC4648] to produce +# Figure 233. + +# { +# "alg": "RSA-OAEP", +# "cty": "JWT", +# "enc": "A128GCM" +# } + +# Figure 232: JWE Protected Header JSON + +# eyJhbGciOiJSU0EtT0FFUCIsImN0eSI6IkpXVCIsImVuYyI6IkExMjhHQ00ifQ + +# Figure 233: JWE Protected Header, base64url-encoded + + + + + + + + + + + + +# Miller Informational [Page 114] + +# RFC 7520 JOSE Cookbook May 2015 + + +# Performing the content encryption operation over the Plaintext +# (Figure 228) with the following: + +# o CEK (Figure 229); + +# o Initialization Vector (Figure 230); and + +# o JWE Protected Header (Figure 233) as authenticated data + +# produces the following: + +# o Ciphertext from Figure 234. + +# o Authentication Tag from Figure 235. + +# SZI4IvKHmwpazl_pJQXX3mHv1ANnOU4Wf9-utWYUcKrBNgCe2OFMf66cSJ8k2Q +# kxaQD3_R60MGE9ofomwtky3GFxMeGRjtpMt9OAvVLsAXB0_UTCBGyBg3C2bWLX +# qZlfJAAoJRUPRk-BimYZY81zVBuIhc7HsQePCpu33SzMsFHjn4lP_idrJz_glZ +# TNgKDt8zdnUPauKTKDNOH1DD4fuzvDYfDIAfqGPyL5sVRwbiXpXdGokEszM-9C +# hMPqW1QNhzuX_Zul3bvrJwr7nuGZs4cUScY3n8yE3AHCLurgls-A9mz1X38xEa +# ulV18l4Fg9tLejdkAuQZjPbqeHQBJe4IwGD5Ee0dQ-Mtz4NnhkIWx-YKBb_Xo2 +# zI3Q_1sYjKUuis7yWW-HTr_vqvFt0bj7WJf2vzB0TZ3dvsoGaTvPH2dyWwumUr +# lx4gmPUzBdwTO6ubfYSDUEEz5py0d_OtWeUSYcCYBKD-aM7tXg26qJo21gYjLf +# hn9zy-W19sOCZGuzgFjPhawXHpvnj_t-0_ES96kogjJLxS1IMU9Y5XmnwZMyNc +# 9EIwnogsCg-hVuvzyP0sIruktmI94_SL1xgMl7o03phcTMxtlMizR88NKU1WkB +# siXMCjy1Noue7MD-ShDp5dmM + +# Figure 234: Ciphertext, base64url-encoded + +# KnIKEhN8U-3C9s4gtSpjSw + +# Figure 235: Authentication Tag, base64url-encoded + +# 6.8. Encryption Output + +# The following compose the resulting JWE object: + +# o JWE Protected Header (Figure 233) + +# o Encrypted Key (Figure 231) + +# o Initialization Vector (Figure 230) + +# o Ciphertext (Figure 234) + +# o Authentication Tag (Figure 235) + + + + + +# Miller Informational [Page 115] + +# RFC 7520 JOSE Cookbook May 2015 + + +# The resulting JWE object using the JWE Compact Serialization: + +# eyJhbGciOiJSU0EtT0FFUCIsImN0eSI6IkpXVCIsImVuYyI6IkExMjhHQ00ifQ +# . +# a0JHRoITfpX4qRewImjlStn8m3CPxBV1ueYlVhjurCyrBg3I7YhCRYjphDOOS4 +# E7rXbr2Fn6NyQq-A-gqT0FXqNjVOGrG-bi13mwy7RoYhjTkBEC6P7sMYMXXx4g +# zMedpiJHQVeyI-zkZV7A9matpgevAJWrXzOUysYGTtwoSN6gtUVtlLaivjvb21 +# O0ul4YxSHV-ByK1kyeetRp_fuYJxHoKLQL9P424sKx2WGYb4zsBIPF4ssl_e5I +# R7nany-25_UmC2urosNkoFz9cQ82MypZP8gqbQJyPN-Fpp4Z-5o6yV64x6yzDU +# F_5JCIdl-Qv6H5dMVIY7q1eKpXcV1lWO_2FefEBqXxXvIjLeZivjNkzogCq3-I +# apSjVFnMjBxjpYLT8muaawo1yy1XXMuinIpNcOY3n4KKrXLrCcteX85m4IIHMZ +# a38s1Hpr56fPPseMA-Jltmt-a9iEDtOzhtxz8AXy9tsCAZV2XBWNG8c3kJusAa +# mBKOYwfk7JhLRDgOnJjlJLhn7TI4UxDp9dCmUXEN6z0v23W15qJIEXNJtqnblp +# ymooeWAHCT4e_Owbim1g0AEpTHUdA2iiLNs9WTX_H_TXuPC8yDDhi1smxS_X_x +# pkIHkiIHWDOLx03BpqDTivpKkBYwqP2UZkcxqX2Fo_GnVrNwlK7Lgxw6FSQvDO +# 0 +# . +# GbX1i9kXz0sxXPmA +# . +# SZI4IvKHmwpazl_pJQXX3mHv1ANnOU4Wf9-utWYUcKrBNgCe2OFMf66cSJ8k2Q +# kxaQD3_R60MGE9ofomwtky3GFxMeGRjtpMt9OAvVLsAXB0_UTCBGyBg3C2bWLX +# qZlfJAAoJRUPRk-BimYZY81zVBuIhc7HsQePCpu33SzMsFHjn4lP_idrJz_glZ +# TNgKDt8zdnUPauKTKDNOH1DD4fuzvDYfDIAfqGPyL5sVRwbiXpXdGokEszM-9C +# hMPqW1QNhzuX_Zul3bvrJwr7nuGZs4cUScY3n8yE3AHCLurgls-A9mz1X38xEa +# ulV18l4Fg9tLejdkAuQZjPbqeHQBJe4IwGD5Ee0dQ-Mtz4NnhkIWx-YKBb_Xo2 +# zI3Q_1sYjKUuis7yWW-HTr_vqvFt0bj7WJf2vzB0TZ3dvsoGaTvPH2dyWwumUr +# lx4gmPUzBdwTO6ubfYSDUEEz5py0d_OtWeUSYcCYBKD-aM7tXg26qJo21gYjLf +# hn9zy-W19sOCZGuzgFjPhawXHpvnj_t-0_ES96kogjJLxS1IMU9Y5XmnwZMyNc +# 9EIwnogsCg-hVuvzyP0sIruktmI94_SL1xgMl7o03phcTMxtlMizR88NKU1WkB +# siXMCjy1Noue7MD-ShDp5dmM +# . +# KnIKEhN8U-3C9s4gtSpjSw + +# Figure 236: JWE Compact Serialization + + + + + + + + + + + + + + + + + +# Miller Informational [Page 116] + +# RFC 7520 JOSE Cookbook May 2015 + + +# The resulting JWE object using the general JWE JSON Serialization: + +# { +# "recipients": [ +# { +# "encrypted_key": "a0JHRoITfpX4qRewImjlStn8m3CPxBV1ueYlVh +# jurCyrBg3I7YhCRYjphDOOS4E7rXbr2Fn6NyQq-A-gqT0FXqNjVO +# GrG-bi13mwy7RoYhjTkBEC6P7sMYMXXx4gzMedpiJHQVeyI-zkZV +# 7A9matpgevAJWrXzOUysYGTtwoSN6gtUVtlLaivjvb21O0ul4YxS +# HV-ByK1kyeetRp_fuYJxHoKLQL9P424sKx2WGYb4zsBIPF4ssl_e +# 5IR7nany-25_UmC2urosNkoFz9cQ82MypZP8gqbQJyPN-Fpp4Z-5 +# o6yV64x6yzDUF_5JCIdl-Qv6H5dMVIY7q1eKpXcV1lWO_2FefEBq +# XxXvIjLeZivjNkzogCq3-IapSjVFnMjBxjpYLT8muaawo1yy1XXM +# uinIpNcOY3n4KKrXLrCcteX85m4IIHMZa38s1Hpr56fPPseMA-Jl +# tmt-a9iEDtOzhtxz8AXy9tsCAZV2XBWNG8c3kJusAamBKOYwfk7J +# hLRDgOnJjlJLhn7TI4UxDp9dCmUXEN6z0v23W15qJIEXNJtqnblp +# ymooeWAHCT4e_Owbim1g0AEpTHUdA2iiLNs9WTX_H_TXuPC8yDDh +# i1smxS_X_xpkIHkiIHWDOLx03BpqDTivpKkBYwqP2UZkcxqX2Fo_ +# GnVrNwlK7Lgxw6FSQvDO0" +# } +# ], +# "protected": "eyJhbGciOiJSU0EtT0FFUCIsImN0eSI6IkpXVCIsImVuYy +# I6IkExMjhHQ00ifQ", +# "iv": "GbX1i9kXz0sxXPmA", +# "ciphertext": "SZI4IvKHmwpazl_pJQXX3mHv1ANnOU4Wf9-utWYUcKrBN +# gCe2OFMf66cSJ8k2QkxaQD3_R60MGE9ofomwtky3GFxMeGRjtpMt9OAv +# VLsAXB0_UTCBGyBg3C2bWLXqZlfJAAoJRUPRk-BimYZY81zVBuIhc7Hs +# QePCpu33SzMsFHjn4lP_idrJz_glZTNgKDt8zdnUPauKTKDNOH1DD4fu +# zvDYfDIAfqGPyL5sVRwbiXpXdGokEszM-9ChMPqW1QNhzuX_Zul3bvrJ +# wr7nuGZs4cUScY3n8yE3AHCLurgls-A9mz1X38xEaulV18l4Fg9tLejd +# kAuQZjPbqeHQBJe4IwGD5Ee0dQ-Mtz4NnhkIWx-YKBb_Xo2zI3Q_1sYj +# KUuis7yWW-HTr_vqvFt0bj7WJf2vzB0TZ3dvsoGaTvPH2dyWwumUrlx4 +# gmPUzBdwTO6ubfYSDUEEz5py0d_OtWeUSYcCYBKD-aM7tXg26qJo21gY +# jLfhn9zy-W19sOCZGuzgFjPhawXHpvnj_t-0_ES96kogjJLxS1IMU9Y5 +# XmnwZMyNc9EIwnogsCg-hVuvzyP0sIruktmI94_SL1xgMl7o03phcTMx +# tlMizR88NKU1WkBsiXMCjy1Noue7MD-ShDp5dmM", +# "tag": "KnIKEhN8U-3C9s4gtSpjSw" +# } + +# Figure 237: General JWE JSON Serialization + + + + + + + + + + + +# Miller Informational [Page 117] + +# RFC 7520 JOSE Cookbook May 2015 + + +# The resulting JWE object using the flattened JWE JSON Serialization: + +# { +# "encrypted_key": "a0JHRoITfpX4qRewImjlStn8m3CPxBV1ueYlVhjurC +# yrBg3I7YhCRYjphDOOS4E7rXbr2Fn6NyQq-A-gqT0FXqNjVOGrG-bi13 +# mwy7RoYhjTkBEC6P7sMYMXXx4gzMedpiJHQVeyI-zkZV7A9matpgevAJ +# WrXzOUysYGTtwoSN6gtUVtlLaivjvb21O0ul4YxSHV-ByK1kyeetRp_f +# uYJxHoKLQL9P424sKx2WGYb4zsBIPF4ssl_e5IR7nany-25_UmC2uros +# NkoFz9cQ82MypZP8gqbQJyPN-Fpp4Z-5o6yV64x6yzDUF_5JCIdl-Qv6 +# H5dMVIY7q1eKpXcV1lWO_2FefEBqXxXvIjLeZivjNkzogCq3-IapSjVF +# nMjBxjpYLT8muaawo1yy1XXMuinIpNcOY3n4KKrXLrCcteX85m4IIHMZ +# a38s1Hpr56fPPseMA-Jltmt-a9iEDtOzhtxz8AXy9tsCAZV2XBWNG8c3 +# kJusAamBKOYwfk7JhLRDgOnJjlJLhn7TI4UxDp9dCmUXEN6z0v23W15q +# JIEXNJtqnblpymooeWAHCT4e_Owbim1g0AEpTHUdA2iiLNs9WTX_H_TX +# uPC8yDDhi1smxS_X_xpkIHkiIHWDOLx03BpqDTivpKkBYwqP2UZkcxqX +# 2Fo_GnVrNwlK7Lgxw6FSQvDO0", +# "protected": "eyJhbGciOiJSU0EtT0FFUCIsImN0eSI6IkpXVCIsImVuYy +# I6IkExMjhHQ00ifQ", +# "iv": "GbX1i9kXz0sxXPmA", +# "ciphertext": "SZI4IvKHmwpazl_pJQXX3mHv1ANnOU4Wf9-utWYUcKrBN +# gCe2OFMf66cSJ8k2QkxaQD3_R60MGE9ofomwtky3GFxMeGRjtpMt9OAv +# VLsAXB0_UTCBGyBg3C2bWLXqZlfJAAoJRUPRk-BimYZY81zVBuIhc7Hs +# QePCpu33SzMsFHjn4lP_idrJz_glZTNgKDt8zdnUPauKTKDNOH1DD4fu +# zvDYfDIAfqGPyL5sVRwbiXpXdGokEszM-9ChMPqW1QNhzuX_Zul3bvrJ +# wr7nuGZs4cUScY3n8yE3AHCLurgls-A9mz1X38xEaulV18l4Fg9tLejd +# kAuQZjPbqeHQBJe4IwGD5Ee0dQ-Mtz4NnhkIWx-YKBb_Xo2zI3Q_1sYj +# KUuis7yWW-HTr_vqvFt0bj7WJf2vzB0TZ3dvsoGaTvPH2dyWwumUrlx4 +# gmPUzBdwTO6ubfYSDUEEz5py0d_OtWeUSYcCYBKD-aM7tXg26qJo21gY +# jLfhn9zy-W19sOCZGuzgFjPhawXHpvnj_t-0_ES96kogjJLxS1IMU9Y5 +# XmnwZMyNc9EIwnogsCg-hVuvzyP0sIruktmI94_SL1xgMl7o03phcTMx +# tlMizR88NKU1WkBsiXMCjy1Noue7MD-ShDp5dmM", +# "tag": "KnIKEhN8U-3C9s4gtSpjSw" +# } + +# Figure 238: Flattened JWE JSON Serialization + + + + + + + + + + + + + + + + +# Miller Informational [Page 118] + +# RFC 7520 JOSE Cookbook May 2015 + + +# 7. Security Considerations + +# This document is designed to provide examples for developers to use +# in checking their implementations. As such, it does not follow some +# of the security considerations and recommendations in the core +# documents (i.e., [JWS], [JWE], [JWK], and [JWA]). For instance: + +# o it does not always generate a new CEK value for every encrypted +# example; + +# o it does not always generate a new Initialization Vector (IV) value +# for every encrypted example; and + +# o it does not always generate a new ephemeral key for every +# ephemeral key example. + +# For each example, data that is expected to be generated for each +# signing or encryption operation is isolated to sections titled +# "Generated Factors". + +# 8. References + +# 8.1. Normative References + +# [JWA] Jones, M., "JSON Web Algorithms (JWA)", RFC 7518, +# DOI 10.17487/RFC7518, May 2015, +# . + +# [JWE] Jones, M. and J. Hildebrand, "JSON Web Encryption (JWE)", +# RFC 7516, DOI 10.17487/RFC7516, May 2015, +# . + +# [JWK] Jones, M., "JSON Web Key (JWK)", RFC 7517, +# DOI 10.17487/RFC7517, May 2015, +# . + +# [JWS] Jones, M., Bradley, J., and N. Sakimura, "JSON Web +# Signature (JWS)", RFC 7515, DOI 10.17487/RFC7515, May +# 2015, . + +# [RFC4648] Josefsson, S., "The Base16, Base32, and Base64 Data +# Encodings", RFC 4648, DOI 10.17487/RFC4648, October 2006, +# . + + + + + + + + +# Miller Informational [Page 119] + +# RFC 7520 JOSE Cookbook May 2015 + + +# 8.2. Informative References + +# [JWT] Jones, M., Bradley, J., and N. Sakimura, "JSON Web Token +# (JWT)", RFC 7519, DOI 10.17487/RFC7519, May 2015, +# . + +# [LOTR-FELLOWSHIP] +# Tolkien, J., "The Fellowship of the Ring", HarperCollins +# Publishers, ePub Edition, ISBN 9780061952838, March 2009. + +# [RFC1951] Deutsch, P., "DEFLATE Compressed Data Format Specification +# version 1.3", RFC 1951, DOI 10.17487/RFC1951, May 1996, +# . + +# [RFC7095] Kewisch, P., "jCard: The JSON Format for vCard", RFC 7095, +# DOI 10.17487/RFC7095, January 2014, +# . + +# Acknowledgements + +# Most of the examples herein use quotes and character names found in +# the novel "The Fellowship of the Ring" [LOTR-FELLOWSHIP], written by +# J. R. R. Tolkien. + +# Thanks to Richard Barnes, Brian Campbell, Mike Jones, and Jim Schaad +# for their input and review of the text. Thanks to Brian Campbell for +# verifying the Compact Serialization examples. + +# Author's Address + +# Matthew Miller +# Cisco Systems, Inc. + +# EMail: mamille2@cisco.com + + + + + + + + + + + + + + + + + +# Miller Informational [Page 120] + + +# Html markup produced by rfcmarkup 1.116, available from https://tools.ietf.org/tools/rfcmarkup/ diff --git a/tests/rfc/test_rfc8037.py b/tests/rfc/test_rfc8037.py new file mode 100644 index 0000000..f534640 --- /dev/null +++ b/tests/rfc/test_rfc8037.py @@ -0,0 +1,793 @@ + +# Disable flake8 reporting +# flake8: noqa + +from jose.jws import verify + +expected_payload = b"It\xe2\x80\x99s a dangerous business, Frodo, going out your door. You step onto the road, and if you don't keep your feet, there\xe2\x80\x99s no knowing where you might be swept off to." + + +# [Docs] [txt|pdf] [draft-ietf-jose-c...] + + + +# Internet Engineering Task Force (IETF) I. Liusvaara +# Request for Comments: 8037 Independent +# Category: Standards Track January 2017 +# ISSN: 2070-1721 + + +# CFRG Elliptic Curve Diffie-Hellman (ECDH) and Signatures +# in JSON Object Signing and Encryption (JOSE) + +# Abstract + +# This document defines how to use the Diffie-Hellman algorithms +# "X25519" and "X448" as well as the signature algorithms "Ed25519" and +# "Ed448" from the IRTF CFRG elliptic curves work in JSON Object +# Signing and Encryption (JOSE). + +# Status of This Memo + +# This is an Internet Standards Track document. + +# This document is a product of the Internet Engineering Task Force +# (IETF). It represents the consensus of the IETF community. It has +# received public review and has been approved for publication by the +# Internet Engineering Steering Group (IESG). Further information on +# Internet Standards is available in Section 2 of RFC 7841. + +# Information about the current status of this document, any errata, +# and how to provide feedback on it may be obtained at +# http://www.rfc-editor.org/info/rfc8037. + +# Copyright Notice + +# Copyright (c) 2017 IETF Trust and the persons identified as the +# document authors. All rights reserved. + +# This document is subject to BCP 78 and the IETF Trust's Legal +# Provisions Relating to IETF Documents +# (http://trustee.ietf.org/license-info) in effect on the date of +# publication of this document. Please review these documents +# carefully, as they describe your rights and restrictions with respect +# to this document. Code Components extracted from this document must +# include Simplified BSD License text as described in Section 4.e of +# the Trust Legal Provisions and are provided without warranty as +# described in the Simplified BSD License. + + + + + + + +# Liusvaara Standards Track [Page 1] + +# RFC 8037 CFRG ECDH and Signatures in JOSE January 2017 + + +# Table of Contents + +# 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 2 +# 1.1. Terminology . . . . . . . . . . . . . . . . . . . . . . . 3 +# 2. Key Type "OKP" . . . . . . . . . . . . . . . . . . . . . . . 3 +# 3. Algorithms . . . . . . . . . . . . . . . . . . . . . . . . . 4 +# 3.1. Signatures . . . . . . . . . . . . . . . . . . . . . . . 4 +# 3.1.1. Signing . . . . . . . . . . . . . . . . . . . . . . . 4 +# 3.1.2. Verification . . . . . . . . . . . . . . . . . . . . 4 +# 3.2. ECDH-ES . . . . . . . . . . . . . . . . . . . . . . . . . 4 +# 3.2.1. Performing the ECDH Operation . . . . . . . . . . . . 5 +# 4. Security Considerations . . . . . . . . . . . . . . . . . . . 5 +# 5. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 6 +# 6. References . . . . . . . . . . . . . . . . . . . . . . . . . 8 +# 6.1. Normative References . . . . . . . . . . . . . . . . . . 8 +# 6.2. Informative References . . . . . . . . . . . . . . . . . 8 +# Appendix A. Examples . . . . . . . . . . . . . . . . . . . . . . 9 +# A.1. Ed25519 Private Key . . . . . . . . . . . . . . . . . . . 9 +# A.2. Ed25519 Public Key . . . . . . . . . . . . . . . . . . . 9 +# A.3. JWK Thumbprint Canonicalization . . . . . . . . . . . . . 9 +# A.4. Ed25519 Signing . . . . . . . . . . . . . . . . . . . . . 10 +# A.5. Ed25519 Validation . . . . . . . . . . . . . . . . . . . 11 +# A.6. ECDH-ES with X25519 . . . . . . . . . . . . . . . . . . . 11 +# A.7. ECDH-ES with X448 . . . . . . . . . . . . . . . . . . . . 12 +# Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . . 14 +# Author's Address . . . . . . . . . . . . . . . . . . . . . . . . 14 + +# 1. Introduction + +# The Internet Research Task Force (IRTF) Crypto Forum Research Group +# (CFRG) selected new Diffie-Hellman algorithms ("X25519" and "X448"; +# [RFC7748]) and signature algorithms ("Ed25519" and "Ed448"; +# [RFC8032]) for asymmetric key cryptography. This document defines +# how to use those algorithms in JOSE in an interoperable manner. + +# This document defines the conventions to use in the context of +# [RFC7515], [RFC7516], and [RFC7517]. + +# While the CFRG also defined two pairs of isogenous elliptic curves +# that underlie these algorithms, these curves are not directly +# exposed, as the algorithms laid on top are sufficient for the +# purposes of JOSE and are much easier to use. + +# All inputs to and outputs from the Elliptic Curve Diffie-Hellman +# (ECDH) and signature functions are defined to be octet strings, with +# the exception of outputs of verification functions, which are +# booleans. + + + + +# Liusvaara Standards Track [Page 2] + +# RFC 8037 CFRG ECDH and Signatures in JOSE January 2017 + + +# 1.1. Terminology + +# The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", +# "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this +# document are to be interpreted as described in [RFC2119]. + +# "JWS Signing Input" and "JWS Signature" are defined by [RFC7515]. + +# "Key Agreement with Elliptic Curve Diffie-Hellman Ephemeral Static" +# is defined by Section 4.6 of [RFC7518]. + +# The JOSE key format ("JSON Web Key (JWK)") is defined by [RFC7517] +# and thumbprints for it ("JSON Web Key (JWK) Thumbprint") in +# [RFC7638]. + +# 2. Key Type "OKP" + +# A new key type (kty) value "OKP" (Octet Key Pair) is defined for +# public key algorithms that use octet strings as private and public +# keys. It has the following parameters: + +# o The parameter "kty" MUST be "OKP". + +# o The parameter "crv" MUST be present and contain the subtype of the +# key (from the "JSON Web Elliptic Curve" registry). + +# o The parameter "x" MUST be present and contain the public key +# encoded using the base64url [RFC4648] encoding. + +# o The parameter "d" MUST be present for private keys and contain the +# private key encoded using the base64url encoding. This parameter +# MUST NOT be present for public keys. + +# Note: Do not assume that there is an underlying elliptic curve, +# despite the existence of the "crv" and "x" parameters. (For +# instance, this key type could be extended to represent Diffie-Hellman +# (DH) algorithms based on hyperelliptic surfaces.) + +# When calculating JWK Thumbprints [RFC7638], the three public key +# fields are included in the hash input in lexicographic order: "crv", +# "kty", and "x". + + + + + + + + + + +# Liusvaara Standards Track [Page 3] + +# RFC 8037 CFRG ECDH and Signatures in JOSE January 2017 + + +# 3. Algorithms + +# 3.1. Signatures + +# For the purpose of using the Edwards-curve Digital Signature +# Algorithm (EdDSA) for signing data using "JSON Web Signature (JWS)" +# [RFC7515], algorithm "EdDSA" is defined here, to be applied as the +# value of the "alg" parameter. + +# The following key subtypes are defined here for use with EdDSA: + +# "crv" EdDSA Variant +# Ed25519 Ed25519 +# Ed448 Ed448 + +# The key type used with these keys is "OKP" and the algorithm used for +# signing is "EdDSA". These subtypes MUST NOT be used for Elliptic +# Curve Diffie-Hellman Ephemeral Static (ECDH-ES). + +# The EdDSA variant used is determined by the subtype of the key +# (Ed25519 for "Ed25519" and Ed448 for "Ed448"). + +# 3.1.1. Signing + +# Signing for these is performed by applying the signing algorithm +# defined in [RFC8032] to the private key (as private key), public key +# (as public key), and the JWS Signing Input (as message). The +# resulting signature is the JWS Signature. All inputs and outputs are +# octet strings. + +# 3.1.2. Verification + +# Verification is performed by applying the verification algorithm +# defined in [RFC8032] to the public key (as public key), the JWS +# Signing Input (as message), and the JWS Signature (as signature). +# All inputs are octet strings. If the algorithm accepts, the +# signature is valid; otherwise, the signature is invalid. + +# 3.2. ECDH-ES + +# The following key subtypes are defined here for purpose of "Key +# Agreement with Elliptic Curve Diffie-Hellman Ephemeral Static" +# (ECDH-ES): + +# "crv" ECDH Function Applied +# X25519 X25519 +# X448 X448 + + + + +# Liusvaara Standards Track [Page 4] + +# RFC 8037 CFRG ECDH and Signatures in JOSE January 2017 + + +# The key type used with these keys is "OKP". These subtypes MUST NOT +# be used for signing. + +# Section 4.6 of [RFC7518] defines the ECDH-ES algorithms +# "ECDH-ES+A128KW", "ECDH-ES+A192KW", "ECDH-ES+A256KW", and "ECDH-ES". + +# 3.2.1. Performing the ECDH Operation + +# The "x" parameter of the "epk" field is set as follows: + +# Apply the appropriate ECDH function to the ephemeral private key (as +# scalar input) and the standard base point (as u-coordinate input). +# The base64url encoding of the output is the value for the "x" +# parameter of the "epk" field. All inputs and outputs are octet +# strings. + +# The Z value (raw key agreement output) for key agreement (to be used +# in subsequent Key Derivation Function (KDF) as per Section 4.6.2 of +# [RFC7518]) is determined as follows: + +# Apply the appropriate ECDH function to the ephemeral private key (as +# scalar input) and receiver public key (as u-coordinate input). The +# output is the Z value. All inputs and outputs are octet strings. + +# 4. Security Considerations + +# Security considerations from [RFC7748] and [RFC8032] apply here. + +# Do not separate key material from information about what key subtype +# it is for. When using keys, check that the algorithm is compatible +# with the key subtype for the key. To do otherwise opens the system +# up to attacks via mixing up algorithms. It is particularly dangerous +# to mix up signature and Message Authentication Code (MAC) algorithms. + +# Although for Ed25519 and Ed448, the signature binds the key used for +# signing, do not assume this, as there are many signature algorithms +# that fail to make such a binding. If key-binding is desired, include +# the key used for signing either inside the JWS protected header or +# the data to sign. + +# If key generation or batch signature verification is performed, a +# well-seeded cryptographic random number generator is REQUIRED. +# Signing and non-batch signature verification are deterministic +# operations and do not need random numbers of any kind. + + + + + + + +# Liusvaara Standards Track [Page 5] + +# RFC 8037 CFRG ECDH and Signatures in JOSE January 2017 + + +# The JSON Web Algorithm (JWA) ECDH-ES KDF construction does not mix +# keys into the final shared secret. In key exchange, such mixing +# could be a bad mistake; whereas here either the receiver public key +# has to be chosen maliciously or the sender has to be malicious in +# order to cause problems. In either case, all security evaporates. + +# The nominal security strengths of X25519 and X448 are ~126 and ~223 +# bits. Therefore, using 256-bit symmetric encryption (especially key +# wrapping and encryption) with X448 is RECOMMENDED. + +# 5. IANA Considerations + +# The following has been added to the "JSON Web Key Types" registry: + +# o "kty" Parameter Value: "OKP" +# o Key Type Description: Octet string key pairs +# o JOSE Implementation Requirements: Optional +# o Change Controller: IESG +# o Specification Document(s): Section 2 of RFC 8037 + +# The following has been added to the "JSON Web Key Parameters" +# registry: + +# o Parameter Name: "crv" +# o Parameter Description: The subtype of key pair +# o Parameter Information Class: Public +# o Used with "kty" Value(s): "OKP" +# o Change Controller: IESG +# o Specification Document(s): Section 2 of RFC 8037 + +# o Parameter Name: "d" +# o Parameter Description: The private key +# o Parameter Information Class: Private +# o Used with "kty" Value(s): "OKP" +# o Change Controller: IESG +# o Specification Document(s): Section 2 of RFC 8037 + +# o Parameter Name: "x" +# o Parameter Description: The public key +# o Parameter Information Class: Public +# o Used with "kty" Value(s): "OKP" +# o Change Controller: IESG +# o Specification Document(s): Section 2 of RFC 8037 + + + + + + + + +# Liusvaara Standards Track [Page 6] + +# RFC 8037 CFRG ECDH and Signatures in JOSE January 2017 + + +# The following has been added to the "JSON Web Signature and +# Encryption Algorithms" registry: + +# o Algorithm Name: "EdDSA" +# o Algorithm Description: EdDSA signature algorithms +# o Algorithm Usage Location(s): "alg" +# o JOSE Implementation Requirements: Optional +# o Change Controller: IESG + +# o Specification Document(s): Section 3.1 of RFC 8037 +# o Algorithm Analysis Documents(s): [RFC8032] + +# The following has been added to the "JSON Web Key Elliptic Curve" +# registry: + +# o Curve Name: "Ed25519" +# o Curve Description: Ed25519 signature algorithm key pairs +# o JOSE Implementation Requirements: Optional +# o Change Controller: IESG +# o Specification Document(s): Section 3.1 of RFC 8037 + +# o Curve Name: "Ed448" +# o Curve Description: Ed448 signature algorithm key pairs +# o JOSE Implementation Requirements: Optional +# o Change Controller: IESG +# o Specification Document(s): Section 3.1 of RFC 8037 + +# o Curve name: "X25519" +# o Curve Description: X25519 function key pairs +# o JOSE Implementation Requirements: Optional +# o Change Controller: IESG +# o Specification Document(s): Section 3.2 of RFC 8037 +# o Analysis Documents(s): [RFC7748] + +# o Curve Name: "X448" +# o Curve Description: X448 function key pairs +# o JOSE Implementation Requirements: Optional +# o Change Controller: IESG +# o Specification Document(s): Section 3.2 of RFC 8037 +# o Analysis Documents(s): [RFC7748] + + + + + + + + + + + +# Liusvaara Standards Track [Page 7] + +# RFC 8037 CFRG ECDH and Signatures in JOSE January 2017 + + +# 6. References + +# 6.1. Normative References + +# [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate +# Requirement Levels", BCP 14, RFC 2119, +# DOI 10.17487/RFC2119, March 1997, +# . + +# [RFC4648] Josefsson, S., "The Base16, Base32, and Base64 Data +# Encodings", RFC 4648, DOI 10.17487/RFC4648, October 2006, +# . + +# [RFC7515] Jones, M., Bradley, J., and N. Sakimura, "JSON Web +# Signature (JWS)", RFC 7515, DOI 10.17487/RFC7515, May +# 2015, . + +# [RFC7517] Jones, M., "JSON Web Key (JWK)", RFC 7517, +# DOI 10.17487/RFC7517, May 2015, +# . + +# [RFC7518] Jones, M., "JSON Web Algorithms (JWA)", RFC 7518, +# DOI 10.17487/RFC7518, May 2015, +# . + +# [RFC7638] Jones, M. and N. Sakimura, "JSON Web Key (JWK) +# Thumbprint", RFC 7638, DOI 10.17487/RFC7638, September +# 2015, . + +# [RFC7748] Langley, A., Hamburg, M., and S. Turner, "Elliptic Curves +# for Security", RFC 7748, DOI 10.17487/RFC7748, January +# 2016, . + +# [RFC8032] Josefsson, S. and I. Liusvaara, "Edwards-Curve Digital +# Signature Algorithm (EdDSA)", RFC 8032, +# DOI 10.17487/RFC8032, January 2017, +# . + +# 6.2. Informative References + +# [RFC7516] Jones, M. and J. Hildebrand, "JSON Web Encryption (JWE)", +# RFC 7516, DOI 10.17487/RFC7516, May 2015, +# . + + + + + + + + +# Liusvaara Standards Track [Page 8] + +# RFC 8037 CFRG ECDH and Signatures in JOSE January 2017 + + +# Appendix A. Examples + +# To the extent possible, these examples use material taken from test +# vectors of [RFC7748] and [RFC8032]. + +# A.1. Ed25519 Private Key + +# {"kty":"OKP","crv":"Ed25519", +# "d":"nWGxne_9WmC6hEr0kuwsxERJxWl7MmkZcDusAxyuf2A", +# "x":"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo"} + +# The hexadecimal dump of private key is: + +# 9d 61 b1 9d ef fd 5a 60 ba 84 4a f4 92 ec 2c c4 +# 44 49 c5 69 7b 32 69 19 70 3b ac 03 1c ae 7f 60 + +# And of the public key is: + +# d7 5a 98 01 82 b1 0a b7 d5 4b fe d3 c9 64 07 3a +# 0e e1 72 f3 da a6 23 25 af 02 1a 68 f7 07 51 1a + +# A.2. Ed25519 Public Key + +# This is the public part of the previous private key (which just omits +# "d"): + +# {"kty":"OKP","crv":"Ed25519", +# "x":"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo"} + +# A.3. JWK Thumbprint Canonicalization + +# The JWK Thumbprint canonicalization of the two examples above (with a +# linebreak inserted for formatting reasons) is: + +# {"crv":"Ed25519","kty":"OKP","x":"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwI +# aaPcHURo"} + +# Which has the SHA-256 hash (in hexadecimal) of +# 90facafea9b1556698540f70c0117a22ea37bd5cf3ed3c47093c1707282b4b89, +# which results in the base64url encoded JWK Thumbprint representation +# of "kPrK_qmxVWaYVA9wwBF6Iuo3vVzz7TxHCTwXBygrS4k". + + + + + + + + + + +# Liusvaara Standards Track [Page 9] + +# RFC 8037 CFRG ECDH and Signatures in JOSE January 2017 + + +# A.4. Ed25519 Signing + +# The JWS protected header is: + +# {"alg":"EdDSA"} + +# This has the base64url encoding of: + +# eyJhbGciOiJFZERTQSJ9 + +# The payload is (text): + +# Example of Ed25519 signing + +# This has the base64url encoding of: + +# RXhhbXBsZSBvZiBFZDI1NTE5IHNpZ25pbmc + +# The JWS signing input is (a concatenation of base64url encoding of +# the (protected) header, a dot, and base64url encoding of the payload) +# is: + +# eyJhbGciOiJFZERTQSJ9.RXhhbXBsZSBvZiBFZDI1NTE5IHNpZ25pbmc + +# Applying the Ed25519 signing algorithm using the private key, public +# key, and the JWS signing input yields the signature (hex): + +# 86 0c 98 d2 29 7f 30 60 a3 3f 42 73 96 72 d6 1b +# 53 cf 3a de fe d3 d3 c6 72 f3 20 dc 02 1b 41 1e +# 9d 59 b8 62 8d c3 51 e2 48 b8 8b 29 46 8e 0e 41 +# 85 5b 0f b7 d8 3b b1 5b e9 02 bf cc b8 cd 0a 02 + +# Converting this to base64url yields: + +# hgyY0il_MGCjP0JzlnLWG1PPOt7-09PGcvMg3AIbQR6dWbhijcNR4ki4iylGjg5BhVsPt +# 9g7sVvpAr_MuM0KAg + +# So the compact serialization of the JWS is (a concatenation of +# signing input, a dot, and base64url encoding of the signature): + +# eyJhbGciOiJFZERTQSJ9.RXhhbXBsZSBvZiBFZDI1NTE5IHNpZ25pbmc.hgyY0il_MGCj +# P0JzlnLWG1PPOt7-09PGcvMg3AIbQR6dWbhijcNR4ki4iylGjg5BhVsPt9g7sVvpAr_Mu +# M0KAg + + + + + + + + +# Liusvaara Standards Track [Page 10] + +# RFC 8037 CFRG ECDH and Signatures in JOSE January 2017 + + +# A.5. Ed25519 Validation + +# The JWS from the example above is: + +# eyJhbGciOiJFZERTQSJ9.RXhhbXBsZSBvZiBFZDI1NTE5IHNpZ25pbmc.hgyY0il_MGCj +# P0JzlnLWG1PPOt7-09PGcvMg3AIbQR6dWbhijcNR4ki4iylGjg5BhVsPt9g7sVvpAr_Mu +# M0KAg + +# This has 2 dots in it, so it might be valid a JWS. Base64url +# decoding the protected header yields: + +# {"alg":"EdDSA"} + +# So this is an EdDSA signature. Now the key has: "kty":"OKP" and +# "crv":"Ed25519", so the signature is Ed25519 signature. + +# The signing input is the part before the second dot: + +# eyJhbGciOiJFZERTQSJ9.RXhhbXBsZSBvZiBFZDI1NTE5IHNpZ25pbmc + +# Applying the Ed25519 verification algorithm to the public key, JWS +# signing input, and the signature yields true. So the signature is +# valid. The message is the base64url decoding of the part between the +# dots: + +# Example of Ed25519 Signing + +# A.6. ECDH-ES with X25519 + +# The public key to encrypt to is: + +# {"kty":"OKP","crv":"X25519","kid":"Bob", +# "x":"3p7bfXt9wbTTW2HC7OQ1Nz-DQ8hbeGdNrfx-FG-IK08"} + +# The public key from the target key is (hex): + +# de 9e db 7d 7b 7d c1 b4 d3 5b 61 c2 ec e4 35 37 +# 3f 83 43 c8 5b 78 67 4d ad fc 7e 14 6f 88 2b 4f + +# The ephemeral secret happens to be (hex): + +# 77 07 6d 0a 73 18 a5 7d 3c 16 c1 72 51 b2 66 45 +# df 4c 2f 87 eb c0 99 2a b1 77 fb a5 1d b9 2c 2a + +# So the ephemeral public key is X25519(ephkey, G) (hex): + +# 85 20 f0 09 89 30 a7 54 74 8b 7d dc b4 3e f7 5a +# 0d bf 3a 0d 26 38 1a f4 eb a4 a9 8e aa 9b 4e 6a + + + +# Liusvaara Standards Track [Page 11] + +# RFC 8037 CFRG ECDH and Signatures in JOSE January 2017 + + +# This is represented as the ephemeral public key value: + +# {"kty":"OKP","crv":"X25519", +# "x":"hSDwCYkwp1R0i33ctD73Wg2_Og0mOBr066SpjqqbTmo"} + +# So the protected header could be, for example: + +# {"alg":"ECDH-ES+A128KW","epk":{"kty":"OKP","crv":"X25519", +# "x":"hSDwCYkwp1R0i33ctD73Wg2_Og0mOBr066SpjqqbTmo"}, +# "enc":"A128GCM","kid":"Bob"} + +# And the sender computes the DH Z value as X25519(ephkey, recv_pub) +# (hex): + +# 4a 5d 9d 5b a4 ce 2d e1 72 8e 3b f4 80 35 0f 25 +# e0 7e 21 c9 47 d1 9e 33 76 f0 9b 3c 1e 16 17 42 + +# The receiver computes the DH Z value as X25519(seckey, ephkey_pub) +# (hex): + +# 4a 5d 9d 5b a4 ce 2d e1 72 8e 3b f4 80 35 0f 25 +# e0 7e 21 c9 47 d1 9e 33 76 f0 9b 3c 1e 16 17 42 + +# This is the same as the sender's value (both sides run this through +# the KDF before using it as a direct encryption key or AES128-KW key). + +# A.7. ECDH-ES with X448 + +# The public key to encrypt to (with a linebreak inserted for +# formatting reasons) is: + +# {"kty":"OKP","crv":"X448","kid":"Dave", +# "x":"PreoKbDNIPW8_AtZm2_sz22kYnEHvbDU80W0MCfYuXL8PjT7QjKhPKcG3LV67D2 +# uB73BxnvzNgk"} + +# The public key from the target key is (hex): + +# 3e b7 a8 29 b0 cd 20 f5 bc fc 0b 59 9b 6f ec cf +# 6d a4 62 71 07 bd b0 d4 f3 45 b4 30 27 d8 b9 72 +# fc 3e 34 fb 42 32 a1 3c a7 06 dc b5 7a ec 3d ae +# 07 bd c1 c6 7b f3 36 09 + +# The ephemeral secret happens to be (hex): + +# 9a 8f 49 25 d1 51 9f 57 75 cf 46 b0 4b 58 00 d4 +# ee 9e e8 ba e8 bc 55 65 d4 98 c2 8d d9 c9 ba f5 +# 74 a9 41 97 44 89 73 91 00 63 82 a6 f1 27 ab 1d +# 9a c2 d8 c0 a5 98 72 6b + + + +# Liusvaara Standards Track [Page 12] + +# RFC 8037 CFRG ECDH and Signatures in JOSE January 2017 + + +# So the ephemeral public key is X448(ephkey, G) (hex): + +# 9b 08 f7 cc 31 b7 e3 e6 7d 22 d5 ae a1 21 07 4a +# 27 3b d2 b8 3d e0 9c 63 fa a7 3d 2c 22 c5 d9 bb +# c8 36 64 72 41 d9 53 d4 0c 5b 12 da 88 12 0d 53 +# 17 7f 80 e5 32 c4 1f a0 + +# This is packed into the ephemeral public key value (a linebreak +# inserted for formatting purposes): + +# {"kty":"OKP","crv":"X448", +# "x":"mwj3zDG34-Z9ItWuoSEHSic70rg94Jxj-qc9LCLF2bvINmRyQdlT1AxbEtqIEg1 +# TF3-A5TLEH6A"} + +# So the protected header could be, for example (a linebreak inserted +# for formatting purposes): + +# {"alg":"ECDH-ES+A256KW","epk":{"kty":"OKP","crv":"X448", +# "x":"mwj3zDG34-Z9ItWuoSEHSic70rg94Jxj-qc9LCLF2bvINmRyQdlT1AxbEtqIEg1 +# TF3-A5TLEH6A"},"enc":"A256GCM","kid":"Dave"} + +# And the sender computes the DH Z value as X448(ephkey,recv_pub) +# (hex): + +# 07 ff f4 18 1a c6 cc 95 ec 1c 16 a9 4a 0f 74 d1 +# 2d a2 32 ce 40 a7 75 52 28 1d 28 2b b6 0c 0b 56 +# fd 24 64 c3 35 54 39 36 52 1c 24 40 30 85 d5 9a +# 44 9a 50 37 51 4a 87 9d + +# The receiver computes the DH Z value as X448(seckey, ephkey_pub) +# (hex): + +# 07 ff f4 18 1a c6 cc 95 ec 1c 16 a9 4a 0f 74 d1 +# 2d a2 32 ce 40 a7 75 52 28 1d 28 2b b6 0c 0b 56 +# fd 24 64 c3 35 54 39 36 52 1c 24 40 30 85 d5 9a +# 44 9a 50 37 51 4a 87 9d + +# This is the same as the sender's value (both sides run this through +# KDF before using it as the direct encryption key or AES256-KW key). + + + + + + + + + + + + +# Liusvaara Standards Track [Page 13] + +# RFC 8037 CFRG ECDH and Signatures in JOSE January 2017 + + +# Acknowledgements + +# Thanks to Michael B. Jones for his comments on an initial draft of +# this document and editorial help. + +# Thanks to Matt Miller for some editorial help. + +# Author's Address + +# Ilari Liusvaara +# Independent + +# Email: ilariliusvaara@welho.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +# Liusvaara Standards Track [Page 14] diff --git a/tests/rfc/test_rfc8410.py b/tests/rfc/test_rfc8410.py new file mode 100644 index 0000000..9ad6ef9 --- /dev/null +++ b/tests/rfc/test_rfc8410.py @@ -0,0 +1,2252 @@ + +# Disable flake8 reporting +# flake8: noqa + +from jose.jws import verify + +expected_payload = b"It\xe2\x80\x99s a dangerous business, Frodo, going out your door. You step onto the road, and if you don't keep your feet, there\xe2\x80\x99s no knowing where you might be swept off to." + + +# [Docs] [txt|pdf] [draft-ietf-jose-c...] + + + +# Internet Engineering Task Force (IETF) S. Josefsson +# Request for Comments: 8410 SJD AB +# Category: Standards Track J. Schaad +# ISSN: 2070-1721 August Cellars +# August 2018 + + +# Algorithm Identifiers for Ed25519, Ed448, X25519, and X448 +# for Use in the Internet X.509 Public Key Infrastructure + +# Abstract + +# This document specifies algorithm identifiers and ASN.1 encoding +# formats for elliptic curve constructs using the curve25519 and +# curve448 curves. The signature algorithms covered are Ed25519 and +# Ed448. The key agreement algorithms covered are X25519 and X448. +# The encoding for public key, private key, and Edwards-curve Digital +# Signature Algorithm (EdDSA) structures is provided. + +# Status of This Memo + +# This is an Internet Standards Track document. + +# This document is a product of the Internet Engineering Task Force +# (IETF). It represents the consensus of the IETF community. It has +# received public review and has been approved for publication by the +# Internet Engineering Steering Group (IESG). Further information on +# Internet Standards is available in Section 2 of RFC 7841. + +# Information about the current status of this document, any errata, +# and how to provide feedback on it may be obtained at +# https://www.rfc-editor.org/info/rfc8410. + +# Copyright Notice + +# Copyright (c) 2018 IETF Trust and the persons identified as the +# document authors. All rights reserved. + +# This document is subject to BCP 78 and the IETF Trust's Legal +# Provisions Relating to IETF Documents +# (https://trustee.ietf.org/license-info) in effect on the date of +# publication of this document. Please review these documents +# carefully, as they describe your rights and restrictions with respect +# to this document. Code Components extracted from this document must +# include Simplified BSD License text as described in Section 4.e of +# the Trust Legal Provisions and are provided without warranty as +# described in the Simplified BSD License. + + + + +# Josefsson & Schaad Standards Track [Page 1] + +# RFC 8410 Safe Curves for X.509 August 2018 + + +# Table of Contents + +# 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 2 +# 2. Requirements Terminology . . . . . . . . . . . . . . . . . . 3 +# 3. Curve25519 and Curve448 Algorithm Identifiers . . . . . . . . 3 +# 4. Subject Public Key Fields . . . . . . . . . . . . . . . . . . 4 +# 5. Key Usage Bits . . . . . . . . . . . . . . . . . . . . . . . 5 +# 6. EdDSA Signatures . . . . . . . . . . . . . . . . . . . . . . 6 +# 7. Private Key Format . . . . . . . . . . . . . . . . . . . . . 7 +# 8. Human-Readable Algorithm Names . . . . . . . . . . . . . . . 8 +# 9. ASN.1 Module . . . . . . . . . . . . . . . . . . . . . . . . 9 +# 10. Examples . . . . . . . . . . . . . . . . . . . . . . . . . . 11 +# 10.1. Example Ed25519 Public Key . . . . . . . . . . . . . . . 11 +# 10.2. Example X25519 Certificate . . . . . . . . . . . . . . . 12 +# 10.3. Examples of Ed25519 Private Key . . . . . . . . . . . . 14 +# 11. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 15 +# 12. Security Considerations . . . . . . . . . . . . . . . . . . . 15 +# 13. References . . . . . . . . . . . . . . . . . . . . . . . . . 16 +# 13.1. Normative References . . . . . . . . . . . . . . . . . . 16 +# 13.2. Informative References . . . . . . . . . . . . . . . . . 16 +# Appendix A. Invalid Encodings . . . . . . . . . . . . . . . . . 18 +# Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . . . 19 +# Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 20 + +# 1. Introduction + +# In [RFC7748], the elliptic curves curve25519 and curve448 are +# described. They are designed with performance and security in mind. +# The curves may be used for Diffie-Hellman and digital signature +# operations. + +# [RFC7748] describes the operations on these curves for the Diffie- +# Hellman operation. A convention has developed that when these two +# curves are used with the Diffie-Hellman operation, they are referred +# to as X25519 and X448. This RFC defines the ASN.1 Object Identifiers +# (OIDs) for the operations X25519 and X448 along with the associated +# parameters. The use of these OIDs is described for public and +# private keys. + +# In [RFC8032] the elliptic curve signature system Edwards-curve +# Digital Signature Algorithm (EdDSA) is described along with a +# recommendation for the use of the curve25519 and curve448. EdDSA has +# defined two modes: the PureEdDSA mode without prehashing and the +# HashEdDSA mode with prehashing. The convention used for identifying +# the algorithm/curve combinations is to use "Ed25519" and "Ed448" for +# the PureEdDSA mode. This document does not provide the conventions + + + + + +# Josefsson & Schaad Standards Track [Page 2] + +# RFC 8410 Safe Curves for X.509 August 2018 + + +# needed for the prehash versions of the signature algorithm. The use +# of the OIDs is described for public keys, private keys and +# signatures. + +# [RFC8032] additionally defines the concept of a context. Contexts +# can be used to differentiate signatures generated for different +# purposes with the same key. The use of contexts is not defined in +# this document for the following reasons: + +# o The current implementations of Ed25519 do not support the use of +# contexts; thus, if specified, it will potentially delay the use of +# these algorithms further. + +# o EdDSA is the only IETF algorithm that currently supports the use +# of contexts; however, there is a possibility that there will be +# confusion between which algorithms need to have separate keys and +# which do not. This may result in a decrease of security for those +# other algorithms. + +# o There are still ongoing discussions among the cryptographic +# community about how effective the use of contexts is for +# preventing attacks. + +# o There needs to be discussions about the correct way to identify +# when context strings are to be used. It is not clear if different +# OIDs should be used for different contexts or the OID should +# merely note that a context string needs to be provided. + +# 2. Requirements Terminology + +# The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", +# "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and +# "OPTIONAL" in this document are to be interpreted as described in +# BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all +# capitals, as shown here. + +# 3. Curve25519 and Curve448 Algorithm Identifiers + +# Certificates conforming to [RFC5280] can convey a public key for any +# public key algorithm. The certificate indicates the algorithm +# through an algorithm identifier. An algorithm identifier consists of +# an OID and optional parameters. + + + + + + + + + +# Josefsson & Schaad Standards Track [Page 3] + +# RFC 8410 Safe Curves for X.509 August 2018 + + +# The AlgorithmIdentifier type, which is included for convenience, is +# defined as follows: + +# AlgorithmIdentifier ::= SEQUENCE { +# algorithm OBJECT IDENTIFIER, +# parameters ANY DEFINED BY algorithm OPTIONAL +# } + +# The fields in AlgorithmIdentifier have the following meanings: + +# o algorithm identifies the cryptographic algorithm with an object +# identifier. Four such OIDs are defined below. + +# o parameters, which are optional, are the associated parameters for +# the algorithm identifier in the algorithm field. + +# In this document, we define four new OIDs for identifying the +# different curve/algorithm pairs: the curves being curve25519 and +# curve448 and the algorithms being ECDH and EdDSA in pure mode. For +# all of the OIDs, the parameters MUST be absent. + +# It is possible to find systems that require the parameters to be +# present. This can be due to either a defect in the original 1997 +# syntax or a programming error where developers never got input where +# this was not true. The optimal solution is to fix these systems; +# where this is not possible, the problem needs to be restricted to +# that subsystem and not propagated to the Internet. + +# The same algorithm identifiers are used for identifying a public key, +# a private key, and a signature (for the two EdDSA related OIDs). +# Additional encoding information is provided below for each of these +# locations. + +# id-X25519 OBJECT IDENTIFIER ::= { 1 3 101 110 } +# id-X448 OBJECT IDENTIFIER ::= { 1 3 101 111 } +# id-Ed25519 OBJECT IDENTIFIER ::= { 1 3 101 112 } +# id-Ed448 OBJECT IDENTIFIER ::= { 1 3 101 113 } + +# 4. Subject Public Key Fields + +# In the X.509 certificate, the subjectPublicKeyInfo field has the +# SubjectPublicKeyInfo type, which has the following ASN.1 syntax: + +# SubjectPublicKeyInfo ::= SEQUENCE { +# algorithm AlgorithmIdentifier, +# subjectPublicKey BIT STRING +# } + + + + +# Josefsson & Schaad Standards Track [Page 4] + +# RFC 8410 Safe Curves for X.509 August 2018 + + +# The fields in SubjectPublicKeyInfo have the following meanings: + +# o algorithm is the algorithm identifier and parameters for the +# public key (see above). + +# o subjectPublicKey contains the byte stream of the public key. The +# algorithms defined in this document always encode the public key +# as an exact multiple of 8 bits. + +# Both [RFC7748] and [RFC8032] define the public key value as being a +# byte string. It should be noted that the public key is computed +# differently for each of these documents; thus, the same private key +# will not produce the same public key. + +# The following is an example of a public key encoded using the textual +# encoding defined in [RFC7468]. + +# -----BEGIN PUBLIC KEY----- +# MCowBQYDK2VwAyEAGb9ECWmEzf6FQbrBZ9w7lshQhqowtrbLDFw4rXAxZuE= +# -----END PUBLIC KEY----- + +# 5. Key Usage Bits + +# The intended application for the key is indicated in the keyUsage +# certificate extension. + +# If the keyUsage extension is present in a certificate that indicates +# id-X25519 or id-X448 in SubjectPublicKeyInfo, then the following MUST +# be present: + +# keyAgreement; + +# one of the following MAY also be present: + +# encipherOnly; or +# decipherOnly. + +# If the keyUsage extension is present in an end-entity certificate +# that indicates id-Ed25519 or id-Ed448, then the keyUsage extension +# MUST contain one or both of the following values: + +# nonRepudiation; and +# digitalSignature. + + + + + + + + +# Josefsson & Schaad Standards Track [Page 5] + +# RFC 8410 Safe Curves for X.509 August 2018 + + +# If the keyUsage extension is present in a certification authority +# certificate that indicates id-Ed25519 or id-Ed448, then the keyUsage +# extension MUST contain one or more of the following values: + +# nonRepudiation; +# digitalSignature; +# keyCertSign; and +# cRLSign. + +# 6. EdDSA Signatures + +# Signatures can be placed in a number of different ASN.1 structures. +# The top level structure for a certificate is given below as being +# illustrative of how signatures are frequently encoded with an +# algorithm identifier and a location for the signature. + +# Certificate ::= SEQUENCE { +# tbsCertificate TBSCertificate, +# signatureAlgorithm AlgorithmIdentifier, +# signatureValue BIT STRING } + +# The same algorithm identifiers are used for signatures as are used +# for public keys. When used to identify signature algorithms, the +# parameters MUST be absent. + +# The data to be signed is prepared for EdDSA. Then, a private key +# operation is performed to generate the signature value. This value +# is the opaque value ENC(R) || ENC(S) described in Section 3.3 of +# [RFC8032]. The octet string representing the signature is encoded +# directly in the BIT STRING without adding any additional ASN.1 +# wrapping. For the Certificate structure, the signature value is +# wrapped in the "signatureValue" BIT STRING field. + + + + + + + + + + + + + + + + + + + +# Josefsson & Schaad Standards Track [Page 6] + +# RFC 8410 Safe Curves for X.509 August 2018 + + +# 7. Private Key Format + +# "Asymmetric Key Packages" [RFC5958] describes how to encode a private +# key in a structure that both identifies what algorithm the private +# key is for and allows for the public key and additional attributes +# about the key to be included as well. For illustration, the ASN.1 +# structure OneAsymmetricKey is replicated below. The algorithm- +# specific details of how a private key is encoded are left for the +# document describing the algorithm itself. + +# OneAsymmetricKey ::= SEQUENCE { +# version Version, +# privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, +# privateKey PrivateKey, +# attributes [0] IMPLICIT Attributes OPTIONAL, +# ..., +# [[2: publicKey [1] IMPLICIT PublicKey OPTIONAL ]], +# ... +# } + +# PrivateKey ::= OCTET STRING + +# PublicKey ::= BIT STRING + +# For the keys defined in this document, the private key is always an +# opaque byte sequence. The ASN.1 type CurvePrivateKey is defined in +# this document to hold the byte sequence. Thus, when encoding a +# OneAsymmetricKey object, the private key is wrapped in a +# CurvePrivateKey object and wrapped by the OCTET STRING of the +# "privateKey" field. + +# CurvePrivateKey ::= OCTET STRING + +# To encode an EdDSA, X25519, or X448 private key, the "privateKey" +# field will hold the encoded private key. The "privateKeyAlgorithm" +# field uses the AlgorithmIdentifier structure. The structure is +# encoded as defined above. If present, the "publicKey" field will +# hold the encoded key as defined in [RFC7748] and [RFC8032]. + + + + + + + + + + + + + +# Josefsson & Schaad Standards Track [Page 7] + +# RFC 8410 Safe Curves for X.509 August 2018 + + +# The following is an example of a private key encoded using the +# textual encoding defined in [RFC7468]. + +# -----BEGIN PRIVATE KEY----- +# MC4CAQAwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC +# -----END PRIVATE KEY----- + +# The following example, in addition to encoding the private key, has +# an attribute included as well as the public key. As with the prior +# example, the textual encoding defined in [RFC7468] is used. + +# -----BEGIN PRIVATE KEY----- +# MHICAQEwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC +# oB8wHQYKKoZIhvcNAQkJFDEPDA1DdXJkbGUgQ2hhaXJzgSEAGb9ECWmEzf6FQbrB +# Z9w7lshQhqowtrbLDFw4rXAxZuE= +# -----END PRIVATE KEY------ + +# NOTE: There exist some private key import functions that have not +# picked up the new ASN.1 structure OneAsymmetricKey that is defined in +# [RFC7748]. This means that they will not accept a private key +# structure that contains the public key field. This means a balancing +# act needs to be done between being able to do a consistency check on +# the key pair and widest ability to import the key. + +# 8. Human-Readable Algorithm Names + +# For the purpose of consistent cross-implementation naming, this +# section establishes human-readable names for the algorithms specified +# in this document. Implementations SHOULD use these names when +# referring to the algorithms. If there is a strong reason to deviate +# from these names -- for example, if the implementation has a +# different naming convention and wants to maintain internal +# consistency -- it is encouraged to deviate as little as possible from +# the names given here. + +# Use the string "ECDH" when referring to a public key of type "X25519" +# or "X448" when the curve is not known or relevant. + +# When the curve is known, use the more specific string of "X25519" or +# "X448". + +# Use the string "EdDSA" when referring to a signing public key or +# signature when the curve is not known or relevant. + +# When the curve is known, use a more specific string. For the id- +# Ed25519 value use the string "Ed25519". For id-Ed448, use "Ed448". + + + + + +# Josefsson & Schaad Standards Track [Page 8] + +# RFC 8410 Safe Curves for X.509 August 2018 + + +# 9. ASN.1 Module + +# For reference purposes, the ASN.1 syntax is presented as an ASN.1 +# module here. + +# -- ASN.1 Module + +# Safecurves-pkix-18 +# { iso(1) identified-organization(3) dod(6) internet(1) +# security(5) mechanisms(5) pkix(7) id-mod(0) +# id-mod-safecurves-pkix(93) } + +# DEFINITIONS EXPLICIT TAGS ::= +# BEGIN + +# IMPORTS +# SIGNATURE-ALGORITHM, KEY-AGREE, PUBLIC-KEY, KEY-WRAP, +# KeyUsage, AlgorithmIdentifier +# FROM AlgorithmInformation-2009 +# {iso(1) identified-organization(3) dod(6) internet(1) security(5) +# mechanisms(5) pkix(7) id-mod(0) +# id-mod-algorithmInformation-02(58)} + +# mda-sha512 +# FROM PKIX1-PSS-OAEP-Algorithms-2009 +# { iso(1) identified-organization(3) dod(6) internet(1) +# security(5) mechanisms(5) pkix(7) id-mod(0) +# id-mod-pkix1-rsa-pkalgs-02(54) } + +# kwa-aes128-wrap, kwa-aes256-wrap +# FROM CMSAesRsaesOaep-2009 +# { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-9(9) +# smime(16) modules(0) id-mod-cms-aes-02(38) } +# ; + + +# id-edwards-curve-algs OBJECT IDENTIFIER ::= { 1 3 101 } + +# id-X25519 OBJECT IDENTIFIER ::= { id-edwards-curve-algs 110 } +# id-X448 OBJECT IDENTIFIER ::= { id-edwards-curve-algs 111 } +# id-Ed25519 OBJECT IDENTIFIER ::= { id-edwards-curve-algs 112 } +# id-Ed448 OBJECT IDENTIFIER ::= { id-edwards-curve-algs 113 } + + + + + + + + + +# Josefsson & Schaad Standards Track [Page 9] + +# RFC 8410 Safe Curves for X.509 August 2018 + + +# sa-Ed25519 SIGNATURE-ALGORITHM ::= { +# IDENTIFIER id-Ed25519 +# PARAMS ARE absent +# PUBLIC-KEYS {pk-Ed25519} +# SMIME-CAPS { IDENTIFIED BY id-Ed25519 } +# } + +# pk-Ed25519 PUBLIC-KEY ::= { +# IDENTIFIER id-Ed25519 +# -- KEY no ASN.1 wrapping -- +# PARAMS ARE absent +# CERT-KEY-USAGE {digitalSignature, nonRepudiation, +# keyCertSign, cRLSign} +# PRIVATE-KEY CurvePrivateKey +# } + +# kaa-X25519 KEY-AGREE ::= { +# IDENTIFIER id-X25519 +# PARAMS ARE absent +# PUBLIC-KEYS {pk-X25519} +# UKM -- TYPE no ASN.1 wrapping -- ARE preferredPresent +# SMIME-CAPS { +# TYPE AlgorithmIdentifier{KEY-WRAP, {KeyWrapAlgorithms}} +# IDENTIFIED BY id-X25519 } +# } + +# pk-X25519 PUBLIC-KEY ::= { +# IDENTIFIER id-X25519 +# -- KEY no ASN.1 wrapping -- +# PARAMS ARE absent +# CERT-KEY-USAGE { keyAgreement } +# PRIVATE-KEY CurvePrivateKey +# } + +# KeyWrapAlgorithms KEY-WRAP ::= { +# kwa-aes128-wrap | kwa-aes256-wrap, +# ... +# } + +# kaa-X448 KEY-AGREE ::= { +# IDENTIFIER id-X448 +# PARAMS ARE absent +# PUBLIC-KEYS {pk-X448} +# UKM -- TYPE no ASN.1 wrapping -- ARE preferredPresent +# SMIME-CAPS { +# TYPE AlgorithmIdentifier{KEY-WRAP, {KeyWrapAlgorithms}} +# IDENTIFIED BY id-X448 } +# } + + + +# Josefsson & Schaad Standards Track [Page 10] + +# RFC 8410 Safe Curves for X.509 August 2018 + + +# pk-X448 PUBLIC-KEY ::= { +# IDENTIFIER id-X448 +# -- KEY no ASN.1 wrapping -- +# PARAMS ARE absent +# CERT-KEY-USAGE { keyAgreement } +# PRIVATE-KEY CurvePrivateKey +# } + +# CurvePrivateKey ::= OCTET STRING + + +# END + +# 10. Examples + +# This section contains illustrations of EdDSA public keys and +# certificates, illustrating parameter choices. + +# 10.1. Example Ed25519 Public Key + +# An example of an Ed25519 public key: + +# Public Key Information: +# Public Key Algorithm: Ed25519 +# Algorithm Security Level: High + +# Public Key Usage: + +# Public Key ID: 9b1f5eeded043385e4f7bc623c5975b90bc8bb3b + +# -----BEGIN PUBLIC KEY----- +# MCowBQYDK2VwAyEAGb9ECWmEzf6FQbrBZ9w7lshQhqowtrbLDFw4rXAxZuE= +# -----END PUBLIC KEY----- + + + + + + + + + + + + + + + + + + +# Josefsson & Schaad Standards Track [Page 11] + +# RFC 8410 Safe Curves for X.509 August 2018 + + +# 10.2. Example X25519 Certificate + +# An example of a self-issued PKIX certificate using Ed25519 to sign an +# X25519 public key would be: + +# 0 300: SEQUENCE { +# 4 223: SEQUENCE { +# 7 3: [0] { +# 9 1: INTEGER 2 +# : } +# 12 8: INTEGER 56 01 47 4A 2A 8D C3 30 +# 22 5: SEQUENCE { +# 24 3: OBJECT IDENTIFIER +# : Ed 25519 signature algorithm { 1 3 101 112 } +# : } +# 29 25: SEQUENCE { +# 31 23: SET { +# 33 21: SEQUENCE { +# 35 3: OBJECT IDENTIFIER commonName (2 5 4 3) +# 40 14: UTF8String 'IETF Test Demo' +# : } +# : } +# : } +# 56 30: SEQUENCE { +# 58 13: UTCTime 01/08/2016 12:19:24 GMT +# 73 13: UTCTime 31/12/2040 23:59:59 GMT +# : } +# 88 25: SEQUENCE { +# 90 23: SET { +# 92 21: SEQUENCE { +# 94 3: OBJECT IDENTIFIER commonName (2 5 4 3) +# 99 14: UTF8String 'IETF Test Demo' +# : } +# : } +# : } +# 115 42: SEQUENCE { +# 117 5: SEQUENCE { +# 119 3: OBJECT IDENTIFIER +# : ECDH 25519 key agreement { 1 3 101 110 } +# : } +# 124 33: BIT STRING +# : 85 20 F0 09 89 30 A7 54 74 8B 7D DC B4 3E F7 5A +# : 0D BF 3A 0D 26 38 1A F4 EB A4 A9 8E AA 9B 4E 6A +# : } +# 159 69: [3] { +# 161 67: SEQUENCE { +# 163 15: SEQUENCE { +# 165 3: OBJECT IDENTIFIER basicConstraints (2 5 29 19) + + + +# Josefsson & Schaad Standards Track [Page 12] + +# RFC 8410 Safe Curves for X.509 August 2018 + + +# 170 1: BOOLEAN TRUE +# 173 5: OCTET STRING, encapsulates { +# 175 3: SEQUENCE { +# 177 1: BOOLEAN FALSE +# : } +# : } +# : } +# 180 14: SEQUENCE { +# 182 3: OBJECT IDENTIFIER keyUsage (2 5 29 15) +# 187 1: BOOLEAN FALSE +# 190 4: OCTET STRING, encapsulates { +# 192 2: BIT STRING 3 unused bits +# : '10000'B (bit 4) +# : } +# : } +# 196 32: SEQUENCE { +# 198 3: OBJECT IDENTIFIER subjectKeyIdentifier (2 5 29 14) +# 203 1: BOOLEAN FALSE +# 206 22: OCTET STRING, encapsulates { +# 208 20: OCTET STRING +# : 9B 1F 5E ED ED 04 33 85 E4 F7 BC 62 3C 59 75 +# : B9 0B C8 BB 3B +# : } +# : } +# : } +# : } +# : } +# 230 5: SEQUENCE { +# 232 3: OBJECT IDENTIFIER +# : Ed 25519 signature algorithm { 1 3 101 112 } +# : } +# 237 65: BIT STRING +# : AF 23 01 FE DD C9 E6 FF C1 CC A7 3D 74 D6 48 A4 +# : 39 80 82 CD DB 69 B1 4E 4D 06 EC F8 1A 25 CE 50 +# : D4 C2 C3 EB 74 6C 4E DD 83 46 85 6E C8 6F 3D CE +# : 1A 18 65 C5 7A C2 7B 50 A0 C3 50 07 F5 E7 D9 07 +# : } + +# -----BEGIN CERTIFICATE----- +# MIIBLDCB36ADAgECAghWAUdKKo3DMDAFBgMrZXAwGTEXMBUGA1UEAwwOSUVURiBUZX +# N0IERlbW8wHhcNMTYwODAxMTIxOTI0WhcNNDAxMjMxMjM1OTU5WjAZMRcwFQYDVQQD +# DA5JRVRGIFRlc3QgRGVtbzAqMAUGAytlbgMhAIUg8AmJMKdUdIt93LQ+91oNvzoNJj +# ga9OukqY6qm05qo0UwQzAPBgNVHRMBAf8EBTADAQEAMA4GA1UdDwEBAAQEAwIDCDAg +# BgNVHQ4BAQAEFgQUmx9e7e0EM4Xk97xiPFl1uQvIuzswBQYDK2VwA0EAryMB/t3J5v +# /BzKc9dNZIpDmAgs3babFOTQbs+BolzlDUwsPrdGxO3YNGhW7Ibz3OGhhlxXrCe1Cg +# w1AH9efZBw== +# -----END CERTIFICATE----- + + + + +# Josefsson & Schaad Standards Track [Page 13] + +# RFC 8410 Safe Curves for X.509 August 2018 + + +# 10.3. Examples of Ed25519 Private Key + +# An example of an Ed25519 private key without the public key: + +# -----BEGIN PRIVATE KEY----- +# MC4CAQAwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC +# -----END PRIVATE KEY----- + +# The same item dumped as ASN.1 yields: + +# 0 30 46: SEQUENCE { +# 2 02 1: INTEGER 0 +# 5 30 5: SEQUENCE { +# 7 06 3: OBJECT IDENTIFIER +# : Ed 25519 signature algorithm { 1 3 101 112 } +# : } +# 12 04 34: OCTET STRING +# : 04 20 D4 EE 72 DB F9 13 58 4A D5 B6 D8 F1 F7 69 +# : F8 AD 3A FE 7C 28 CB F1 D4 FB E0 97 A8 8F 44 75 +# : 58 42 +# : } + +# Note that the value of the private key is: + +# D4 EE 72 DB F9 13 58 4A D5 B6 D8 F1 F7 69 F8 AD +# 3A FE 7C 28 CB F1 D4 FB E0 97 A8 8F 44 75 58 42 + +# An example of the same Ed25519 private key encoded with an attribute +# and the public key: + +# -----BEGIN PRIVATE KEY----- +# MHICAQEwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC +# oB8wHQYKKoZIhvcNAQkJFDEPDA1DdXJkbGUgQ2hhaXJzgSEAGb9ECWmEzf6FQbrB +# Z9w7lshQhqowtrbLDFw4rXAxZuE= +# -----END PRIVATE KEY----- + + + + + + + + + + + + + + + + +# Josefsson & Schaad Standards Track [Page 14] + +# RFC 8410 Safe Curves for X.509 August 2018 + + +# The same item dumped as ASN.1 yields: + +# 0 114: SEQUENCE { +# 2 1: INTEGER 1 +# 5 5: SEQUENCE { +# 7 3: OBJECT IDENTIFIER '1 3 101 112' +# : } +# 12 34: OCTET STRING, encapsulates { +# : 04 20 D4 EE 72 DB F9 13 58 4A D5 B6 D8 F1 F7 69 +# : F8 AD 3A FE 7C 28 CB F1 D4 FB E0 97 A8 8F 44 75 +# : 58 42 +# : } +# 48 31: [0] { +# 50 29: SEQUENCE { +# 52 10: OBJECT IDENTIFIER '1 2 840 113549 1 9 9 20' +# 64 15: SET { +# 66 13: UTF8String 'Curdle Chairs' +# : } +# : } +# : } +# 81 33: [1] 00 19 BF 44 09 69 84 CD FE 85 41 BA C1 67 DC 3B +# 96 C8 50 86 AA 30 B6 B6 CB 0C 5C 38 AD 70 31 66 +# E1 +# : } + +# 11. IANA Considerations + +# For the ASN.1 module in Section 9, IANA has registered value 93 for +# "id-mod-safecurves-pkix" in the "SMI Security for PKIX Module +# Identifier" (1.3.6.1.5.5.7.0) registry. + +# The OIDs are being independently registered in the IANA registry "SMI +# Security for Cryptographic Algorithms" in [RFC8411]. + +# 12. Security Considerations + +# The security considerations of [RFC5280], [RFC7748], and [RFC8032] +# apply accordingly. + +# The procedures for going from a private key to a public key are +# different when used with Diffie-Hellman versus when used with Edwards +# Signatures. This means that the same public key cannot be used for +# both ECDH and EdDSA. + + + + + + + + +# Josefsson & Schaad Standards Track [Page 15] + +# RFC 8410 Safe Curves for X.509 August 2018 + + +# 13. References + +# 13.1. Normative References + +# [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate +# Requirement Levels", BCP 14, RFC 2119, +# DOI 10.17487/RFC2119, March 1997, +# . + +# [RFC5280] Cooper, D., Santesson, S., Farrell, S., Boeyen, S., +# Housley, R., and W. Polk, "Internet X.509 Public Key +# Infrastructure Certificate and Certificate Revocation List +# (CRL) Profile", RFC 5280, DOI 10.17487/RFC5280, May 2008, +# . + +# [RFC5480] Turner, S., Brown, D., Yiu, K., Housley, R., and T. Polk, +# "Elliptic Curve Cryptography Subject Public Key +# Information", RFC 5480, DOI 10.17487/RFC5480, March 2009, +# . + +# [RFC5958] Turner, S., "Asymmetric Key Packages", RFC 5958, +# DOI 10.17487/RFC5958, August 2010, +# . + +# [RFC7748] Langley, A., Hamburg, M., and S. Turner, "Elliptic Curves +# for Security", RFC 7748, DOI 10.17487/RFC7748, January +# 2016, . + +# [RFC8032] Josefsson, S. and I. Liusvaara, "Edwards-Curve Digital +# Signature Algorithm (EdDSA)", RFC 8032, +# DOI 10.17487/RFC8032, January 2017, +# . + +# [RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC +# 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, +# May 2017, . + +# 13.2. Informative References + +# [RFC3279] Bassham, L., Polk, W., and R. Housley, "Algorithms and +# Identifiers for the Internet X.509 Public Key +# Infrastructure Certificate and Certificate Revocation List +# (CRL) Profile", RFC 3279, DOI 10.17487/RFC3279, April +# 2002, . + + + + + + + +# Josefsson & Schaad Standards Track [Page 16] + +# RFC 8410 Safe Curves for X.509 August 2018 + + +# [RFC4055] Schaad, J., Kaliski, B., and R. Housley, "Additional +# Algorithms and Identifiers for RSA Cryptography for use in +# the Internet X.509 Public Key Infrastructure Certificate +# and Certificate Revocation List (CRL) Profile", RFC 4055, +# DOI 10.17487/RFC4055, June 2005, +# . + +# [RFC5639] Lochter, M. and J. Merkle, "Elliptic Curve Cryptography +# (ECC) Brainpool Standard Curves and Curve Generation", +# RFC 5639, DOI 10.17487/RFC5639, March 2010, +# . + +# [RFC7468] Josefsson, S. and S. Leonard, "Textual Encodings of PKIX, +# PKCS, and CMS Structures", RFC 7468, DOI 10.17487/RFC7468, +# April 2015, . + +# [RFC8411] Schaad, J. and R. Andrews, "IANA Registration for the +# Cryptographic Algorithm Object Identifier Range", +# RFC 8411, DOI 10.17487/RFC8411, August 2018, +# . + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +# Josefsson & Schaad Standards Track [Page 17] + +# RFC 8410 Safe Curves for X.509 August 2018 + + +# Appendix A. Invalid Encodings + +# There are a number of things that need to be dealt with when a new +# key part is decoded and imported into the system. A partial list of +# these includes: + +# o ASN.1 encoding errors: Two items are highlighted here. First, the +# use of an OCTET STRING rather than a BIT STRING for the public +# key. The use of OCTET STRING was a copy error that existed in a +# previous draft version of this document; the structure is correct +# in [RFC5958]. However, any early implementation may have this +# wrong. Second, the value of the version field is required to be 0 +# if the publicKey is absent and 1 if present. This is called out +# in [RFC5958], but was not duplicated above. + +# o Key encoding errors: Both [RFC7748] and [RFC8032] have formatting +# requirements for keys that need to be enforced. In some cases, +# the enforcement is done at the time of importing, for example, +# doing masking or a mod p operation. In other cases, the +# enforcement is done by rejecting the keys and having an import +# failure. + +# o Key mismatch errors: If a public key is provided, it may not agree +# with the private key because either it is wrong or the wrong +# algorithm was used. + +# Some systems are also going to be stricter on what they accept. As +# stated in [RFC5958], BER decoding of OneAsymmetricKey objects is a +# requirement for compliance. Despite this requirement, some acceptors +# will only decode DER formats. The following is a BER encoding of a +# private key; it is valid, but it may not be accepted by many systems. + +# -----BEGIN PRIVATE KEY----- +# MIACAQAwgAYDK2VwAAAEIgQg1O5y2/kTWErVttjx92n4rTr+fCjL8dT74Jeoj0R1W +# EIAAA== +# -----END PRIVATE KEY----- + +# What follows here is a brief sampling of some incorrect keys. + +# In the following example, the private key does not match the masking +# requirements for X25519. For this example, the top bits are set to +# zero and the bottom three bits are set to 001. + +# -----BEGIN PRIVATE KEY----- +# MFMCAQEwBQYDK2VuBCIEIPj///////////////////////////////////////8/oS +# MDIQCEfA0sN1I082XmYJVRh6NzWg92E9FgnTpqTYxTrqpaIg== +# -----END PRIVATE KEY----- + + + + +# Josefsson & Schaad Standards Track [Page 18] + +# RFC 8410 Safe Curves for X.509 August 2018 + + +# In the following examples, the key is the wrong length because an +# all-zero byte has been removed. In one case, the first byte has been +# removed; in the other case, the last byte has been removed. + +# -----BEGIN PRIVATE KEY----- +# MFICAQEwBQYDK2VwBCIEIC3GfeUYbZGTAhwLEE2cbvJL7ivTlcy17VottfN6L8HwoS +# IDIADBfk2Lv/J8H7YYwj/OmIcDx++jzVkKrKwS0/HjyQyM +# -----END PRIVATE KEY----- + +# -----BEGIN PRIVATE KEY----- +# MFICAQEwBQYDK2VwBCIEILJXn1VaLqvausjUaZexwI/ozmOFjfEk78KcYN+7hsNJoS +# IDIACdQhJwzi/MCGcsQeQnIUh2JFybDxSrZxuLudJmpJLk +# -----END PRIVATE KEY----- + +# Acknowledgments + +# Text and/or inspiration were drawn from [RFC5280], [RFC3279], +# [RFC4055], [RFC5480], and [RFC5639]. + +# The following people discussed the document and provided feedback: +# Klaus Hartke, Ilari Liusvaara, Erwann Abalea, Rick Andrews, Rob +# Stradling, James Manger, Nikos Mavrogiannopoulos, Russ Housley, David +# Benjamin, Brian Smith, and Alex Wilson. + +# A big thank you to Symantec for kindly donating the OIDs used in this +# document. + + + + + + + + + + + + + + + + + + + + + + + + + +# Josefsson & Schaad Standards Track [Page 19] + +# RFC 8410 Safe Curves for X.509 August 2018 + + +# Authors' Addresses + +# Simon Josefsson +# SJD AB + +# Email: simon@josefsson.org + + +# Jim Schaad +# August Cellars + +# Email: ietf@augustcellars.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +# Josefsson & Schaad Standards Track [Page 20] + + + + + + + +# Internet Engineering Task Force (IETF) S. Josefsson +# Request for Comments: 8410 SJD AB +# Category: Standards Track J. Schaad +# ISSN: 2070-1721 August Cellars +# August 2018 + + +# Algorithm Identifiers for Ed25519, Ed448, X25519, and X448 +# for Use in the Internet X.509 Public Key Infrastructure + +# Abstract + +# This document specifies algorithm identifiers and ASN.1 encoding +# formats for elliptic curve constructs using the curve25519 and +# curve448 curves. The signature algorithms covered are Ed25519 and +# Ed448. The key agreement algorithms covered are X25519 and X448. +# The encoding for public key, private key, and Edwards-curve Digital +# Signature Algorithm (EdDSA) structures is provided. + +# Status of This Memo + +# This is an Internet Standards Track document. + +# This document is a product of the Internet Engineering Task Force +# (IETF). It represents the consensus of the IETF community. It has +# received public review and has been approved for publication by the +# Internet Engineering Steering Group (IESG). Further information on +# Internet Standards is available in Section 2 of RFC 7841. + +# Information about the current status of this document, any errata, +# and how to provide feedback on it may be obtained at +# https://www.rfc-editor.org/info/rfc8410. + +# Copyright Notice + +# Copyright (c) 2018 IETF Trust and the persons identified as the +# document authors. All rights reserved. + +# This document is subject to BCP 78 and the IETF Trust's Legal +# Provisions Relating to IETF Documents +# (https://trustee.ietf.org/license-info) in effect on the date of +# publication of this document. Please review these documents +# carefully, as they describe your rights and restrictions with respect +# to this document. Code Components extracted from this document must +# include Simplified BSD License text as described in Section 4.e of +# the Trust Legal Provisions and are provided without warranty as +# described in the Simplified BSD License. + + + + +# Josefsson & Schaad Standards Track [Page 1] + +# RFC 8410 Safe Curves for X.509 August 2018 + + +# Table of Contents + +# 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 2 +# 2. Requirements Terminology . . . . . . . . . . . . . . . . . . 3 +# 3. Curve25519 and Curve448 Algorithm Identifiers . . . . . . . . 3 +# 4. Subject Public Key Fields . . . . . . . . . . . . . . . . . . 4 +# 5. Key Usage Bits . . . . . . . . . . . . . . . . . . . . . . . 5 +# 6. EdDSA Signatures . . . . . . . . . . . . . . . . . . . . . . 6 +# 7. Private Key Format . . . . . . . . . . . . . . . . . . . . . 7 +# 8. Human-Readable Algorithm Names . . . . . . . . . . . . . . . 8 +# 9. ASN.1 Module . . . . . . . . . . . . . . . . . . . . . . . . 9 +# 10. Examples . . . . . . . . . . . . . . . . . . . . . . . . . . 11 +# 10.1. Example Ed25519 Public Key . . . . . . . . . . . . . . . 11 +# 10.2. Example X25519 Certificate . . . . . . . . . . . . . . . 12 +# 10.3. Examples of Ed25519 Private Key . . . . . . . . . . . . 14 +# 11. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 15 +# 12. Security Considerations . . . . . . . . . . . . . . . . . . . 15 +# 13. References . . . . . . . . . . . . . . . . . . . . . . . . . 16 +# 13.1. Normative References . . . . . . . . . . . . . . . . . . 16 +# 13.2. Informative References . . . . . . . . . . . . . . . . . 16 +# Appendix A. Invalid Encodings . . . . . . . . . . . . . . . . . 18 +# Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . . . 19 +# Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 20 + +# 1. Introduction + +# In [RFC7748], the elliptic curves curve25519 and curve448 are +# described. They are designed with performance and security in mind. +# The curves may be used for Diffie-Hellman and digital signature +# operations. + +# [RFC7748] describes the operations on these curves for the Diffie- +# Hellman operation. A convention has developed that when these two +# curves are used with the Diffie-Hellman operation, they are referred +# to as X25519 and X448. This RFC defines the ASN.1 Object Identifiers +# (OIDs) for the operations X25519 and X448 along with the associated +# parameters. The use of these OIDs is described for public and +# private keys. + +# In [RFC8032] the elliptic curve signature system Edwards-curve +# Digital Signature Algorithm (EdDSA) is described along with a +# recommendation for the use of the curve25519 and curve448. EdDSA has +# defined two modes: the PureEdDSA mode without prehashing and the +# HashEdDSA mode with prehashing. The convention used for identifying +# the algorithm/curve combinations is to use "Ed25519" and "Ed448" for +# the PureEdDSA mode. This document does not provide the conventions + + + + + +# Josefsson & Schaad Standards Track [Page 2] + +# RFC 8410 Safe Curves for X.509 August 2018 + + +# needed for the prehash versions of the signature algorithm. The use +# of the OIDs is described for public keys, private keys and +# signatures. + +# [RFC8032] additionally defines the concept of a context. Contexts +# can be used to differentiate signatures generated for different +# purposes with the same key. The use of contexts is not defined in +# this document for the following reasons: + +# o The current implementations of Ed25519 do not support the use of +# contexts; thus, if specified, it will potentially delay the use of +# these algorithms further. + +# o EdDSA is the only IETF algorithm that currently supports the use +# of contexts; however, there is a possibility that there will be +# confusion between which algorithms need to have separate keys and +# which do not. This may result in a decrease of security for those +# other algorithms. + +# o There are still ongoing discussions among the cryptographic +# community about how effective the use of contexts is for +# preventing attacks. + +# o There needs to be discussions about the correct way to identify +# when context strings are to be used. It is not clear if different +# OIDs should be used for different contexts or the OID should +# merely note that a context string needs to be provided. + +# 2. Requirements Terminology + +# The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", +# "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and +# "OPTIONAL" in this document are to be interpreted as described in +# BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all +# capitals, as shown here. + +# 3. Curve25519 and Curve448 Algorithm Identifiers + +# Certificates conforming to [RFC5280] can convey a public key for any +# public key algorithm. The certificate indicates the algorithm +# through an algorithm identifier. An algorithm identifier consists of +# an OID and optional parameters. + + + + + + + + + +# Josefsson & Schaad Standards Track [Page 3] + +# RFC 8410 Safe Curves for X.509 August 2018 + + +# The AlgorithmIdentifier type, which is included for convenience, is +# defined as follows: + +# AlgorithmIdentifier ::= SEQUENCE { +# algorithm OBJECT IDENTIFIER, +# parameters ANY DEFINED BY algorithm OPTIONAL +# } + +# The fields in AlgorithmIdentifier have the following meanings: + +# o algorithm identifies the cryptographic algorithm with an object +# identifier. Four such OIDs are defined below. + +# o parameters, which are optional, are the associated parameters for +# the algorithm identifier in the algorithm field. + +# In this document, we define four new OIDs for identifying the +# different curve/algorithm pairs: the curves being curve25519 and +# curve448 and the algorithms being ECDH and EdDSA in pure mode. For +# all of the OIDs, the parameters MUST be absent. + +# It is possible to find systems that require the parameters to be +# present. This can be due to either a defect in the original 1997 +# syntax or a programming error where developers never got input where +# this was not true. The optimal solution is to fix these systems; +# where this is not possible, the problem needs to be restricted to +# that subsystem and not propagated to the Internet. + +# The same algorithm identifiers are used for identifying a public key, +# a private key, and a signature (for the two EdDSA related OIDs). +# Additional encoding information is provided below for each of these +# locations. + +# id-X25519 OBJECT IDENTIFIER ::= { 1 3 101 110 } +# id-X448 OBJECT IDENTIFIER ::= { 1 3 101 111 } +# id-Ed25519 OBJECT IDENTIFIER ::= { 1 3 101 112 } +# id-Ed448 OBJECT IDENTIFIER ::= { 1 3 101 113 } + +# 4. Subject Public Key Fields + +# In the X.509 certificate, the subjectPublicKeyInfo field has the +# SubjectPublicKeyInfo type, which has the following ASN.1 syntax: + +# SubjectPublicKeyInfo ::= SEQUENCE { +# algorithm AlgorithmIdentifier, +# subjectPublicKey BIT STRING +# } + + + + +# Josefsson & Schaad Standards Track [Page 4] + +# RFC 8410 Safe Curves for X.509 August 2018 + + +# The fields in SubjectPublicKeyInfo have the following meanings: + +# o algorithm is the algorithm identifier and parameters for the +# public key (see above). + +# o subjectPublicKey contains the byte stream of the public key. The +# algorithms defined in this document always encode the public key +# as an exact multiple of 8 bits. + +# Both [RFC7748] and [RFC8032] define the public key value as being a +# byte string. It should be noted that the public key is computed +# differently for each of these documents; thus, the same private key +# will not produce the same public key. + +# The following is an example of a public key encoded using the textual +# encoding defined in [RFC7468]. + +# -----BEGIN PUBLIC KEY----- +# MCowBQYDK2VwAyEAGb9ECWmEzf6FQbrBZ9w7lshQhqowtrbLDFw4rXAxZuE= +# -----END PUBLIC KEY----- + +# 5. Key Usage Bits + +# The intended application for the key is indicated in the keyUsage +# certificate extension. + +# If the keyUsage extension is present in a certificate that indicates +# id-X25519 or id-X448 in SubjectPublicKeyInfo, then the following MUST +# be present: + +# keyAgreement; + +# one of the following MAY also be present: + +# encipherOnly; or +# decipherOnly. + +# If the keyUsage extension is present in an end-entity certificate +# that indicates id-Ed25519 or id-Ed448, then the keyUsage extension +# MUST contain one or both of the following values: + +# nonRepudiation; and +# digitalSignature. + + + + + + + + +# Josefsson & Schaad Standards Track [Page 5] + +# RFC 8410 Safe Curves for X.509 August 2018 + + +# If the keyUsage extension is present in a certification authority +# certificate that indicates id-Ed25519 or id-Ed448, then the keyUsage +# extension MUST contain one or more of the following values: + +# nonRepudiation; +# digitalSignature; +# keyCertSign; and +# cRLSign. + +# 6. EdDSA Signatures + +# Signatures can be placed in a number of different ASN.1 structures. +# The top level structure for a certificate is given below as being +# illustrative of how signatures are frequently encoded with an +# algorithm identifier and a location for the signature. + +# Certificate ::= SEQUENCE { +# tbsCertificate TBSCertificate, +# signatureAlgorithm AlgorithmIdentifier, +# signatureValue BIT STRING } + +# The same algorithm identifiers are used for signatures as are used +# for public keys. When used to identify signature algorithms, the +# parameters MUST be absent. + +# The data to be signed is prepared for EdDSA. Then, a private key +# operation is performed to generate the signature value. This value +# is the opaque value ENC(R) || ENC(S) described in Section 3.3 of +# [RFC8032]. The octet string representing the signature is encoded +# directly in the BIT STRING without adding any additional ASN.1 +# wrapping. For the Certificate structure, the signature value is +# wrapped in the "signatureValue" BIT STRING field. + + + + + + + + + + + + + + + + + + + +# Josefsson & Schaad Standards Track [Page 6] + +# RFC 8410 Safe Curves for X.509 August 2018 + + +# 7. Private Key Format + +# "Asymmetric Key Packages" [RFC5958] describes how to encode a private +# key in a structure that both identifies what algorithm the private +# key is for and allows for the public key and additional attributes +# about the key to be included as well. For illustration, the ASN.1 +# structure OneAsymmetricKey is replicated below. The algorithm- +# specific details of how a private key is encoded are left for the +# document describing the algorithm itself. + +# OneAsymmetricKey ::= SEQUENCE { +# version Version, +# privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, +# privateKey PrivateKey, +# attributes [0] IMPLICIT Attributes OPTIONAL, +# ..., +# [[2: publicKey [1] IMPLICIT PublicKey OPTIONAL ]], +# ... +# } + +# PrivateKey ::= OCTET STRING + +# PublicKey ::= BIT STRING + +# For the keys defined in this document, the private key is always an +# opaque byte sequence. The ASN.1 type CurvePrivateKey is defined in +# this document to hold the byte sequence. Thus, when encoding a +# OneAsymmetricKey object, the private key is wrapped in a +# CurvePrivateKey object and wrapped by the OCTET STRING of the +# "privateKey" field. + +# CurvePrivateKey ::= OCTET STRING + +# To encode an EdDSA, X25519, or X448 private key, the "privateKey" +# field will hold the encoded private key. The "privateKeyAlgorithm" +# field uses the AlgorithmIdentifier structure. The structure is +# encoded as defined above. If present, the "publicKey" field will +# hold the encoded key as defined in [RFC7748] and [RFC8032]. + + + + + + + + + + + + + +# Josefsson & Schaad Standards Track [Page 7] + +# RFC 8410 Safe Curves for X.509 August 2018 + + +# The following is an example of a private key encoded using the +# textual encoding defined in [RFC7468]. + +# -----BEGIN PRIVATE KEY----- +# MC4CAQAwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC +# -----END PRIVATE KEY----- + +# The following example, in addition to encoding the private key, has +# an attribute included as well as the public key. As with the prior +# example, the textual encoding defined in [RFC7468] is used. + +# -----BEGIN PRIVATE KEY----- +# MHICAQEwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC +# oB8wHQYKKoZIhvcNAQkJFDEPDA1DdXJkbGUgQ2hhaXJzgSEAGb9ECWmEzf6FQbrB +# Z9w7lshQhqowtrbLDFw4rXAxZuE= +# -----END PRIVATE KEY------ + +# NOTE: There exist some private key import functions that have not +# picked up the new ASN.1 structure OneAsymmetricKey that is defined in +# [RFC7748]. This means that they will not accept a private key +# structure that contains the public key field. This means a balancing +# act needs to be done between being able to do a consistency check on +# the key pair and widest ability to import the key. + +# 8. Human-Readable Algorithm Names + +# For the purpose of consistent cross-implementation naming, this +# section establishes human-readable names for the algorithms specified +# in this document. Implementations SHOULD use these names when +# referring to the algorithms. If there is a strong reason to deviate +# from these names -- for example, if the implementation has a +# different naming convention and wants to maintain internal +# consistency -- it is encouraged to deviate as little as possible from +# the names given here. + +# Use the string "ECDH" when referring to a public key of type "X25519" +# or "X448" when the curve is not known or relevant. + +# When the curve is known, use the more specific string of "X25519" or +# "X448". + +# Use the string "EdDSA" when referring to a signing public key or +# signature when the curve is not known or relevant. + +# When the curve is known, use a more specific string. For the id- +# Ed25519 value use the string "Ed25519". For id-Ed448, use "Ed448". + + + + + +# Josefsson & Schaad Standards Track [Page 8] + +# RFC 8410 Safe Curves for X.509 August 2018 + + +# 9. ASN.1 Module + +# For reference purposes, the ASN.1 syntax is presented as an ASN.1 +# module here. + +# -- ASN.1 Module + +# Safecurves-pkix-18 +# { iso(1) identified-organization(3) dod(6) internet(1) +# security(5) mechanisms(5) pkix(7) id-mod(0) +# id-mod-safecurves-pkix(93) } + +# DEFINITIONS EXPLICIT TAGS ::= +# BEGIN + +# IMPORTS +# SIGNATURE-ALGORITHM, KEY-AGREE, PUBLIC-KEY, KEY-WRAP, +# KeyUsage, AlgorithmIdentifier +# FROM AlgorithmInformation-2009 +# {iso(1) identified-organization(3) dod(6) internet(1) security(5) +# mechanisms(5) pkix(7) id-mod(0) +# id-mod-algorithmInformation-02(58)} + +# mda-sha512 +# FROM PKIX1-PSS-OAEP-Algorithms-2009 +# { iso(1) identified-organization(3) dod(6) internet(1) +# security(5) mechanisms(5) pkix(7) id-mod(0) +# id-mod-pkix1-rsa-pkalgs-02(54) } + +# kwa-aes128-wrap, kwa-aes256-wrap +# FROM CMSAesRsaesOaep-2009 +# { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-9(9) +# smime(16) modules(0) id-mod-cms-aes-02(38) } +# ; + + +# id-edwards-curve-algs OBJECT IDENTIFIER ::= { 1 3 101 } + +# id-X25519 OBJECT IDENTIFIER ::= { id-edwards-curve-algs 110 } +# id-X448 OBJECT IDENTIFIER ::= { id-edwards-curve-algs 111 } +# id-Ed25519 OBJECT IDENTIFIER ::= { id-edwards-curve-algs 112 } +# id-Ed448 OBJECT IDENTIFIER ::= { id-edwards-curve-algs 113 } + + + + + + + + + +# Josefsson & Schaad Standards Track [Page 9] + +# RFC 8410 Safe Curves for X.509 August 2018 + + +# sa-Ed25519 SIGNATURE-ALGORITHM ::= { +# IDENTIFIER id-Ed25519 +# PARAMS ARE absent +# PUBLIC-KEYS {pk-Ed25519} +# SMIME-CAPS { IDENTIFIED BY id-Ed25519 } +# } + +# pk-Ed25519 PUBLIC-KEY ::= { +# IDENTIFIER id-Ed25519 +# -- KEY no ASN.1 wrapping -- +# PARAMS ARE absent +# CERT-KEY-USAGE {digitalSignature, nonRepudiation, +# keyCertSign, cRLSign} +# PRIVATE-KEY CurvePrivateKey +# } + +# kaa-X25519 KEY-AGREE ::= { +# IDENTIFIER id-X25519 +# PARAMS ARE absent +# PUBLIC-KEYS {pk-X25519} +# UKM -- TYPE no ASN.1 wrapping -- ARE preferredPresent +# SMIME-CAPS { +# TYPE AlgorithmIdentifier{KEY-WRAP, {KeyWrapAlgorithms}} +# IDENTIFIED BY id-X25519 } +# } + +# pk-X25519 PUBLIC-KEY ::= { +# IDENTIFIER id-X25519 +# -- KEY no ASN.1 wrapping -- +# PARAMS ARE absent +# CERT-KEY-USAGE { keyAgreement } +# PRIVATE-KEY CurvePrivateKey +# } + +# KeyWrapAlgorithms KEY-WRAP ::= { +# kwa-aes128-wrap | kwa-aes256-wrap, +# ... +# } + +# kaa-X448 KEY-AGREE ::= { +# IDENTIFIER id-X448 +# PARAMS ARE absent +# PUBLIC-KEYS {pk-X448} +# UKM -- TYPE no ASN.1 wrapping -- ARE preferredPresent +# SMIME-CAPS { +# TYPE AlgorithmIdentifier{KEY-WRAP, {KeyWrapAlgorithms}} +# IDENTIFIED BY id-X448 } +# } + + + +# Josefsson & Schaad Standards Track [Page 10] + +# RFC 8410 Safe Curves for X.509 August 2018 + + +# pk-X448 PUBLIC-KEY ::= { +# IDENTIFIER id-X448 +# -- KEY no ASN.1 wrapping -- +# PARAMS ARE absent +# CERT-KEY-USAGE { keyAgreement } +# PRIVATE-KEY CurvePrivateKey +# } + +# CurvePrivateKey ::= OCTET STRING + + +# END + +# 10. Examples + +# This section contains illustrations of EdDSA public keys and +# certificates, illustrating parameter choices. + +# 10.1. Example Ed25519 Public Key + +# An example of an Ed25519 public key: + +# Public Key Information: +# Public Key Algorithm: Ed25519 +# Algorithm Security Level: High + +# Public Key Usage: + +# Public Key ID: 9b1f5eeded043385e4f7bc623c5975b90bc8bb3b + +# -----BEGIN PUBLIC KEY----- +# MCowBQYDK2VwAyEAGb9ECWmEzf6FQbrBZ9w7lshQhqowtrbLDFw4rXAxZuE= +# -----END PUBLIC KEY----- + + + + + + + + + + + + + + + + + + +# Josefsson & Schaad Standards Track [Page 11] + +# RFC 8410 Safe Curves for X.509 August 2018 + + +# 10.2. Example X25519 Certificate + +# An example of a self-issued PKIX certificate using Ed25519 to sign an +# X25519 public key would be: + +# 0 300: SEQUENCE { +# 4 223: SEQUENCE { +# 7 3: [0] { +# 9 1: INTEGER 2 +# : } +# 12 8: INTEGER 56 01 47 4A 2A 8D C3 30 +# 22 5: SEQUENCE { +# 24 3: OBJECT IDENTIFIER +# : Ed 25519 signature algorithm { 1 3 101 112 } +# : } +# 29 25: SEQUENCE { +# 31 23: SET { +# 33 21: SEQUENCE { +# 35 3: OBJECT IDENTIFIER commonName (2 5 4 3) +# 40 14: UTF8String 'IETF Test Demo' +# : } +# : } +# : } +# 56 30: SEQUENCE { +# 58 13: UTCTime 01/08/2016 12:19:24 GMT +# 73 13: UTCTime 31/12/2040 23:59:59 GMT +# : } +# 88 25: SEQUENCE { +# 90 23: SET { +# 92 21: SEQUENCE { +# 94 3: OBJECT IDENTIFIER commonName (2 5 4 3) +# 99 14: UTF8String 'IETF Test Demo' +# : } +# : } +# : } +# 115 42: SEQUENCE { +# 117 5: SEQUENCE { +# 119 3: OBJECT IDENTIFIER +# : ECDH 25519 key agreement { 1 3 101 110 } +# : } +# 124 33: BIT STRING +# : 85 20 F0 09 89 30 A7 54 74 8B 7D DC B4 3E F7 5A +# : 0D BF 3A 0D 26 38 1A F4 EB A4 A9 8E AA 9B 4E 6A +# : } +# 159 69: [3] { +# 161 67: SEQUENCE { +# 163 15: SEQUENCE { +# 165 3: OBJECT IDENTIFIER basicConstraints (2 5 29 19) + + + +# Josefsson & Schaad Standards Track [Page 12] + +# RFC 8410 Safe Curves for X.509 August 2018 + + +# 170 1: BOOLEAN TRUE +# 173 5: OCTET STRING, encapsulates { +# 175 3: SEQUENCE { +# 177 1: BOOLEAN FALSE +# : } +# : } +# : } +# 180 14: SEQUENCE { +# 182 3: OBJECT IDENTIFIER keyUsage (2 5 29 15) +# 187 1: BOOLEAN FALSE +# 190 4: OCTET STRING, encapsulates { +# 192 2: BIT STRING 3 unused bits +# : '10000'B (bit 4) +# : } +# : } +# 196 32: SEQUENCE { +# 198 3: OBJECT IDENTIFIER subjectKeyIdentifier (2 5 29 14) +# 203 1: BOOLEAN FALSE +# 206 22: OCTET STRING, encapsulates { +# 208 20: OCTET STRING +# : 9B 1F 5E ED ED 04 33 85 E4 F7 BC 62 3C 59 75 +# : B9 0B C8 BB 3B +# : } +# : } +# : } +# : } +# : } +# 230 5: SEQUENCE { +# 232 3: OBJECT IDENTIFIER +# : Ed 25519 signature algorithm { 1 3 101 112 } +# : } +# 237 65: BIT STRING +# : AF 23 01 FE DD C9 E6 FF C1 CC A7 3D 74 D6 48 A4 +# : 39 80 82 CD DB 69 B1 4E 4D 06 EC F8 1A 25 CE 50 +# : D4 C2 C3 EB 74 6C 4E DD 83 46 85 6E C8 6F 3D CE +# : 1A 18 65 C5 7A C2 7B 50 A0 C3 50 07 F5 E7 D9 07 +# : } + +# -----BEGIN CERTIFICATE----- +# MIIBLDCB36ADAgECAghWAUdKKo3DMDAFBgMrZXAwGTEXMBUGA1UEAwwOSUVURiBUZX +# N0IERlbW8wHhcNMTYwODAxMTIxOTI0WhcNNDAxMjMxMjM1OTU5WjAZMRcwFQYDVQQD +# DA5JRVRGIFRlc3QgRGVtbzAqMAUGAytlbgMhAIUg8AmJMKdUdIt93LQ+91oNvzoNJj +# ga9OukqY6qm05qo0UwQzAPBgNVHRMBAf8EBTADAQEAMA4GA1UdDwEBAAQEAwIDCDAg +# BgNVHQ4BAQAEFgQUmx9e7e0EM4Xk97xiPFl1uQvIuzswBQYDK2VwA0EAryMB/t3J5v +# /BzKc9dNZIpDmAgs3babFOTQbs+BolzlDUwsPrdGxO3YNGhW7Ibz3OGhhlxXrCe1Cg +# w1AH9efZBw== +# -----END CERTIFICATE----- + + + + +# Josefsson & Schaad Standards Track [Page 13] + +# RFC 8410 Safe Curves for X.509 August 2018 + + +# 10.3. Examples of Ed25519 Private Key + +# An example of an Ed25519 private key without the public key: + +# -----BEGIN PRIVATE KEY----- +# MC4CAQAwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC +# -----END PRIVATE KEY----- + +# The same item dumped as ASN.1 yields: + +# 0 30 46: SEQUENCE { +# 2 02 1: INTEGER 0 +# 5 30 5: SEQUENCE { +# 7 06 3: OBJECT IDENTIFIER +# : Ed 25519 signature algorithm { 1 3 101 112 } +# : } +# 12 04 34: OCTET STRING +# : 04 20 D4 EE 72 DB F9 13 58 4A D5 B6 D8 F1 F7 69 +# : F8 AD 3A FE 7C 28 CB F1 D4 FB E0 97 A8 8F 44 75 +# : 58 42 +# : } + +# Note that the value of the private key is: + +# D4 EE 72 DB F9 13 58 4A D5 B6 D8 F1 F7 69 F8 AD +# 3A FE 7C 28 CB F1 D4 FB E0 97 A8 8F 44 75 58 42 + +# An example of the same Ed25519 private key encoded with an attribute +# and the public key: + +# -----BEGIN PRIVATE KEY----- +# MHICAQEwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC +# oB8wHQYKKoZIhvcNAQkJFDEPDA1DdXJkbGUgQ2hhaXJzgSEAGb9ECWmEzf6FQbrB +# Z9w7lshQhqowtrbLDFw4rXAxZuE= +# -----END PRIVATE KEY----- + + + + + + + + + + + + + + + + +# Josefsson & Schaad Standards Track [Page 14] + +# RFC 8410 Safe Curves for X.509 August 2018 + + +# The same item dumped as ASN.1 yields: + +# 0 114: SEQUENCE { +# 2 1: INTEGER 1 +# 5 5: SEQUENCE { +# 7 3: OBJECT IDENTIFIER '1 3 101 112' +# : } +# 12 34: OCTET STRING, encapsulates { +# : 04 20 D4 EE 72 DB F9 13 58 4A D5 B6 D8 F1 F7 69 +# : F8 AD 3A FE 7C 28 CB F1 D4 FB E0 97 A8 8F 44 75 +# : 58 42 +# : } +# 48 31: [0] { +# 50 29: SEQUENCE { +# 52 10: OBJECT IDENTIFIER '1 2 840 113549 1 9 9 20' +# 64 15: SET { +# 66 13: UTF8String 'Curdle Chairs' +# : } +# : } +# : } +# 81 33: [1] 00 19 BF 44 09 69 84 CD FE 85 41 BA C1 67 DC 3B +# 96 C8 50 86 AA 30 B6 B6 CB 0C 5C 38 AD 70 31 66 +# E1 +# : } + +# 11. IANA Considerations + +# For the ASN.1 module in Section 9, IANA has registered value 93 for +# "id-mod-safecurves-pkix" in the "SMI Security for PKIX Module +# Identifier" (1.3.6.1.5.5.7.0) registry. + +# The OIDs are being independently registered in the IANA registry "SMI +# Security for Cryptographic Algorithms" in [RFC8411]. + +# 12. Security Considerations + +# The security considerations of [RFC5280], [RFC7748], and [RFC8032] +# apply accordingly. + +# The procedures for going from a private key to a public key are +# different when used with Diffie-Hellman versus when used with Edwards +# Signatures. This means that the same public key cannot be used for +# both ECDH and EdDSA. + + + + + + + + +# Josefsson & Schaad Standards Track [Page 15] + +# RFC 8410 Safe Curves for X.509 August 2018 + + +# 13. References + +# 13.1. Normative References + +# [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate +# Requirement Levels", BCP 14, RFC 2119, +# DOI 10.17487/RFC2119, March 1997, +# . + +# [RFC5280] Cooper, D., Santesson, S., Farrell, S., Boeyen, S., +# Housley, R., and W. Polk, "Internet X.509 Public Key +# Infrastructure Certificate and Certificate Revocation List +# (CRL) Profile", RFC 5280, DOI 10.17487/RFC5280, May 2008, +# . + +# [RFC5480] Turner, S., Brown, D., Yiu, K., Housley, R., and T. Polk, +# "Elliptic Curve Cryptography Subject Public Key +# Information", RFC 5480, DOI 10.17487/RFC5480, March 2009, +# . + +# [RFC5958] Turner, S., "Asymmetric Key Packages", RFC 5958, +# DOI 10.17487/RFC5958, August 2010, +# . + +# [RFC7748] Langley, A., Hamburg, M., and S. Turner, "Elliptic Curves +# for Security", RFC 7748, DOI 10.17487/RFC7748, January +# 2016, . + +# [RFC8032] Josefsson, S. and I. Liusvaara, "Edwards-Curve Digital +# Signature Algorithm (EdDSA)", RFC 8032, +# DOI 10.17487/RFC8032, January 2017, +# . + +# [RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC +# 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, +# May 2017, . + +# 13.2. Informative References + +# [RFC3279] Bassham, L., Polk, W., and R. Housley, "Algorithms and +# Identifiers for the Internet X.509 Public Key +# Infrastructure Certificate and Certificate Revocation List +# (CRL) Profile", RFC 3279, DOI 10.17487/RFC3279, April +# 2002, . + + + + + + + +# Josefsson & Schaad Standards Track [Page 16] + +# RFC 8410 Safe Curves for X.509 August 2018 + + +# [RFC4055] Schaad, J., Kaliski, B., and R. Housley, "Additional +# Algorithms and Identifiers for RSA Cryptography for use in +# the Internet X.509 Public Key Infrastructure Certificate +# and Certificate Revocation List (CRL) Profile", RFC 4055, +# DOI 10.17487/RFC4055, June 2005, +# . + +# [RFC5639] Lochter, M. and J. Merkle, "Elliptic Curve Cryptography +# (ECC) Brainpool Standard Curves and Curve Generation", +# RFC 5639, DOI 10.17487/RFC5639, March 2010, +# . + +# [RFC7468] Josefsson, S. and S. Leonard, "Textual Encodings of PKIX, +# PKCS, and CMS Structures", RFC 7468, DOI 10.17487/RFC7468, +# April 2015, . + +# [RFC8411] Schaad, J. and R. Andrews, "IANA Registration for the +# Cryptographic Algorithm Object Identifier Range", +# RFC 8411, DOI 10.17487/RFC8411, August 2018, +# . + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +# Josefsson & Schaad Standards Track [Page 17] + +# RFC 8410 Safe Curves for X.509 August 2018 + + +# Appendix A. Invalid Encodings + +# There are a number of things that need to be dealt with when a new +# key part is decoded and imported into the system. A partial list of +# these includes: + +# o ASN.1 encoding errors: Two items are highlighted here. First, the +# use of an OCTET STRING rather than a BIT STRING for the public +# key. The use of OCTET STRING was a copy error that existed in a +# previous draft version of this document; the structure is correct +# in [RFC5958]. However, any early implementation may have this +# wrong. Second, the value of the version field is required to be 0 +# if the publicKey is absent and 1 if present. This is called out +# in [RFC5958], but was not duplicated above. + +# o Key encoding errors: Both [RFC7748] and [RFC8032] have formatting +# requirements for keys that need to be enforced. In some cases, +# the enforcement is done at the time of importing, for example, +# doing masking or a mod p operation. In other cases, the +# enforcement is done by rejecting the keys and having an import +# failure. + +# o Key mismatch errors: If a public key is provided, it may not agree +# with the private key because either it is wrong or the wrong +# algorithm was used. + +# Some systems are also going to be stricter on what they accept. As +# stated in [RFC5958], BER decoding of OneAsymmetricKey objects is a +# requirement for compliance. Despite this requirement, some acceptors +# will only decode DER formats. The following is a BER encoding of a +# private key; it is valid, but it may not be accepted by many systems. + +# -----BEGIN PRIVATE KEY----- +# MIACAQAwgAYDK2VwAAAEIgQg1O5y2/kTWErVttjx92n4rTr+fCjL8dT74Jeoj0R1W +# EIAAA== +# -----END PRIVATE KEY----- + +# What follows here is a brief sampling of some incorrect keys. + +# In the following example, the private key does not match the masking +# requirements for X25519. For this example, the top bits are set to +# zero and the bottom three bits are set to 001. + +# -----BEGIN PRIVATE KEY----- +# MFMCAQEwBQYDK2VuBCIEIPj///////////////////////////////////////8/oS +# MDIQCEfA0sN1I082XmYJVRh6NzWg92E9FgnTpqTYxTrqpaIg== +# -----END PRIVATE KEY----- + + + + +# Josefsson & Schaad Standards Track [Page 18] + +# RFC 8410 Safe Curves for X.509 August 2018 + + +# In the following examples, the key is the wrong length because an +# all-zero byte has been removed. In one case, the first byte has been +# removed; in the other case, the last byte has been removed. + +# -----BEGIN PRIVATE KEY----- +# MFICAQEwBQYDK2VwBCIEIC3GfeUYbZGTAhwLEE2cbvJL7ivTlcy17VottfN6L8HwoS +# IDIADBfk2Lv/J8H7YYwj/OmIcDx++jzVkKrKwS0/HjyQyM +# -----END PRIVATE KEY----- + +# -----BEGIN PRIVATE KEY----- +# MFICAQEwBQYDK2VwBCIEILJXn1VaLqvausjUaZexwI/ozmOFjfEk78KcYN+7hsNJoS +# IDIACdQhJwzi/MCGcsQeQnIUh2JFybDxSrZxuLudJmpJLk +# -----END PRIVATE KEY----- + +# Acknowledgments + +# Text and/or inspiration were drawn from [RFC5280], [RFC3279], +# [RFC4055], [RFC5480], and [RFC5639]. + +# The following people discussed the document and provided feedback: +# Klaus Hartke, Ilari Liusvaara, Erwann Abalea, Rick Andrews, Rob +# Stradling, James Manger, Nikos Mavrogiannopoulos, Russ Housley, David +# Benjamin, Brian Smith, and Alex Wilson. + +# A big thank you to Symantec for kindly donating the OIDs used in this +# document. + + + + + + + + + + + + + + + + + + + + + + + + + +# Josefsson & Schaad Standards Track [Page 19] + +# RFC 8410 Safe Curves for X.509 August 2018 + + +# Authors' Addresses + +# Simon Josefsson +# SJD AB + +# Email: simon@josefsson.org + + +# Jim Schaad +# August Cellars + +# Email: ietf@augustcellars.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +# Josefsson & Schaad Standards Track [Page 20] diff --git a/tests/test_asn1.py b/tests/test_asn1.py new file mode 100644 index 0000000..4167ea9 --- /dev/null +++ b/tests/test_asn1.py @@ -0,0 +1,167 @@ +"""Tests for ``jose.backends._asn1``.""" +import base64 + +import pytest + +try: + from jose.backends import _asn1 +except ImportError: + _asn1 = None + +pytestmark = [ + pytest.mark.pycrypto, + pytest.mark.pycryptodome, + pytest.mark.skipif(_asn1 is None, reason="ASN1 backend not available") +] + +PKCS1_PRIVATE_KEY = b"""MIIJKwIBAAKCAgEAtSKfSeI0fukRIX38AHlKB1YPp +X8PUYN2JdvfM+XjNmLfU1M74N0VmdzIX95sneQGO9kC2xMIE+AIlt52Yf/KgBZggA +lS9Y0Vx8DsSL2HvOjguAdXir3vYLvAyyHin/mUisJOqccFKChHKjnk0uXy/38+1r1 +7/cYTp76brKpU1I4kM20M//dbvLBWjfzyw9ehufr74aVwr+0xJfsBVr2oaQFww/XH +Gz69Q7yHK6DbxYO4w4q2sIfcC4pT8XTPHo4JZ2M733Ea8a7HxtZS563/mhhRZLU5a +ynQpwaVv2U++CL6EvGt8TlNZOkeRv8wz+Rt8B70jzoRpVK36rR+pHKlXhMGT619v8 +2LneTdsqA25Wi2Ld/c0niuul24A6+aaj2u9SWbxA9LmVtFntvNbRaHXE1SLpLPoIp +8uppGF02Nz2v3ld8gCnTTWfq/BQ80Qy8e0coRRABECZrjIMzHEg6MloRDy4na0pRQ +v61VogqRKDU2r3/VezFPQDb3ciYsZjWBr3HpNOkUjTrvLmFyOE9Q5R/qQGmc6BYtf +k5rn7iIfXlkJAZHXhBy+ElBuiBM+YSkFM7dH92sSIoZ05V4MP09Xcppx7kdwsJy72 +Sust9Hnd9B7V35YnVF6W791lVHnenhCJOziRmkH4xLLbPkaST2Ks3IHH7tVltM6Ns +Rk3jNdVMCAwEAAQKCAgEArx+0JXigDHtFZr4pYEPjwMgCBJ2dr8+L8PptB/4g+LoK +9MKqR7M4aTO+PoILPXPyWvZq/meeDakyZLrcdc8ad1ArKF7baDBpeGEbkRA9JfV5H +jNq/ea4gyvDMCGou8ZPSQCnkRmr8LFQbJDgnM5Za5AYrwEv2aEh67IrTHq53W83rM +ioIumCNiG+7TQ7egEGiYsQ745GLrECLZhKKRTgt/T+k1cSk1LLJawme5XgJUw+3D9 +GddJEepvYoL+wZ/gnO2ADyPnPdQ7oc2NPcFMXpmIQf29+/g7FflatfQhkIv+eC6bB +51DhdMi1zyp2hOhzKg6jn74ixVX+Hts2/cMiAPu0NaWmU9n8g7HmXWc4+uSO/fssG +jI3DLYKd5xnhrq4a3ZO5oJLeMO9U71+Ykctg23PTHwNAGrsPYdjGcBnJEdtbXa31a +gI5PAG6rgGUY3iSoWqHLgBTxrX04TWVvLQi8wbxh7BEF0yasOeZKxdE2IWYg75zGs +jluyHlOnpRa5lSf6KZ6thh9eczFHYtS4DvYBcZ9hZW/g87ie28SkBFxxl0brYt9uK +NYJvuajVG8kT80AC7Wzg2q7Wmnoww3JNJUbNths5dqKyUSlMFMIB/vOePFHLrA6qD +fAnsQHgUb9WHhUrYsH20XKpqR2OjmWU05bV4pSMW/JwG37o+px1yKECggEBANnwx0 +d7ksEMvJjeN5plDy3eMLifBI+6SL/o5TXDoFM6rJxF+0UP70uouYJq2dI+DCSA6c/ +Esn7WAOirY177adKcBV8biwAtmKHnFnCs/kwAZq8lMvQPtNPJ/vq2n40kO48h8fxb +eGcmyAqFPZ4YKSxrPA4cdbHIuFSt9WyaUcVFmzdTFHVlRP70EXdmXHt84byWNB4CH +eq8zmrNxPNAi65nEkUks7iBQMtuvyV2+aXjDOTBMCd66IhIh2iZq1O7kXUwgh1OH9 +hCa7oriHyAdgkKdKCWocmbPPENOETgjraA9wRIXwOYTDb1X5hMvi1mCHo8xjMju4s +zD03xJVi7WrsCggEBANTEblCkxEyhJqaMZF3U3df2Yr/ZtHqsrTr4lwB/MOKkzmuS +rROxheEkKIsxbiV+AxTvtPR1FQrlqbhTJRwy+pw4KPJ7P4fq2R/YBqvXSNBCamTt6 +l2XdXqnAk3A++cOEZ2lU9ubfgdeN2Ih8rgdn1LWeOSjCWfExmkoU61/Xe6xAMeXKQ +SlHKSnX9voxuE2xINHeU6ZAKy1kGmrJtEiWnI8b8C4s8fTyDtXJ1Lasys0iHO2Tz2 +jUhf4IJwb87Lk7Ize2MrI+oPzVDXlmkbjkB4tYyoiRTj8rk8pwBW/HVv002pjOLTa +4kz1kQ3lsZ/3As4zfNi7mWEhadmEsAIfYkkCggEBANO39r/Yqj5kUyrmZXnVxyM2A +Hq58EJ4I4hbhZ/vRWbVTy4ZRfpXeo4zgNPTXXvCzyT/HyS53vUcjJF7PfPdpXX2H7 +m/Fg+8O9S8m64mQHwwv5BSQOecAnzkdJG2q9T/Z+Sqg1w2uAbtQ9QEkFFvA0ClhBf +pSeTGK1wICq3QVLOh5SGf0fYhxR8wl284v4svTFRaTpMAV3Pcq2JSN4xgHdH1S2hk +OTt6RSnbklGg/PFMWxA3JMKVwiPy4aiZ8DhNtQb1ctFpPcJm9CRNejAI06IAyD/hV +ZZ2+oLp5snypHFjY5SDgdoKL7AMOyvHEdEkmAO32ot/oQefOLTtGOzURVUCggEBAL +Sx5iYi6HtT2SlUzeBKaeWBYDgiwf31LGGKwWMwoem5oX0GYmr5NwQP20brQeohbKi +ZMwrxbF+G0G60Xi3mtaN6pnvYZAogTymWI4RJH5OO9CCnVYUKnkD+GRzDqqt97UP/ +Joq5MX08bLiwsBvhPG/zqVQzikdQfFjOYNJV+wY92LWpELLbLso/Q0/WDyExjA8Z4 +lH36vTCddTn/91Y2Ytu/FGmCzjICaMrzz+0cLlesgvjZsSoMY4dskQiEQN7G9I/Z8 +pAiVEKlBf52N4fYUPfs/oShMty/O5KPNG7L0nrUKlnfr9JrStC2l/9FK8P7pgEbiD +6obY11FlhMMF8udECggEBAIKhvOFtipD1jqDOpjOoR9sK/lRR5bVVWQfamMDN1Awm +jJbVHS8hhtYUM/4sh2p12P6RgoO8fODf1vEcWFh3xxNZE1pPCPaICD9i5U+NRvPz2 +vC900HcraLRrUFaRzwhqOOknYJSBrGzW+Cx3YSeaOCgnKyI8B5gw4C0G0iL1dSsz2 +bR1O4GNOVfT3R6joZEXATFo/Kc2L0YAvApBNUYvY0kbjJ/JfTO5060SsWftf4iw3j +rhSn9RwTTYdq/kErGFWvDGJn2MiuhMe2onNfVzIGRmdUxHwi1ulkspAn/fmY7f0hZ +pskDwcHyZmbKZuk+NU/FJ8IAcmvk9y7m25nSSc8=""" +PKCS8_PRIVATE_KEY = b"""MIIJRQIBADANBgkqhkiG9w0BAQEFAASCCS8wggkrAg +EAAoICAQC1Ip9J4jR+6REhffwAeUoHVg+lfw9Rg3Yl298z5eM2Yt9TUzvg3RWZ3Mh +f3myd5AY72QLbEwgT4AiW3nZh/8qAFmCACVL1jRXHwOxIvYe86OC4B1eKve9gu8DL +IeKf+ZSKwk6pxwUoKEcqOeTS5fL/fz7WvXv9xhOnvpusqlTUjiQzbQz/91u8sFaN/ +PLD16G5+vvhpXCv7TEl+wFWvahpAXDD9ccbPr1DvIcroNvFg7jDirawh9wLilPxdM +8ejglnYzvfcRrxrsfG1lLnrf+aGFFktTlrKdCnBpW/ZT74IvoS8a3xOU1k6R5G/zD +P5G3wHvSPOhGlUrfqtH6kcqVeEwZPrX2/zYud5N2yoDblaLYt39zSeK66XbgDr5pq +Pa71JZvED0uZW0We281tFodcTVIuks+giny6mkYXTY3Pa/eV3yAKdNNZ+r8FDzRDL +x7RyhFEAEQJmuMgzMcSDoyWhEPLidrSlFC/rVWiCpEoNTavf9V7MU9ANvdyJixmNY +Gvcek06RSNOu8uYXI4T1DlH+pAaZzoFi1+TmufuIh9eWQkBkdeEHL4SUG6IEz5hKQ +Uzt0f3axIihnTlXgw/T1dymnHuR3CwnLvZK6y30ed30HtXflidUXpbv3WVUed6eEI +k7OJGaQfjEsts+RpJPYqzcgcfu1WW0zo2xGTeM11UwIDAQABAoICAQCvH7QleKAMe +0VmvilgQ+PAyAIEnZ2vz4vw+m0H/iD4ugr0wqpHszhpM74+ggs9c/Ja9mr+Z54NqT +Jkutx1zxp3UCsoXttoMGl4YRuRED0l9XkeM2r95riDK8MwIai7xk9JAKeRGavwsVB +skOCczllrkBivAS/ZoSHrsitMerndbzesyKgi6YI2Ib7tNDt6AQaJixDvjkYusQIt +mEopFOC39P6TVxKTUsslrCZ7leAlTD7cP0Z10kR6m9igv7Bn+Cc7YAPI+c91DuhzY +09wUxemYhB/b37+DsV+Vq19CGQi/54LpsHnUOF0yLXPKnaE6HMqDqOfviLFVf4e2z +b9wyIA+7Q1paZT2fyDseZdZzj65I79+ywaMjcMtgp3nGeGurhrdk7mgkt4w71TvX5 +iRy2Dbc9MfA0Aauw9h2MZwGckR21tdrfVqAjk8AbquAZRjeJKhaocuAFPGtfThNZW +8tCLzBvGHsEQXTJqw55krF0TYhZiDvnMayOW7IeU6elFrmVJ/opnq2GH15zMUdi1L +gO9gFxn2Flb+DzuJ7bxKQEXHGXRuti324o1gm+5qNUbyRPzQALtbODartaaejDDck +0lRs22Gzl2orJRKUwUwgH+8548UcusDqoN8CexAeBRv1YeFStiwfbRcqmpHY6OZZT +TltXilIxb8nAbfuj6nHXIoQKCAQEA2fDHR3uSwQy8mN43mmUPLd4wuJ8Ej7pIv+jl +NcOgUzqsnEX7RQ/vS6i5gmrZ0j4MJIDpz8SyftYA6KtjXvtp0pwFXxuLAC2YoecWc +Kz+TABmryUy9A+008n++rafjSQ7jyHx/Ft4ZybICoU9nhgpLGs8Dhx1sci4VK31bJ +pRxUWbN1MUdWVE/vQRd2Zce3zhvJY0HgId6rzOas3E80CLrmcSRSSzuIFAy26/JXb +5peMM5MEwJ3roiEiHaJmrU7uRdTCCHU4f2EJruiuIfIB2CQp0oJahyZs88Q04ROCO +toD3BEhfA5hMNvVfmEy+LWYIejzGMyO7izMPTfElWLtauwKCAQEA1MRuUKTETKEmp +oxkXdTd1/Ziv9m0eqytOviXAH8w4qTOa5KtE7GF4SQoizFuJX4DFO+09HUVCuWpuF +MlHDL6nDgo8ns/h+rZH9gGq9dI0EJqZO3qXZd1eqcCTcD75w4RnaVT25t+B143YiH +yuB2fUtZ45KMJZ8TGaShTrX9d7rEAx5cpBKUcpKdf2+jG4TbEg0d5TpkArLWQaasm +0SJacjxvwLizx9PIO1cnUtqzKzSIc7ZPPaNSF/ggnBvzsuTsjN7Yysj6g/NUNeWaR +uOQHi1jKiJFOPyuTynAFb8dW/TTamM4tNriTPWRDeWxn/cCzjN82LuZYSFp2YSwAh +9iSQKCAQEA07f2v9iqPmRTKuZledXHIzYAernwQngjiFuFn+9FZtVPLhlF+ld6jjO +A09Nde8LPJP8fJLne9RyMkXs9892ldfYfub8WD7w71LybriZAfDC/kFJA55wCfOR0 +kbar1P9n5KqDXDa4Bu1D1ASQUW8DQKWEF+lJ5MYrXAgKrdBUs6HlIZ/R9iHFHzCXb +zi/iy9MVFpOkwBXc9yrYlI3jGAd0fVLaGQ5O3pFKduSUaD88UxbEDckwpXCI/LhqJ +nwOE21BvVy0Wk9wmb0JE16MAjTogDIP+FVlnb6gunmyfKkcWNjlIOB2govsAw7K8c +R0SSYA7fai3+hB584tO0Y7NRFVQKCAQEAtLHmJiLoe1PZKVTN4Epp5YFgOCLB/fUs +YYrBYzCh6bmhfQZiavk3BA/bRutB6iFsqJkzCvFsX4bQbrReLea1o3qme9hkCiBPK +ZYjhEkfk470IKdVhQqeQP4ZHMOqq33tQ/8mirkxfTxsuLCwG+E8b/OpVDOKR1B8WM +5g0lX7Bj3YtakQstsuyj9DT9YPITGMDxniUffq9MJ11Of/3VjZi278UaYLOMgJoyv +PP7RwuV6yC+NmxKgxjh2yRCIRA3sb0j9nykCJUQqUF/nY3h9hQ9+z+hKEy3L87ko8 +0bsvSetQqWd+v0mtK0LaX/0Urw/umARuIPqhtjXUWWEwwXy50QKCAQEAgqG84W2Kk +PWOoM6mM6hH2wr+VFHltVVZB9qYwM3UDCaMltUdLyGG1hQz/iyHanXY/pGCg7x84N +/W8RxYWHfHE1kTWk8I9ogIP2LlT41G8/Pa8L3TQdytotGtQVpHPCGo46SdglIGsbN +b4LHdhJ5o4KCcrIjwHmDDgLQbSIvV1KzPZtHU7gY05V9PdHqOhkRcBMWj8pzYvRgC +8CkE1Ri9jSRuMn8l9M7nTrRKxZ+1/iLDeOuFKf1HBNNh2r+QSsYVa8MYmfYyK6Ex7 +aic19XMgZGZ1TEfCLW6WSykCf9+Zjt/SFmmyQPBwfJmZspm6T41T8UnwgBya+T3Lu +bbmdJJzw==""" +PKCS1_PUBLIC_KEY = b"""MIICCgKCAgEAtSKfSeI0fukRIX38AHlKB1YPpX8PUY +N2JdvfM+XjNmLfU1M74N0VmdzIX95sneQGO9kC2xMIE+AIlt52Yf/KgBZggAlS9Y0 +Vx8DsSL2HvOjguAdXir3vYLvAyyHin/mUisJOqccFKChHKjnk0uXy/38+1r17/cYT +p76brKpU1I4kM20M//dbvLBWjfzyw9ehufr74aVwr+0xJfsBVr2oaQFww/XHGz69Q +7yHK6DbxYO4w4q2sIfcC4pT8XTPHo4JZ2M733Ea8a7HxtZS563/mhhRZLU5aynQpw +aVv2U++CL6EvGt8TlNZOkeRv8wz+Rt8B70jzoRpVK36rR+pHKlXhMGT619v82LneT +dsqA25Wi2Ld/c0niuul24A6+aaj2u9SWbxA9LmVtFntvNbRaHXE1SLpLPoIp8uppG +F02Nz2v3ld8gCnTTWfq/BQ80Qy8e0coRRABECZrjIMzHEg6MloRDy4na0pRQv61Vo +gqRKDU2r3/VezFPQDb3ciYsZjWBr3HpNOkUjTrvLmFyOE9Q5R/qQGmc6BYtfk5rn7 +iIfXlkJAZHXhBy+ElBuiBM+YSkFM7dH92sSIoZ05V4MP09Xcppx7kdwsJy72Sust9 +Hnd9B7V35YnVF6W791lVHnenhCJOziRmkH4xLLbPkaST2Ks3IHH7tVltM6NsRk3jN +dVMCAwEAAQ==""" +PKCS8_PUBLIC_KEY = b"""MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAg +EAtSKfSeI0fukRIX38AHlKB1YPpX8PUYN2JdvfM+XjNmLfU1M74N0VmdzIX95sneQ +GO9kC2xMIE+AIlt52Yf/KgBZggAlS9Y0Vx8DsSL2HvOjguAdXir3vYLvAyyHin/mU +isJOqccFKChHKjnk0uXy/38+1r17/cYTp76brKpU1I4kM20M//dbvLBWjfzyw9ehu +fr74aVwr+0xJfsBVr2oaQFww/XHGz69Q7yHK6DbxYO4w4q2sIfcC4pT8XTPHo4JZ2 +M733Ea8a7HxtZS563/mhhRZLU5aynQpwaVv2U++CL6EvGt8TlNZOkeRv8wz+Rt8B7 +0jzoRpVK36rR+pHKlXhMGT619v82LneTdsqA25Wi2Ld/c0niuul24A6+aaj2u9SWb +xA9LmVtFntvNbRaHXE1SLpLPoIp8uppGF02Nz2v3ld8gCnTTWfq/BQ80Qy8e0coRR +ABECZrjIMzHEg6MloRDy4na0pRQv61VogqRKDU2r3/VezFPQDb3ciYsZjWBr3HpNO +kUjTrvLmFyOE9Q5R/qQGmc6BYtfk5rn7iIfXlkJAZHXhBy+ElBuiBM+YSkFM7dH92 +sSIoZ05V4MP09Xcppx7kdwsJy72Sust9Hnd9B7V35YnVF6W791lVHnenhCJOziRmk +H4xLLbPkaST2Ks3IHH7tVltM6NsRk3jNdVMCAwEAAQ==""" + + +def test_rsa_private_key_pkcs1_to_pkcs8(): + pkcs1 = base64.b64decode(PKCS1_PRIVATE_KEY) + pkcs8 = base64.b64decode(PKCS8_PRIVATE_KEY) + + assert _asn1.rsa_private_key_pkcs1_to_pkcs8(pkcs1) == pkcs8 + + +def test_rsa_private_key_pkcs8_to_pkcs1(): + pkcs1 = base64.b64decode(PKCS1_PRIVATE_KEY) + pkcs8 = base64.b64decode(PKCS8_PRIVATE_KEY) + + assert _asn1.rsa_private_key_pkcs8_to_pkcs1(pkcs8) == pkcs1 + + +def test_rsa_public_key_pkcs1_to_pkcs8(): + pkcs1 = base64.b64decode(PKCS1_PUBLIC_KEY) + pkcs8 = base64.b64decode(PKCS8_PUBLIC_KEY) + + assert _asn1.rsa_public_key_pkcs1_to_pkcs8(pkcs1) == pkcs8 + + +def test_rsa_public_key_pkcs8_to_pkcs1(): + pkcs1 = base64.b64decode(PKCS1_PUBLIC_KEY) + pkcs8 = base64.b64decode(PKCS8_PUBLIC_KEY) + + assert _asn1.rsa_public_key_pkcs8_to_pkcs1(pkcs8) == pkcs1 diff --git a/tests/test_backends.py b/tests/test_backends.py new file mode 100644 index 0000000..6e633a1 --- /dev/null +++ b/tests/test_backends.py @@ -0,0 +1,35 @@ +"""Test the default import handling.""" +try: + from jose.backends.rsa_backend import RSAKey as PurePythonRSAKey +except ImportError: + PurePythonRSAKey = None +try: + from jose.backends.cryptography_backend import CryptographyRSAKey, CryptographyECKey +except ImportError: + CryptographyRSAKey = CryptographyECKey = None +try: + from jose.backends.pycrypto_backend import RSAKey as PyCryptoRSAKey +except ImportError: + PyCryptoRSAKey = None +try: + from jose.backends.ecdsa_backend import ECDSAECKey as PurePythonECDSAKey +except ImportError: + PurePythonRSAKey = None + +from jose.backends import ECKey, RSAKey + + +def test_default_ec_backend(): + if CryptographyECKey is not None: + assert ECKey is CryptographyECKey + else: + assert ECKey is PurePythonECDSAKey + + +def test_default_rsa_backend(): + if CryptographyRSAKey is not None: + assert RSAKey is CryptographyRSAKey + elif PyCryptoRSAKey is not None: + assert RSAKey is PyCryptoRSAKey + else: + assert RSAKey is PurePythonRSAKey diff --git a/tests/test_firebase.py b/tests/test_firebase.py new file mode 100644 index 0000000..b31ac8f --- /dev/null +++ b/tests/test_firebase.py @@ -0,0 +1,51 @@ + +import json + +import pytest + +from jose import jwt + +from jose.backends import RSAKey +try: + from jose.backends.rsa_backend import RSAKey as RsaRSAKey +except ImportError: + RsaRSAKey = None + +firebase_certs = { + "6f83ab6e516e718fba9ddeb6647fd5fb752a151b": "-----BEGIN CERTIFICATE-----\nMIIDHDCCAgSgAwIBAgIIP5V2bjX2bXUwDQYJKoZIhvcNAQEFBQAwMTEvMC0GA1UE\nAxMmc2VjdXJldG9rZW4uc3lzdGVtLmdzZXJ2aWNlYWNjb3VudC5jb20wHhcNMTYw\nODMxMDA0NTI2WhcNMTYwOTAzMDExNTI2WjAxMS8wLQYDVQQDEyZzZWN1cmV0b2tl\nbi5zeXN0ZW0uZ3NlcnZpY2VhY2NvdW50LmNvbTCCASIwDQYJKoZIhvcNAQEBBQAD\nggEPADCCAQoCggEBAKHHtOMXBD+0YTtZHuzFrERiiwa+D6Ybq4SUHlicgRPV3Uk2\nvnTOqg1EhxshEXqjkAQbbRop9hhHTc+p8rBxgYGuLcZsBhGrnRqU6FnTTiWB1x5V\nvOfCkPE60W07gi8p+HyB8cqw1Tz2LnRUw/15888CrspVeumtNUkhXSRKzeS2BI4l\nkuOMkqmsMSu1yB5IZm5meMyta1uhJnP93jKmdar19RkZXOlFcT+fsSY2FPuqvDvX\nssChgZgNV5qtk0CIzexmFJaUFzpKE/RxqdIJooB1H83fUBGVK+9v3Ko+BI+GEvUc\nxIGAEWu2KrbjwPNzzC3/UV9aSfHEOJxQoutPviECAwEAAaM4MDYwDAYDVR0TAQH/\nBAIwADAOBgNVHQ8BAf8EBAMCB4AwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwIwDQYJ\nKoZIhvcNAQEFBQADggEBAIHOiqxXm1IcuXE87ELyKYDG0/gZPzCHz98h/x0LExrs\nd0bOYOIA08rt6qllmP24oT3hQt86HmDb932pm/fjaLL68x81TjYq6cFO0JxOzts+\nY+9XxkdP8Qu7UJ8Dx+rRvDN1MUxLTvBVXdamhkhDusx7PB5kK1ixWtf91qrl/J9e\nUYQBnJ4E9wI8U5HVkW3IBWvsFt/+gMO1EcoNBdB2cY/4N3l3oxm5PSNDS4DTEs2f\nAYZDqo6PJt2tTRGSmvLBKSCqcT7eWBbIwBht3Uw8CvOMbVYGBWjbFeua3Q3fe+p7\n7UbFOLIvSGR516kyZqxy9pLoA9+2TvbpYwWu6mLCZtg=\n-----END CERTIFICATE-----\n", + "fc2da7fa53d92e3bcba8a17e74b34da9dd585065": "-----BEGIN CERTIFICATE-----\nMIIDHDCCAgSgAwIBAgIINfZYQW9uekMwDQYJKoZIhvcNAQEFBQAwMTEvMC0GA1UE\nAxMmc2VjdXJldG9rZW4uc3lzdGVtLmdzZXJ2aWNlYWNjb3VudC5jb20wHhcNMTYw\nODI5MDA0NTI2WhcNMTYwOTAxMDExNTI2WjAxMS8wLQYDVQQDEyZzZWN1cmV0b2tl\nbi5zeXN0ZW0uZ3NlcnZpY2VhY2NvdW50LmNvbTCCASIwDQYJKoZIhvcNAQEBBQAD\nggEPADCCAQoCggEBAMvfJ5DY7lV4txW0zn9ayMxwAp5BzUhyIbuZkmsmMLRrNl+i\nid4lawojB846YtcTPZLD/5QpXRumAAUI5NA023fxaUdriM25zewpSnZWs6eUf0O6\nONES8Xk4WD2fbyPz6cgnsFEfMslNd3NypRiB9fVG6LFj6TFHC64o/YEeQB2dwkJZ\nXknKSEkFJSRC83TiHUlWzaRjmTdGRrvGEWHxr+xJltP8tPPlJUKu2VadgMbGlkKU\n5dBRhvWwZZW0zJupuKzd27O2lPkxfbx9vrUbsfqZcN4OY5Xg+ijQJVTv0/qcplsd\nPZ9Uui0QsBOPbrIO+5/Tq9FIBqxzUlpWwetv6pMCAwEAAaM4MDYwDAYDVR0TAQH/\nBAIwADAOBgNVHQ8BAf8EBAMCB4AwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwIwDQYJ\nKoZIhvcNAQEFBQADggEBALqWwzIQSK94hxTmxlA+RoyMvb8fyTcECM2qY+n+PDb5\nMvt8zqM6AwGjK1hvcUg08BEsnqqRqC81dkSEReS9KCoTY/oQ0sCCpwL3QP3puoxp\nfZU9CSwvnrFTJjC2Q/b8BlWta4CSDwpxpy/K3wm6tRn5ED4rPcP4FRqWU5jyHiug\nRrNkKiG7TeBBvQ3ZlF9K4JSx1yn9g7EvPBcmygop5FIKI1uS+URxeyavtlwfnTTs\nDtRVV/x0LDkHoJ2Agy7l2MqT7eoRKh5VNucQONLrcZT1AY02eZi/WVSjgpzC48eP\nV9xlcgIaRbS/JDULYgW5h0uVdRNqSVGJ6yBLXT2uaBA=\n-----END CERTIFICATE-----\n", + "8226146523a1b8894ba03ad525667b9475d393f5": "-----BEGIN CERTIFICATE-----\nMIIDHDCCAgSgAwIBAgIIWAKW/IRYcAwwDQYJKoZIhvcNAQEFBQAwMTEvMC0GA1UE\nAxMmc2VjdXJldG9rZW4uc3lzdGVtLmdzZXJ2aWNlYWNjb3VudC5jb20wHhcNMTYw\nODMwMDA0NTI2WhcNMTYwOTAyMDExNTI2WjAxMS8wLQYDVQQDEyZzZWN1cmV0b2tl\nbi5zeXN0ZW0uZ3NlcnZpY2VhY2NvdW50LmNvbTCCASIwDQYJKoZIhvcNAQEBBQAD\nggEPADCCAQoCggEBALJAt+ws+XNdDnDSYFp0YnQ5e8QqfMFrwp1l1r/mNSUF840I\nsbm50Z89aNpQgFOsORS/TYyHOeasiBhsJ5HWmfxo0PBTFifKI/OedLlltxZZCHa+\nEO/75Fbeydexokvfq6thT7C+xL45kJzbvKKNAw4WCAW6vwzyz+d/IrWCs9Iqa2ZX\nSiKnMPzPxZj6s+AhHPVxsR8dBMZ+NdK/wh9OcPWjLAxLEWBvd0Gp315bIVjVc9pV\neYcTapu/s4DSwgz4twovAyUziwsa+HJ+2FFNDZExf/XQUVBW5le8gGEdfl3kW1yu\nzdO6e1LwVTDAXULydPBL5lb6vTX2/ICmMzHXzIUCAwEAAaM4MDYwDAYDVR0TAQH/\nBAIwADAOBgNVHQ8BAf8EBAMCB4AwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwIwDQYJ\nKoZIhvcNAQEFBQADggEBAHyACbK1WfP9WspLfxvgJaNvwvygnP6cggLMvqq/dRxP\nlemvxfVaHK19sIXI6p0H4RBjI9FID5otzuyV54p1LBKgLIMTWcMYdL0wieeBg4Ud\nwgLEutIERpJU5oRMpSuZZYW75d0o+U1qOEhDswliqW1xofxNjRgNyrOYc6hMJzIS\ng9U4C4fplT/m3x5uQNjfzN/0CxfQf54WaD15w1lPGQAMJSWQDaxDTi41bW0Jwp4N\ndshOVn+btUUwL5TXDKaVkg1IHfG57FwvPJ5hKs4pbP5SIm+Sc1utIMMTBsRDRJVK\nyHaB5Bj9KcpQk7FvdT/KtzetPowhnxu9ow+KJcnP+7w=\n-----END CERTIFICATE-----\n", + "dd694b16c1b0ce31878a72dfa6c0cd4db3dd7edf": "-----BEGIN CERTIFICATE-----\nMIIDHDCCAgSgAwIBAgIIffru9igojE4wDQYJKoZIhvcNAQEFBQAwMTEvMC0GA1UE\nAxMmc2VjdXJldG9rZW4uc3lzdGVtLmdzZXJ2aWNlYWNjb3VudC5jb20wHhcNMTYw\nOTAxMDA0NTI2WhcNMTYwOTA0MDExNTI2WjAxMS8wLQYDVQQDEyZzZWN1cmV0b2tl\nbi5zeXN0ZW0uZ3NlcnZpY2VhY2NvdW50LmNvbTCCASIwDQYJKoZIhvcNAQEBBQAD\nggEPADCCAQoCggEBALaxpG4i7EgYpzaJsykaZzKmTTnm+kIPJBKb6t41ByUWt7J+\nnoUmlMiAVkXj7GAmc3usroJdYNZ8iMSpAWsIMgg7HLrqv/hMDY6+33rCqsvXD2tF\nCtJbRKzSMKu+AIc1uirkX3L3aHfKRzFbsr+8JqOigY3sVAb42FeATVHB0uCRyoE5\nfqxbt8nIPCFR/lFP51L0Wf5hGIH5kHJEuXx/7GOUQPN196P3sRI9jLv6nrWqGTAR\nVhuY9KXRz0jlVQeKZV5mWstcIXgxn2MfzfoHx4nuSNknJdrfHNp0r2XPf9Fre7Jd\n73slrVUwL2VWyZJdIBxJuYz2QjEQLzz+eJGyWcMCAwEAAaM4MDYwDAYDVR0TAQH/\nBAIwADAOBgNVHQ8BAf8EBAMCB4AwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwIwDQYJ\nKoZIhvcNAQEFBQADggEBAFTpRr9/cEkFHSbP5c7gr926kSxe1e9u9JjzR7l9Zv5l\nfskkLxIZcGlx/FoccWCwDPYl2Nh0Pr++TJ2hWVe/LpjppUxl4QVqfbVzyJezn2UR\nhLnGASQ0ckPoNTJhxjA6PVGtyXWB67oCDEgz/Pl6jjKEMtilyXh93rBmOpt6jq9e\nlwiZaa5wTUwIhHI972rLveYkssVkspmp4RIWHoh1nxUjYPMtcTCf9GFjEMLNdDBj\nYldCEzL34V60ObBSkzV3Zx7UNwoa80+SEJc9gQsBHVJbjXl7V9ODL52OHnciiEA8\n+d/xy2tBzdCD5EUR3aaYZYqQ16VV6LeU8FoxFn6/nxw=\n-----END CERTIFICATE-----\n", + "f4b0a5c73ad85a5da09f0e7f76463631339e0bbf": "-----BEGIN CERTIFICATE-----\nMIIDHDCCAgSgAwIBAgIIWDhBeVUilCcwDQYJKoZIhvcNAQEFBQAwMTEvMC0GA1UE\nAxMmc2VjdXJldG9rZW4uc3lzdGVtLmdzZXJ2aWNlYWNjb3VudC5jb20wHhcNMTYw\nNzAxMDA0NTI2WhcNMTYwNzA0MDExNTI2WjAxMS8wLQYDVQQDEyZzZWN1cmV0b2tl\nbi5zeXN0ZW0uZ3NlcnZpY2VhY2NvdW50LmNvbTCCASIwDQYJKoZIhvcNAQEBBQAD\nggEPADCCAQoCggEBALRWaRmoi5EFyj5TBrUGKFI6uBJ4x9wSHq9tlRL1qmnwzdNb\nlDoeoh6Gw3H54IqM0XqjZZwgV5KXOQDOaoUpMBRH93x7Ma7NjhiDtpQr0JSbFIQL\nsIay/VxQ9gfa/I83HViEAbF1FXjhBKniwFKUv26mU30upZfsDQkHM8OLc/iXRvhA\nYn7S732Oefdv0kJ9t3h+WOGKGVkYfDaAGn5Uyzx+9oyyLY33borKOBBzphSQlZCr\nL569zTXvvLgvdStrsPGaiRGj64DGXD6LCg6acLJcMUvlVUO6THHJHVgp8pzlrPQG\n3B1rZk61lZqJyjK/nTi2tY9GPLfdxOfDAMjNoz8CAwEAAaM4MDYwDAYDVR0TAQH/\nBAIwADAOBgNVHQ8BAf8EBAMCB4AwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwIwDQYJ\nKoZIhvcNAQEFBQADggEBAIlFwO3C+X2+na0nLjR+zQYGHzZYqFe4V67P6ugFJxun\nxP8pyDCYAGer1mkDcIyDacdQ3natNp0xv61a0yk5tSmDYZbXZRTFdLkf/GzH+VmH\nEMl5W4TvxjAe/x2opm3QUaPC+jVlvndcP99FF5ULFp7/PwSTp8uzyrd/fhSFaxhq\nuIW4syNzDSpDItzUsiKCtsKGYX/qvd/cNP8cXlPd5rWTM4Sic9Baf2nXuHaZRkBr\nSJYcxdh8xbGsY1tC8TIgWot6GXtldNvXDLqRUwb2t6Rr3Tqhbc0CcHndTCuHXf0i\n0s9jU/UCrNhhmaD0rZLHQ2tuN6W/xpOHKtO0a8Lys7c=\n-----END CERTIFICATE-----\n" +} + +firebase_token = "eyJhbGciOiJSUzI1NiIsImtpZCI6ImY0YjBhNWM3M2FkODVhNWRhMDlmMGU3Zjc2NDYzNjMxMzM5ZTBiYmYifQ.eyJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vd2Vkb3RyYW5zZmVyLTIwMTYiLCJhdWQiOiJ3ZWRvdHJhbnNmZXItMjAxNiIsImF1dGhfdGltZSI6MTQ2NzM0NjI3MCwidXNlcl9pZCI6IjRjemVXVllIekNNVnN0WEZOYldHVXBKYmJTZzEiLCJzdWIiOiI0Y3plV1ZZSHpDTVZzdFhGTmJXR1VwSmJiU2cxIiwiaWF0IjoxNDY3MzQ2MjcwLCJleHAiOjE0NjczNDk4NzAsImVtYWlsIjoic2V1bkBjbXUuY29tIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJmaXJlYmFzZSI6eyJpZGVudGl0aWVzIjp7InBhc3N3b3JkIjpbInNldW5AY211LmNvbSJdLCJlbWFpbCI6WyJzZXVuQGNtdS5jb20iXX19fQ.U-fYjx8rMm5tYV24r0uEcNQtIe3UKULxsHecLdGzTbi1v-VKzKDk_QPL26SPDoU8JUMY3nJQ1hOE9AapBrQck8NVUZSKFMD49XdtsyoN2kKdinpFR1hSxIE0L2dRStS7OZ8sGiX866lNa52Cr6TXSsnMD6N2P0OtVE5EeD1Nf-AiJ-gsaLrP4tBnmj1MNYhEYVHb6sAUrT3nEI9gWmeKcPWPfn76FGTdGWZ2mjdaeAG4RbuFL4cHdOISA_0HVLGJxuNyEHAHybDX8mVdNW_F4yzL3H-SmPFY5Kv3tCdBzpzhUKfNOnFFmf2ggFOJnDsqMp-TZaIPk6ce_ltqhQ0dnQ" + + +@pytest.mark.skipif(RSAKey is RsaRSAKey, reason="python-rsa backend does not support certificates") +class TestFirebase: + + def test_individual_cert(self): + jwt.decode( + firebase_token, + firebase_certs["f4b0a5c73ad85a5da09f0e7f76463631339e0bbf"], + algorithms='RS256', + options={'verify_exp': False, 'verify_aud': False} + ) + + def test_certs_dict(self): + jwt.decode( + firebase_token, + firebase_certs, + algorithms='RS256', + options={'verify_exp': False, 'verify_aud': False} + ) + + def test_certs_string(self): + certs = json.dumps(firebase_certs) + jwt.decode( + firebase_token, + certs, + algorithms='RS256', + options={'verify_exp': False, 'verify_aud': False} + ) diff --git a/tests/test_jwk.py b/tests/test_jwk.py new file mode 100644 index 0000000..6ea5a1b --- /dev/null +++ b/tests/test_jwk.py @@ -0,0 +1,137 @@ +from jose import jwk +from jose.exceptions import JWKError +from jose.backends.base import Key +from jose.backends import ECKey, RSAKey + +import pytest + +hmac_key = { + "kty": "oct", + "kid": "018c0ae5-4d9b-471b-bfd6-eef314bc7037", + "use": "sig", + "alg": "HS256", + "k": "hJtXIZ2uSN5kbQfbtTNWbpdmhkV8FJG-Onbc6mxCcYg" +} + +rsa_key = { + "kty": "RSA", + "kid": "bilbo.baggins@hobbiton.example", + "use": "sig", + "n": "n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw", + "e": "AQAB" +} + +ec_key = { + "kty": "EC", + "kid": "bilbo.baggins@hobbiton.example", + "use": "sig", + "crv": "P-521", + "x": "AHKZLLOsCOzz5cY97ewNUajB957y-C-U88c3v13nmGZx6sYl_oJXu9A5RkTKqjqvjyekWF-7ytDyRXYgCF5cj0Kt", + "y": "AdymlHvOiLxXkEhayXQnNCvDX4h9htZaCJN34kfmC6pV5OhQHiraVySsUdaQkAgDPrwQrJmbnX9cwlGfP-HqHZR1" +} + + +class TestJWK: + + def test_interface(self): + + key = jwk.Key("key", "ALG") + + with pytest.raises(NotImplementedError): + key.sign('') + + with pytest.raises(NotImplementedError): + key.verify('', '') + + def test_invalid_hash_alg(self): + with pytest.raises(JWKError): + key = jwk.HMACKey(hmac_key, 'RS512') + + with pytest.raises(JWKError): + key = RSAKey(rsa_key, 'HS512') + + with pytest.raises(JWKError): + key = ECKey(ec_key, 'RS512') # noqa: F841 + + def test_invalid_jwk(self): + + with pytest.raises(JWKError): + key = jwk.HMACKey(rsa_key, 'HS256') + + with pytest.raises(JWKError): + key = RSAKey(hmac_key, 'RS256') + + with pytest.raises(JWKError): + key = ECKey(rsa_key, 'ES256') # noqa: F841 + + def test_RSAKey_errors(self): + + rsa_key = { + "kty": "RSA", + "kid": "bilbo.baggins@hobbiton.example", + "use": "sig", + "n": "n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw", + "e": "AQAB" + } + + with pytest.raises(JWKError): + key = RSAKey(rsa_key, 'HS256') + + rsa_key = { + "kty": "oct", + "kid": "bilbo.baggins@hobbiton.example", + "use": "sig", + "n": "n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw", + "e": "AQAB" + } + + with pytest.raises(JWKError): + key = RSAKey(rsa_key, 'RS256') # noqa: F841 + + def test_construct_from_jwk(self): + + hmac_key = { + "kty": "oct", + "kid": "018c0ae5-4d9b-471b-bfd6-eef314bc7037", + "use": "sig", + "alg": "HS256", + "k": "hJtXIZ2uSN5kbQfbtTNWbpdmhkV8FJG-Onbc6mxCcYg" + } + + key = jwk.construct(hmac_key) + assert isinstance(key, jwk.Key) + + def test_construct_EC_from_jwk(self): + key = ECKey(ec_key, algorithm='ES512') + assert isinstance(key, jwk.Key) + + def test_construct_from_jwk_missing_alg(self): + + hmac_key = { + "kty": "oct", + "kid": "018c0ae5-4d9b-471b-bfd6-eef314bc7037", + "use": "sig", + "k": "hJtXIZ2uSN5kbQfbtTNWbpdmhkV8FJG-Onbc6mxCcYg" + } + + with pytest.raises(JWKError): + key = jwk.construct(hmac_key) + + with pytest.raises(JWKError): + key = jwk.construct("key", algorithm="NONEXISTENT") # noqa: F841 + + def test_get_key(self): + hs_key = jwk.get_key("HS256") + assert hs_key == jwk.HMACKey + assert issubclass(hs_key, Key) + assert issubclass(jwk.get_key("RS256"), Key) + assert issubclass(jwk.get_key("ES256"), Key) + + assert jwk.get_key("NONEXISTENT") is None + + def test_register_key(self): + assert jwk.register_key("ALG", jwk.Key) + assert jwk.get_key("ALG") == jwk.Key + + with pytest.raises(TypeError): + assert jwk.register_key("ALG", object) diff --git a/tests/test_jws.py b/tests/test_jws.py new file mode 100644 index 0000000..f543a03 --- /dev/null +++ b/tests/test_jws.py @@ -0,0 +1,346 @@ +import json + +from jose import jwk +from jose import jws +from jose.constants import ALGORITHMS +from jose.exceptions import JWSError + +import pytest + + +@pytest.fixture +def payload(): + payload = b"test payload" + return payload + + +class TestJWS(object): + + def test_unicode_token(self): + token = u'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhIjoiYiJ9.jiMyrsmD8AoHWeQgmxZ5yq8z0lXS67_QGs52AzC8Ru8' + jws.verify(token, 'secret', ['HS256']) + + def test_multiple_keys(self): + old_jwk_verify = jwk.HMACKey.verify + try: + token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhIjoiYiJ9.jiMyrsmD8AoHWeQgmxZ5yq8z0lXS67_QGs52AzC8Ru8' + + def raise_exception(self, msg, sig): + if self.prepared_key == b'incorrect': + raise Exception("Mocked function jose.jwk.HMACKey.verify") + else: + return True + + jwk.HMACKey.verify = raise_exception + jws.verify(token, {'keys': ['incorrect', 'secret']}, ['HS256']) + finally: + jwk.HMACKey.verify = old_jwk_verify + + def test_invalid_algorithm(self): + token = u'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhIjoiYiJ9.jiMyrsmD8AoHWeQgmxZ5yq8z0lXS67_QGs52AzC8Ru8' + with pytest.raises(JWSError): + jws.verify(token, 'secret', [None]) + + def test_not_enough_segments(self): + token = 'eyJhIjoiYiJ9.jiMyrsmD8AoHWeQgmxZ5yq8z0lXS67_QGs52AzC8Ru8' + with pytest.raises(JWSError): + jws.verify(token, 'secret', ['HS256']) + + def test_header_invalid_padding(self): + token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9A.eyJhIjoiYiJ9.jiMyrsmD8AoHWeQgmxZ5yq8z0lXS67_QGs52AzC8Ru8' + with pytest.raises(JWSError): + jws.verify(token, 'secret', ['HS256']) + + def test_header_not_json(self): + token = 'dGVzdA.eyJhIjoiYiJ9.jiMyrsmD8AoHWeQgmxZ5yq8z0lXS67_QGs52AzC8Ru8' + with pytest.raises(JWSError): + jws.verify(token, 'secret', ['HS256']) + + def test_claims_invalid_padding(self): + token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.AeyJhIjoiYiJ9.jiMyrsmD8AoHWeQgmxZ5yq8z0lXS67_QGs52AzC8Ru8' + with pytest.raises(JWSError): + jws.verify(token, 'secret', ['HS256']) + + def test_claims_not_json(self): + token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.dGVzdA.jiMyrsmD8AoHWeQgmxZ5yq8z0lXS67_QGs52AzC8Ru8' + with pytest.raises(JWSError): + jws.verify(token, 'secret', ['HS256']) + + def test_invalid_key(self, payload): + with pytest.raises(JWSError): + jws.sign(payload, 'secret', algorithm='RS256') + + +class TestHMAC(object): + + def testHMAC256(self, payload): + token = jws.sign(payload, 'secret', algorithm=ALGORITHMS.HS256) + assert jws.verify(token, 'secret', ALGORITHMS.HS256) == payload + + def testHMAC384(self, payload): + token = jws.sign(payload, 'secret', algorithm=ALGORITHMS.HS384) + assert jws.verify(token, 'secret', ALGORITHMS.HS384) == payload + + def testHMAC512(self, payload): + token = jws.sign(payload, 'secret', algorithm=ALGORITHMS.HS512) + assert jws.verify(token, 'secret', ALGORITHMS.HS512) == payload + + def test_wrong_alg(self, payload): + token = jws.sign(payload, 'secret', algorithm=ALGORITHMS.HS256) + with pytest.raises(JWSError): + jws.verify(token, 'secret', ALGORITHMS.HS384) + + def test_wrong_key(self, payload): + token = jws.sign(payload, 'secret', algorithm=ALGORITHMS.HS256) + with pytest.raises(JWSError): + jws.verify(token, 'another', ALGORITHMS.HS256) + + def test_unsupported_alg(self, payload): + with pytest.raises(JWSError): + jws.sign(payload, 'secret', algorithm='SOMETHING') + + def test_add_headers(self, payload): + + additional_headers = { + 'test': 'header' + } + + expected_headers = { + 'test': 'header', + 'alg': 'HS256', + 'typ': 'JWT', + } + + token = jws.sign(payload, 'secret', headers=additional_headers) + header, payload, signing_input, signature = jws._load(token) + assert expected_headers == header + + +rsa_private_key = """-----BEGIN RSA PRIVATE KEY----- +MIIJKwIBAAKCAgEAtSKfSeI0fukRIX38AHlKB1YPpX8PUYN2JdvfM+XjNmLfU1M7 +4N0VmdzIX95sneQGO9kC2xMIE+AIlt52Yf/KgBZggAlS9Y0Vx8DsSL2HvOjguAdX +ir3vYLvAyyHin/mUisJOqccFKChHKjnk0uXy/38+1r17/cYTp76brKpU1I4kM20M +//dbvLBWjfzyw9ehufr74aVwr+0xJfsBVr2oaQFww/XHGz69Q7yHK6DbxYO4w4q2 +sIfcC4pT8XTPHo4JZ2M733Ea8a7HxtZS563/mhhRZLU5aynQpwaVv2U++CL6EvGt +8TlNZOkeRv8wz+Rt8B70jzoRpVK36rR+pHKlXhMGT619v82LneTdsqA25Wi2Ld/c +0niuul24A6+aaj2u9SWbxA9LmVtFntvNbRaHXE1SLpLPoIp8uppGF02Nz2v3ld8g +CnTTWfq/BQ80Qy8e0coRRABECZrjIMzHEg6MloRDy4na0pRQv61VogqRKDU2r3/V +ezFPQDb3ciYsZjWBr3HpNOkUjTrvLmFyOE9Q5R/qQGmc6BYtfk5rn7iIfXlkJAZH +XhBy+ElBuiBM+YSkFM7dH92sSIoZ05V4MP09Xcppx7kdwsJy72Sust9Hnd9B7V35 +YnVF6W791lVHnenhCJOziRmkH4xLLbPkaST2Ks3IHH7tVltM6NsRk3jNdVMCAwEA +AQKCAgEArx+0JXigDHtFZr4pYEPjwMgCBJ2dr8+L8PptB/4g+LoK9MKqR7M4aTO+ +PoILPXPyWvZq/meeDakyZLrcdc8ad1ArKF7baDBpeGEbkRA9JfV5HjNq/ea4gyvD +MCGou8ZPSQCnkRmr8LFQbJDgnM5Za5AYrwEv2aEh67IrTHq53W83rMioIumCNiG+ +7TQ7egEGiYsQ745GLrECLZhKKRTgt/T+k1cSk1LLJawme5XgJUw+3D9GddJEepvY +oL+wZ/gnO2ADyPnPdQ7oc2NPcFMXpmIQf29+/g7FflatfQhkIv+eC6bB51DhdMi1 +zyp2hOhzKg6jn74ixVX+Hts2/cMiAPu0NaWmU9n8g7HmXWc4+uSO/fssGjI3DLYK +d5xnhrq4a3ZO5oJLeMO9U71+Ykctg23PTHwNAGrsPYdjGcBnJEdtbXa31agI5PAG +6rgGUY3iSoWqHLgBTxrX04TWVvLQi8wbxh7BEF0yasOeZKxdE2IWYg75zGsjluyH +lOnpRa5lSf6KZ6thh9eczFHYtS4DvYBcZ9hZW/g87ie28SkBFxxl0brYt9uKNYJv +uajVG8kT80AC7Wzg2q7Wmnoww3JNJUbNths5dqKyUSlMFMIB/vOePFHLrA6qDfAn +sQHgUb9WHhUrYsH20XKpqR2OjmWU05bV4pSMW/JwG37o+px1yKECggEBANnwx0d7 +ksEMvJjeN5plDy3eMLifBI+6SL/o5TXDoFM6rJxF+0UP70uouYJq2dI+DCSA6c/E +sn7WAOirY177adKcBV8biwAtmKHnFnCs/kwAZq8lMvQPtNPJ/vq2n40kO48h8fxb +eGcmyAqFPZ4YKSxrPA4cdbHIuFSt9WyaUcVFmzdTFHVlRP70EXdmXHt84byWNB4C +Heq8zmrNxPNAi65nEkUks7iBQMtuvyV2+aXjDOTBMCd66IhIh2iZq1O7kXUwgh1O +H9hCa7oriHyAdgkKdKCWocmbPPENOETgjraA9wRIXwOYTDb1X5hMvi1mCHo8xjMj +u4szD03xJVi7WrsCggEBANTEblCkxEyhJqaMZF3U3df2Yr/ZtHqsrTr4lwB/MOKk +zmuSrROxheEkKIsxbiV+AxTvtPR1FQrlqbhTJRwy+pw4KPJ7P4fq2R/YBqvXSNBC +amTt6l2XdXqnAk3A++cOEZ2lU9ubfgdeN2Ih8rgdn1LWeOSjCWfExmkoU61/Xe6x +AMeXKQSlHKSnX9voxuE2xINHeU6ZAKy1kGmrJtEiWnI8b8C4s8fTyDtXJ1Lasys0 +iHO2Tz2jUhf4IJwb87Lk7Ize2MrI+oPzVDXlmkbjkB4tYyoiRTj8rk8pwBW/HVv0 +02pjOLTa4kz1kQ3lsZ/3As4zfNi7mWEhadmEsAIfYkkCggEBANO39r/Yqj5kUyrm +ZXnVxyM2AHq58EJ4I4hbhZ/vRWbVTy4ZRfpXeo4zgNPTXXvCzyT/HyS53vUcjJF7 +PfPdpXX2H7m/Fg+8O9S8m64mQHwwv5BSQOecAnzkdJG2q9T/Z+Sqg1w2uAbtQ9QE +kFFvA0ClhBfpSeTGK1wICq3QVLOh5SGf0fYhxR8wl284v4svTFRaTpMAV3Pcq2JS +N4xgHdH1S2hkOTt6RSnbklGg/PFMWxA3JMKVwiPy4aiZ8DhNtQb1ctFpPcJm9CRN +ejAI06IAyD/hVZZ2+oLp5snypHFjY5SDgdoKL7AMOyvHEdEkmAO32ot/oQefOLTt +GOzURVUCggEBALSx5iYi6HtT2SlUzeBKaeWBYDgiwf31LGGKwWMwoem5oX0GYmr5 +NwQP20brQeohbKiZMwrxbF+G0G60Xi3mtaN6pnvYZAogTymWI4RJH5OO9CCnVYUK +nkD+GRzDqqt97UP/Joq5MX08bLiwsBvhPG/zqVQzikdQfFjOYNJV+wY92LWpELLb +Lso/Q0/WDyExjA8Z4lH36vTCddTn/91Y2Ytu/FGmCzjICaMrzz+0cLlesgvjZsSo +MY4dskQiEQN7G9I/Z8pAiVEKlBf52N4fYUPfs/oShMty/O5KPNG7L0nrUKlnfr9J +rStC2l/9FK8P7pgEbiD6obY11FlhMMF8udECggEBAIKhvOFtipD1jqDOpjOoR9sK +/lRR5bVVWQfamMDN1AwmjJbVHS8hhtYUM/4sh2p12P6RgoO8fODf1vEcWFh3xxNZ +E1pPCPaICD9i5U+NRvPz2vC900HcraLRrUFaRzwhqOOknYJSBrGzW+Cx3YSeaOCg +nKyI8B5gw4C0G0iL1dSsz2bR1O4GNOVfT3R6joZEXATFo/Kc2L0YAvApBNUYvY0k +bjJ/JfTO5060SsWftf4iw3jrhSn9RwTTYdq/kErGFWvDGJn2MiuhMe2onNfVzIGR +mdUxHwi1ulkspAn/fmY7f0hZpskDwcHyZmbKZuk+NU/FJ8IAcmvk9y7m25nSSc8= +-----END RSA PRIVATE KEY-----""" + +rsa_public_key = """-----BEGIN PUBLIC KEY----- +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtSKfSeI0fukRIX38AHlK +B1YPpX8PUYN2JdvfM+XjNmLfU1M74N0VmdzIX95sneQGO9kC2xMIE+AIlt52Yf/K +gBZggAlS9Y0Vx8DsSL2HvOjguAdXir3vYLvAyyHin/mUisJOqccFKChHKjnk0uXy +/38+1r17/cYTp76brKpU1I4kM20M//dbvLBWjfzyw9ehufr74aVwr+0xJfsBVr2o +aQFww/XHGz69Q7yHK6DbxYO4w4q2sIfcC4pT8XTPHo4JZ2M733Ea8a7HxtZS563/ +mhhRZLU5aynQpwaVv2U++CL6EvGt8TlNZOkeRv8wz+Rt8B70jzoRpVK36rR+pHKl +XhMGT619v82LneTdsqA25Wi2Ld/c0niuul24A6+aaj2u9SWbxA9LmVtFntvNbRaH +XE1SLpLPoIp8uppGF02Nz2v3ld8gCnTTWfq/BQ80Qy8e0coRRABECZrjIMzHEg6M +loRDy4na0pRQv61VogqRKDU2r3/VezFPQDb3ciYsZjWBr3HpNOkUjTrvLmFyOE9Q +5R/qQGmc6BYtfk5rn7iIfXlkJAZHXhBy+ElBuiBM+YSkFM7dH92sSIoZ05V4MP09 +Xcppx7kdwsJy72Sust9Hnd9B7V35YnVF6W791lVHnenhCJOziRmkH4xLLbPkaST2 +Ks3IHH7tVltM6NsRk3jNdVMCAwEAAQ== +-----END PUBLIC KEY-----""" + + +@pytest.fixture +def jwk_set(): + return {u'keys': [{u'alg': u'RS256', + u'e': u'AQAB', + u'kid': u'40aa42edac0614d7ca3f57f97ee866cdfba3b61a', + u'kty': u'RSA', + u'n': u'6lm9AEGLPFpVqnfeVFuTIZsj7vz_kxla6uW1WWtosM_MtIjXkyyiSolxiSOs3bzG66iVm71023QyOzKYFbio0hI-yZauG3g9nH-zb_AHScsjAKagHtrHmTdtq0JcNkQnAaaUwxVbjwMlYAcOh87W5jWj_MAcPvc-qjy8-WJ81UgoOUZNiKByuF4-9igxKZeskGRXuTPX64kWGBmKl-tM7VnCGMKoK3m92NPrktfBoNN_EGGthNfQsKFUdQFJFtpMuiXp9Gib7dcMGabxcG2GUl-PU086kPUyUdUYiMN2auKSOxSUZgDjT7DcI8Sn8kdQ0-tImaHi54JNa1PNNdKRpw', + u'use': u'sig'}, + {u'alg': u'RS256', + u'e': u'AQAB', + u'kid': u'8fbbeea40332d2c0d27e37e1904af29b64594e57', + u'kty': u'RSA', + u'n': u'z7h6_rt35-j6NV2iQvYIuR3xvsxmEImgMl8dc8CFl4SzEWrry3QILajKxQZA9YYYfXIcZUG_6R6AghVMJetNIl2AhCoEr3RQjjNsm9PE6h5p2kQ-zIveFeb__4oIkVihYtxtoYBSdVj69nXLUAJP2bxPfU8RDp5X7hT62pKR05H8QLxH8siIQ5qR2LGFw_dJcitAVRRQofuaj_9u0CLZBfinqyRkBc7a0zi7pBxtEiIbn9sRr8Kkb_Boap6BHbnLS-YFBVarcgFBbifRf7NlK5dqE9z4OUb-dx8wCMRIPVAx_hV4Qx2anTgp1sDA6V4vd4NaCOZX-mSctNZqQmKtNw', + u'use': u'sig'}, + {u'alg': u'RS256', + u'e': u'AQAB', + u'kid': u'6758b0b8eb341e90454860432d6a1648bf4de03b', + u'kty': u'RSA', + u'n': u'5K0rYaA7xtqSe1nFn_nCA10uUXY81NcohMeFsYLbBlx_NdpsmbpgtXJ6ektYR7rUdtMMLu2IONlNhkWlx-lge91okyacUrWHP88PycilUE-RnyVjbPEm3seR0VefgALfN4y_e77ljq2F7W2_kbUkTvDzriDIWvQT0WwVF5FIOBydfDDs92S-queaKgLBwt50SXJCZryLew5ODrwVsFGI4Et6MLqjS-cgWpCNwzcRqjBRsse6DXnex_zSRII4ODzKIfX4qdFBKZHO_BkTsK9DNkUayrr9cz8rFRK6TEH6XTVabgsyd6LP6PTxhpiII_pTYRSWk7CGMnm2nO0dKxzaFQ', + u'use': u'sig'}]} + + +google_id_token = ( + 'eyJhbGciOiJSUzI1NiIsImtpZCI6IjhmYmJlZWE0MDMzMmQyYzBkMjdlMzdlMTkwN' + 'GFmMjliNjQ1OTRlNTcifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5' + 'jb20iLCJhdF9oYXNoIjoiUUY5RnRjcHlmbUFBanJuMHVyeUQ5dyIsImF1ZCI6IjQw' + 'NzQwODcxODE5Mi5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsInN1YiI6IjEwN' + 'zkzMjQxNjk2NTIwMzIzNDA3NiIsImF6cCI6IjQwNzQwODcxODE5Mi5hcHBzLmdvb2' + 'dsZXVzZXJjb250ZW50LmNvbSIsImlhdCI6MTQ2ODYyMjQ4MCwiZXhwIjoxNDY4NjI' + '2MDgwfQ.Nz6VREh7smvfVRWNHlbKZ6W_DX57akRUGrDTcns06ndAwrslwUlBeFsWY' + 'RLon_tDw0QCeQCGvw7l1AT440UQBRP-mtqK_2Yny2JmIQ7Ll6UAIHRhXOD1uj9w5v' + 'X0jyI1MbjDtODeDWWn_9EDJRBd4xmwKhAONuWodTgSi7qGe1UVmzseFNNkKdoo54d' + 'XhCJiyiRAMnWB_FQDveRJghche131pd9O_E4Wj6hf_zCcMTaDaLDOmElcQe-WsKWA' + 'A3YwHFEWOLO_7x6u4uGmhItPGH7zsOTzYxPYhZMSZusgVg9fbE1kSlHVSyQrcp_rR' + 'WNz7vOIbvIlBR9Jrq5MIqbkkg' +) + + +class TestGetKeys(object): + + def test_dict(self): + assert ({},) == jws._get_keys({}) + + def test_custom_object(self): + class MyDict(dict): + pass + mydict = MyDict() + assert (mydict,) == jws._get_keys(mydict) + + def test_RFC7517_string(self): + key = '{"keys": [{}, {}]}' + assert [{}, {}] == jws._get_keys(key) + + def test_RFC7517_jwk(self): + key = {'kty': 'hsa', 'k': 'secret', 'alg': 'HS256', 'use': 'sig'} + assert (key, ) == jws._get_keys(key) + + def test_RFC7517_mapping(self): + key = {"keys": [{}, {}]} + assert [{}, {}] == jws._get_keys(key) + + def test_string(self): + assert ('test',) == jws._get_keys('test') + + def test_tuple(self): + assert ('test', 'key') == jws._get_keys(('test', 'key')) + + def test_list(self): + assert ['test', 'key'] == jws._get_keys(['test', 'key']) + + +class TestRSA(object): + + def test_jwk_set(self, jwk_set): + # Would raise a JWSError if validation failed. + payload = jws.verify(google_id_token, jwk_set, ALGORITHMS.RS256) + iss = json.loads(payload.decode('utf-8'))['iss'] + assert iss == "https://accounts.google.com" + + def test_jwk_set_failure(self, jwk_set): + # Remove the key that was used to sign this token. + del jwk_set['keys'][1] + with pytest.raises(JWSError): + payload = jws.verify(google_id_token, jwk_set, ALGORITHMS.RS256) # noqa: F841 + + def test_RSA256(self, payload): + token = jws.sign(payload, rsa_private_key, algorithm=ALGORITHMS.RS256) + assert jws.verify(token, rsa_public_key, ALGORITHMS.RS256) == payload + + def test_RSA384(self, payload): + token = jws.sign(payload, rsa_private_key, algorithm=ALGORITHMS.RS384) + assert jws.verify(token, rsa_public_key, ALGORITHMS.RS384) == payload + + def test_RSA512(self, payload): + token = jws.sign(payload, rsa_private_key, algorithm=ALGORITHMS.RS512) + assert jws.verify(token, rsa_public_key, ALGORITHMS.RS512) == payload + + def test_wrong_alg(self, payload): + token = jws.sign(payload, rsa_private_key, algorithm=ALGORITHMS.RS256) + with pytest.raises(JWSError): + jws.verify(token, rsa_public_key, ALGORITHMS.RS384) + + def test_wrong_key(self, payload): + token = jws.sign(payload, rsa_private_key, algorithm=ALGORITHMS.RS256) + with pytest.raises(JWSError): + jws.verify(token, rsa_public_key, ALGORITHMS.HS256) + + +ec_private_key = """-----BEGIN EC PRIVATE KEY----- +MIHcAgEBBEIBzs13YUnYbLfYXTz4SG4DE4rPmsL3wBTdy34JcO+BDpI+NDZ0pqam +UM/1sGZT+8hqUjSeQo6oz+Mx0VS6SJh31zygBwYFK4EEACOhgYkDgYYABACYencK +8pm/iAeDVptaEZTZwNT0yW/muVwvvwkzS/D6GDCLsnLfI6e1FwEnTJF/GPFUlN5l +9JSLxsbbFdM1muI+NgBE6ZLR1GZWjsNzu7BOB8RMy/mvSTokZwyIaWvWSn3hOF4i +/4iczJnzJhUKDqHe5dJ//PLd7R3WVHxkvv7jFNTKYg== +-----END EC PRIVATE KEY-----""" + +ec_public_key = """-----BEGIN PUBLIC KEY----- +MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQAmHp3CvKZv4gHg1abWhGU2cDU9Mlv +5rlcL78JM0vw+hgwi7Jy3yOntRcBJ0yRfxjxVJTeZfSUi8bG2xXTNZriPjYAROmS +0dRmVo7Dc7uwTgfETMv5r0k6JGcMiGlr1kp94TheIv+InMyZ8yYVCg6h3uXSf/zy +3e0d1lR8ZL7+4xTUymI= +-----END PUBLIC KEY-----""" + + +class TestEC(object): + + def test_EC256(self, payload): + token = jws.sign(payload, ec_private_key, algorithm=ALGORITHMS.ES256) + assert jws.verify(token, ec_public_key, ALGORITHMS.ES256) == payload + + def test_EC384(self, payload): + token = jws.sign(payload, ec_private_key, algorithm=ALGORITHMS.ES384) + assert jws.verify(token, ec_public_key, ALGORITHMS.ES384) == payload + + def test_EC512(self, payload): + token = jws.sign(payload, ec_private_key, algorithm=ALGORITHMS.ES512) + assert jws.verify(token, ec_public_key, ALGORITHMS.ES512) == payload + + def test_wrong_alg(self, payload): + token = jws.sign(payload, ec_private_key, algorithm=ALGORITHMS.ES256) + with pytest.raises(JWSError): + jws.verify(token, rsa_public_key, ALGORITHMS.ES384) + + +class TestLoad(object): + + def test_header_not_mapping(self): + token = 'WyJ0ZXN0Il0.eyJhIjoiYiJ9.jiMyrsmD8AoHWeQgmxZ5yq8z0lXS67_QGs52AzC8Ru8' + with pytest.raises(JWSError): + jws.verify(token, 'secret', ['HS256']) + + def test_claims_not_mapping(self): + token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.WyJ0ZXN0Il0.jiMyrsmD8AoHWeQgmxZ5yq8z0lXS67_QGs52AzC8Ru8' + with pytest.raises(JWSError): + jws.verify(token, 'secret', ['HS256']) + + def test_signature_padding(self): + token = 'eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJoZWxsbyI6ICJ3b3JsZCJ9.aatvagLDLoaiJKxOKqpBXSEGy7SYSifZhjntgm9ctpyj8' + with pytest.raises(JWSError): + jws.verify(token, 'secret', ['HS256']) diff --git a/tests/test_jwt.py b/tests/test_jwt.py new file mode 100644 index 0000000..03f5c28 --- /dev/null +++ b/tests/test_jwt.py @@ -0,0 +1,604 @@ + +import base64 +import json + +from jose import jws +from jose import jwt +from jose.exceptions import JWTError + +from datetime import datetime +from datetime import timedelta + +import pytest + + +@pytest.fixture +def claims(): + claims = { + 'a': 'b' + } + return claims + + +@pytest.fixture +def key(): + return 'secret' + + +@pytest.fixture +def headers(): + headers = { + 'kid': 'my-key-id', + } + return headers + + +class TestJWT: + + def test_no_alg(self, claims, key): + token = jwt.encode(claims, key, algorithm='HS384') + b64header, b64payload, b64signature = token.split('.') + header_json = base64.urlsafe_b64decode(b64header.encode('utf-8')) + header = json.loads(header_json.decode('utf-8')) + del header['alg'] + bad_header_json_bytes = json.dumps(header).encode('utf-8') + bad_b64header_bytes = base64.urlsafe_b64encode(bad_header_json_bytes) + bad_b64header_bytes_short = bad_b64header_bytes.replace(b'=', b'') + bad_b64header = bad_b64header_bytes.decode('utf-8') + bad_token = '.'.join([bad_b64header, b64payload, b64signature]) + with pytest.raises(JWTError): + jwt.decode( + token=bad_token, + key=key, + algorithms=[]) + + def test_invalid_claims_json(self): + old_jws_verify = jws.verify + try: + token = u'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhIjoiYiJ9.jiMyrsmD8AoHWeQgmxZ5yq8z0lXS67_QGs52AzC8Ru8' + + def return_invalid_json(token, key, algorithms, verify=True): + return b'["a", "b"}' + + jws.verify = return_invalid_json + + with pytest.raises(JWTError, match='Invalid payload string: '): + jwt.decode(token, 'secret', ['HS256']) + finally: + jws.verify = old_jws_verify + + def test_invalid_claims(self): + old_jws_verify = jws.verify + try: + token = u'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhIjoiYiJ9.jiMyrsmD8AoHWeQgmxZ5yq8z0lXS67_QGs52AzC8Ru8' + + def return_encoded_array(token, key, algorithms, verify=True): + return b'["a","b"]' + + jws.verify = return_encoded_array + + with pytest.raises(JWTError, match='Invalid payload string: must be a json object'): + jwt.decode(token, 'secret', ['HS256']) + finally: + jws.verify = old_jws_verify + + def test_non_default_alg(self, claims, key): + encoded = jwt.encode(claims, key, algorithm='HS384') + decoded = jwt.decode(encoded, key, algorithms='HS384') + assert claims == decoded + + def test_non_default_alg_positional_bwcompat(self, claims, key): + encoded = jwt.encode(claims, key, 'HS384') + decoded = jwt.decode(encoded, key, 'HS384') + assert claims == decoded + + def test_no_alg_default_headers(self, claims, key, headers): + token = jwt.encode(claims, key, algorithm='HS384') + b64header, b64payload, b64signature = token.split('.') + bad_token = b64header + '.' + b64payload + with pytest.raises(JWTError): + jwt.get_unverified_headers(bad_token) + + def test_non_default_headers(self, claims, key, headers): + encoded = jwt.encode(claims, key, headers=headers) + decoded = jwt.decode(encoded, key) + assert claims == decoded + all_headers = jwt.get_unverified_headers(encoded) + for k, v in headers.items(): + assert all_headers[k] == v + + def test_deterministic_headers(self): + from collections import OrderedDict + from jose.utils import base64url_decode + + claims = {"a": "b"} + key = "secret" + + headers1 = OrderedDict(( + ('kid', 'my-key-id'), + ('another_key', 'another_value'), + )) + encoded1 = jwt.encode(claims, key, algorithm='HS256', headers=headers1) + encoded_headers1 = encoded1.split('.', 1)[0] + + headers2 = OrderedDict(( + ('another_key', 'another_value'), + ('kid', 'my-key-id'), + )) + encoded2 = jwt.encode(claims, key, algorithm='HS256', headers=headers2) + encoded_headers2 = encoded2.split('.', 1)[0] + + assert encoded_headers1 == encoded_headers2 + + # manually decode header to compare it to known good + decoded_headers1 = base64url_decode(encoded_headers1.encode('utf-8')) + assert decoded_headers1 == b"""{"alg":"HS256","another_key":"another_value","kid":"my-key-id","typ":"JWT"}""" + + def test_encode(self, claims, key): + + expected = ( + ( + 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9' + '.eyJhIjoiYiJ9' + '.xNtk2S0CNbCBZX_f67pFgGRugaP1xi2ICfet3nwOSxw' + ), + ( + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9' + '.eyJhIjoiYiJ9' + '.jiMyrsmD8AoHWeQgmxZ5yq8z0lXS67_QGs52AzC8Ru8' + ) + ) + + encoded = jwt.encode(claims, key) + + assert encoded in expected + + def test_decode(self, claims, key): + + token = ( + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9' + '.eyJhIjoiYiJ9' + '.jiMyrsmD8AoHWeQgmxZ5yq8z0lXS67_QGs52AzC8Ru8' + ) + + decoded = jwt.decode(token, key) + + assert decoded == claims + + def test_leeway_is_int(self): + pass + + def test_leeway_is_timedelta(self, claims, key): + + nbf = datetime.utcnow() + timedelta(seconds=5) + leeway = timedelta(seconds=10) + + claims = { + 'nbf': nbf, + } + + options = { + 'leeway': leeway + } + + token = jwt.encode(claims, key) + jwt.decode(token, key, options=options) + + def test_iat_not_int(self, key): + + claims = { + 'iat': 'test' + } + + token = jwt.encode(claims, key) + + with pytest.raises(JWTError): + jwt.decode(token, key) + + def test_nbf_not_int(self, key): + + claims = { + 'nbf': 'test' + } + + token = jwt.encode(claims, key) + + with pytest.raises(JWTError): + jwt.decode(token, key) + + def test_nbf_datetime(self, key): + + nbf = datetime.utcnow() - timedelta(seconds=5) + + claims = { + 'nbf': nbf + } + + token = jwt.encode(claims, key) + jwt.decode(token, key) + + def test_nbf_with_leeway(self, key): + + nbf = datetime.utcnow() + timedelta(seconds=5) + + claims = { + 'nbf': nbf, + } + + options = { + 'leeway': 10 + } + + token = jwt.encode(claims, key) + jwt.decode(token, key, options=options) + + def test_nbf_in_future(self, key): + + nbf = datetime.utcnow() + timedelta(seconds=5) + + claims = { + 'nbf': nbf + } + + token = jwt.encode(claims, key) + + with pytest.raises(JWTError): + jwt.decode(token, key) + + def test_nbf_skip(self, key): + + nbf = datetime.utcnow() + timedelta(seconds=5) + + claims = { + 'nbf': nbf + } + + token = jwt.encode(claims, key) + + with pytest.raises(JWTError): + jwt.decode(token, key) + + options = { + 'verify_nbf': False + } + + jwt.decode(token, key, options=options) + + def test_exp_not_int(self, key): + + claims = { + 'exp': 'test' + } + + token = jwt.encode(claims, key) + + with pytest.raises(JWTError): + jwt.decode(token, key) + + def test_exp_datetime(self, key): + + exp = datetime.utcnow() + timedelta(seconds=5) + + claims = { + 'exp': exp + } + + token = jwt.encode(claims, key) + jwt.decode(token, key) + + def test_exp_with_leeway(self, key): + + exp = datetime.utcnow() - timedelta(seconds=5) + + claims = { + 'exp': exp, + } + + options = { + 'leeway': 10 + } + + token = jwt.encode(claims, key) + jwt.decode(token, key, options=options) + + def test_exp_in_past(self, key): + + exp = datetime.utcnow() - timedelta(seconds=5) + + claims = { + 'exp': exp + } + + token = jwt.encode(claims, key) + + with pytest.raises(JWTError): + jwt.decode(token, key) + + def test_exp_skip(self, key): + + exp = datetime.utcnow() - timedelta(seconds=5) + + claims = { + 'exp': exp + } + + token = jwt.encode(claims, key) + + with pytest.raises(JWTError): + jwt.decode(token, key) + + options = { + 'verify_exp': False + } + + jwt.decode(token, key, options=options) + + def test_aud_string(self, key): + + aud = 'audience' + + claims = { + 'aud': aud + } + + token = jwt.encode(claims, key) + jwt.decode(token, key, audience=aud) + + def test_aud_list(self, key): + + aud = 'audience' + + claims = { + 'aud': [aud] + } + + token = jwt.encode(claims, key) + jwt.decode(token, key, audience=aud) + + def test_aud_list_multiple(self, key): + + aud = 'audience' + + claims = { + 'aud': [aud, 'another'] + } + + token = jwt.encode(claims, key) + jwt.decode(token, key, audience=aud) + + def test_aud_list_is_strings(self, key): + + aud = 'audience' + + claims = { + 'aud': [aud, 1] + } + + token = jwt.encode(claims, key) + with pytest.raises(JWTError): + jwt.decode(token, key, audience=aud) + + def test_aud_case_sensitive(self, key): + + aud = 'audience' + + claims = { + 'aud': [aud] + } + + token = jwt.encode(claims, key) + with pytest.raises(JWTError): + jwt.decode(token, key, audience='AUDIENCE') + + def test_aud_empty_claim(self, claims, key): + + aud = 'audience' + + token = jwt.encode(claims, key) + jwt.decode(token, key, audience=aud) + + def test_aud_not_string_or_list(self, key): + + aud = 1 + + claims = { + 'aud': aud + } + + token = jwt.encode(claims, key) + with pytest.raises(JWTError): + jwt.decode(token, key) + + def test_aud_given_number(self, key): + + aud = 'audience' + + claims = { + 'aud': aud + } + + token = jwt.encode(claims, key) + with pytest.raises(JWTError): + jwt.decode(token, key, audience=1) + + def test_iss_string(self, key): + + iss = 'issuer' + + claims = { + 'iss': iss + } + + token = jwt.encode(claims, key) + jwt.decode(token, key, issuer=iss) + + def test_iss_list(self, key): + + iss = 'issuer' + + claims = { + 'iss': iss + } + + token = jwt.encode(claims, key) + jwt.decode(token, key, issuer=['https://issuer', 'issuer']) + + def test_iss_tuple(self, key): + + iss = 'issuer' + + claims = { + 'iss': iss + } + + token = jwt.encode(claims, key) + jwt.decode(token, key, issuer=('https://issuer', 'issuer')) + + def test_iss_invalid(self, key): + + iss = 'issuer' + + claims = { + 'iss': iss + } + + token = jwt.encode(claims, key) + with pytest.raises(JWTError): + jwt.decode(token, key, issuer='another') + + def test_sub_string(self, key): + + sub = 'subject' + + claims = { + 'sub': sub + } + + token = jwt.encode(claims, key) + jwt.decode(token, key) + + def test_sub_invalid(self, key): + + sub = 1 + + claims = { + 'sub': sub + } + + token = jwt.encode(claims, key) + with pytest.raises(JWTError): + jwt.decode(token, key) + + def test_sub_correct(self, key): + + sub = 'subject' + + claims = { + 'sub': sub + } + + token = jwt.encode(claims, key) + jwt.decode(token, key, subject=sub) + + def test_sub_incorrect(self, key): + + sub = 'subject' + + claims = { + 'sub': sub + } + + token = jwt.encode(claims, key) + with pytest.raises(JWTError): + jwt.decode(token, key, subject='another') + + def test_jti_string(self, key): + + jti = 'JWT ID' + + claims = { + 'jti': jti + } + + token = jwt.encode(claims, key) + jwt.decode(token, key) + + def test_jti_invalid(self, key): + + jti = 1 + + claims = { + 'jti': jti + } + + token = jwt.encode(claims, key) + with pytest.raises(JWTError): + jwt.decode(token, key) + + def test_at_hash(self, claims, key): + access_token = '' + token = jwt.encode(claims, key, access_token=access_token) + payload = jwt.decode(token, key, access_token=access_token) + assert 'at_hash' in payload + + def test_at_hash_invalid(self, claims, key): + token = jwt.encode(claims, key, access_token='') + with pytest.raises(JWTError): + jwt.decode(token, key, access_token='') + + def test_at_hash_missing_access_token(self, claims, key): + token = jwt.encode(claims, key, access_token='') + with pytest.raises(JWTError): + jwt.decode(token, key) + + def test_at_hash_missing_claim(self, claims, key): + token = jwt.encode(claims, key) + payload = jwt.decode(token, key, access_token='') + assert 'at_hash' not in payload + + def test_at_hash_unable_to_calculate(self, claims, key): + token = jwt.encode(claims, key, access_token='') + with pytest.raises(JWTError): + jwt.decode(token, key, access_token='\xe2') + + def test_bad_claims(self): + bad_token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.iOJ5SiNfaNO_pa2J4Umtb3b3zmk5C18-mhTCVNsjnck' + with pytest.raises(JWTError): + jwt.get_unverified_claims(bad_token) + + def test_unverified_claims_string(self): + token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.aW52YWxpZCBjbGFpbQ.iOJ5SiNfaNO_pa2J4Umtb3b3zmk5C18-mhTCVNsjnck' + with pytest.raises(JWTError): + jwt.get_unverified_claims(token) + + def test_unverified_claims_list(self): + token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.WyJpbnZhbGlkIiwgImNsYWltcyJd.nZvw_Rt1FfUPb5OiVbrSYZGtWSE5c-gdJ6nQnTTBkYo' + with pytest.raises(JWTError): + jwt.get_unverified_claims(token) + + def test_unverified_claims_object(self, claims, key): + token = jwt.encode(claims, key) + assert jwt.get_unverified_claims(token) == claims + + @pytest.mark.parametrize( + "claim,value", [ + ("aud", "aud"), + ("ait", "ait"), + ("exp", datetime.utcnow() + timedelta(seconds=3600)), + ("nbf", datetime.utcnow() - timedelta(seconds=5)), + ("iss", "iss"), + ("sub", "sub"), + ("jti", "jti"), + ] + ) + def test_require(self, claims, key, claim, value): + options = {"require_" + claim: True, "verify_" + claim: False} + + token = jwt.encode(claims, key) + with pytest.raises(JWTError): + jwt.decode(token, key, options=options, audience=str(value)) + + new_claims = dict(claims) + new_claims[claim] = value + token = jwt.encode(new_claims, key) + jwt.decode(token, key, options=options, audience=str(value)) diff --git a/tests/test_utils.py b/tests/test_utils.py new file mode 100644 index 0000000..71ced1f --- /dev/null +++ b/tests/test_utils.py @@ -0,0 +1,16 @@ + +from datetime import timedelta + +from jose import utils + + +class TestUtils: + + def test_total_seconds(self): + td = timedelta(seconds=5) + + assert utils.timedelta_total_seconds(td) == 5 + + def test_long_to_base64(self): + assert utils.long_to_base64(0xDEADBEEF) == b'3q2-7w' + assert utils.long_to_base64(0xCAFED00D, size=10) == b'AAAAAAAAyv7QDQ' diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..f709556 --- /dev/null +++ b/tox.ini @@ -0,0 +1,50 @@ +[tox] +minversion = 3.4.0 +envlist = + py{27,35,36,py}-{base,cryptography-only,pycryptodome-norsa,pycrypto-norsa,compatibility}, + flake8 +skip_missing_interpreters = True + +[testenv:basecommand] +commands = + pip --version + py.test --cov-report term-missing --cov jose {posargs} + +[testenv:compatibility] +extras = + cryptography + pycrypto + pycryptodome + +[testenv] +deps = + pytest + pytest-cov + pytest-runner +commands_pre = + # Remove the python-rsa and python-ecdsa backends + only: pip uninstall -y ecdsa rsa + # Remove just the python-rsa backend + norsa: pip uninstall -y rsa +commands = + # Test the python-rsa backend + base: {[testenv:basecommand]commands} -m "not (cryptography or pycryptodome or pycrypto or backend_compatibility)" + # Test the pyca/cryptography backend + cryptography: {[testenv:basecommand]commands} -m "not (pycryptodome or pycrypto or backend_compatibility)" + # Test the pycryptodome backend + pycryptodome: {[testenv:basecommand]commands} -m "not (cryptography or pycrypto or backend_compatibility)" + # Test the pycrypto backend + pycrypto: {[testenv:basecommand]commands} -m "not (cryptography or pycryptodome or backend_compatibility)" + # Test cross-backend compatibility and coexistence + compatibility: {[testenv:basecommand]commands} +extras = + cryptography: cryptography + pycryptodome: pycryptodome + pycrypto: pycrypto + compatibility: {[testenv:compatibility]extras} + +[testenv:flake8] +skip_install= True +deps = + flake8 +commands = flake8 jose setup.py