WIP
diff --git a/jsonschema/_legacy_validators.py b/jsonschema/_legacy_validators.py
index 141f15a..5cb6148 100644
--- a/jsonschema/_legacy_validators.py
+++ b/jsonschema/_legacy_validators.py
@@ -1,7 +1,7 @@
 from referencing.jsonschema import lookup_recursive_ref
 
 from jsonschema import _utils
-from jsonschema.exceptions import ValidationError
+from jsonschema.exceptions import ValidationError, keywords
 
 
 def ignore_ref_siblings(schema):
@@ -160,7 +160,7 @@
                 schema_path=property,
             )
         elif subschema.get("required", False):
-            error = ValidationError(f"{property!r} is a required property")
+            error = keywords.Required(property_name=property)
             error._set(
                 validator="required",
                 validator_value=subschema["required"],
diff --git a/jsonschema/_validators.py b/jsonschema/_validators.py
index 45b53c9..820a79c 100644
--- a/jsonschema/_validators.py
+++ b/jsonschema/_validators.py
@@ -1,6 +1,7 @@
 from fractions import Fraction
 import re
 
+from jsonschema import exceptions
 from jsonschema._utils import (
     ensure_list,
     equal,
@@ -12,6 +13,7 @@
     uniq,
 )
 from jsonschema.exceptions import FormatError, ValidationError
+import jsonschema.exceptions.keywords  # noqa: F401
 
 
 def patternProperties(validator, patternProperties, instance, schema):
@@ -315,7 +317,7 @@
         return
     for property in required:
         if property not in instance:
-            yield ValidationError(f"{property!r} is a required property")
+            yield exceptions.keywords.Required(property_name=property)
 
 
 def minProperties(validator, mP, instance, schema):
diff --git a/jsonschema/exceptions.py b/jsonschema/exceptions/__init__.py
similarity index 100%
rename from jsonschema/exceptions.py
rename to jsonschema/exceptions/__init__.py
diff --git a/jsonschema/exceptions/keywords.py b/jsonschema/exceptions/keywords.py
new file mode 100644
index 0000000..9cb37f6
--- /dev/null
+++ b/jsonschema/exceptions/keywords.py
@@ -0,0 +1,11 @@
+from jsonschema.exceptions import ValidationError
+
+
+class Required(ValidationError):
+    def __init__(self, property_name):
+        ValidationError.__init__(
+            self,
+            message=f"{property_name!r} is a required property",
+        )
+
+        self.property_name = property_name
diff --git a/jsonschema/tests/test_validators.py b/jsonschema/tests/test_validators.py
index 940a86d..f638b95 100644
--- a/jsonschema/tests/test_validators.py
+++ b/jsonschema/tests/test_validators.py
@@ -24,6 +24,7 @@
     protocols,
     validators,
 )
+import jsonschema.exceptions.keywords
 
 
 def fail(validator, errors, instance, schema):
@@ -1760,6 +1761,18 @@
             with self.assertRaises(exceptions.ValidationError):
                 validator.validate(instance)
 
+    def test_required_missing(self):
+        if self.Validator is not validators.Draft3Validator:
+            schema = {"required": ["a", "b"]}
+        else:
+            required = dict(required=True)
+            schema = {"properties": dict(a=required, b=required)}
+        validator = self.Validator(schema)
+
+        with self.assertRaises(exceptions.keywords.Required) as e:
+            validator.validate({"a": 37})
+        self.assertEqual(e.exception.property_name, "b")
+
     def test_it_creates_a_ref_resolver_if_not_provided(self):
         with self.assertWarns(DeprecationWarning):
             resolver = self.Validator({}).resolver