support encoding certificate policies in CertificateBuilder
diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py
index 58587b9..e1ef63b 100644
--- a/src/cryptography/hazmat/backends/openssl/backend.py
+++ b/src/cryptography/hazmat/backends/openssl/backend.py
@@ -94,6 +94,20 @@
     return s
 
 
+def _encode_asn1_utf8_str(backend, string):
+    """
+    Create an ASN1_UTF8STRING from a Python unicode string.
+    This object will be a ASN1_STRING with UTF8 type in OpenSSL and
+    can be decoded with ASN1_STRING_to_UTF8.
+    """
+    s = backend._lib.ASN1_UTF8STRING_new()
+    res = backend._lib.ASN1_STRING_set(
+        s, string.encode("utf8"), len(string.encode("utf8"))
+    )
+    assert res == 1
+    return s
+
+
 def _encode_asn1_str_gc(backend, data, length):
     s = _encode_asn1_str(backend, data, length)
     s = backend._ffi.gc(s, backend._lib.ASN1_OCTET_STRING_free)
@@ -136,6 +150,81 @@
     return subject
 
 
+def _encode_certificate_policies(backend, certificate_policies):
+    cp = backend._lib.sk_POLICYINFO_new_null()
+    assert cp != backend._ffi.NULL
+    cp = backend._ffi.gc(cp, backend._lib.sk_POLICYINFO_free)
+    for policy_info in certificate_policies:
+        pi = backend._lib.POLICYINFO_new()
+        assert pi != backend._ffi.NULL
+        res = backend._lib.sk_POLICYINFO_push(cp, pi)
+        assert res >= 1
+        oid = _txt2obj(backend, policy_info.policy_identifier.dotted_string)
+        pi.policyid = oid
+        if policy_info.policy_qualifiers:
+            pqis = backend._lib.sk_POLICYQUALINFO_new_null()
+            assert pqis != backend._ffi.NULL
+            for qualifier in policy_info.policy_qualifiers:
+                pqi = backend._lib.POLICYQUALINFO_new()
+                assert pqi != backend._ffi.NULL
+                res = backend._lib.sk_POLICYQUALINFO_push(pqis, pqi)
+                assert res >= 1
+                if isinstance(qualifier, six.text_type):
+                    pqi.pqualid = _txt2obj(
+                        backend, x509.OID_CPS_QUALIFIER.dotted_string
+                    )
+                    pqi.d.cpsuri = _encode_asn1_str(
+                        backend,
+                        qualifier.encode("ascii"),
+                        len(qualifier.encode("ascii"))
+                    )
+                else:
+                    assert isinstance(qualifier, x509.UserNotice)
+                    pqi.pqualid = _txt2obj(
+                        backend, x509.OID_CPS_USER_NOTICE.dotted_string
+                    )
+                    un = backend._lib.USERNOTICE_new()
+                    assert un != backend._ffi.NULL
+                    pqi.d.usernotice = un
+                    if qualifier.explicit_text:
+                        un.exptext = _encode_asn1_utf8_str(
+                            backend, qualifier.explicit_text
+                        )
+
+                    un.noticeref = _encode_notice_reference(
+                        backend, qualifier.notice_reference
+                    )
+
+            pi.qualifiers = pqis
+
+    pp = backend._ffi.new('unsigned char **')
+    r = backend._lib.i2d_CERTIFICATEPOLICIES(cp, pp)
+    assert r > 0
+    pp = backend._ffi.gc(
+        pp, lambda pointer: backend._lib.OPENSSL_free(pointer[0])
+    )
+    return pp, r
+
+
+def _encode_notice_reference(backend, notice):
+    if notice is None:
+        return backend._ffi.NULL
+    else:
+        nr = backend._lib.NOTICEREF_new()
+        assert nr != backend._ffi.NULL
+        # organization is a required field
+        nr.organization = _encode_asn1_utf8_str(backend, notice.organization)
+
+        notice_stack = backend._lib.sk_ASN1_INTEGER_new_null()
+        nr.noticenos = notice_stack
+        for number in notice.notice_numbers:
+            num = _encode_asn1_int(backend, number)
+            res = backend._lib.sk_ASN1_INTEGER_push(notice_stack, num)
+            assert res >= 1
+
+        return nr
+
+
 def _txt2obj(backend, name):
     """
     Converts a Python string with an ASN.1 object ID in dotted form to a
@@ -487,6 +576,7 @@
     ExtensionOID.ISSUER_ALTERNATIVE_NAME: _encode_alt_name,
     ExtensionOID.EXTENDED_KEY_USAGE: _encode_extended_key_usage,
     ExtensionOID.AUTHORITY_KEY_IDENTIFIER: _encode_authority_key_identifier,
+    x509.OID_CERTIFICATE_POLICIES: _encode_certificate_policies,
     ExtensionOID.AUTHORITY_INFORMATION_ACCESS: (
         _encode_authority_information_access
     ),
diff --git a/tests/test_x509.py b/tests/test_x509.py
index b9ea139..a54cdc5 100644
--- a/tests/test_x509.py
+++ b/tests/test_x509.py
@@ -1694,6 +1694,95 @@
         with pytest.raises(ValueError):
             builder.sign(issuer_private_key, hashes.SHA512(), backend)
 
+    @pytest.mark.parametrize(
+        "cp",
+        [
+            x509.CertificatePolicies([
+                x509.PolicyInformation(
+                    x509.ObjectIdentifier("2.16.840.1.12345.1.2.3.4.1"),
+                    [u"http://other.com/cps"]
+                )
+            ]),
+            x509.CertificatePolicies([
+                x509.PolicyInformation(
+                    x509.ObjectIdentifier("2.16.840.1.12345.1.2.3.4.1"),
+                    None
+                )
+            ]),
+            x509.CertificatePolicies([
+                x509.PolicyInformation(
+                    x509.ObjectIdentifier("2.16.840.1.12345.1.2.3.4.1"),
+                    [
+                        u"http://example.com/cps",
+                        u"http://other.com/cps",
+                        x509.UserNotice(
+                            x509.NoticeReference(u"my org", [1, 2, 3, 4]),
+                            u"thing"
+                        )
+                    ]
+                )
+            ]),
+            x509.CertificatePolicies([
+                x509.PolicyInformation(
+                    x509.ObjectIdentifier("2.16.840.1.12345.1.2.3.4.1"),
+                    [
+                        u"http://example.com/cps",
+                        x509.UserNotice(
+                            x509.NoticeReference(u"UTF8\u2122'", [1, 2, 3, 4]),
+                            u"We heart UTF8!\u2122"
+                        )
+                    ]
+                )
+            ]),
+            x509.CertificatePolicies([
+                x509.PolicyInformation(
+                    x509.ObjectIdentifier("2.16.840.1.12345.1.2.3.4.1"),
+                    [x509.UserNotice(None, u"thing")]
+                )
+            ]),
+            x509.CertificatePolicies([
+                x509.PolicyInformation(
+                    x509.ObjectIdentifier("2.16.840.1.12345.1.2.3.4.1"),
+                    [
+                        x509.UserNotice(
+                            x509.NoticeReference(u"my org", [1, 2, 3, 4]),
+                            None
+                        )
+                    ]
+                )
+            ])
+        ]
+    )
+    @pytest.mark.requires_backend_interface(interface=RSABackend)
+    @pytest.mark.requires_backend_interface(interface=X509Backend)
+    def test_certificate_policies(self, cp, backend):
+        issuer_private_key = RSA_KEY_2048.private_key(backend)
+        subject_private_key = RSA_KEY_2048.private_key(backend)
+
+        not_valid_before = datetime.datetime(2002, 1, 1, 12, 1)
+        not_valid_after = datetime.datetime(2030, 12, 31, 8, 30)
+
+        cert = x509.CertificateBuilder().subject_name(
+            x509.Name([x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US')])
+        ).issuer_name(
+            x509.Name([x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US')])
+        ).not_valid_before(
+            not_valid_before
+        ).not_valid_after(
+            not_valid_after
+        ).public_key(
+            subject_private_key.public_key()
+        ).serial_number(
+            123
+        ).add_extension(
+            cp, critical=False
+        ).sign(issuer_private_key, hashes.SHA256(), backend)
+
+        ext = cert.extensions.get_extension_for_oid(
+            x509.OID_CERTIFICATE_POLICIES
+        )
+        assert ext.value == cp
+
     @pytest.mark.requires_backend_interface(interface=RSABackend)
     @pytest.mark.requires_backend_interface(interface=X509Backend)
     def test_issuer_alt_name(self, backend):