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