2 // X509Certificates.cs: Handles X.509 certificates.
5 // Sebastien Pouliot (spouliot@motus.com)
7 // (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
11 using System.Security.Cryptography;
12 using SSCX = System.Security.Cryptography.X509Certificates;
15 namespace Mono.Security.X509 {
18 // a. Internet X.509 Public Key Infrastructure Certificate and CRL Profile
19 // http://www.ietf.org/rfc/rfc3280.txt
20 // b. ITU ASN.1 standards (free download)
21 // http://www.itu.int/ITU-T/studygroups/com17/languages/
23 public class X509Certificate {
27 private byte[] m_encodedcert;
28 private DateTime m_from;
29 private DateTime m_until;
30 private string m_issuername;
31 private string m_keyalgo;
32 private byte[] m_keyalgoparams;
33 private string m_subject;
34 private byte[] m_publickey;
35 private byte[] signature;
36 private string m_signaturealgo;
37 private byte[] m_signaturealgoparams;
39 // from http://www.ietf.org/rfc/rfc2459.txt
41 //Certificate ::= SEQUENCE {
42 // tbsCertificate TBSCertificate,
43 // signatureAlgorithm AlgorithmIdentifier,
44 // signature BIT STRING }
46 //TBSCertificate ::= SEQUENCE {
47 // version [0] Version DEFAULT v1,
48 // serialNumber CertificateSerialNumber,
49 // signature AlgorithmIdentifier,
53 // subjectPublicKeyInfo SubjectPublicKeyInfo,
54 // issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
55 // -- If present, version shall be v2 or v3
56 // subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
57 // -- If present, version shall be v2 or v3
58 // extensions [3] Extensions OPTIONAL
59 // -- If present, version shall be v3 -- }
61 private byte[] serialnumber;
63 private byte[] issuerUniqueID;
64 private byte[] subjectUniqueID;
65 private X509Extensions extensions;
67 // that's were the real job is!
68 private void Parse (byte[] data)
70 string e = "Input data cannot be coded as a valid certificate.";
72 decoder = new ASN1 (data);
74 if (decoder.Tag != 0x30)
75 throw new CryptographicException (e);
76 // Certificate / TBSCertificate
77 if (decoder [0].Tag != 0x30)
78 throw new CryptographicException (e);
80 ASN1 tbsCertificate = decoder [0];
83 // Certificate / TBSCertificate / Version
84 ASN1 v = decoder [0][tbs];
85 version = 1; // DEFAULT v1
87 // version (optional) is present only in v2+ certs
88 version += v.Value [0]; // zero based
92 // Certificate / TBSCertificate / CertificateSerialNumber
93 ASN1 sn = decoder [0][tbs++];
95 throw new CryptographicException (e);
96 serialnumber = sn.Value;
97 Array.Reverse (serialnumber, 0, serialnumber.Length);
99 // Certificate / TBSCertificate / AlgorithmIdentifier
100 ASN1 signatureAlgo = tbsCertificate.Element (tbs++, 0x30);
102 ASN1 issuer = tbsCertificate.Element (tbs++, 0x30);
103 m_issuername = X501.ToString (issuer);
105 ASN1 validity = tbsCertificate.Element (tbs++, 0x30);
106 ASN1 notBefore = validity [0];
107 m_from = ASN1Convert.ToDateTime (notBefore);
108 ASN1 notAfter = validity [1];
109 m_until = ASN1Convert.ToDateTime (notAfter);
111 ASN1 subject = tbsCertificate.Element (tbs++, 0x30);
112 m_subject = X501.ToString (subject);
114 ASN1 subjectPublicKeyInfo = tbsCertificate.Element (tbs++, 0x30);
116 ASN1 algorithm = subjectPublicKeyInfo.Element (0, 0x30);
117 ASN1 algo = algorithm.Element (0, 0x06);
118 m_keyalgo = ASN1Convert.ToOID (algo);
119 // parameters ANY DEFINED BY algorithm OPTIONAL
120 // so we dont ask for a specific (Element) type and return DER
121 ASN1 parameters = algorithm [1];
122 m_keyalgoparams = ((algorithm.Count > 1) ? parameters.GetBytes () : null);
124 ASN1 subjectPublicKey = subjectPublicKeyInfo.Element (1, 0x03);
125 // we must drop th first byte (which is the number of unused bits
127 int n = subjectPublicKey.Length - 1;
128 m_publickey = new byte [n];
129 Array.Copy (subjectPublicKey.Value, 1, m_publickey, 0, n);
131 // signature processing
132 byte[] bitstring = decoder [2].Value;
133 // first byte contains unused bits in first byte
134 signature = new byte [bitstring.Length - 1];
135 Array.Copy (bitstring, 1, signature, 0, signature.Length);
137 algorithm = decoder [1];
138 algo = algorithm.Element (0, 0x06);
139 m_signaturealgo = ASN1Convert.ToOID (algo);
140 parameters = algorithm [1];
141 if (parameters != null)
142 m_signaturealgoparams = parameters.GetBytes ();
144 m_signaturealgoparams = null;
146 // Certificate / TBSCertificate / issuerUniqueID
147 ASN1 issuerUID = tbsCertificate.Element (tbs, 0xA1);
148 if (issuerUID != null) {
150 issuerUniqueID = issuerUID.Value;
153 // Certificate / TBSCertificate / subjectUniqueID
154 ASN1 subjectUID = tbsCertificate.Element (tbs, 0xA2);
155 if (subjectUID != null) {
157 subjectUniqueID = subjectUID.Value;
160 // Certificate / TBSCertificate / Extensions
161 ASN1 extns = tbsCertificate.Element (tbs, 0xA3);
162 if ((extns != null) && (extns.Count == 1))
163 extensions = new X509Extensions (extns [0]);
165 extensions = new X509Extensions (null);
167 // keep a copy of the original data
168 m_encodedcert = (byte[]) data.Clone ();
171 throw new CryptographicException (e);
177 public X509Certificate (byte[] data)
183 private byte[] GetUnsignedBigInteger (byte[] integer)
185 if (integer [0] == 0x00) {
186 // this first byte is added so we're sure it's an unsigned integer
187 // however we can't feed it into RSAParameters or DSAParameters
188 int length = integer.Length - 1;
189 byte[] uinteger = new byte [length];
190 Array.Copy (integer, 1, uinteger, 0, length);
201 DSAParameters dsaParams = new DSAParameters ();
202 // for DSA m_publickey contains 1 ASN.1 integer - Y
203 ASN1 pubkey = new ASN1 (m_publickey);
204 if ((pubkey == null) || (pubkey.Tag != 0x02))
206 dsaParams.Y = GetUnsignedBigInteger (pubkey.Value);
208 ASN1 param = new ASN1 (m_keyalgoparams);
209 if ((param == null) || (param.Tag != 0x30) || (param.Count < 3))
211 if ((param [0].Tag != 0x02) || (param [1].Tag != 0x02) || (param [2].Tag != 0x02))
213 dsaParams.P = GetUnsignedBigInteger (param [0].Value);
214 dsaParams.Q = GetUnsignedBigInteger (param [1].Value);
215 dsaParams.G = GetUnsignedBigInteger (param [2].Value);
217 // BUG: MS BCL 1.0 can't import a key which
218 // isn't the same size as the one present in
220 DSACryptoServiceProvider dsa = new DSACryptoServiceProvider (dsaParams.Y.Length << 3);
221 dsa.ImportParameters (dsaParams);
226 public X509Extensions Extensions {
227 get { return extensions; }
232 HashAlgorithm hash = null;
233 switch (m_signaturealgo) {
234 case "1.2.840.113549.1.1.2": // MD2 with RSA encryption
235 // maybe someone installed MD2 ?
236 hash = HashAlgorithm.Create ("MD2");
238 case "1.2.840.113549.1.1.4": // MD5 with RSA encryption
239 hash = MD5.Create ();
241 case "1.2.840.113549.1.1.5": // SHA-1 with RSA Encryption
242 case "1.3.14.3.2.29": // SHA1 with RSA signature
243 case "1.2.840.10040.4.3": // SHA1-1 with DSA
244 hash = SHA1.Create ();
250 byte[] toBeSigned = decoder [0].GetBytes ();
251 return hash.ComputeHash (toBeSigned, 0, toBeSigned.Length);
259 public virtual string IssuerName {
260 get { return m_issuername; }
263 public virtual string KeyAlgorithm {
264 get { return m_keyalgo; }
267 public virtual byte[] KeyAlgorithmParameters {
268 get { return m_keyalgoparams; }
271 public virtual byte[] PublicKey {
272 get { return m_publickey; }
275 public virtual RSA RSA {
277 RSAParameters rsaParams = new RSAParameters ();
278 // for RSA m_publickey contains 2 ASN.1 integers
279 // the modulus and the public exponent
280 ASN1 pubkey = new ASN1 (m_publickey);
281 ASN1 modulus = pubkey [0];
282 if ((modulus == null) || (modulus.Tag != 0x02))
284 ASN1 exponent = pubkey [1];
285 if (exponent.Tag != 0x02)
288 rsaParams.Modulus = GetUnsignedBigInteger (modulus.Value);
289 rsaParams.Exponent = exponent.Value;
291 // BUG: MS BCL 1.0 can't import a key which
292 // isn't the same size as the one present in
294 int keySize = (rsaParams.Modulus.Length << 3);
295 RSACryptoServiceProvider rsa = new RSACryptoServiceProvider (keySize);
296 rsa.ImportParameters (rsaParams);
301 public virtual byte[] RawData {
302 get { return (byte[]) m_encodedcert.Clone (); }
305 public virtual byte[] SerialNumber {
306 get { return serialnumber; }
309 public virtual byte[] Signature {
311 switch (m_signaturealgo) {
312 case "1.2.840.113549.1.1.2": // MD2 with RSA encryption
313 case "1.2.840.113549.1.1.4": // MD5 with RSA encryption
314 case "1.2.840.113549.1.1.5": // SHA-1 with RSA Encryption
315 case "1.3.14.3.2.29": // SHA1 with RSA signature
317 case "1.2.840.10040.4.3": // SHA-1 with DSA
318 ASN1 sign = new ASN1 (signature);
319 if ((sign == null) || (sign.Count != 2))
321 // parts may be less than 20 bytes (i.e. first bytes were 0x00)
322 byte[] part1 = sign [0].Value;
323 byte[] part2 = sign [1].Value;
324 byte[] sig = new byte [40];
325 Array.Copy (part1, 0, sig, (20 - part1.Length), part1.Length);
326 Array.Copy (part2, 0, sig, (40 - part2.Length), part2.Length);
329 throw new CryptographicException ("Unsupported hash algorithm: " + m_signaturealgo);
334 public virtual string SignatureAlgorithm {
335 get { return m_signaturealgo; }
338 public virtual byte[] SignatureAlgorithmParameters {
339 get { return m_signaturealgoparams; }
342 public virtual string SubjectName {
343 get { return m_subject; }
346 public virtual DateTime ValidFrom {
347 get { return m_from; }
350 public virtual DateTime ValidUntil {
351 get { return m_until; }
355 get { return version; }
358 public bool IsCurrent {
359 get { return WasCurrent (DateTime.UtcNow); }
362 public bool WasCurrent (DateTime date)
364 return ((date > ValidFrom) && (date <= ValidUntil));
367 private byte[] GetHash (string hashName)
369 byte[] toBeSigned = decoder [0].GetBytes ();
370 HashAlgorithm ha = HashAlgorithm.Create (hashName);
371 return ha.ComputeHash (toBeSigned);
374 public bool VerifySignature (DSA dsa)
376 // signatureOID is check by both this.Hash and this.Signature
377 DSASignatureDeformatter v = new DSASignatureDeformatter (dsa);
378 // only SHA-1 is supported
379 v.SetHashAlgorithm ("SHA1");
380 return v.VerifySignature (this.Hash, this.Signature);
383 internal bool VerifySignature (RSA rsa)
385 RSAPKCS1SignatureDeformatter v = new RSAPKCS1SignatureDeformatter (rsa);
386 switch (m_signaturealgo) {
387 // MD2 with RSA encryption
388 case "1.2.840.113549.1.1.2":
389 // maybe someone installed MD2 ?
390 v.SetHashAlgorithm ("MD2");
392 // MD5 with RSA encryption
393 case "1.2.840.113549.1.1.4":
394 v.SetHashAlgorithm ("MD5");
396 // SHA-1 with RSA Encryption
397 case "1.2.840.113549.1.1.5":
398 case "1.3.14.3.2.29":
399 v.SetHashAlgorithm ("SHA1");
402 throw new CryptographicException ("Unsupported hash algorithm: " + m_signaturealgo);
404 return v.VerifySignature (this.Hash, this.Signature);
407 public bool VerifySignature (AsymmetricAlgorithm aa)
410 return VerifySignature (aa as RSA);
412 return VerifySignature (aa as DSA);
414 throw new NotSupportedException ("Unknown Asymmetric Algorithm " + aa.ToString ());
417 public bool CheckSignature (byte[] hash, string hashAlgorithm, byte[] signature)
419 RSACryptoServiceProvider r = (RSACryptoServiceProvider) RSA;
420 return r.VerifyHash (hash, hashAlgorithm, signature);
423 public bool IsSelfSigned {
425 if (m_issuername == m_subject)
426 return VerifySignature (RSA);