2 // X509Certificates.cs: Handles X.509 certificates.
5 // Sebastien Pouliot <sebastien@ximian.com>
7 // (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
8 // (C) 2004 Novell (http://www.novell.com)
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:
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
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.
35 using System.Security.Cryptography;
36 using SSCX = System.Security.Cryptography.X509Certificates;
39 namespace Mono.Security.X509 {
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/
52 class X509Certificate {
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,
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 -- }
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.";
102 decoder = new ASN1 (data);
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];
113 // Certificate / TBSCertificate / Version
114 ASN1 v = decoder [0][tbs];
115 version = 1; // DEFAULT v1
117 // version (optional) is present only in v2+ certs
118 version += v.Value [0]; // zero based
122 // Certificate / TBSCertificate / CertificateSerialNumber
123 ASN1 sn = decoder [0][tbs++];
125 throw new CryptographicException (e);
126 serialnumber = sn.Value;
127 Array.Reverse (serialnumber, 0, serialnumber.Length);
129 // Certificate / TBSCertificate / AlgorithmIdentifier
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
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 ();
175 m_signaturealgoparams = null;
177 // Certificate / TBSCertificate / issuerUniqueID
178 ASN1 issuerUID = tbsCertificate.Element (tbs, 0xA1);
179 if (issuerUID != null) {
181 issuerUniqueID = issuerUID.Value;
184 // Certificate / TBSCertificate / subjectUniqueID
185 ASN1 subjectUID = tbsCertificate.Element (tbs, 0xA2);
186 if (subjectUID != null) {
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]);
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);
208 public X509Certificate (byte[] 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);
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))
237 dsaParams.Y = GetUnsignedBigInteger (pubkey.Value);
239 ASN1 param = new ASN1 (m_keyalgoparams);
240 if ((param == null) || (param.Tag != 0x30) || (param.Count < 3))
242 if ((param [0].Tag != 0x02) || (param [1].Tag != 0x02) || (param [2].Tag != 0x02))
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
251 DSACryptoServiceProvider dsa = new DSACryptoServiceProvider (dsaParams.Y.Length << 3);
252 dsa.ImportParameters (dsaParams);
257 public X509ExtensionCollection Extensions {
258 get { return extensions; }
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");
270 case "1.2.840.113549.1.1.4": // MD5 with RSA encryption
271 hash = MD5.Create ();
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 ();
281 if ((decoder == null) || (decoder.Count < 1))
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 {
300 if (m_keyalgoparams == null)
302 return (byte[]) m_keyalgoparams.Clone ();
306 public virtual byte[] PublicKey {
308 if (m_publickey == null)
310 return (byte[]) m_publickey.Clone ();
314 public virtual RSA RSA {
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))
323 ASN1 exponent = pubkey [1];
324 if (exponent.Tag != 0x02)
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
333 int keySize = (rsaParams.Modulus.Length << 3);
334 RSACryptoServiceProvider rsa = new RSACryptoServiceProvider (keySize);
335 rsa.ImportParameters (rsaParams);
340 public virtual byte[] RawData {
342 if (m_encodedcert == null)
344 return (byte[]) m_encodedcert.Clone ();
348 public virtual byte[] SerialNumber {
350 if (serialnumber == null)
352 return (byte[]) serialnumber.Clone ();
356 public virtual byte[] Signature {
358 if (signature == null)
361 switch (m_signaturealgo) {
362 case "1.2.840.113549.1.1.2": // MD2 with RSA encryption
363 case "1.2.840.113549.1.1.4": // MD5 with RSA encryption
364 case "1.2.840.113549.1.1.5": // SHA-1 with RSA Encryption
365 case "1.3.14.3.2.29": // SHA1 with RSA signature
366 return (byte[]) signature.Clone ();
368 case "1.2.840.10040.4.3": // SHA-1 with DSA
369 ASN1 sign = new ASN1 (signature);
370 if ((sign == null) || (sign.Count != 2))
372 // parts may be less than 20 bytes (i.e. first bytes were 0x00)
373 byte[] part1 = sign [0].Value;
374 byte[] part2 = sign [1].Value;
375 byte[] sig = new byte [40];
376 Buffer.BlockCopy (part1, 0, sig, (20 - part1.Length), part1.Length);
377 Buffer.BlockCopy (part2, 0, sig, (40 - part2.Length), part2.Length);
381 throw new CryptographicException ("Unsupported hash algorithm: " + m_signaturealgo);
386 public virtual string SignatureAlgorithm {
387 get { return m_signaturealgo; }
390 public virtual byte[] SignatureAlgorithmParameters {
392 if (m_signaturealgoparams == null)
393 return m_signaturealgoparams;
394 return (byte[]) m_signaturealgoparams.Clone ();
398 public virtual string SubjectName {
399 get { return m_subject; }
402 public virtual DateTime ValidFrom {
403 get { return m_from; }
406 public virtual DateTime ValidUntil {
407 get { return m_until; }
411 get { return version; }
414 public bool IsCurrent {
415 get { return WasCurrent (DateTime.UtcNow); }
418 public bool WasCurrent (DateTime instant)
420 return ((instant > ValidFrom) && (instant <= ValidUntil));
423 internal bool VerifySignature (DSA dsa)
425 // signatureOID is check by both this.Hash and this.Signature
426 DSASignatureDeformatter v = new DSASignatureDeformatter (dsa);
427 // only SHA-1 is supported
428 v.SetHashAlgorithm ("SHA1");
429 return v.VerifySignature (this.Hash, this.Signature);
432 internal bool VerifySignature (RSA rsa)
434 RSAPKCS1SignatureDeformatter v = new RSAPKCS1SignatureDeformatter (rsa);
435 switch (m_signaturealgo) {
436 // MD2 with RSA encryption
437 case "1.2.840.113549.1.1.2":
438 // maybe someone installed MD2 ?
439 v.SetHashAlgorithm ("MD2");
441 // MD5 with RSA encryption
442 case "1.2.840.113549.1.1.4":
443 v.SetHashAlgorithm ("MD5");
445 // SHA-1 with RSA Encryption
446 case "1.2.840.113549.1.1.5":
447 case "1.3.14.3.2.29":
448 v.SetHashAlgorithm ("SHA1");
451 throw new CryptographicException ("Unsupported hash algorithm: " + m_signaturealgo);
453 return v.VerifySignature (this.Hash, this.Signature);
456 public bool VerifySignature (AsymmetricAlgorithm aa)
459 throw new ArgumentNullException ("aa");
462 return VerifySignature (aa as RSA);
464 return VerifySignature (aa as DSA);
466 throw new NotSupportedException ("Unknown Asymmetric Algorithm " + aa.ToString ());
469 public bool CheckSignature (byte[] hash, string hashAlgorithm, byte[] signature)
471 RSACryptoServiceProvider r = (RSACryptoServiceProvider) RSA;
472 return r.VerifyHash (hash, hashAlgorithm, signature);
475 public bool IsSelfSigned {
477 if (m_issuername == m_subject)
478 return VerifySignature (RSA);