Add keystone-manage create_jws_keypair functionality
Thw JSON Web Token provider implementation is going to need keys in order to issue and validate tokens, very similar to how the fernet provider operates, but using asymmetric signing instead of symmetric encryption. This commit addes a new subcommand to the keystone-manage binary that creates a ECDSA key pair for creating and validating JWS tokens. bp json-web-tokens Change-Id: I9cf5c168bae2a90aba3d696e3f6ce3028998121a
This commit is contained in:
parent
781aea6193
commit
1abe8a2ec0
@ -8,6 +8,7 @@ Invoking ``keystone-manage`` by itself will give you some usage information.
|
||||
Available commands:
|
||||
|
||||
* ``bootstrap``: Perform the basic bootstrap process.
|
||||
* ``create_jws_keypair``: Create an ECDSA key pair for JWS token signing.
|
||||
* ``credential_migrate``: Encrypt credentials using a new primary key.
|
||||
* ``credential_rotate``: Rotate Fernet keys for credential encryption.
|
||||
* ``credential_setup``: Setup a Fernet key repository for credential encryption.
|
||||
|
@ -31,6 +31,7 @@ from keystone.cmd import bootstrap
|
||||
from keystone.cmd import doctor
|
||||
from keystone.common import driver_hints
|
||||
from keystone.common import fernet_utils
|
||||
from keystone.common import jwt_utils
|
||||
from keystone.common import sql
|
||||
from keystone.common.sql import upgrades
|
||||
from keystone.common import utils
|
||||
@ -478,6 +479,43 @@ class FernetRotate(BasePermissionsSetup):
|
||||
keystone_user_id, keystone_group_id, 'fernet_receipts')
|
||||
|
||||
|
||||
class CreateJWSKeyPair(BasePermissionsSetup):
|
||||
"""Create a key pair for signing and validating JWS tokens.
|
||||
|
||||
This command creates a public and private key pair to use for signing and
|
||||
validating JWS token signatures. The key pair is written to the directory
|
||||
where the command is invoked.
|
||||
|
||||
"""
|
||||
|
||||
name = 'create_jws_keypair'
|
||||
|
||||
@classmethod
|
||||
def add_argument_parser(cls, subparsers):
|
||||
parser = super(CreateJWSKeyPair, cls).add_argument_parser(subparsers)
|
||||
|
||||
parser.add_argument(
|
||||
'--force', action='store_true',
|
||||
help=('Forcibly overwrite keys if they already exist')
|
||||
)
|
||||
return parser
|
||||
|
||||
@classmethod
|
||||
def main(cls):
|
||||
current_directory = os.getcwd()
|
||||
private_key_path = os.path.join(current_directory, 'private.pem')
|
||||
public_key_path = os.path.join(current_directory, 'public.pem')
|
||||
|
||||
if os.path.isfile(private_key_path) and not CONF.command.force:
|
||||
raise SystemExit(_('Private key %(path)s already exists')
|
||||
% {'path': private_key_path})
|
||||
if os.path.isfile(public_key_path) and not CONF.command.force:
|
||||
raise SystemExit(_('Public key %(path)s already exists')
|
||||
% {'path': public_key_path})
|
||||
|
||||
jwt_utils.create_jws_keypair(private_key_path, public_key_path)
|
||||
|
||||
|
||||
class TokenSetup(BasePermissionsSetup):
|
||||
"""Setup a key repository for tokens.
|
||||
|
||||
@ -1253,6 +1291,7 @@ CMDS = [
|
||||
DomainConfigUpload,
|
||||
FernetRotate,
|
||||
FernetSetup,
|
||||
CreateJWSKeyPair,
|
||||
MappingPopulate,
|
||||
MappingPurge,
|
||||
MappingEngineTester,
|
||||
|
43
keystone/common/jwt_utils.py
Normal file
43
keystone/common/jwt_utils.py
Normal file
@ -0,0 +1,43 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives.asymmetric import ec
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
|
||||
|
||||
def create_jws_keypair(private_key_path, public_key_path):
|
||||
"""Create an ECDSA key pair using an secp256r1, or NIST P-256, curve.
|
||||
|
||||
:param private_key_path: location to save the private key
|
||||
:param public_key_path: location to save the public key
|
||||
|
||||
"""
|
||||
private_key = ec.generate_private_key(ec.SECP256R1(), default_backend())
|
||||
|
||||
with open(private_key_path, 'wb') as f:
|
||||
f.write(
|
||||
private_key.private_bytes(
|
||||
encoding=serialization.Encoding.PEM,
|
||||
format=serialization.PrivateFormat.PKCS8,
|
||||
encryption_algorithm=serialization.NoEncryption()
|
||||
)
|
||||
)
|
||||
|
||||
public_key = private_key.public_key()
|
||||
with open(public_key_path, 'wb') as f:
|
||||
f.write(
|
||||
public_key.public_bytes(
|
||||
encoding=serialization.Encoding.PEM,
|
||||
format=serialization.PublicFormat.SubjectPublicKeyInfo
|
||||
)
|
||||
)
|
Loading…
Reference in New Issue
Block a user