(DISTFILES): Comment out a few missing files.
[mono-project.git] / mcs / class / corlib / Mono.Security.X509 / X509Certificate.cs
blob1a867aeccd0a7228718af07fa38f4e15f93ef4a9
1 //
2 // X509Certificates.cs: Handles X.509 certificates.
3 //
4 // Author:
5 // Sebastien Pouliot <sebastien@ximian.com>
6 //
7 // (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
8 // (C) 2004 Novell (http://www.novell.com)
9 //
12 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 //
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 //
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System;
35 using System.Security.Cryptography;
36 using SSCX = System.Security.Cryptography.X509Certificates;
37 using System.Text;
39 namespace Mono.Security.X509 {
41 // References:
42 // a. Internet X.509 Public Key Infrastructure Certificate and CRL Profile
43 // http://www.ietf.org/rfc/rfc3280.txt
44 // b. ITU ASN.1 standards (free download)
45 // http://www.itu.int/ITU-T/studygroups/com17/languages/
47 #if INSIDE_CORLIB
48 internal
49 #else
50 public
51 #endif
52 class X509Certificate {
54 private ASN1 decoder;
56 private byte[] m_encodedcert;
57 private DateTime m_from;
58 private DateTime m_until;
59 private string m_issuername;
60 private string m_keyalgo;
61 private byte[] m_keyalgoparams;
62 private string m_subject;
63 private byte[] m_publickey;
64 private byte[] signature;
65 private string m_signaturealgo;
66 private byte[] m_signaturealgoparams;
67 private byte[] certhash;
69 // from http://www.ietf.org/rfc/rfc2459.txt
71 //Certificate ::= SEQUENCE {
72 // tbsCertificate TBSCertificate,
73 // signatureAlgorithm AlgorithmIdentifier,
74 // signature BIT STRING }
76 //TBSCertificate ::= SEQUENCE {
77 // version [0] Version DEFAULT v1,
78 // serialNumber CertificateSerialNumber,
79 // signature AlgorithmIdentifier,
80 // issuer Name,
81 // validity Validity,
82 // subject Name,
83 // subjectPublicKeyInfo SubjectPublicKeyInfo,
84 // issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
85 // -- If present, version shall be v2 or v3
86 // subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
87 // -- If present, version shall be v2 or v3
88 // extensions [3] Extensions OPTIONAL
89 // -- If present, version shall be v3 -- }
90 private int version;
91 private byte[] serialnumber;
93 private byte[] issuerUniqueID;
94 private byte[] subjectUniqueID;
95 private X509ExtensionCollection extensions;
97 // that's were the real job is!
98 private void Parse (byte[] data)
100 string e = "Input data cannot be coded as a valid certificate.";
101 try {
102 decoder = new ASN1 (data);
103 // Certificate
104 if (decoder.Tag != 0x30)
105 throw new CryptographicException (e);
106 // Certificate / TBSCertificate
107 if (decoder [0].Tag != 0x30)
108 throw new CryptographicException (e);
110 ASN1 tbsCertificate = decoder [0];
112 int tbs = 0;
113 // Certificate / TBSCertificate / Version
114 ASN1 v = decoder [0][tbs];
115 version = 1; // DEFAULT v1
116 if (v.Tag == 0xA0) {
117 // version (optional) is present only in v2+ certs
118 version += v.Value [0]; // zero based
119 tbs++;
122 // Certificate / TBSCertificate / CertificateSerialNumber
123 ASN1 sn = decoder [0][tbs++];
124 if (sn.Tag != 0x02)
125 throw new CryptographicException (e);
126 serialnumber = sn.Value;
127 Array.Reverse (serialnumber, 0, serialnumber.Length);
129 // Certificate / TBSCertificate / AlgorithmIdentifier
130 tbs++;
131 // ASN1 signatureAlgo = tbsCertificate.Element (tbs++, 0x30);
133 ASN1 issuer = tbsCertificate.Element (tbs++, 0x30);
134 m_issuername = X501.ToString (issuer);
136 ASN1 validity = tbsCertificate.Element (tbs++, 0x30);
137 ASN1 notBefore = validity [0];
138 m_from = ASN1Convert.ToDateTime (notBefore);
139 ASN1 notAfter = validity [1];
140 m_until = ASN1Convert.ToDateTime (notAfter);
142 ASN1 subject = tbsCertificate.Element (tbs++, 0x30);
143 m_subject = X501.ToString (subject);
145 ASN1 subjectPublicKeyInfo = tbsCertificate.Element (tbs++, 0x30);
147 ASN1 algorithm = subjectPublicKeyInfo.Element (0, 0x30);
148 ASN1 algo = algorithm.Element (0, 0x06);
149 m_keyalgo = ASN1Convert.ToOid (algo);
150 // parameters ANY DEFINED BY algorithm OPTIONAL
151 // so we dont ask for a specific (Element) type and return DER
152 ASN1 parameters = algorithm [1];
153 m_keyalgoparams = ((algorithm.Count > 1) ? parameters.GetBytes () : null);
155 ASN1 subjectPublicKey = subjectPublicKeyInfo.Element (1, 0x03);
156 // we must drop th first byte (which is the number of unused bits
157 // in the BITSTRING)
158 int n = subjectPublicKey.Length - 1;
159 m_publickey = new byte [n];
160 Buffer.BlockCopy (subjectPublicKey.Value, 1, m_publickey, 0, n);
162 // signature processing
163 byte[] bitstring = decoder [2].Value;
164 // first byte contains unused bits in first byte
165 signature = new byte [bitstring.Length - 1];
166 Buffer.BlockCopy (bitstring, 1, signature, 0, signature.Length);
168 algorithm = decoder [1];
169 algo = algorithm.Element (0, 0x06);
170 m_signaturealgo = ASN1Convert.ToOid (algo);
171 parameters = algorithm [1];
172 if (parameters != null)
173 m_signaturealgoparams = parameters.GetBytes ();
174 else
175 m_signaturealgoparams = null;
177 // Certificate / TBSCertificate / issuerUniqueID
178 ASN1 issuerUID = tbsCertificate.Element (tbs, 0xA1);
179 if (issuerUID != null) {
180 tbs++;
181 issuerUniqueID = issuerUID.Value;
184 // Certificate / TBSCertificate / subjectUniqueID
185 ASN1 subjectUID = tbsCertificate.Element (tbs, 0xA2);
186 if (subjectUID != null) {
187 tbs++;
188 subjectUniqueID = subjectUID.Value;
191 // Certificate / TBSCertificate / Extensions
192 ASN1 extns = tbsCertificate.Element (tbs, 0xA3);
193 if ((extns != null) && (extns.Count == 1))
194 extensions = new X509ExtensionCollection (extns [0]);
195 else
196 extensions = new X509ExtensionCollection (null);
198 // keep a copy of the original data
199 m_encodedcert = (byte[]) data.Clone ();
201 catch (Exception ex) {
202 throw new CryptographicException (e, ex);
206 // constructors
208 public X509Certificate (byte[] data)
210 if (data != null)
211 Parse (data);
214 private byte[] GetUnsignedBigInteger (byte[] integer)
216 if (integer [0] == 0x00) {
217 // this first byte is added so we're sure it's an unsigned integer
218 // however we can't feed it into RSAParameters or DSAParameters
219 int length = integer.Length - 1;
220 byte[] uinteger = new byte [length];
221 Buffer.BlockCopy (integer, 1, uinteger, 0, length);
222 return uinteger;
224 else
225 return integer;
228 // public methods
230 public DSA DSA {
231 get {
232 DSAParameters dsaParams = new DSAParameters ();
233 // for DSA m_publickey contains 1 ASN.1 integer - Y
234 ASN1 pubkey = new ASN1 (m_publickey);
235 if ((pubkey == null) || (pubkey.Tag != 0x02))
236 return null;
237 dsaParams.Y = GetUnsignedBigInteger (pubkey.Value);
239 ASN1 param = new ASN1 (m_keyalgoparams);
240 if ((param == null) || (param.Tag != 0x30) || (param.Count < 3))
241 return null;
242 if ((param [0].Tag != 0x02) || (param [1].Tag != 0x02) || (param [2].Tag != 0x02))
243 return null;
244 dsaParams.P = GetUnsignedBigInteger (param [0].Value);
245 dsaParams.Q = GetUnsignedBigInteger (param [1].Value);
246 dsaParams.G = GetUnsignedBigInteger (param [2].Value);
248 // BUG: MS BCL 1.0 can't import a key which
249 // isn't the same size as the one present in
250 // the container.
251 DSACryptoServiceProvider dsa = new DSACryptoServiceProvider (dsaParams.Y.Length << 3);
252 dsa.ImportParameters (dsaParams);
253 return (DSA) dsa;
257 public X509ExtensionCollection Extensions {
258 get { return extensions; }
261 public byte[] Hash {
262 get {
263 if (certhash == null) {
264 HashAlgorithm hash = null;
265 switch (m_signaturealgo) {
266 case "1.2.840.113549.1.1.2": // MD2 with RSA encryption
267 // maybe someone installed MD2 ?
268 hash = HashAlgorithm.Create ("MD2");
269 break;
270 case "1.2.840.113549.1.1.4": // MD5 with RSA encryption
271 hash = MD5.Create ();
272 break;
273 case "1.2.840.113549.1.1.5": // SHA-1 with RSA Encryption
274 case "1.3.14.3.2.29": // SHA1 with RSA signature
275 case "1.2.840.10040.4.3": // SHA1-1 with DSA
276 hash = SHA1.Create ();
277 break;
278 default:
279 return null;
281 if ((decoder == null) || (decoder.Count < 1))
282 return null;
283 byte[] toBeSigned = decoder [0].GetBytes ();
284 certhash = hash.ComputeHash (toBeSigned, 0, toBeSigned.Length);
286 return (byte[]) certhash.Clone ();
290 public virtual string IssuerName {
291 get { return m_issuername; }
294 public virtual string KeyAlgorithm {
295 get { return m_keyalgo; }
298 public virtual byte[] KeyAlgorithmParameters {
299 get {
300 if (m_keyalgoparams == null)
301 return null;
302 return (byte[]) m_keyalgoparams.Clone ();
306 public virtual byte[] PublicKey {
307 get {
308 if (m_publickey == null)
309 return null;
310 return (byte[]) m_publickey.Clone ();
314 public virtual RSA RSA {
315 get {
316 RSAParameters rsaParams = new RSAParameters ();
317 // for RSA m_publickey contains 2 ASN.1 integers
318 // the modulus and the public exponent
319 ASN1 pubkey = new ASN1 (m_publickey);
320 ASN1 modulus = pubkey [0];
321 if ((modulus == null) || (modulus.Tag != 0x02))
322 return null;
323 ASN1 exponent = pubkey [1];
324 if (exponent.Tag != 0x02)
325 return null;
327 rsaParams.Modulus = GetUnsignedBigInteger (modulus.Value);
328 rsaParams.Exponent = exponent.Value;
330 // BUG: MS BCL 1.0 can't import a key which
331 // isn't the same size as the one present in
332 // the container.
333 int keySize = (rsaParams.Modulus.Length << 3);
334 RSACryptoServiceProvider rsa = new RSACryptoServiceProvider (keySize);
335 rsa.ImportParameters (rsaParams);
336 return (RSA)rsa;
340 public virtual byte[] RawData {
341 get { return (byte[]) m_encodedcert.Clone (); }
344 public virtual byte[] SerialNumber {
345 get {
346 if (serialnumber == null)
347 return null;
348 return (byte[]) serialnumber.Clone ();
352 public virtual byte[] Signature {
353 get {
354 if (signature == null)
355 return null;
357 switch (m_signaturealgo) {
358 case "1.2.840.113549.1.1.2": // MD2 with RSA encryption
359 case "1.2.840.113549.1.1.4": // MD5 with RSA encryption
360 case "1.2.840.113549.1.1.5": // SHA-1 with RSA Encryption
361 case "1.3.14.3.2.29": // SHA1 with RSA signature
362 return (byte[]) signature.Clone ();
364 case "1.2.840.10040.4.3": // SHA-1 with DSA
365 ASN1 sign = new ASN1 (signature);
366 if ((sign == null) || (sign.Count != 2))
367 return null;
368 // parts may be less than 20 bytes (i.e. first bytes were 0x00)
369 byte[] part1 = sign [0].Value;
370 byte[] part2 = sign [1].Value;
371 byte[] sig = new byte [40];
372 Buffer.BlockCopy (part1, 0, sig, (20 - part1.Length), part1.Length);
373 Buffer.BlockCopy (part2, 0, sig, (40 - part2.Length), part2.Length);
374 return sig;
376 default:
377 throw new CryptographicException ("Unsupported hash algorithm: " + m_signaturealgo);
382 public virtual string SignatureAlgorithm {
383 get { return m_signaturealgo; }
386 public virtual byte[] SignatureAlgorithmParameters {
387 get {
388 if (m_signaturealgoparams == null)
389 return m_signaturealgoparams;
390 return (byte[]) m_signaturealgoparams.Clone ();
394 public virtual string SubjectName {
395 get { return m_subject; }
398 public virtual DateTime ValidFrom {
399 get { return m_from; }
402 public virtual DateTime ValidUntil {
403 get { return m_until; }
406 public int Version {
407 get { return version; }
410 public bool IsCurrent {
411 get { return WasCurrent (DateTime.UtcNow); }
414 public bool WasCurrent (DateTime instant)
416 return ((instant > ValidFrom) && (instant <= ValidUntil));
419 internal bool VerifySignature (DSA dsa)
421 // signatureOID is check by both this.Hash and this.Signature
422 DSASignatureDeformatter v = new DSASignatureDeformatter (dsa);
423 // only SHA-1 is supported
424 v.SetHashAlgorithm ("SHA1");
425 return v.VerifySignature (this.Hash, this.Signature);
428 internal bool VerifySignature (RSA rsa)
430 RSAPKCS1SignatureDeformatter v = new RSAPKCS1SignatureDeformatter (rsa);
431 switch (m_signaturealgo) {
432 // MD2 with RSA encryption
433 case "1.2.840.113549.1.1.2":
434 // maybe someone installed MD2 ?
435 v.SetHashAlgorithm ("MD2");
436 break;
437 // MD5 with RSA encryption
438 case "1.2.840.113549.1.1.4":
439 v.SetHashAlgorithm ("MD5");
440 break;
441 // SHA-1 with RSA Encryption
442 case "1.2.840.113549.1.1.5":
443 case "1.3.14.3.2.29":
444 v.SetHashAlgorithm ("SHA1");
445 break;
446 default:
447 throw new CryptographicException ("Unsupported hash algorithm: " + m_signaturealgo);
449 return v.VerifySignature (this.Hash, this.Signature);
452 public bool VerifySignature (AsymmetricAlgorithm aa)
454 if (aa == null)
455 throw new ArgumentNullException ("aa");
457 if (aa is RSA)
458 return VerifySignature (aa as RSA);
459 else if (aa is DSA)
460 return VerifySignature (aa as DSA);
461 else
462 throw new NotSupportedException ("Unknown Asymmetric Algorithm " + aa.ToString ());
465 public bool CheckSignature (byte[] hash, string hashAlgorithm, byte[] signature)
467 RSACryptoServiceProvider r = (RSACryptoServiceProvider) RSA;
468 return r.VerifyHash (hash, hashAlgorithm, signature);
471 public bool IsSelfSigned {
472 get {
473 if (m_issuername == m_subject)
474 return VerifySignature (RSA);
475 else
476 return false;