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
{
341 get { return (byte[]) m_encodedcert.Clone (); }
344 public virtual byte[] SerialNumber
{
346 if (serialnumber
== null)
348 return (byte[]) serialnumber
.Clone ();
352 public virtual byte[] Signature
{
354 if (signature
== 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))
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
);
377 throw new CryptographicException ("Unsupported hash algorithm: " + m_signaturealgo
);
382 public virtual string SignatureAlgorithm
{
383 get { return m_signaturealgo; }
386 public virtual byte[] SignatureAlgorithmParameters
{
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; }
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");
437 // MD5 with RSA encryption
438 case "1.2.840.113549.1.1.4":
439 v
.SetHashAlgorithm ("MD5");
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");
447 throw new CryptographicException ("Unsupported hash algorithm: " + m_signaturealgo
);
449 return v
.VerifySignature (this.Hash
, this.Signature
);
452 public bool VerifySignature (AsymmetricAlgorithm aa
)
455 throw new ArgumentNullException ("aa");
458 return VerifySignature (aa
as RSA
);
460 return VerifySignature (aa
as DSA
);
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
{
473 if (m_issuername
== m_subject
)
474 return VerifySignature (RSA
);