disable broken tests on net_4_0
[mcs.git] / class / corlib / Mono.Security.X509 / X509CRL.cs
blobca338453f4c2e21464cec5a7033fe0dc63aca469
1 //
2 // X509CRL.cs: Handles X.509 certificates revocation lists.
3 //
4 // Author:
5 // Sebastien Pouliot <sebastien@ximian.com>
6 //
7 // Copyright (C) 2004,2006 Novell Inc. (http://www.novell.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 using System;
30 using System.Collections;
31 using System.Globalization;
32 using System.IO;
33 using System.Security.Cryptography;
35 using Mono.Security.X509.Extensions;
37 namespace Mono.Security.X509 {
39 * CertificateList ::= SEQUENCE {
40 * tbsCertList TBSCertList,
41 * signatureAlgorithm AlgorithmIdentifier,
42 * signature BIT STRING
43 * }
45 * TBSCertList ::= SEQUENCE {
46 * version Version OPTIONAL,
47 * -- if present, MUST be v2
48 * signature AlgorithmIdentifier,
49 * issuer Name,
50 * thisUpdate Time,
51 * nextUpdate Time OPTIONAL,
52 * revokedCertificates SEQUENCE OF SEQUENCE {
53 * userCertificate CertificateSerialNumber,
54 * revocationDate Time,
55 * crlEntryExtensions Extensions OPTIONAL
56 * -- if present, MUST be v2
57 * } OPTIONAL,
58 * crlExtensions [0] Extensions OPTIONAL }
59 * -- if present, MUST be v2
61 #if INSIDE_CORLIB
62 internal
63 #else
64 public
65 #endif
66 class X509Crl {
68 public class X509CrlEntry {
70 private byte[] sn;
71 private DateTime revocationDate;
72 private X509ExtensionCollection extensions;
74 internal X509CrlEntry (byte[] serialNumber, DateTime revocationDate, X509ExtensionCollection extensions)
76 sn = serialNumber;
77 this.revocationDate = revocationDate;
78 if (extensions == null)
79 this.extensions = new X509ExtensionCollection ();
80 else
81 this.extensions = extensions;
84 internal X509CrlEntry (ASN1 entry)
86 sn = entry [0].Value;
87 Array.Reverse (sn);
88 revocationDate = ASN1Convert.ToDateTime (entry [1]);
89 extensions = new X509ExtensionCollection (entry [2]);
92 public byte[] SerialNumber {
93 get { return (byte[]) sn.Clone (); }
96 public DateTime RevocationDate {
97 get { return revocationDate; }
100 public X509ExtensionCollection Extensions {
101 get { return extensions; }
104 public byte[] GetBytes ()
106 ASN1 sequence = new ASN1 (0x30);
107 sequence.Add (new ASN1 (0x02, sn));
108 sequence.Add (ASN1Convert.FromDateTime (revocationDate));
109 if (extensions.Count > 0)
110 sequence.Add (new ASN1 (extensions.GetBytes ()));
111 return sequence.GetBytes ();
115 private string issuer;
116 private byte version;
117 private DateTime thisUpdate;
118 private DateTime nextUpdate;
119 private ArrayList entries;
120 private string signatureOID;
121 private byte[] signature;
122 private X509ExtensionCollection extensions;
123 private byte[] encoded;
124 private byte[] hash_value;
126 public X509Crl (byte[] crl)
128 if (crl == null)
129 throw new ArgumentNullException ("crl");
130 encoded = (byte[]) crl.Clone ();
131 Parse (encoded);
134 private void Parse (byte[] crl)
136 string e = "Input data cannot be coded as a valid CRL.";
137 try {
138 // CertificateList ::= SEQUENCE {
139 ASN1 encodedCRL = new ASN1 (encoded);
140 if ((encodedCRL.Tag != 0x30) || (encodedCRL.Count != 3))
141 throw new CryptographicException (e);
143 // CertificateList / TBSCertList,
144 ASN1 toBeSigned = encodedCRL [0];
145 if ((toBeSigned.Tag != 0x30) || (toBeSigned.Count < 3))
146 throw new CryptographicException (e);
148 int n = 0;
149 // CertificateList / TBSCertList / Version OPTIONAL, -- if present, MUST be v2
150 if (toBeSigned [n].Tag == 0x02) {
151 version = (byte) (toBeSigned [n++].Value [0] + 1);
153 else
154 version = 1; // DEFAULT
155 // CertificateList / TBSCertList / AlgorithmIdentifier,
156 signatureOID = ASN1Convert.ToOid (toBeSigned [n++][0]);
157 // CertificateList / TBSCertList / Name,
158 issuer = X501.ToString (toBeSigned [n++]);
159 // CertificateList / TBSCertList / Time,
160 thisUpdate = ASN1Convert.ToDateTime (toBeSigned [n++]);
161 // CertificateList / TBSCertList / Time OPTIONAL,
162 ASN1 next = toBeSigned [n++];
163 if ((next.Tag == 0x17) || (next.Tag == 0x18)) {
164 nextUpdate = ASN1Convert.ToDateTime (next);
165 next = toBeSigned [n++];
167 // CertificateList / TBSCertList / revokedCertificates SEQUENCE OF SEQUENCE {
168 entries = new ArrayList ();
169 // this is OPTIONAL so it may not be present if no entries exists
170 if ((next != null) && (next.Tag == 0x30)) {
171 ASN1 revokedCertificates = next;
172 for (int i=0; i < revokedCertificates.Count; i++) {
173 entries.Add (new X509CrlEntry (revokedCertificates [i]));
175 } else {
176 n--;
178 // CertificateList / TBSCertList / crlExtensions [0] Extensions OPTIONAL }
179 ASN1 extns = toBeSigned [n];
180 if ((extns != null) && (extns.Tag == 0xA0) && (extns.Count == 1))
181 extensions = new X509ExtensionCollection (extns [0]);
182 else
183 extensions = new X509ExtensionCollection (null); // result in a read only object
184 // CertificateList / AlgorithmIdentifier
185 string signatureAlgorithm = ASN1Convert.ToOid (encodedCRL [1][0]);
186 if (signatureOID != signatureAlgorithm)
187 throw new CryptographicException (e + " [Non-matching signature algorithms in CRL]");
189 // CertificateList / BIT STRING
190 byte[] bitstring = encodedCRL [2].Value;
191 // first byte contains unused bits in first byte
192 signature = new byte [bitstring.Length - 1];
193 Buffer.BlockCopy (bitstring, 1, signature, 0, signature.Length);
195 catch {
196 throw new CryptographicException (e);
200 public ArrayList Entries {
201 get { return ArrayList.ReadOnly (entries); }
204 public X509CrlEntry this [int index] {
205 get { return (X509CrlEntry) entries [index]; }
208 public X509CrlEntry this [byte[] serialNumber] {
209 get { return GetCrlEntry (serialNumber); }
212 public X509ExtensionCollection Extensions {
213 get { return extensions; }
216 public byte[] Hash {
217 get {
218 if (hash_value == null) {
219 ASN1 encodedCRL = new ASN1 (encoded);
220 byte[] toBeSigned = encodedCRL [0].GetBytes ();
221 HashAlgorithm ha = HashAlgorithm.Create (GetHashName ());
222 hash_value = ha.ComputeHash (toBeSigned);
224 return hash_value;
228 public string IssuerName {
229 get { return issuer; }
232 public DateTime NextUpdate {
233 get { return nextUpdate; }
236 public DateTime ThisUpdate {
237 get { return thisUpdate; }
240 public string SignatureAlgorithm {
241 get { return signatureOID; }
244 public byte[] Signature {
245 get {
246 if (signature == null)
247 return null;
248 return (byte[]) signature.Clone ();
252 public byte[] RawData {
253 get { return (byte[]) encoded.Clone (); }
256 public byte Version {
257 get { return version; }
260 public bool IsCurrent {
261 get { return WasCurrent (DateTime.Now); }
264 public bool WasCurrent (DateTime instant)
266 if (nextUpdate == DateTime.MinValue)
267 return (instant >= thisUpdate);
268 else
269 return ((instant >= thisUpdate) && (instant <= nextUpdate));
272 public byte[] GetBytes ()
274 return (byte[]) encoded.Clone ();
277 private bool Compare (byte[] array1, byte[] array2)
279 if ((array1 == null) && (array2 == null))
280 return true;
281 if ((array1 == null) || (array2 == null))
282 return false;
283 if (array1.Length != array2.Length)
284 return false;
285 for (int i=0; i < array1.Length; i++) {
286 if (array1 [i] != array2 [i])
287 return false;
289 return true;
292 public X509CrlEntry GetCrlEntry (X509Certificate x509)
294 if (x509 == null)
295 throw new ArgumentNullException ("x509");
297 return GetCrlEntry (x509.SerialNumber);
300 public X509CrlEntry GetCrlEntry (byte[] serialNumber)
302 if (serialNumber == null)
303 throw new ArgumentNullException ("serialNumber");
305 for (int i=0; i < entries.Count; i++) {
306 X509CrlEntry entry = (X509CrlEntry) entries [i];
307 if (Compare (serialNumber, entry.SerialNumber))
308 return entry;
310 return null;
313 public bool VerifySignature (X509Certificate x509)
315 if (x509 == null)
316 throw new ArgumentNullException ("x509");
318 // 1. x509 certificate must be a CA certificate (unknown for v1 or v2 certs)
319 if (x509.Version >= 3) {
320 // 1.1. Check for "cRLSign" bit in KeyUsage extension
321 X509Extension ext = x509.Extensions ["2.5.29.15"];
322 if (ext != null) {
323 KeyUsageExtension keyUsage = new KeyUsageExtension (ext);
324 if (!keyUsage.Support (KeyUsages.cRLSign))
325 return false;
327 // 1.2. Check for ca = true in BasicConstraint
328 ext = x509.Extensions ["2.5.29.19"];
329 if (ext != null) {
330 BasicConstraintsExtension basicConstraints = new BasicConstraintsExtension (ext);
331 if (!basicConstraints.CertificateAuthority)
332 return false;
335 // 2. CRL issuer must match CA subject name
336 if (issuer != x509.SubjectName)
337 return false;
338 // 3. Check the CRL signature with the CA certificate public key
339 switch (signatureOID) {
340 case "1.2.840.10040.4.3":
341 return VerifySignature (x509.DSA);
342 default:
343 return VerifySignature (x509.RSA);
347 private string GetHashName ()
349 switch (signatureOID) {
350 // MD2 with RSA encryption
351 case "1.2.840.113549.1.1.2":
352 // maybe someone installed MD2 ?
353 return "MD2";
354 // MD5 with RSA encryption
355 case "1.2.840.113549.1.1.4":
356 return "MD5";
357 // SHA-1 with DSA
358 case "1.2.840.10040.4.3":
359 // SHA-1 with RSA Encryption
360 case "1.2.840.113549.1.1.5":
361 return "SHA1";
362 default:
363 throw new CryptographicException ("Unsupported hash algorithm: " + signatureOID);
367 internal bool VerifySignature (DSA dsa)
369 if (signatureOID != "1.2.840.10040.4.3")
370 throw new CryptographicException ("Unsupported hash algorithm: " + signatureOID);
371 DSASignatureDeformatter v = new DSASignatureDeformatter (dsa);
372 // only SHA-1 is supported
373 v.SetHashAlgorithm ("SHA1");
374 ASN1 sign = new ASN1 (signature);
375 if ((sign == null) || (sign.Count != 2))
376 return false;
377 // parts may be less than 20 bytes (i.e. first bytes were 0x00)
378 byte[] part1 = sign [0].Value;
379 byte[] part2 = sign [1].Value;
380 byte[] sig = new byte [40];
381 // parts may be less than 20 bytes (i.e. first bytes were 0x00)
382 // parts may be more than 20 bytes (i.e. first byte > 0x80, negative)
383 int s1 = System.Math.Max (0, part1.Length - 20);
384 int e1 = System.Math.Max (0, 20 - part1.Length);
385 Buffer.BlockCopy (part1, s1, sig, e1, part1.Length - s1);
386 int s2 = System.Math.Max (0, part2.Length - 20);
387 int e2 = System.Math.Max (20, 40 - part2.Length);
388 Buffer.BlockCopy (part2, s2, sig, e2, part2.Length - s2);
389 return v.VerifySignature (Hash, sig);
392 internal bool VerifySignature (RSA rsa)
394 RSAPKCS1SignatureDeformatter v = new RSAPKCS1SignatureDeformatter (rsa);
395 v.SetHashAlgorithm (GetHashName ());
396 return v.VerifySignature (Hash, signature);
399 public bool VerifySignature (AsymmetricAlgorithm aa)
401 if (aa == null)
402 throw new ArgumentNullException ("aa");
404 // only validate the signature (in case we don't have the CA certificate)
405 if (aa is RSA)
406 return VerifySignature (aa as RSA);
407 else if (aa is DSA)
408 return VerifySignature (aa as DSA);
409 else
410 throw new NotSupportedException ("Unknown Asymmetric Algorithm " + aa.ToString ());
413 static public X509Crl CreateFromFile (string filename)
415 byte[] crl = null;
416 using (FileStream fs = File.Open (filename, FileMode.Open, FileAccess.Read, FileShare.Read)) {
417 crl = new byte [fs.Length];
418 fs.Read (crl, 0, crl.Length);
419 fs.Close ();
421 return new X509Crl (crl);