def test_sign_with_extension(self): builder = ocsp.OCSPResponseBuilder() cert, issuer = _cert_and_issuer() root_cert, private_key = _generate_root() current_time = datetime.datetime.utcnow().replace(microsecond=0) this_update = current_time - datetime.timedelta(days=1) next_update = this_update + datetime.timedelta(days=7) builder = (builder.responder_id(ocsp.OCSPResponderEncoding.HASH, root_cert).add_response( cert, issuer, hashes.SHA1(), ocsp.OCSPCertStatus.GOOD, this_update, next_update, None, None, ).add_extension( x509.OCSPNonce(b"012345"), False)) resp = builder.sign(private_key, hashes.SHA256()) assert len(resp.extensions) == 1 assert resp.extensions[0].value == x509.OCSPNonce(b"012345") assert resp.extensions[0].critical is False private_key.public_key().verify(resp.signature, resp.tbs_response_bytes, ec.ECDSA(hashes.SHA256()))
def test_response_extensions(self): resp = _load_data( os.path.join("x509", "ocsp", "resp-revoked-reason.der"), ocsp.load_der_ocsp_response, ) assert len(resp.extensions) == 1 ext = resp.extensions[0] assert ext.critical is False assert ext.value == x509.OCSPNonce( b'\x04\x105\x957\x9fa\x03\x83\x87\x89rW\x8f\xae\x99\xf7"')
def test_load_request_with_extensions(self): req = _load_data( os.path.join("x509", "ocsp", "req-ext-nonce.der"), ocsp.load_der_ocsp_request, ) assert len(req.extensions) == 1 ext = req.extensions[0] assert ext.critical is False assert ext.value == x509.OCSPNonce( b"\x04\x10{\x80Z\x1d7&\xb8\xb8OH\xd2\xf8\xbf\xd7-\xfd")
def test_add_extension_twice(self): builder = ocsp.OCSPRequestBuilder() builder = builder.add_extension(x509.OCSPNonce(b"123"), False) with pytest.raises(ValueError): builder.add_extension(x509.OCSPNonce(b"123"), False)
class TestOCSPRequestBuilder(object): def test_add_two_certs(self): cert, issuer = _cert_and_issuer() builder = ocsp.OCSPRequestBuilder() builder = builder.add_certificate(cert, issuer, hashes.SHA1()) with pytest.raises(ValueError): builder.add_certificate(cert, issuer, hashes.SHA1()) def test_create_ocsp_request_no_req(self): builder = ocsp.OCSPRequestBuilder() with pytest.raises(ValueError): builder.build() def test_create_ocsp_request_invalid_alg(self): cert, issuer = _cert_and_issuer() builder = ocsp.OCSPRequestBuilder() with pytest.raises(ValueError): builder.add_certificate(cert, issuer, hashes.MD5()) def test_add_extension_twice(self): builder = ocsp.OCSPRequestBuilder() builder = builder.add_extension(x509.OCSPNonce(b"123"), False) with pytest.raises(ValueError): builder.add_extension(x509.OCSPNonce(b"123"), False) def test_add_invalid_extension(self): builder = ocsp.OCSPRequestBuilder() with pytest.raises(TypeError): builder.add_extension("notanext", False) def test_create_ocsp_request_invalid_cert(self): cert, issuer = _cert_and_issuer() builder = ocsp.OCSPRequestBuilder() with pytest.raises(TypeError): builder.add_certificate(b"notacert", issuer, hashes.SHA1()) with pytest.raises(TypeError): builder.add_certificate(cert, b"notacert", hashes.SHA1()) def test_create_ocsp_request(self): cert, issuer = _cert_and_issuer() builder = ocsp.OCSPRequestBuilder() builder = builder.add_certificate(cert, issuer, hashes.SHA1()) req = builder.build() serialized = req.public_bytes(serialization.Encoding.DER) assert serialized == base64.b64decode( b"MEMwQTA/MD0wOzAJBgUrDgMCGgUABBRAC0Z68eay0wmDug1gfn5ZN0gkxAQUw5zz" b"/NNGCDS7zkZ/oHxb8+IIy1kCAj8g") @pytest.mark.parametrize( ("ext", "critical"), [ [x509.OCSPNonce(b"0000"), False], [x509.OCSPNonce(b"\x00\x01\x02"), True], ], ) def test_create_ocsp_request_with_extension(self, ext, critical): cert, issuer = _cert_and_issuer() builder = ocsp.OCSPRequestBuilder() builder = builder.add_certificate(cert, issuer, hashes.SHA1()).add_extension( ext, critical) req = builder.build() assert len(req.extensions) == 1 assert req.extensions[0].value == ext assert req.extensions[0].oid == ext.oid assert req.extensions[0].critical is critical
def _decode_nonce(backend, nonce): nonce = backend._ffi.cast("ASN1_OCTET_STRING *", nonce) nonce = backend._ffi.gc(nonce, backend._lib.ASN1_OCTET_STRING_free) return x509.OCSPNonce(_asn1_string_to_bytes(backend, nonce))
if not os.path.exists(ocsp_base): os.makedirs(ocsp_base) ocsp_builder = ocsp.OCSPRequestBuilder() ocsp_builder = ocsp_builder.add_certificate( data["child-cert"]["parsed_cert"].pub.loaded, CertificateAuthority.objects.get( name=data["child-cert"]["ca"]).pub.loaded, hashes.SHA1(), ) no_nonce_req = ocsp_builder.build().public_bytes(Encoding.DER) with open(os.path.join(ocsp_base, ocsp_data["no-nonce"]["filename"]), "wb") as stream: stream.write(no_nonce_req) ocsp_builder = ocsp_builder.add_extension(x509.OCSPNonce( hex_to_bytes(ocsp_data["nonce"]["nonce"])), critical=False) nonce_req = ocsp_builder.build().public_bytes(Encoding.DER) with open(os.path.join(ocsp_base, ocsp_data["nonce"]["filename"]), "wb") as stream: stream.write(nonce_req) else: # updating only contrib, so remove existing data data = {} # Load data from Sphinx files if args.generate_contrib: for filename in os.listdir(os.path.join(_sphinx_dir, "ca")): name, _ext = os.path.splitext(filename) with open(os.path.join(_sphinx_dir, "ca", filename), "rb") as stream:
def _get_ocsp_response(data, pebble_urlopen, log): try: ocsp_request = ocsp.load_der_ocsp_request(data) except Exception: log('Error while decoding OCSP request') return ocsp.OCSPResponseBuilder.build_unsuccessful( ocsp.OCSPResponseStatus.MALFORMED_REQUEST) log('OCSP request for certificate # {0}'.format( ocsp_request.serial_number)) # Process possible extensions nonce = None for ext in ocsp_request.extensions: if isinstance(ext.value, x509.OCSPNonce): nonce = ext.value.nonce continue if ext.critical: return ocsp.OCSPResponseBuilder.build_unsuccessful( ocsp.OCSPResponseStatus.MALFORMED_REQUEST) # Determine issuer root_count = int(os.environ.get('PEBBLE_ALTERNATE_ROOTS') or '0') + 1 for root in range(root_count): req, intermediate, intermediate_key = _get_sample_request_for_root( root, ocsp_request.hash_algorithm, pebble_urlopen) if req.issuer_key_hash == ocsp_request.issuer_key_hash and req.issuer_name_hash == ocsp_request.issuer_name_hash: log('Identified intermediate certificate {0}'.format( intermediate.subject)) break intermediate = None intermediate_key = None if intermediate is None or intermediate_key is None: log(ocsp_request.issuer_key_hash, ocsp_request.issuer_name_hash) log('Cannot identify intermediate certificate') return ocsp.OCSPResponseBuilder.build_unsuccessful( ocsp.OCSPResponseStatus.UNAUTHORIZED) serial_hex = hex(ocsp_request.serial_number)[2:] if len(serial_hex) % 2 == 1: serial_hex = '0' + serial_hex try: url = pebble_urlopen("/cert-status-by-serial/{0}".format(serial_hex)) except urllib.error.HTTPError as e: if e.code == 404: log('Unknown certificate with # {0}'.format( ocsp_request.serial_number)) return ocsp.OCSPResponseBuilder.build_unsuccessful( ocsp.OCSPResponseStatus.UNAUTHORIZED) raise data = json.loads(url.read()) log('Pebble result on certificate:', json.dumps(data, sort_keys=True, indent=2)) cert = x509.load_pem_x509_certificate(data['Certificate'].encode('utf-8'), backend=default_backend()) now = datetime.datetime.now() if data['Status'] == 'Revoked': cert_status = ocsp.OCSPCertStatus.REVOKED revoked_at = data.get('RevokedAt') if revoked_at is not None: revoked_at = ' '.join( revoked_at.split(' ')[:2]) # remove time zones if '.' in revoked_at: revoked_at = revoked_at[:revoked_at.index( '.')] # remove milli- or nanoseconds revoked_at = datetime.datetime.strptime(revoked_at, '%Y-%m-%d %H:%M:%S') revocation_time = revoked_at, revocation_reason = RECOVATION_REASONS.get( data.get('Reason'), x509.ReasonFlags.unspecified) elif data['Status'] == 'Valid': cert_status = ocsp.OCSPCertStatus.GOOD revocation_time = None revocation_reason = None else: log('Unknown certificate status "{0}"'.format(data['Status'])) return ocsp.OCSPResponseBuilder.build_unsuccessful( ocsp.OCSPResponseStatus.INTERNAL_ERROR) response = ocsp.OCSPResponseBuilder() response = response.add_response(cert=cert, issuer=intermediate, algorithm=ocsp_request.hash_algorithm, cert_status=cert_status, this_update=now, next_update=None, revocation_time=revocation_time, revocation_reason=revocation_reason) response = response.responder_id(ocsp.OCSPResponderEncoding.HASH, intermediate) if nonce is not None: response = response.add_extension(x509.OCSPNonce(nonce), False) return response.sign(intermediate_key, hashes.SHA256())
ocsp_base = os.path.join(args.dest, 'ocsp') if not os.path.exists(ocsp_base): os.makedirs(ocsp_base) ocsp_builder = ocsp.OCSPRequestBuilder() ocsp_builder = ocsp_builder.add_certificate( data['child-cert']['parsed_cert'].x509, CertificateAuthority.objects.get(name=data['child-cert']['ca']).x509, hashes.SHA1() ) no_nonce_req = ocsp_builder.build().public_bytes(Encoding.DER) with open(os.path.join(ocsp_base, ocsp_data['no-nonce']['filename']), 'wb') as stream: stream.write(no_nonce_req) ocsp_builder = ocsp_builder.add_extension( x509.OCSPNonce(hex_to_bytes(ocsp_data['nonce']['nonce'])), critical=False ) nonce_req = ocsp_builder.build().public_bytes(Encoding.DER) with open(os.path.join(ocsp_base, ocsp_data['nonce']['filename']), 'wb') as stream: stream.write(nonce_req) else: # updating only contrib, so remove existing data data = {} # Load data from Sphinx files if args.generate_contrib: for filename in os.listdir(os.path.join(_sphinx_dir, 'ca')): name, _ext = os.path.splitext(filename) with open(os.path.join(_sphinx_dir, 'ca', filename), 'rb') as stream: pem = stream.read()