certificate policies extension support

Adds a bunch of ancillary classes to support this.
diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py
index dfc0af8..3509303 100644
--- a/src/cryptography/x509.py
+++ b/src/cryptography/x509.py
@@ -69,6 +69,8 @@
     "1.3.6.1.5.5.7.48.1.5": "OCSPNoCheck",
     "1.3.6.1.5.5.7.48.1": "OCSP",
     "1.3.6.1.5.5.7.48.2": "caIssuers",
+    "1.3.6.1.5.5.7.2.1": "id-qt-cps",
+    "1.3.6.1.5.5.7.2.2": "id-qt-unotice",
 }
 
 
@@ -460,6 +462,120 @@
     access_location = utils.read_only_property("_access_location")
 
 
+class CertificatePolicies(object):
+    def __init__(self, policies):
+        if not all(map(lambda x: isinstance(x, PolicyInformation), policies)):
+            raise TypeError(
+                "Every item in the policies list must be a "
+                "PolicyInformation"
+            )
+
+        self._policies = policies
+
+    def __iter__(self):
+        return iter(self._policies)
+
+    def __len__(self):
+        return len(self._policies)
+
+    def __repr__(self):
+        return "<CertificatePolicies({0})>".format(self._policies)
+
+
+class PolicyInformation(object):
+    def __init__(self, policy_identifier, policy_qualifiers):
+        if not isinstance(policy_identifier, ObjectIdentifier):
+            raise TypeError("policy_identifier must be an ObjectIdentifier")
+
+        self._policy_identifier = policy_identifier
+        if policy_qualifiers and not all(
+            map(
+                lambda x: isinstance(x, PolicyQualifierInfo), policy_qualifiers
+            )
+        ):
+            raise TypeError(
+                "policy_qualifiers must be a list of PolicyQualifierInfo "
+                "objects or None"
+            )
+
+        self._policy_qualifiers = policy_qualifiers
+
+    def __repr__(self):
+        return (
+            "<PolicyInformation(policy_identifier={0.policy_identifier}, polic"
+            "y_qualifiers={0.policy_qualifiers})>".format(self)
+        )
+
+    policy_identifier = utils.read_only_property("_policy_identifier")
+    policy_qualifiers = utils.read_only_property("_policy_qualifiers")
+
+
+class PolicyQualifierInfo(object):
+    def __init__(self, qualifier):
+        if not isinstance(qualifier, (six.string_types, UserNotice)):
+            raise ValueError("qualifier must be string or UserNotice")
+
+        if isinstance(qualifier, six.string_types):
+            self._policy_qualifier_id = OID_CPS_QUALIFIER
+        else:
+            self._policy_qualifier_id = OID_CPS_USER_NOTICE
+
+        self._qualifier = qualifier
+
+    def __repr__(self):
+        return (
+            "<PolicyQualifierInfo(policy_qualifier_id={0.policy_qualifier_id}"
+            ", qualifier={0.qualifier})>".format(self)
+        )
+
+    policy_qualifier_id = utils.read_only_property("_policy_qualifier_id")
+    qualifier = utils.read_only_property("_qualifier")
+
+
+class UserNotice(object):
+    def __init__(self, notice_reference, explicit_text):
+        if notice_reference and not isinstance(
+            notice_reference, NoticeReference
+        ):
+            raise TypeError(
+                "notice_reference must be None or a NoticeReference"
+            )
+
+        self._notice_reference = notice_reference
+        self._explicit_text = explicit_text
+
+    def __repr__(self):
+        return (
+            "<UserNotice(notice_reference={0.notice_reference}, explicit_text="
+            "{0.explicit_text})>".format(self)
+        )
+
+    notice_reference = utils.read_only_property("_notice_reference")
+    explicit_text = utils.read_only_property("_explicit_text")
+
+
+class NoticeReference(object):
+    def __init__(self, organization, notice_numbers):
+        self._organization = organization
+        if notice_numbers and not all(
+            map(lambda x: isinstance(x, int), notice_numbers)
+        ):
+            raise TypeError(
+                "notice_numbers must be a list of integers or None"
+            )
+
+        self._notice_numbers = notice_numbers
+
+    def __repr__(self):
+        return (
+            "<NoticeReference(organization={0.organization}, notice_numbers="
+            "{0.notice_numbers})>".format(self)
+        )
+
+    organization = utils.read_only_property("_organization")
+    notice_numbers = utils.read_only_property("_notice_numbers")
+
+
 class SubjectKeyIdentifier(object):
     def __init__(self, digest):
         self._digest = digest
@@ -874,6 +990,9 @@
 OID_CA_ISSUERS = ObjectIdentifier("1.3.6.1.5.5.7.48.2")
 OID_OCSP = ObjectIdentifier("1.3.6.1.5.5.7.48.1")
 
+OID_CPS_QUALIFIER = ObjectIdentifier("1.3.6.1.5.5.7.2.1")
+OID_CPS_USER_NOTICE = ObjectIdentifier("1.3.6.1.5.5.7.2.2")
+
 
 @six.add_metaclass(abc.ABCMeta)
 class Certificate(object):
diff --git a/tests/test_x509_ext.py b/tests/test_x509_ext.py
index 06a6860..c630343 100644
--- a/tests/test_x509_ext.py
+++ b/tests/test_x509_ext.py
@@ -39,6 +39,123 @@
         )
 
 
+class TestNoticeReference(object):
+    def test_notice_numbers_not_all_int(self):
+        with pytest.raises(TypeError):
+            x509.NoticeReference("org", [1, 2, "three"])
+
+    def test_notice_numbers_none(self):
+        nr = x509.NoticeReference("org", None)
+        assert nr.organization == "org"
+        assert nr.notice_numbers is None
+
+    def test_repr(self):
+        nr = x509.NoticeReference("org", [1, 3, 4])
+
+        assert repr(nr) == (
+            "<NoticeReference(organization=org, notice_numbers=[1, 3, 4])>"
+        )
+
+
+class TestUserNotice(object):
+    def test_notice_reference_invalid(self):
+        with pytest.raises(TypeError):
+            x509.UserNotice("invalid", None)
+
+    def test_notice_reference_none(self):
+        un = x509.UserNotice(None, "text")
+        assert un.notice_reference is None
+        assert un.explicit_text == "text"
+
+    def test_repr(self):
+        un = x509.UserNotice(x509.NoticeReference("org", None), "text")
+        assert repr(un) == (
+            "<UserNotice(notice_reference=<NoticeReference(organization=org, "
+            "notice_numbers=None)>, explicit_text=text)>"
+        )
+
+
+class TestPolicyQualifierInfo(object):
+    def test_invalid_qualifier(self):
+        with pytest.raises(ValueError):
+            x509.PolicyQualifierInfo(None)
+
+    def test_string_qualifier(self):
+        pqi = x509.PolicyQualifierInfo("1.2.3")
+        assert pqi.policy_qualifier_id == x509.OID_CPS_QUALIFIER
+        assert pqi.qualifier == "1.2.3"
+
+    def test_user_notice_qualifier(self):
+        pqi = x509.PolicyQualifierInfo(x509.UserNotice(None, "text"))
+        assert pqi.policy_qualifier_id == x509.OID_CPS_USER_NOTICE
+        assert isinstance(pqi.qualifier, x509.UserNotice)
+
+    def test_repr(self):
+        pqi = x509.PolicyQualifierInfo("1.2.3.4")
+        assert repr(pqi) == (
+            "<PolicyQualifierInfo(policy_qualifier_id=<ObjectIdentifier(oid=1."
+            "3.6.1.5.5.7.2.1, name=id-qt-cps)>, qualifier=1.2.3.4)>"
+        )
+
+
+class TestPolicyInformation(object):
+    def test_invalid_policy_identifier(self):
+        with pytest.raises(TypeError):
+            x509.PolicyInformation("notanoid", None)
+
+    def test_none_policy_qualifiers(self):
+        pi = x509.PolicyInformation(x509.ObjectIdentifier("1.2.3"), None)
+        assert pi.policy_identifier == x509.ObjectIdentifier("1.2.3")
+        assert pi.policy_qualifiers is None
+
+    def test_policy_qualifiers(self):
+        pq = [x509.PolicyQualifierInfo("string")]
+        pi = x509.PolicyInformation(x509.ObjectIdentifier("1.2.3"), pq)
+        assert pi.policy_identifier == x509.ObjectIdentifier("1.2.3")
+        assert pi.policy_qualifiers == pq
+
+    def test_invalid_policy_identifiers(self):
+        with pytest.raises(TypeError):
+            x509.PolicyInformation(x509.ObjectIdentifier("1.2.3"), [1, 2])
+
+    def test_repr(self):
+        pq = [x509.PolicyQualifierInfo("string")]
+        pi = x509.PolicyInformation(x509.ObjectIdentifier("1.2.3"), pq)
+        assert repr(pi) == (
+            "<PolicyInformation(policy_identifier=<ObjectIdentifier(oid=1.2.3,"
+            " name=Unknown OID)>, policy_qualifiers=[<PolicyQualifierInfo(poli"
+            "cy_qualifier_id=<ObjectIdentifier(oid=1.3.6.1.5.5.7.2.1, name=id-"
+            "qt-cps)>, qualifier=string)>])>"
+        )
+
+
+class TestCertificatePolicies(object):
+    def test_invalid_policies(self):
+        pq = [x509.PolicyQualifierInfo("string")]
+        pi = x509.PolicyInformation(x509.ObjectIdentifier("1.2.3"), pq)
+        with pytest.raises(TypeError):
+            x509.CertificatePolicies([1, pi])
+
+    def test_iter_len(self):
+        pq = [x509.PolicyQualifierInfo("string")]
+        pi = x509.PolicyInformation(x509.ObjectIdentifier("1.2.3"), pq)
+        cp = x509.CertificatePolicies([pi])
+        assert len(cp) == 1
+        for policyinfo in cp:
+            assert policyinfo == pi
+
+    def test_repr(self):
+        pq = [x509.PolicyQualifierInfo("string")]
+        pi = x509.PolicyInformation(x509.ObjectIdentifier("1.2.3"), pq)
+        cp = x509.CertificatePolicies([pi])
+        assert repr(cp) == (
+            "<CertificatePolicies([<PolicyInformation(policy_identifier=<Objec"
+            "tIdentifier(oid=1.2.3, name=Unknown OID)>, policy_qualifiers=[<Po"
+            "licyQualifierInfo(policy_qualifier_id=<ObjectIdentifier(oid=1.3.6"
+            ".1.5.5.7.2.1, name=id-qt-cps)>, qualifier=string)>])>])>"
+        )
+
+
 class TestKeyUsage(object):
     def test_key_agreement_false_encipher_decipher_true(self):
         with pytest.raises(ValueError):