2 # -*- encoding: utf-8 -*-
4 from ctypescrypto.x509 import X509,X509Store,utc,StackOfX509
5 from ctypescrypto.oid import Oid
6 from tempfile import NamedTemporaryFile
13 class TestCertInfo(unittest.TestCase):
14 ca_cert="""-----BEGIN CERTIFICATE-----
15 MIIDxzCCAq+gAwIBAgIJAJ3NNJJNENcOMA0GCSqGSIb3DQEBCwUAMHoxMDAuBgNV
16 BAoMJ9Cj0LTQvtGB0YLQvtCy0LXRgNGP0Y7RidC40Lkg0YbQtdC90YLRgDEiMCAG
17 A1UEAwwZ0JLQuNC60YLQvtGAINCS0LDQs9C90LXRgDELMAkGA1UEBhMCUlUxFTAT
18 BgNVBAgMDNCc0L7RgdC60LLQsDAeFw0xODA0MjkxMjA3NTBaFw0xODA1MjkxMjA3
19 NTBaMHoxMDAuBgNVBAoMJ9Cj0LTQvtGB0YLQvtCy0LXRgNGP0Y7RidC40Lkg0YbQ
20 tdC90YLRgDEiMCAGA1UEAwwZ0JLQuNC60YLQvtGAINCS0LDQs9C90LXRgDELMAkG
21 A1UEBhMCUlUxFTATBgNVBAgMDNCc0L7RgdC60LLQsDCCASIwDQYJKoZIhvcNAQEB
22 BQADggEPADCCAQoCggEBALUXej45EASSw0LRzBQdrktr17KrCaPuVePvdMZYLv6R
23 bN14WwLAAMhk2YfKRSsiv6Vv+n6waWXLKxSKcbS+GU0aHJ/0o85ay7lbZCxvk1Fq
24 pIZeEeILLYOkS+HhFzVXZ4OarO/eZl+tbB1aR/locA5xCHiMqo6pwWRoHMHNriWw
25 DAH3bUxe3DBi3wjQRBq6V6SolDUjjX1CHDH6zaCoe2cQAPsqti6vOG2q08MzEYuC
26 6yDQhjNgXaziUtMo/wpmOsHReTGd44TyMG0FSu8Rs4kVueVBIHjKQpHYMpcb4PI/
27 lhbQgI6495KRU7d5o0FoEsdmcr7ZJ+tFTIUkudBeSA8CAwEAAaNQME4wHQYDVR0O
28 BBYEFCqBD8LZD/Gr4TMy1YyzqzCFt/BEMB8GA1UdIwQYMBaAFCqBD8LZD/Gr4TMy
29 1YyzqzCFt/BEMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAJ3tJjXO
30 c3zUvVQLmUN9J1UcDx9HTNvarOrUXqOiO8ZZIsWdk9+wWXq6oSktdM/ksLL2XyOB
31 rDbFFgVZnniTJufutWUsyQxpSe5eEhqBCqeAPVKEj1AulPgMs8zM6s/uOnTw1W35
32 RzCewrHAnrbQo/hSmzYR101C6k8lUr3jyr69LxIzcqURKdEoatxHH2pFkY1uSkIi
33 qzJrUJeJQrHq1yub2cuNvB/WDmjwdLtPHADtwWRaU65/9yVr5/f54aztuodGM4iX
34 hfB1qdrCFhaN3ku6/VUIpMdnuxcYTx7b2M4KDwG6t5wNCOdfHoMzYYJ6wJbk8pdH
36 -----END CERTIFICATE-----
38 cert1="""-----BEGIN CERTIFICATE-----
39 MIIEVDCCAzygAwIBAgIJAPlBrd9jYtl5MA0GCSqGSIb3DQEBCwUAMHoxMDAuBgNV
40 BAoMJ9Cj0LTQvtGB0YLQvtCy0LXRgNGP0Y7RidC40Lkg0YbQtdC90YLRgDEiMCAG
41 A1UEAwwZ0JLQuNC60YLQvtGAINCS0LDQs9C90LXRgDELMAkGA1UEBhMCUlUxFTAT
42 BgNVBAgMDNCc0L7RgdC60LLQsDAeFw0xODA0MjkxMjM4MDZaFw0yODA0MjYxMjM4
43 MDZaMIGBMQswCQYDVQQGEwJSVTEVMBMGA1UECAwM0JzQvtGB0LrQstCwMRUwEwYD
44 VQQHDAzQnNC+0YHQutCy0LAxIDAeBgNVBAoMF9Cn0LDRgdGC0L3QvtC1INC70LjR
45 htC+MSIwIAYDVQQDDBnQktC40LrRgtC+0YAg0JLQsNCz0L3QtdGAMIIBIjANBgkq
46 hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArvwAU7NxWPzy1zmrQRACzWiIRB15bRRg
47 bMoBjdalivIUPrmOBHcWGl16tFZHikDBoTNUPcExyzqeLaAARGG4SIc/mv3tdQyA
48 5Mt02UciW6GIMDMvpz97EY1Zr7fTzYKDPAeF0vaYM5e98Rf0J5OB9NVMxaeylc81
49 u4WBCLy4H3lwixB2cPbs0UCC/Pt/hXKlWukkFjGFm0xspu62IRDvuyEkJXhcryrB
50 VN1WEXNCRwfFEqO+Og4/L+hh9ZyNchbfIJdjenytA1HS9jHpB/C0x32eHCunC2Uf
51 7/rSWHtRAaKf67wNcM3TO25cnoWYfitZ8dj0LgMVCpZQMbwk8qYWgwIDAQABo4HU
52 MIHRMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgXgMBMGA1UdJQQMMAoGCCsGAQUFBwMB
53 MB0GA1UdDgQWBBSvfzRZ/IT2OHoNscl5MuifIqOstzAfBgNVHSMEGDAWgBQqgQ/C
54 2Q/xq+EzMtWMs6swhbfwRDBDBggrBgEFBQcBAQQ3MDUwMwYIKwYBBQUHMAKGJ2h0
55 dHA6Ly93d3cud2FnbmVyLnBwLnJ1LzQ1LmZyZWUubmV0LmRlcjAdBgNVHREEFjAU
56 gRJ2aXR1c0B3YWduZXIucHAucnUwDQYJKoZIhvcNAQELBQADggEBAFq3Xle9+pwE
57 zBhXXCYJZh4b0KGXYp/Ru7kOqNXKWz6rIcxfqozrKZo8qSkxK/g2KOSXM4jXCy1p
58 vfl3qproieewQmHTNvuVSnOjGNSkpsj2fkx7ew5mKb/rrH97a8JU1BhESOR2D32h
59 0d6l3eROs+nSPxQ8i1FIudbVMp6kg2cY7WEZRgijGlOar3M4SdYUYT5GJvO7cPm/
60 B9JpzGu20rEegXftdjyjUj4k0PuIw/fYN9lHrYh/+TK8IMHAZ4nhsqwZH1IWjTsk
61 iJW/uZYOIYhHMieEEoVcgAEKL5uiYB95O690Qtm6Q0hKbIzCClQSuaCSKWQRVBfz
63 -----END CERTIFICATE-----
65 pubkey1="""-----BEGIN PUBLIC KEY-----
66 MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArvwAU7NxWPzy1zmrQRAC
67 zWiIRB15bRRgbMoBjdalivIUPrmOBHcWGl16tFZHikDBoTNUPcExyzqeLaAARGG4
68 SIc/mv3tdQyA5Mt02UciW6GIMDMvpz97EY1Zr7fTzYKDPAeF0vaYM5e98Rf0J5OB
69 9NVMxaeylc81u4WBCLy4H3lwixB2cPbs0UCC/Pt/hXKlWukkFjGFm0xspu62IRDv
70 uyEkJXhcryrBVN1WEXNCRwfFEqO+Og4/L+hh9ZyNchbfIJdjenytA1HS9jHpB/C0
71 x32eHCunC2Uf7/rSWHtRAaKf67wNcM3TO25cnoWYfitZ8dj0LgMVCpZQMbwk8qYW
73 -----END PUBLIC KEY-----
75 digicert_cert="""digicert.crt
76 -----BEGIN CERTIFICATE-----
77 MIIG5jCCBc6gAwIBAgIQAze5KDR8YKauxa2xIX84YDANBgkqhkiG9w0BAQUFADBs
78 MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
79 d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
80 ZSBFViBSb290IENBMB4XDTA3MTEwOTEyMDAwMFoXDTIxMTExMDAwMDAwMFowaTEL
81 MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
82 LmRpZ2ljZXJ0LmNvbTEoMCYGA1UEAxMfRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
83 RVYgQ0EtMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPOWYth1bhn/
84 PzR8SU8xfg0ETpmB4rOFVZEwscCvcLssqOcYqj9495BoUoYBiJfiOwZlkKq9ZXbC
85 7L4QWzd4g2B1Rca9dKq2n6Q6AVAXxDlpufFP74LByvNK28yeUE9NQKM6kOeGZrzw
86 PnYoTNF1gJ5qNRQ1A57bDIzCKK1Qss72kaPDpQpYSfZ1RGy6+c7pqzoC4E3zrOJ6
87 4GAiBTyC01Li85xH+DvYskuTVkq/cKs+6WjIHY9YHSpNXic9rQpZL1oRIEDZaARo
88 LfTAhAsKG3jf7RpY3PtBWm1r8u0c7lwytlzs16YDMqbo3rcoJ1mIgP97rYlY1R4U
89 pPKwcNSgPqcCAwEAAaOCA4UwggOBMA4GA1UdDwEB/wQEAwIBhjA7BgNVHSUENDAy
90 BggrBgEFBQcDAQYIKwYBBQUHAwIGCCsGAQUFBwMDBggrBgEFBQcDBAYIKwYBBQUH
91 AwgwggHEBgNVHSAEggG7MIIBtzCCAbMGCWCGSAGG/WwCATCCAaQwOgYIKwYBBQUH
92 AgEWLmh0dHA6Ly93d3cuZGlnaWNlcnQuY29tL3NzbC1jcHMtcmVwb3NpdG9yeS5o
93 dG0wggFkBggrBgEFBQcCAjCCAVYeggFSAEEAbgB5ACAAdQBzAGUAIABvAGYAIAB0
94 AGgAaQBzACAAQwBlAHIAdABpAGYAaQBjAGEAdABlACAAYwBvAG4AcwB0AGkAdAB1
95 AHQAZQBzACAAYQBjAGMAZQBwAHQAYQBuAGMAZQAgAG8AZgAgAHQAaABlACAARABp
96 AGcAaQBDAGUAcgB0ACAARQBWACAAQwBQAFMAIABhAG4AZAAgAHQAaABlACAAUgBl
97 AGwAeQBpAG4AZwAgAFAAYQByAHQAeQAgAEEAZwByAGUAZQBtAGUAbgB0ACAAdwBo
98 AGkAYwBoACAAbABpAG0AaQB0ACAAbABpAGEAYgBpAGwAaQB0AHkAIABhAG4AZAAg
99 AGEAcgBlACAAaQBuAGMAbwByAHAAbwByAGEAdABlAGQAIABoAGUAcgBlAGkAbgAg
100 AGIAeQAgAHIAZQBmAGUAcgBlAG4AYwBlAC4wEgYDVR0TAQH/BAgwBgEB/wIBADCB
101 gwYIKwYBBQUHAQEEdzB1MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2Vy
102 dC5jb20wTQYIKwYBBQUHMAKGQWh0dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NBQ2Vy
103 dHMvRGlnaUNlcnRIaWdoQXNzdXJhbmNlRVZSb290Q0EuY3J0MIGPBgNVHR8EgYcw
104 gYQwQKA+oDyGOmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEhpZ2hB
105 c3N1cmFuY2VFVlJvb3RDQS5jcmwwQKA+oDyGOmh0dHA6Ly9jcmw0LmRpZ2ljZXJ0
106 LmNvbS9EaWdpQ2VydEhpZ2hBc3N1cmFuY2VFVlJvb3RDQS5jcmwwHQYDVR0OBBYE
107 FExYyyXwQU9S9CjIgUObpqig5pLlMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSYJhoI
108 Au9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQBMeheHKF0XvLIyc7/NLvVYMR3wsXFU
109 nNabZ5PbLwM+Fm8eA8lThKNWYB54lBuiqG+jpItSkdfdXJW777UWSemlQk808kf/
110 roF/E1S3IMRwFcuBCoHLdFfcnN8kpCkMGPAc5K4HM+zxST5Vz25PDVR708noFUjU
111 xbvcNRx3RQdIRYW9135TuMAW2ZXNi419yWBP0aKb49Aw1rRzNubS+QOy46T15bg+
112 BEkAui6mSnKDcp33C4ypieez12Qf1uNgywPE3IjpnSUBAHHLA7QpYCWP+UbRe3Gu
113 zVMSW4SOwg/H7ZMZ2cn6j1g0djIvruFQFGHUqFijyDATI+/GJYw2jxyA
114 -----END CERTIFICATE-----
116 def test_readpubkey(self):
119 self.assertEqual(p.exportpub(),self.pubkey1)
122 self.assertEqual(c.pem(),self.cert1)
123 def test_subject(self):
125 self.assertEqual(unicode(c.subject),u'C=RU,ST=Москва,L=Москва,O=Частное лицо,CN=Виктор Вагнер')
126 def test_subject_str(self):
128 self.assertEqual(str(c.subject),b'C=RU,ST=\\D0\\9C\\D0\\BE\\D1\\81\\D0\\BA\\D0\\B2\\D0\\B0,L=\\D0\\9C\\D0\\BE\\D1\\81\\D0\\BA\\D0\\B2\\D0\\B0,O=\\D0\\A7\\D0\\B0\\D1\\81\\D1\\82\\D0\\BD\\D0\\BE\\D0\\B5 \\D0\\BB\\D0\\B8\\D1\\86\\D0\\BE,CN=\\D0\\92\\D0\\B8\\D0\\BA\\D1\\82\\D0\\BE\\D1\\80 \\D0\\92\\D0\\B0\\D0\\B3\\D0\\BD\\D0\\B5\\D1\\80')
129 def test_subject_len(self):
131 self.assertEqual(len(c.subject),5)
132 def test_issuer(self):
134 self.assertEqual(unicode(c.issuer),u'O=Удостоверяющий центр,CN=Виктор Вагнер,C=RU,ST=Москва')
135 def test_subjectfields(self):
137 self.assertEqual(c.subject[Oid("C")],"RU")
138 with self.assertRaises(TypeError):
140 self.assertEqual(c.subject[Oid("L")],u'\u041c\u043e\u0441\u043a\u0432\u0430')
141 def test_subjectmodify(self):
143 with self.assertRaises(ValueError):
144 c.subject[Oid("CN")]=u'Foo'
145 with self.assertRaises(ValueError):
146 del c.subject[Oid('CN')]
147 def test_subjectbadsubfield(self):
149 with self.assertRaises(KeyError):
150 x=c.subject[Oid("streetAddress")]
151 def test_subjectfieldindex(self):
153 self.assertEqual(repr(c.subject[0]),repr((Oid('C'),u'RU')))
154 def test_subjectbadindex(self):
156 with self.assertRaises(IndexError):
158 with self.assertRaises(IndexError):
160 def test_notBefore(self):
162 self.assertEqual(c.startDate,datetime.datetime(2018,4,29,12,38,6,0,utc))
163 def test_notAfter(self):
165 self.assertEqual(c.endDate,datetime.datetime(2028,4,26,12,38,6,0,utc))
166 def test_subjectHash(self):
168 self.assertEqual(hash(c.subject),0x1f3ed722)
169 def test_issuerHash(self):
171 self.assertEqual(hash(c.issuer),0x55224769)
172 def test_namecomp(self):
174 ca=X509(self.ca_cert)
175 self.assertEqual(c.issuer,ca.subject)
176 self.assertNotEqual(c.subject,c.issuer)
177 self.assertEqual(ca.issuer,ca.subject)
178 def test_serial(self):
180 self.assertEqual(c.serial,0xf941addf6362d979L)
181 def test_version(self):
183 self.assertEqual(c.version,3)
184 def test_ca_cert(self):
185 ca=X509(self.ca_cert)
186 self.assertTrue(ca.check_ca())
187 notca=X509(self.cert1)
188 self.assertFalse(notca.check_ca())
189 def test_extension_count(self):
190 cert=X509(self.cert1)
191 self.assertTrue(len(cert.extensions),4)
192 ca_cert=X509(self.ca_cert)
193 self.assertEqual(len(ca_cert.extensions),3)
194 def test_extension_outofrange(self):
195 cert=X509(self.cert1)
196 with self.assertRaises(IndexError):
198 with self.assertRaises(IndexError):
200 def test_extension_oid(self):
201 cert=X509(self.cert1)
202 ext=cert.extensions[0]
204 self.assertTrue(isinstance(ext_id,Oid))
205 self.assertEqual(ext_id,Oid('basicConstraints'))
206 def test_extension_text(self):
207 cert=X509(self.cert1)
208 ext=cert.extensions[0]
209 self.assertEqual(str(ext),'CA:FALSE')
210 self.assertEqual(unicode(ext),u'CA:FALSE')
211 def test_extenson_find(self):
212 cert=X509(self.cert1)
213 exts=cert.extensions.find(Oid('subjectAltName'))
214 self.assertEqual(len(exts),1)
215 self.assertEqual(exts[0].oid,Oid('subjectAltName'))
216 def test_extension_bad_find(self):
217 cert=X509(self.cert1)
218 with self.assertRaises(TypeError):
219 exts=cert.extensions.find('subjectAltName')
220 def test_extenson_critical(self):
221 cert=X509(self.digicert_cert)
222 crit_exts=cert.extensions.find_critical()
223 self.assertEqual(len(crit_exts),2)
224 other_exts=cert.extensions.find_critical(False)
225 self.assertEqual(len(crit_exts)+len(other_exts),len(cert.extensions))
226 self.assertEqual(crit_exts[0].critical,True)
227 self.assertEqual(other_exts[0].critical,False)
228 def test_verify_by_key(self):
229 ca=X509(self.ca_cert)
231 self.assertTrue(ca.verify(key=pubkey))
234 self.assertFalse(c.verify(key=pk2))
235 self.assertTrue(c.verify(key=pubkey))
236 def test_verify_self_singed(self):
237 ca=X509(self.ca_cert)
238 self.assertTrue(ca.verify())
239 def test_default_filestore(self):
240 store=X509Store(default=True)
242 # Cert signed by our CA shouldn't be successfully verified
243 # by default CA store
244 self.assertFalse(c1.verify(store))
245 # but cert, downloaded from some commercial CA - should.
246 c2=X509(self.digicert_cert)
247 self.assertTrue(c2.verify(store))
248 def test_verify_by_filestore(self):
249 trusted=NamedTemporaryFile(delete=False)
250 trusted.write(self.ca_cert)
252 goodcert=X509(self.cert1)
253 badcert=X509(self.cert1[0:-30]+"GG"+self.cert1[-28:])
254 gitcert=X509(self.digicert_cert)
255 store=X509Store(file=trusted.name)
256 os.unlink(trusted.name)
257 # We should successfuly verify certificate signed by our CA cert
258 self.assertTrue(goodcert.verify(store))
259 # We should reject corrupted certificate
260 self.assertFalse(badcert.verify(store))
261 # And if we specify explicitely certificate file, certificate,
262 # signed by some commercial CA should be rejected too
263 self.assertFalse(gitcert.verify(store))
265 def test_verify_by_dirstore(self):
267 def test_certstack1(self):
269 l.append(X509(self.cert1))
270 self.assertEqual(unicode(l[0].subject[Oid('CN')]),u'Виктор Вагнер')
271 l.append(X509(self.ca_cert))
272 l.append(X509(self.digicert_cert))
273 stack=StackOfX509(certs=l)
274 self.assertEqual(len(stack),3)
275 self.assertTrue(isinstance(stack[1],X509))
276 self.assertEqual(unicode(stack[0].subject[Oid('CN')]),u'Виктор Вагнер')
277 with self.assertRaises(IndexError):
279 with self.assertRaises(IndexError):
282 self.assertEqual(len(stack),2)
283 self.assertEqual(unicode(stack[0].subject[Oid('CN')]),u'Виктор Вагнер')
284 self.assertEqual(unicode(stack[1].subject[Oid('CN')]),u'DigiCert High Assurance EV CA-1')
285 def test_certstack2(self):
287 stack.append(X509(self.cert1))
288 stack.append(X509(self.ca_cert))
290 stack[1]=X509(self.digicert_cert)
291 self.assertEqual(len(stack),2)
292 self.assertEqual(unicode(stack[1].subject[Oid('CN')]),u'DigiCert High Assurance EV CA-1')
293 with self.assertRaises(IndexError):
295 with self.assertRaises(IndexError):
297 with self.assertRaises(TypeError):
299 with self.assertRaises(TypeError):
300 stack.append(self.cert1)
301 def test_certstack3(self):
303 l.append(X509(self.cert1))
304 self.assertEqual(unicode(l[0].subject[Oid('CN')]),u'Виктор Вагнер')
305 l.append(X509(self.ca_cert))
306 l.append(X509(self.digicert_cert))
307 stack=StackOfX509(certs=l)
308 stack2=StackOfX509(ptr=stack.ptr,disposable=False)
309 with self.assertRaises(ValueError):
310 stack3=StackOfX509(ptr=stack.ptr,certs=l)
311 with self.assertRaises(ValueError):
313 with self.assertRaises(ValueError):
315 if __name__ == '__main__':