keyserver: add generate key function

The superuser API, initdb, and tests will all need this functionality.
This commit is contained in:
Jimmy Zelinskie 2016-04-06 20:03:04 -04:00 committed by Jimmy Zelinskie
parent 23a8a29654
commit d19eb16b45
4 changed files with 35 additions and 24 deletions

View file

@ -1,10 +1,18 @@
import json
from calendar import timegm
from datetime import datetime, timedelta
from hashlib import sha256
from peewee import JOIN_LEFT_OUTER
from Crypto.PublicKey import RSA
from jwkest.jwk import RSAKey
from data.database import db_for_update, User, ServiceKey, ServiceKeyApproval
from data.model import ServiceKeyDoesNotExist, ServiceKeyAlreadyApproved, db_transaction, config
from data.model.notification import create_notification, delete_all_notifications_by_path_prefix
from util import canonicalize
def _expired_keys_clause(service):
return ((ServiceKey.service == service) &
@ -49,6 +57,18 @@ def create_service_key(name, kid, service, jwk, metadata, expiration_date):
_notify_superusers(key)
_gc_expired(service)
return key
def generate_service_key(service, expiration_date, kid=None, name='', metadata=None):
private_key = RSA.generate(2048)
jwk = RSAKey(key=private_key.publickey()).serialize()
if kid is None:
kid = sha256(json.dumps(canonicalize(jwk), separators=(',', ':'))).hexdigest()
key = create_service_key(name, kid, service, jwk, metadata or {}, expiration_date)
return (private_key, key)
def replace_service_key(old_kid, kid, jwk, metadata, expiration_date):
try:

View file

@ -1,17 +1,13 @@
""" Superuser API. """
import json
import logging
import os
import string
from datetime import datetime
from hashlib import sha256
from random import SystemRandom
from Crypto.PublicKey import RSA
from flask import request, make_response, jsonify
from jwkest.jwk import RSAKey
import features
@ -26,7 +22,6 @@ from endpoints.api import (ApiResource, nickname, resource, validate_json_reques
from endpoints.api.logs import get_logs, get_aggregate_logs
from data import model
from data.database import ServiceKeyApprovalType
from util import canonicalize
from util.useremails import send_confirmation_email, send_recovery_email
@ -576,22 +571,17 @@ class SuperUserServiceKeyManagement(ApiResource):
'ip': request.remote_addr,
})
# Generate the private key but *do not save it on the server anywhere*.
private_key = RSA.generate(2048)
jwk = RSAKey(key=private_key.publickey()).serialize()
kid = sha256(json.dumps(canonicalize(jwk), separators=(',', ':'))).hexdigest()
# Create the service key.
model.service_keys.create_service_key(body.get('name', ''), kid, body['service'], jwk,
metadata, expiration_date)
# Generate a key with a private key that we *never save*.
(private_key, key) = model.service_keys.generate_service_key(body['service'], metadata,
expiration_date,
name=body.get('name', ''))
# Auto-approve the service key.
model.service_keys.approve_service_key(kid, user, ServiceKeyApprovalType.SUPERUSER,
model.service_keys.approve_service_key(key.kid, user, ServiceKeyApprovalType.SUPERUSER,
notes=body.get('notes', ''))
# Log the creation and auto-approval of the service key.
key_log_metadata = {
'kid': kid,
'kid': key.kid,
'preshared': True,
'service': body['service'],
'name': body.get('name', ''),
@ -603,7 +593,7 @@ class SuperUserServiceKeyManagement(ApiResource):
log_action('service_key_approve', None, key_log_metadata)
return jsonify({
'kid': kid,
'kid': key.kid,
'name': body.get('name', ''),
'public_key': private_key.publickey().exportKey('PEM'),
'private_key': private_key.exportKey('PEM'),

View file

@ -157,20 +157,18 @@ def __create_subtree(with_storage, repo, structure, creator_username, parent, ta
def __generate_service_key(kid, name, user, timestamp, approval_type, expiration=None,
metadata=None):
private_key = RSA.generate(1024)
jwk = RSAKey(key=private_key.publickey()).serialize()
metadata=None, service='sample_service'):
_, key = model.service_keys.generate_service_key(service, expiration, kid=kid,
name=name, metadata=metadata)
metadata = metadata or {}
model.service_keys.create_service_key(name, kid, 'sample_service', jwk, metadata, expiration)
if approval_type is not None:
model.service_keys.approve_service_key(kid, user, approval_type,
model.service_keys.approve_service_key(key.kid, user, approval_type,
notes='The **test** apporval')
key_metadata = {
'kid': kid,
'preshared': True,
'service': 'sample_service',
'service': service,
'name': name,
'expiration_date': expiration,
'auto_approved': True
@ -664,6 +662,9 @@ def populate_database(minimal=False, with_storage=False):
__generate_service_key('kid4', 'autorotatingkey', new_user_1, six_ago,
ServiceKeyApprovalType.KEY_ROTATION, today + timedelta(1),
dict(rotation_ttl=timedelta(hours=12).total_seconds()))
__generate_service_key('kid5', 'key for another service', new_user_1, today,
ServiceKeyApprovalType.SUPERUSER, today + timedelta(14),
service='different_sample_service')
model.log.log_action('org_create_team', org.username, performer=new_user_1,
timestamp=week_ago, metadata={'team': 'readers'})

Binary file not shown.