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 // Copyright (C) 2004-2006 Novell, Inc (http://www.novell.com)
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 using System
.Runtime
.Serialization
;
32 using System
.Security
.Cryptography
;
33 using SSCX
= System
.Security
.Cryptography
.X509Certificates
;
34 using System
.Security
.Permissions
;
37 using Mono
.Security
.Cryptography
;
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/
48 internal class X509Certificate
: ISerializable
{
50 public class X509Certificate
: ISerializable
{
55 private byte[] m_encodedcert
;
56 private DateTime m_from
;
57 private DateTime m_until
;
59 private string m_issuername
;
60 private string m_keyalgo
;
61 private byte[] m_keyalgoparams
;
63 private string m_subject
;
64 private byte[] m_publickey
;
65 private byte[] signature
;
66 private string m_signaturealgo
;
67 private byte[] m_signaturealgoparams
;
68 private byte[] certhash
;
72 // from http://www.ietf.org/rfc/rfc2459.txt
74 //Certificate ::= SEQUENCE {
75 // tbsCertificate TBSCertificate,
76 // signatureAlgorithm AlgorithmIdentifier,
77 // signature BIT STRING }
79 //TBSCertificate ::= SEQUENCE {
80 // version [0] Version DEFAULT v1,
81 // serialNumber CertificateSerialNumber,
82 // signature AlgorithmIdentifier,
86 // subjectPublicKeyInfo SubjectPublicKeyInfo,
87 // issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
88 // -- If present, version shall be v2 or v3
89 // subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
90 // -- If present, version shall be v2 or v3
91 // extensions [3] Extensions OPTIONAL
92 // -- If present, version shall be v3 -- }
94 private byte[] serialnumber
;
96 private byte[] issuerUniqueID
;
97 private byte[] subjectUniqueID
;
98 private X509ExtensionCollection extensions
;
100 private static string encoding_error
= Locale
.GetText ("Input data cannot be coded as a valid certificate.");
103 // that's were the real job is!
104 private void Parse (byte[] data
)
107 decoder
= new ASN1 (data
);
109 if (decoder
.Tag
!= 0x30)
110 throw new CryptographicException (encoding_error
);
111 // Certificate / TBSCertificate
112 if (decoder
[0].Tag
!= 0x30)
113 throw new CryptographicException (encoding_error
);
115 ASN1 tbsCertificate
= decoder
[0];
118 // Certificate / TBSCertificate / Version
119 ASN1 v
= decoder
[0][tbs
];
120 version
= 1; // DEFAULT v1
121 if ((v
.Tag
== 0xA0) && (v
.Count
> 0)) {
122 // version (optional) is present only in v2+ certs
123 version
+= v
[0].Value
[0]; // zero based
127 // Certificate / TBSCertificate / CertificateSerialNumber
128 ASN1 sn
= decoder
[0][tbs
++];
130 throw new CryptographicException (encoding_error
);
131 serialnumber
= sn
.Value
;
132 Array
.Reverse (serialnumber
, 0, serialnumber
.Length
);
134 // Certificate / TBSCertificate / AlgorithmIdentifier
136 // ASN1 signatureAlgo = tbsCertificate.Element (tbs++, 0x30);
138 issuer
= tbsCertificate
.Element (tbs
++, 0x30);
139 m_issuername
= X501
.ToString (issuer
);
141 ASN1 validity
= tbsCertificate
.Element (tbs
++, 0x30);
142 ASN1 notBefore
= validity
[0];
143 m_from
= ASN1Convert
.ToDateTime (notBefore
);
144 ASN1 notAfter
= validity
[1];
145 m_until
= ASN1Convert
.ToDateTime (notAfter
);
147 subject
= tbsCertificate
.Element (tbs
++, 0x30);
148 m_subject
= X501
.ToString (subject
);
150 ASN1 subjectPublicKeyInfo
= tbsCertificate
.Element (tbs
++, 0x30);
152 ASN1 algorithm
= subjectPublicKeyInfo
.Element (0, 0x30);
153 ASN1 algo
= algorithm
.Element (0, 0x06);
154 m_keyalgo
= ASN1Convert
.ToOid (algo
);
155 // parameters ANY DEFINED BY algorithm OPTIONAL
156 // so we dont ask for a specific (Element) type and return DER
157 ASN1 parameters
= algorithm
[1];
158 m_keyalgoparams
= ((algorithm
.Count
> 1) ? parameters
.GetBytes () : null);
160 ASN1 subjectPublicKey
= subjectPublicKeyInfo
.Element (1, 0x03);
161 // we must drop th first byte (which is the number of unused bits
163 int n
= subjectPublicKey
.Length
- 1;
164 m_publickey
= new byte [n
];
165 Buffer
.BlockCopy (subjectPublicKey
.Value
, 1, m_publickey
, 0, n
);
167 // signature processing
168 byte[] bitstring
= decoder
[2].Value
;
169 // first byte contains unused bits in first byte
170 signature
= new byte [bitstring
.Length
- 1];
171 Buffer
.BlockCopy (bitstring
, 1, signature
, 0, signature
.Length
);
173 algorithm
= decoder
[1];
174 algo
= algorithm
.Element (0, 0x06);
175 m_signaturealgo
= ASN1Convert
.ToOid (algo
);
176 parameters
= algorithm
[1];
177 if (parameters
!= null)
178 m_signaturealgoparams
= parameters
.GetBytes ();
180 m_signaturealgoparams
= null;
182 // Certificate / TBSCertificate / issuerUniqueID
183 ASN1 issuerUID
= tbsCertificate
.Element (tbs
, 0x81);
184 if (issuerUID
!= null) {
186 issuerUniqueID
= issuerUID
.Value
;
189 // Certificate / TBSCertificate / subjectUniqueID
190 ASN1 subjectUID
= tbsCertificate
.Element (tbs
, 0x82);
191 if (subjectUID
!= null) {
193 subjectUniqueID
= subjectUID
.Value
;
196 // Certificate / TBSCertificate / Extensions
197 ASN1 extns
= tbsCertificate
.Element (tbs
, 0xA3);
198 if ((extns
!= null) && (extns
.Count
== 1))
199 extensions
= new X509ExtensionCollection (extns
[0]);
201 extensions
= new X509ExtensionCollection (null);
203 // keep a copy of the original data
204 m_encodedcert
= (byte[]) data
.Clone ();
206 catch (Exception ex
) {
207 throw new CryptographicException (encoding_error
, ex
);
213 public X509Certificate (byte[] data
)
216 // does it looks like PEM ?
217 if ((data
.Length
> 0) && (data
[0] != 0x30)) {
219 data
= PEM ("CERTIFICATE", data
);
221 catch (Exception ex
) {
222 throw new CryptographicException (encoding_error
, ex
);
229 private byte[] GetUnsignedBigInteger (byte[] integer
)
231 if (integer
[0] == 0x00) {
232 // this first byte is added so we're sure it's an unsigned integer
233 // however we can't feed it into RSAParameters or DSAParameters
234 int length
= integer
.Length
- 1;
235 byte[] uinteger
= new byte [length
];
236 Buffer
.BlockCopy (integer
, 1, uinteger
, 0, length
);
247 if (m_keyalgoparams
== null)
248 throw new CryptographicException ("Missing key algorithm parameters.");
251 DSAParameters dsaParams
= new DSAParameters ();
252 // for DSA m_publickey contains 1 ASN.1 integer - Y
253 ASN1 pubkey
= new ASN1 (m_publickey
);
254 if ((pubkey
== null) || (pubkey
.Tag
!= 0x02))
256 dsaParams
.Y
= GetUnsignedBigInteger (pubkey
.Value
);
258 ASN1 param
= new ASN1 (m_keyalgoparams
);
259 if ((param
== null) || (param
.Tag
!= 0x30) || (param
.Count
< 3))
261 if ((param
[0].Tag
!= 0x02) || (param
[1].Tag
!= 0x02) || (param
[2].Tag
!= 0x02))
263 dsaParams
.P
= GetUnsignedBigInteger (param
[0].Value
);
264 dsaParams
.Q
= GetUnsignedBigInteger (param
[1].Value
);
265 dsaParams
.G
= GetUnsignedBigInteger (param
[2].Value
);
267 // BUG: MS BCL 1.0 can't import a key which
268 // isn't the same size as the one present in
271 _dsa
= new DSAManaged (dsaParams
.Y
.Length
<< 3);
273 _dsa
= (DSA
) new DSACryptoServiceProvider (dsaParams
.Y
.Length
<< 3);
274 _dsa
.ImportParameters (dsaParams
);
287 public X509ExtensionCollection Extensions
{
288 get { return extensions; }
293 if (certhash
== null) {
294 HashAlgorithm hash
= null;
295 switch (m_signaturealgo
) {
296 case "1.2.840.113549.1.1.2": // MD2 with RSA encryption
297 // maybe someone installed MD2 ?
299 hash
= HashAlgorithm
.Create ("MD2");
301 hash
= Mono
.Security
.Cryptography
.MD2
.Create ();
304 case "1.2.840.113549.1.1.4": // MD5 with RSA encryption
305 hash
= MD5
.Create ();
307 case "1.2.840.113549.1.1.5": // SHA-1 with RSA Encryption
308 case "1.3.14.3.2.29": // SHA1 with RSA signature
309 case "1.2.840.10040.4.3": // SHA1-1 with DSA
310 hash
= SHA1
.Create ();
315 if ((decoder
== null) || (decoder
.Count
< 1))
317 byte[] toBeSigned
= decoder
[0].GetBytes ();
318 certhash
= hash
.ComputeHash (toBeSigned
, 0, toBeSigned
.Length
);
320 return (byte[]) certhash
.Clone ();
324 public virtual string IssuerName
{
325 get { return m_issuername; }
328 public virtual string KeyAlgorithm
{
329 get { return m_keyalgo; }
332 public virtual byte[] KeyAlgorithmParameters
{
334 if (m_keyalgoparams
== null)
336 return (byte[]) m_keyalgoparams
.Clone ();
338 set { m_keyalgoparams = value; }
341 public virtual byte[] PublicKey
{
343 if (m_publickey
== null)
345 return (byte[]) m_publickey
.Clone ();
349 public virtual RSA RSA
{
352 RSAParameters rsaParams
= new RSAParameters ();
353 // for RSA m_publickey contains 2 ASN.1 integers
354 // the modulus and the public exponent
355 ASN1 pubkey
= new ASN1 (m_publickey
);
356 ASN1 modulus
= pubkey
[0];
357 if ((modulus
== null) || (modulus
.Tag
!= 0x02))
359 ASN1 exponent
= pubkey
[1];
360 if (exponent
.Tag
!= 0x02)
363 rsaParams
.Modulus
= GetUnsignedBigInteger (modulus
.Value
);
364 rsaParams
.Exponent
= exponent
.Value
;
366 // BUG: MS BCL 1.0 can't import a key which
367 // isn't the same size as the one present in
369 int keySize
= (rsaParams
.Modulus
.Length
<< 3);
371 _rsa
= new RSAManaged (keySize
);
373 _rsa
= (RSA
) new RSACryptoServiceProvider (keySize
);
374 _rsa
.ImportParameters (rsaParams
);
387 public virtual byte[] RawData
{
389 if (m_encodedcert
== null)
391 return (byte[]) m_encodedcert
.Clone ();
395 public virtual byte[] SerialNumber
{
397 if (serialnumber
== null)
399 return (byte[]) serialnumber
.Clone ();
403 public virtual byte[] Signature
{
405 if (signature
== null)
408 switch (m_signaturealgo
) {
409 case "1.2.840.113549.1.1.2": // MD2 with RSA encryption
410 case "1.2.840.113549.1.1.4": // MD5 with RSA encryption
411 case "1.2.840.113549.1.1.5": // SHA-1 with RSA Encryption
412 case "1.3.14.3.2.29": // SHA1 with RSA signature
413 return (byte[]) signature
.Clone ();
415 case "1.2.840.10040.4.3": // SHA-1 with DSA
416 ASN1 sign
= new ASN1 (signature
);
417 if ((sign
== null) || (sign
.Count
!= 2))
419 byte[] part1
= sign
[0].Value
;
420 byte[] part2
= sign
[1].Value
;
421 byte[] sig
= new byte [40];
422 // parts may be less than 20 bytes (i.e. first bytes were 0x00)
423 // parts may be more than 20 bytes (i.e. first byte > 0x80, negative)
424 int s1
= System
.Math
.Max (0, part1
.Length
- 20);
425 int e1
= System
.Math
.Max (0, 20 - part1
.Length
);
426 Buffer
.BlockCopy (part1
, s1
, sig
, e1
, part1
.Length
- s1
);
427 int s2
= System
.Math
.Max (0, part2
.Length
- 20);
428 int e2
= System
.Math
.Max (20, 40 - part2
.Length
);
429 Buffer
.BlockCopy (part2
, s2
, sig
, e2
, part2
.Length
- s2
);
433 throw new CryptographicException ("Unsupported hash algorithm: " + m_signaturealgo
);
438 public virtual string SignatureAlgorithm
{
439 get { return m_signaturealgo; }
442 public virtual byte[] SignatureAlgorithmParameters
{
444 if (m_signaturealgoparams
== null)
445 return m_signaturealgoparams
;
446 return (byte[]) m_signaturealgoparams
.Clone ();
450 public virtual string SubjectName
{
451 get { return m_subject; }
454 public virtual DateTime ValidFrom
{
455 get { return m_from; }
458 public virtual DateTime ValidUntil
{
459 get { return m_until; }
463 get { return version; }
466 public bool IsCurrent
{
467 get { return WasCurrent (DateTime.UtcNow); }
470 public bool WasCurrent (DateTime instant
)
472 return ((instant
> ValidFrom
) && (instant
<= ValidUntil
));
475 // uncommon v2 "extension"
476 public byte[] IssuerUniqueIdentifier
{
478 if (issuerUniqueID
== null)
480 return (byte[]) issuerUniqueID
.Clone ();
484 // uncommon v2 "extension"
485 public byte[] SubjectUniqueIdentifier
{
487 if (subjectUniqueID
== null)
489 return (byte[]) subjectUniqueID
.Clone ();
493 internal bool VerifySignature (DSA dsa
)
495 // signatureOID is check by both this.Hash and this.Signature
496 DSASignatureDeformatter v
= new DSASignatureDeformatter (dsa
);
497 // only SHA-1 is supported
498 v
.SetHashAlgorithm ("SHA1");
499 return v
.VerifySignature (this.Hash
, this.Signature
);
502 internal string GetHashNameFromOID (string oid
)
505 // MD2 with RSA encryption
506 case "1.2.840.113549.1.1.2":
507 // maybe someone installed MD2 ?
509 // MD5 with RSA encryption
510 case "1.2.840.113549.1.1.4":
512 // SHA-1 with RSA Encryption
513 case "1.2.840.113549.1.1.5":
514 case "1.3.14.3.2.29":
521 internal bool VerifySignature (RSA rsa
)
523 RSAPKCS1SignatureDeformatter v
= new RSAPKCS1SignatureDeformatter (rsa
);
524 string hashName
= GetHashNameFromOID (m_signaturealgo
);
525 if (hashName
== null)
526 throw new CryptographicException ("Unsupported hash algorithm: " + m_signaturealgo
);
528 v
.SetHashAlgorithm (hashName
);
529 return v
.VerifySignature (this.Hash
, this.Signature
);
532 public bool VerifySignature (AsymmetricAlgorithm aa
)
535 throw new ArgumentNullException ("aa");
538 return VerifySignature (aa
as RSA
);
540 return VerifySignature (aa
as DSA
);
542 throw new NotSupportedException ("Unknown Asymmetric Algorithm " + aa
.ToString ());
545 public bool CheckSignature (byte[] hash
, string hashAlgorithm
, byte[] signature
)
548 string hashName
= GetHashNameFromOID (hashAlgorithm
);
549 HashAlgorithm algo
= HashAlgorithm
.Create (hashName
);
550 return PKCS1
.Verify_v15 (RSA
, algo
, hash
, signature
);
552 RSACryptoServiceProvider r
= (RSACryptoServiceProvider
) RSA
;
553 return r
.VerifyHash (hash
, hashAlgorithm
, signature
);
557 public bool IsSelfSigned
{
559 if (m_issuername
== m_subject
)
560 return VerifySignature (RSA
);
566 public ASN1
GetIssuerName ()
571 public ASN1
GetSubjectName ()
576 protected X509Certificate (SerializationInfo info
, StreamingContext context
)
578 Parse ((byte[]) info
.GetValue ("raw", typeof (byte[])));
581 [SecurityPermission (SecurityAction
.Demand
, SerializationFormatter
= true)]
582 public virtual void GetObjectData (SerializationInfo info
, StreamingContext context
)
584 info
.AddValue ("raw", m_encodedcert
);
585 // note: we NEVER serialize the private key
588 static byte[] PEM (string type
, byte[] data
)
590 string pem
= Encoding
.ASCII
.GetString (data
);
591 string header
= String
.Format ("-----BEGIN {0}-----", type
);
592 string footer
= String
.Format ("-----END {0}-----", type
);
593 int start
= pem
.IndexOf (header
) + header
.Length
;
594 int end
= pem
.IndexOf (footer
, start
);
595 string base64
= pem
.Substring (start
, (end
- start
));
596 return Convert
.FromBase64String (base64
);