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 MIIEDzCCAvegAwIBAgIJAN9Ejmna3JJ7MA0GCSqGSIb3DQEBBQUAMIGdMQswCQYD
16 VQQGEwJSVTEVMBMGA1UECAwM0JzQvtGB0LrQstCwMTAwLgYDVQQKDCfQo9C00L7R
17 gdGC0L7QstC10YDRj9GO0YnQuNC5INGG0LXQvdGC0YAxIjAgBgNVBAMMGdCS0LjQ
18 utGC0L7RgCDQktCw0LPQvdC10YAxITAfBgkqhkiG9w0BCQEWEnZpdHVzQHdhZ25l
19 ci5wcC5ydTAeFw0xNDEwMjYxNDQ2MzJaFw0xNzEwMjUxNDQ2MzJaMIGdMQswCQYD
20 VQQGEwJSVTEVMBMGA1UECAwM0JzQvtGB0LrQstCwMTAwLgYDVQQKDCfQo9C00L7R
21 gdGC0L7QstC10YDRj9GO0YnQuNC5INGG0LXQvdGC0YAxIjAgBgNVBAMMGdCS0LjQ
22 utGC0L7RgCDQktCw0LPQvdC10YAxITAfBgkqhkiG9w0BCQEWEnZpdHVzQHdhZ25l
23 ci5wcC5ydTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJn+nL8CDaM0
24 KNafGYdEDuFuCKHFxCcbaT7ecGbwjPKtnqZLOnYpa2iLFY+n7zAYw1MRkFkaR8b+
25 +AeFPWS9T60ygeWysT9XTS77Fdl69Tmr8HChRk0BuLB3vFCy34vBHSG6Frdm8PtS
26 PLEleldiwUCHLS+EckrnJylQK13X3HofSbIGzKK53MsuQOtp2dJw3b7MILM/+XBm
27 RgZlEbTEPYMOH8CE3mu9/LqXfIRQM7+nmzcNZI3RAwxEVHOSHEbgFZaksTj8rMIa
28 SrJwknmxHntI3P5PSNNbs0SO3TW8ePDIIpbVcjNsMX4qGX8b+8quZuzciKOto8S0
29 0A6eOBd8Vi0CAwEAAaNQME4wHQYDVR0OBBYEFKzcbd6+N1TKfBjvmyTvw8+DnzAZ
30 MB8GA1UdIwQYMBaAFKzcbd6+N1TKfBjvmyTvw8+DnzAZMAwGA1UdEwQFMAMBAf8w
31 DQYJKoZIhvcNAQEFBQADggEBAAa1PpkpL842hh8jLXIpA/nK8aVDDcu5p3pA72/b
32 noFnZuKcuaSUOz1rrLqxDK2JB3lmChQaVx3pZwqJgA0h0XBScar+8wM2TfeyW+oU
33 Gr5tOAxoHVRpgn6oCoJkKo0HS2/NA12T/gYsXhXJXn4tuvDjaUzY+K+hhAWh64oL
34 /c61eKfCZKp50t9Eoua0xHII2Mveb27Ps46j/CZ1r0ts7sGieOqjQo3GZOOikG6F
35 vFY/2KV16/FdBovTFWMyKrzlYHm0Wgt28IWqhocq/golLfvkz3VAkLQvOF2i6hNc
36 4feBv69SRTsTCFN9PtJCtxPX/K9LZKeccBKgGjrHQpAF+JU=
37 -----END CERTIFICATE-----
39 cert1="""-----BEGIN CERTIFICATE-----
40 MIIEDzCCAvegAwIBAgIJAN9Ejmna3JJ8MA0GCSqGSIb3DQEBBQUAMIGdMQswCQYD
41 VQQGEwJSVTEVMBMGA1UECAwM0JzQvtGB0LrQstCwMTAwLgYDVQQKDCfQo9C00L7R
42 gdGC0L7QstC10YDRj9GO0YnQuNC5INGG0LXQvdGC0YAxIjAgBgNVBAMMGdCS0LjQ
43 utGC0L7RgCDQktCw0LPQvdC10YAxITAfBgkqhkiG9w0BCQEWEnZpdHVzQHdhZ25l
44 ci5wcC5ydTAeFw0xNDEwMjYxOTA3MTdaFw0yNDEwMjMxOTA3MTdaMIGBMQswCQYD
45 VQQGEwJSVTEVMBMGA1UECAwM0JzQvtGB0LrQstCwMRUwEwYDVQQHDAzQnNC+0YHQ
46 utCy0LAxIDAeBgNVBAoMF9Cn0LDRgdGC0L3QvtC1INC70LjRhtC+MSIwIAYDVQQD
47 DBnQktC40LrRgtC+0YAg0JLQsNCz0L3QtdGAMIIBIjANBgkqhkiG9w0BAQEFAAOC
48 AQ8AMIIBCgKCAQEArQSfrrxNROyzNEz60G2EHBP+E4BL0b1QytGAZZiQp2XIhhQe
49 b7mx+c4mpwgvD7/IdAcK+YVGx78nfY723T3wG48U7HzFNbLvNDycxyXecXbvCmRs
50 xPy8TxkwPf6TIT3UcixtwMMqZFqlAtSTDmOOWSaUuftL/+yFk729xDoYkOZhFwUS
51 UM5SbEZ0JpufWFjDi3Qwj3ZOTXliHC3e4C7187Me0Nne59dttyKpq1YAThn4Srar
52 vZYU6Ykk/LUae0FCvfeiKLShWY05XnPVmvPiiFTXJP8/Au8kfezlA4b+eS81zWq2
53 BFvNlBQsgf04S88oew0CuBBgtjUIIw7XZkS03QIDAQABo2wwajAJBgNVHRMEAjAA
54 MB0GA1UdDgQWBBRflZBerCFYheRQne/sWL3zY7GiAzAfBgNVHSMEGDAWgBSs3G3e
55 vjdUynwY75sk78PPg58wGTAdBgNVHREEFjAUgRJ2aXR1c0B3YWduZXIucHAucnUw
56 DQYJKoZIhvcNAQEFBQADggEBAGx1z0ylq90hP3x/2DmfVUYBA46CiGnV4NSiaOWE
57 Y18jCuG3W8FcI7JP4uEEjKyz3XbuhTFW2GsZ2L3FGgpA5eXBikgCn5kRpOHgb45r
58 SxE8u3TwVlYlaF+7RHPYLqmgb25d/O/28McemMmTGecPC9edbtDqLv03aJ0t4gXn
59 BD+xTJOP74Yhu5IPIV92J6pSBpIoy+qiyOA1iRpOWzrVHVR504vAaFxlfZs3VJhP
60 uo291iEXyooazJdbWwZwcwk7WrNNKhqktPTg0X1ZHNnGwOAGPzwNJFGPeFj71r0t
61 aFWU5EMRKaZK75keXq/RdaOAenl+nKF6xA2XHDhGgdndFfY=
62 -----END CERTIFICATE-----
64 pubkey1="""-----BEGIN PUBLIC KEY-----
65 MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArQSfrrxNROyzNEz60G2E
66 HBP+E4BL0b1QytGAZZiQp2XIhhQeb7mx+c4mpwgvD7/IdAcK+YVGx78nfY723T3w
67 G48U7HzFNbLvNDycxyXecXbvCmRsxPy8TxkwPf6TIT3UcixtwMMqZFqlAtSTDmOO
68 WSaUuftL/+yFk729xDoYkOZhFwUSUM5SbEZ0JpufWFjDi3Qwj3ZOTXliHC3e4C71
69 87Me0Nne59dttyKpq1YAThn4SrarvZYU6Ykk/LUae0FCvfeiKLShWY05XnPVmvPi
70 iFTXJP8/Au8kfezlA4b+eS81zWq2BFvNlBQsgf04S88oew0CuBBgtjUIIw7XZkS0
72 -----END PUBLIC KEY-----
74 digicert_cert="""digicert.crt
75 -----BEGIN CERTIFICATE-----
76 MIIG5jCCBc6gAwIBAgIQAze5KDR8YKauxa2xIX84YDANBgkqhkiG9w0BAQUFADBs
77 MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
78 d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
79 ZSBFViBSb290IENBMB4XDTA3MTEwOTEyMDAwMFoXDTIxMTExMDAwMDAwMFowaTEL
80 MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
81 LmRpZ2ljZXJ0LmNvbTEoMCYGA1UEAxMfRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
82 RVYgQ0EtMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPOWYth1bhn/
83 PzR8SU8xfg0ETpmB4rOFVZEwscCvcLssqOcYqj9495BoUoYBiJfiOwZlkKq9ZXbC
84 7L4QWzd4g2B1Rca9dKq2n6Q6AVAXxDlpufFP74LByvNK28yeUE9NQKM6kOeGZrzw
85 PnYoTNF1gJ5qNRQ1A57bDIzCKK1Qss72kaPDpQpYSfZ1RGy6+c7pqzoC4E3zrOJ6
86 4GAiBTyC01Li85xH+DvYskuTVkq/cKs+6WjIHY9YHSpNXic9rQpZL1oRIEDZaARo
87 LfTAhAsKG3jf7RpY3PtBWm1r8u0c7lwytlzs16YDMqbo3rcoJ1mIgP97rYlY1R4U
88 pPKwcNSgPqcCAwEAAaOCA4UwggOBMA4GA1UdDwEB/wQEAwIBhjA7BgNVHSUENDAy
89 BggrBgEFBQcDAQYIKwYBBQUHAwIGCCsGAQUFBwMDBggrBgEFBQcDBAYIKwYBBQUH
90 AwgwggHEBgNVHSAEggG7MIIBtzCCAbMGCWCGSAGG/WwCATCCAaQwOgYIKwYBBQUH
91 AgEWLmh0dHA6Ly93d3cuZGlnaWNlcnQuY29tL3NzbC1jcHMtcmVwb3NpdG9yeS5o
92 dG0wggFkBggrBgEFBQcCAjCCAVYeggFSAEEAbgB5ACAAdQBzAGUAIABvAGYAIAB0
93 AGgAaQBzACAAQwBlAHIAdABpAGYAaQBjAGEAdABlACAAYwBvAG4AcwB0AGkAdAB1
94 AHQAZQBzACAAYQBjAGMAZQBwAHQAYQBuAGMAZQAgAG8AZgAgAHQAaABlACAARABp
95 AGcAaQBDAGUAcgB0ACAARQBWACAAQwBQAFMAIABhAG4AZAAgAHQAaABlACAAUgBl
96 AGwAeQBpAG4AZwAgAFAAYQByAHQAeQAgAEEAZwByAGUAZQBtAGUAbgB0ACAAdwBo
97 AGkAYwBoACAAbABpAG0AaQB0ACAAbABpAGEAYgBpAGwAaQB0AHkAIABhAG4AZAAg
98 AGEAcgBlACAAaQBuAGMAbwByAHAAbwByAGEAdABlAGQAIABoAGUAcgBlAGkAbgAg
99 AGIAeQAgAHIAZQBmAGUAcgBlAG4AYwBlAC4wEgYDVR0TAQH/BAgwBgEB/wIBADCB
100 gwYIKwYBBQUHAQEEdzB1MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2Vy
101 dC5jb20wTQYIKwYBBQUHMAKGQWh0dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NBQ2Vy
102 dHMvRGlnaUNlcnRIaWdoQXNzdXJhbmNlRVZSb290Q0EuY3J0MIGPBgNVHR8EgYcw
103 gYQwQKA+oDyGOmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEhpZ2hB
104 c3N1cmFuY2VFVlJvb3RDQS5jcmwwQKA+oDyGOmh0dHA6Ly9jcmw0LmRpZ2ljZXJ0
105 LmNvbS9EaWdpQ2VydEhpZ2hBc3N1cmFuY2VFVlJvb3RDQS5jcmwwHQYDVR0OBBYE
106 FExYyyXwQU9S9CjIgUObpqig5pLlMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSYJhoI
107 Au9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQBMeheHKF0XvLIyc7/NLvVYMR3wsXFU
108 nNabZ5PbLwM+Fm8eA8lThKNWYB54lBuiqG+jpItSkdfdXJW777UWSemlQk808kf/
109 roF/E1S3IMRwFcuBCoHLdFfcnN8kpCkMGPAc5K4HM+zxST5Vz25PDVR708noFUjU
110 xbvcNRx3RQdIRYW9135TuMAW2ZXNi419yWBP0aKb49Aw1rRzNubS+QOy46T15bg+
111 BEkAui6mSnKDcp33C4ypieez12Qf1uNgywPE3IjpnSUBAHHLA7QpYCWP+UbRe3Gu
112 zVMSW4SOwg/H7ZMZ2cn6j1g0djIvruFQFGHUqFijyDATI+/GJYw2jxyA
113 -----END CERTIFICATE-----
115 def test_readpubkey(self):
118 self.assertEqual(p.exportpub(),self.pubkey1)
121 self.assertEqual(c.pem(),self.cert1)
122 def test_subject(self):
124 self.assertEqual(unicode(c.subject),u'C=RU,ST=Москва,L=Москва,O=Частное лицо,CN=Виктор Вагнер')
125 def test_subject_str(self):
127 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')
128 def test_subject_len(self):
130 self.assertEqual(len(c.subject),5)
131 def test_issuer(self):
133 self.assertEqual(unicode(c.issuer),u'C=RU,ST=Москва,O=Удостоверяющий центр,CN=Виктор Вагнер,emailAddress=vitus@wagner.pp.ru')
134 def test_subjectfields(self):
136 self.assertEqual(c.subject[Oid("C")],"RU")
137 with self.assertRaises(TypeError):
139 self.assertEqual(c.subject[Oid("L")],u'\u041c\u043e\u0441\u043a\u0432\u0430')
140 def test_subjectmodify(self):
142 with self.assertRaises(ValueError):
143 c.subject[Oid("CN")]=u'Foo'
144 with self.assertRaises(ValueError):
145 del c.subject[Oid('CN')]
146 def test_subjectbadsubfield(self):
148 with self.assertRaises(KeyError):
149 x=c.subject[Oid("streetAddress")]
150 def test_subjectfieldindex(self):
152 self.assertEqual(repr(c.subject[0]),repr((Oid('C'),u'RU')))
153 def test_subjectbadindex(self):
155 with self.assertRaises(IndexError):
157 with self.assertRaises(IndexError):
159 def test_notBefore(self):
161 self.assertEqual(c.startDate,datetime.datetime(2014,10,26,19,07,17,0,utc))
162 def test_notAfter(self):
164 self.assertEqual(c.endDate,datetime.datetime(2024,10,23,19,7,17,0,utc))
165 def test_subjectHash(self):
167 self.assertEqual(hash(c.subject),0x1f3ed722)
168 def test_issuerHash(self):
170 self.assertEqual(hash(c.issuer),0x7d3ea8c3)
171 def test_namecomp(self):
173 ca=X509(self.ca_cert)
174 self.assertEqual(c.issuer,ca.subject)
175 self.assertNotEqual(c.subject,c.issuer)
176 self.assertEqual(ca.issuer,ca.subject)
177 def test_serial(self):
179 self.assertEqual(c.serial,0xDF448E69DADC927CL)
180 def test_version(self):
182 self.assertEqual(c.version,3)
183 def test_ca_cert(self):
184 ca=X509(self.ca_cert)
185 self.assertTrue(ca.check_ca())
186 notca=X509(self.cert1)
187 self.assertFalse(notca.check_ca())
188 def test_extension_count(self):
189 cert=X509(self.cert1)
190 self.assertTrue(len(cert.extensions),4)
191 ca_cert=X509(self.ca_cert)
192 self.assertEqual(len(ca_cert.extensions),3)
193 def test_extension_outofrange(self):
194 cert=X509(self.cert1)
195 with self.assertRaises(IndexError):
197 with self.assertRaises(IndexError):
199 def test_extension_oid(self):
200 cert=X509(self.cert1)
201 ext=cert.extensions[0]
203 self.assertTrue(isinstance(ext_id,Oid))
204 self.assertEqual(ext_id,Oid('basicConstraints'))
205 def test_extension_text(self):
206 cert=X509(self.cert1)
207 ext=cert.extensions[0]
208 self.assertEqual(str(ext),'CA:FALSE')
209 self.assertEqual(unicode(ext),u'CA:FALSE')
210 def test_extenson_find(self):
211 cert=X509(self.cert1)
212 exts=cert.extensions.find(Oid('subjectAltName'))
213 self.assertEqual(len(exts),1)
214 self.assertEqual(exts[0].oid,Oid('subjectAltName'))
215 def test_extension_bad_find(self):
216 cert=X509(self.cert1)
217 with self.assertRaises(TypeError):
218 exts=cert.extensions.find('subjectAltName')
219 def test_extenson_critical(self):
220 cert=X509(self.digicert_cert)
221 crit_exts=cert.extensions.find_critical()
222 self.assertEqual(len(crit_exts),2)
223 other_exts=cert.extensions.find_critical(False)
224 self.assertEqual(len(crit_exts)+len(other_exts),len(cert.extensions))
225 self.assertEqual(crit_exts[0].critical,True)
226 self.assertEqual(other_exts[0].critical,False)
227 def test_verify_by_key(self):
228 ca=X509(self.ca_cert)
230 self.assertTrue(ca.verify(key=pubkey))
233 self.assertFalse(c.verify(key=pk2))
234 self.assertTrue(c.verify(key=pubkey))
235 def test_verify_self_singed(self):
236 ca=X509(self.ca_cert)
237 self.assertTrue(ca.verify())
238 def test_default_filestore(self):
239 store=X509Store(default=True)
241 # Cert signed by our CA shouldn't be successfully verified
242 # by default CA store
243 self.assertFalse(c1.verify(store))
244 # but cert, downloaded from some commercial CA - should.
245 c2=X509(self.digicert_cert)
246 self.assertTrue(c2.verify(store))
247 def test_verify_by_filestore(self):
248 trusted=NamedTemporaryFile(delete=False)
249 trusted.write(self.ca_cert)
251 goodcert=X509(self.cert1)
252 badcert=X509(self.cert1[0:-30]+"GG"+self.cert1[-28:])
253 gitcert=X509(self.digicert_cert)
254 store=X509Store(file=trusted.name)
255 os.unlink(trusted.name)
256 # We should successfuly verify certificate signed by our CA cert
257 self.assertTrue(goodcert.verify(store))
258 # We should reject corrupted certificate
259 self.assertFalse(badcert.verify(store))
260 # And if we specify explicitely certificate file, certificate,
261 # signed by some commercial CA should be rejected too
262 self.assertFalse(gitcert.verify(store))
264 def test_verify_by_dirstore(self):
266 def test_certstack1(self):
268 l.append(X509(self.cert1))
269 self.assertEqual(unicode(l[0].subject[Oid('CN')]),u'Виктор Вагнер')
270 l.append(X509(self.ca_cert))
271 l.append(X509(self.digicert_cert))
272 stack=StackOfX509(certs=l)
273 self.assertEqual(len(stack),3)
274 self.assertTrue(isinstance(stack[1],X509))
275 self.assertEqual(unicode(stack[0].subject[Oid('CN')]),u'Виктор Вагнер')
276 with self.assertRaises(IndexError):
278 with self.assertRaises(IndexError):
281 self.assertEqual(len(stack),2)
282 self.assertEqual(unicode(stack[0].subject[Oid('CN')]),u'Виктор Вагнер')
283 self.assertEqual(unicode(stack[1].subject[Oid('CN')]),u'DigiCert High Assurance EV CA-1')
284 def test_certstack2(self):
286 stack.append(X509(self.cert1))
287 stack.append(X509(self.ca_cert))
289 stack[1]=X509(self.digicert_cert)
290 self.assertEqual(len(stack),2)
291 self.assertEqual(unicode(stack[1].subject[Oid('CN')]),u'DigiCert High Assurance EV CA-1')
292 with self.assertRaises(IndexError):
294 with self.assertRaises(IndexError):
296 with self.assertRaises(TypeError):
298 with self.assertRaises(TypeError):
299 stack.append(self.cert1)
300 def test_certstack3(self):
302 l.append(X509(self.cert1))
303 self.assertEqual(unicode(l[0].subject[Oid('CN')]),u'Виктор Вагнер')
304 l.append(X509(self.ca_cert))
305 l.append(X509(self.digicert_cert))
306 stack=StackOfX509(certs=l)
307 stack2=StackOfX509(ptr=stack.ptr,disposable=False)
308 with self.assertRaises(ValueError):
309 stack3=StackOfX509(ptr=stack.ptr,certs=l)
310 with self.assertRaises(ValueError):
312 with self.assertRaises(ValueError):
314 if __name__ == '__main__':