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
{
52 const string encoding_error
= "Input data cannot be coded as a valid certificate.";
56 private byte[] m_encodedcert
;
57 private DateTime m_from
;
58 private DateTime m_until
;
60 private string m_issuername
;
61 private string m_keyalgo
;
62 private byte[] m_keyalgoparams
;
64 private string m_subject
;
65 private byte[] m_publickey
;
66 private byte[] signature
;
67 private string m_signaturealgo
;
68 private byte[] m_signaturealgoparams
;
69 private byte[] certhash
;
73 // from http://www.ietf.org/rfc/rfc2459.txt
75 //Certificate ::= SEQUENCE {
76 // tbsCertificate TBSCertificate,
77 // signatureAlgorithm AlgorithmIdentifier,
78 // signature BIT STRING }
80 //TBSCertificate ::= SEQUENCE {
81 // version [0] Version DEFAULT v1,
82 // serialNumber CertificateSerialNumber,
83 // signature AlgorithmIdentifier,
87 // subjectPublicKeyInfo SubjectPublicKeyInfo,
88 // issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
89 // -- If present, version shall be v2 or v3
90 // subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
91 // -- If present, version shall be v2 or v3
92 // extensions [3] Extensions OPTIONAL
93 // -- If present, version shall be v3 -- }
95 private byte[] serialnumber
;
97 private byte[] issuerUniqueID
;
98 private byte[] subjectUniqueID
;
99 private X509ExtensionCollection extensions
;
101 // that's were the real job is!
102 private void Parse (byte[] data
)
105 decoder
= new ASN1 (data
);
107 if (decoder
.Tag
!= 0x30)
108 throw new CryptographicException (encoding_error
);
109 // Certificate / TBSCertificate
110 if (decoder
[0].Tag
!= 0x30)
111 throw new CryptographicException (encoding_error
);
113 ASN1 tbsCertificate
= decoder
[0];
116 // Certificate / TBSCertificate / Version
117 ASN1 v
= decoder
[0][tbs
];
118 version
= 1; // DEFAULT v1
119 if ((v
.Tag
== 0xA0) && (v
.Count
> 0)) {
120 // version (optional) is present only in v2+ certs
121 version
+= v
[0].Value
[0]; // zero based
125 // Certificate / TBSCertificate / CertificateSerialNumber
126 ASN1 sn
= decoder
[0][tbs
++];
128 throw new CryptographicException (encoding_error
);
129 serialnumber
= sn
.Value
;
130 Array
.Reverse (serialnumber
, 0, serialnumber
.Length
);
132 // Certificate / TBSCertificate / AlgorithmIdentifier
134 // ASN1 signatureAlgo = tbsCertificate.Element (tbs++, 0x30);
136 issuer
= tbsCertificate
.Element (tbs
++, 0x30);
137 m_issuername
= X501
.ToString (issuer
);
139 ASN1 validity
= tbsCertificate
.Element (tbs
++, 0x30);
140 ASN1 notBefore
= validity
[0];
141 m_from
= ASN1Convert
.ToDateTime (notBefore
);
142 ASN1 notAfter
= validity
[1];
143 m_until
= ASN1Convert
.ToDateTime (notAfter
);
145 subject
= tbsCertificate
.Element (tbs
++, 0x30);
146 m_subject
= X501
.ToString (subject
);
148 ASN1 subjectPublicKeyInfo
= tbsCertificate
.Element (tbs
++, 0x30);
150 ASN1 algorithm
= subjectPublicKeyInfo
.Element (0, 0x30);
151 ASN1 algo
= algorithm
.Element (0, 0x06);
152 m_keyalgo
= ASN1Convert
.ToOid (algo
);
153 // parameters ANY DEFINED BY algorithm OPTIONAL
154 // so we dont ask for a specific (Element) type and return DER
155 ASN1 parameters
= algorithm
[1];
156 m_keyalgoparams
= ((algorithm
.Count
> 1) ? parameters
.GetBytes () : null);
158 ASN1 subjectPublicKey
= subjectPublicKeyInfo
.Element (1, 0x03);
159 // we must drop th first byte (which is the number of unused bits
161 int n
= subjectPublicKey
.Length
- 1;
162 m_publickey
= new byte [n
];
163 Buffer
.BlockCopy (subjectPublicKey
.Value
, 1, m_publickey
, 0, n
);
165 // signature processing
166 byte[] bitstring
= decoder
[2].Value
;
167 // first byte contains unused bits in first byte
168 signature
= new byte [bitstring
.Length
- 1];
169 Buffer
.BlockCopy (bitstring
, 1, signature
, 0, signature
.Length
);
171 algorithm
= decoder
[1];
172 algo
= algorithm
.Element (0, 0x06);
173 m_signaturealgo
= ASN1Convert
.ToOid (algo
);
174 parameters
= algorithm
[1];
175 if (parameters
!= null)
176 m_signaturealgoparams
= parameters
.GetBytes ();
178 m_signaturealgoparams
= null;
180 // Certificate / TBSCertificate / issuerUniqueID
181 ASN1 issuerUID
= tbsCertificate
.Element (tbs
, 0x81);
182 if (issuerUID
!= null) {
184 issuerUniqueID
= issuerUID
.Value
;
187 // Certificate / TBSCertificate / subjectUniqueID
188 ASN1 subjectUID
= tbsCertificate
.Element (tbs
, 0x82);
189 if (subjectUID
!= null) {
191 subjectUniqueID
= subjectUID
.Value
;
194 // Certificate / TBSCertificate / Extensions
195 ASN1 extns
= tbsCertificate
.Element (tbs
, 0xA3);
196 if ((extns
!= null) && (extns
.Count
== 1))
197 extensions
= new X509ExtensionCollection (extns
[0]);
199 extensions
= new X509ExtensionCollection (null);
201 // keep a copy of the original data
202 m_encodedcert
= (byte[]) data
.Clone ();
204 catch (Exception ex
) {
205 throw new CryptographicException (encoding_error
, ex
);
211 public X509Certificate (byte[] data
)
214 // does it looks like PEM ?
215 if ((data
.Length
> 0) && (data
[0] != 0x30)) {
217 data
= PEM ("CERTIFICATE", data
);
219 catch (Exception ex
) {
220 throw new CryptographicException (encoding_error
, ex
);
227 private byte[] GetUnsignedBigInteger (byte[] integer
)
229 if (integer
[0] == 0x00) {
230 // this first byte is added so we're sure it's an unsigned integer
231 // however we can't feed it into RSAParameters or DSAParameters
232 int length
= integer
.Length
- 1;
233 byte[] uinteger
= new byte [length
];
234 Buffer
.BlockCopy (integer
, 1, uinteger
, 0, length
);
245 if (m_keyalgoparams
== null)
246 throw new CryptographicException ("Missing key algorithm parameters.");
249 DSAParameters dsaParams
= new DSAParameters ();
250 // for DSA m_publickey contains 1 ASN.1 integer - Y
251 ASN1 pubkey
= new ASN1 (m_publickey
);
252 if ((pubkey
== null) || (pubkey
.Tag
!= 0x02))
254 dsaParams
.Y
= GetUnsignedBigInteger (pubkey
.Value
);
256 ASN1 param
= new ASN1 (m_keyalgoparams
);
257 if ((param
== null) || (param
.Tag
!= 0x30) || (param
.Count
< 3))
259 if ((param
[0].Tag
!= 0x02) || (param
[1].Tag
!= 0x02) || (param
[2].Tag
!= 0x02))
261 dsaParams
.P
= GetUnsignedBigInteger (param
[0].Value
);
262 dsaParams
.Q
= GetUnsignedBigInteger (param
[1].Value
);
263 dsaParams
.G
= GetUnsignedBigInteger (param
[2].Value
);
265 // BUG: MS BCL 1.0 can't import a key which
266 // isn't the same size as the one present in
268 _dsa
= (DSA
) new DSACryptoServiceProvider (dsaParams
.Y
.Length
<< 3);
269 _dsa
.ImportParameters (dsaParams
);
281 public X509ExtensionCollection Extensions
{
282 get { return extensions; }
287 if (certhash
== null) {
288 HashAlgorithm hash
= null;
289 switch (m_signaturealgo
) {
290 case "1.2.840.113549.1.1.2": // MD2 with RSA encryption
291 // maybe someone installed MD2 ?
293 hash
= HashAlgorithm
.Create ("MD2");
295 hash
= Mono
.Security
.Cryptography
.MD2
.Create ();
298 case "1.2.840.113549.1.1.4": // MD5 with RSA encryption
299 hash
= MD5
.Create ();
301 case "1.2.840.113549.1.1.5": // SHA-1 with RSA Encryption
302 case "1.3.14.3.2.29": // SHA1 with RSA signature
303 case "1.2.840.10040.4.3": // SHA1-1 with DSA
304 hash
= SHA1
.Create ();
309 if ((decoder
== null) || (decoder
.Count
< 1))
311 byte[] toBeSigned
= decoder
[0].GetBytes ();
312 certhash
= hash
.ComputeHash (toBeSigned
, 0, toBeSigned
.Length
);
314 return (byte[]) certhash
.Clone ();
318 public virtual string IssuerName
{
319 get { return m_issuername; }
322 public virtual string KeyAlgorithm
{
323 get { return m_keyalgo; }
326 public virtual byte[] KeyAlgorithmParameters
{
328 if (m_keyalgoparams
== null)
330 return (byte[]) m_keyalgoparams
.Clone ();
332 set { m_keyalgoparams = value; }
335 public virtual byte[] PublicKey
{
337 if (m_publickey
== null)
339 return (byte[]) m_publickey
.Clone ();
343 public virtual RSA RSA
{
346 RSAParameters rsaParams
= new RSAParameters ();
347 // for RSA m_publickey contains 2 ASN.1 integers
348 // the modulus and the public exponent
349 ASN1 pubkey
= new ASN1 (m_publickey
);
350 ASN1 modulus
= pubkey
[0];
351 if ((modulus
== null) || (modulus
.Tag
!= 0x02))
353 ASN1 exponent
= pubkey
[1];
354 if (exponent
.Tag
!= 0x02)
357 rsaParams
.Modulus
= GetUnsignedBigInteger (modulus
.Value
);
358 rsaParams
.Exponent
= exponent
.Value
;
360 // BUG: MS BCL 1.0 can't import a key which
361 // isn't the same size as the one present in
363 int keySize
= (rsaParams
.Modulus
.Length
<< 3);
364 _rsa
= (RSA
) new RSACryptoServiceProvider (keySize
);
365 _rsa
.ImportParameters (rsaParams
);
377 public virtual byte[] RawData
{
379 if (m_encodedcert
== null)
381 return (byte[]) m_encodedcert
.Clone ();
385 public virtual byte[] SerialNumber
{
387 if (serialnumber
== null)
389 return (byte[]) serialnumber
.Clone ();
393 public virtual byte[] Signature
{
395 if (signature
== null)
398 switch (m_signaturealgo
) {
399 case "1.2.840.113549.1.1.2": // MD2 with RSA encryption
400 case "1.2.840.113549.1.1.4": // MD5 with RSA encryption
401 case "1.2.840.113549.1.1.5": // SHA-1 with RSA Encryption
402 case "1.3.14.3.2.29": // SHA1 with RSA signature
403 return (byte[]) signature
.Clone ();
405 case "1.2.840.10040.4.3": // SHA-1 with DSA
406 ASN1 sign
= new ASN1 (signature
);
407 if ((sign
== null) || (sign
.Count
!= 2))
409 byte[] part1
= sign
[0].Value
;
410 byte[] part2
= sign
[1].Value
;
411 byte[] sig
= new byte [40];
412 // parts may be less than 20 bytes (i.e. first bytes were 0x00)
413 // parts may be more than 20 bytes (i.e. first byte > 0x80, negative)
414 int s1
= System
.Math
.Max (0, part1
.Length
- 20);
415 int e1
= System
.Math
.Max (0, 20 - part1
.Length
);
416 Buffer
.BlockCopy (part1
, s1
, sig
, e1
, part1
.Length
- s1
);
417 int s2
= System
.Math
.Max (0, part2
.Length
- 20);
418 int e2
= System
.Math
.Max (20, 40 - part2
.Length
);
419 Buffer
.BlockCopy (part2
, s2
, sig
, e2
, part2
.Length
- s2
);
423 throw new CryptographicException ("Unsupported hash algorithm: " + m_signaturealgo
);
428 public virtual string SignatureAlgorithm
{
429 get { return m_signaturealgo; }
432 public virtual byte[] SignatureAlgorithmParameters
{
434 if (m_signaturealgoparams
== null)
435 return m_signaturealgoparams
;
436 return (byte[]) m_signaturealgoparams
.Clone ();
440 public virtual string SubjectName
{
441 get { return m_subject; }
444 public virtual DateTime ValidFrom
{
445 get { return m_from; }
448 public virtual DateTime ValidUntil
{
449 get { return m_until; }
453 get { return version; }
456 public bool IsCurrent
{
457 get { return WasCurrent (DateTime.UtcNow); }
460 public bool WasCurrent (DateTime instant
)
462 return ((instant
> ValidFrom
) && (instant
<= ValidUntil
));
465 // uncommon v2 "extension"
466 public byte[] IssuerUniqueIdentifier
{
468 if (issuerUniqueID
== null)
470 return (byte[]) issuerUniqueID
.Clone ();
474 // uncommon v2 "extension"
475 public byte[] SubjectUniqueIdentifier
{
477 if (subjectUniqueID
== null)
479 return (byte[]) subjectUniqueID
.Clone ();
483 internal bool VerifySignature (DSA dsa
)
485 // signatureOID is check by both this.Hash and this.Signature
486 DSASignatureDeformatter v
= new DSASignatureDeformatter (dsa
);
487 // only SHA-1 is supported
488 v
.SetHashAlgorithm ("SHA1");
489 return v
.VerifySignature (this.Hash
, this.Signature
);
492 internal string GetHashNameFromOID (string oid
)
495 // MD2 with RSA encryption
496 case "1.2.840.113549.1.1.2":
497 // maybe someone installed MD2 ?
499 // MD5 with RSA encryption
500 case "1.2.840.113549.1.1.4":
502 // SHA-1 with RSA Encryption
503 case "1.2.840.113549.1.1.5":
504 case "1.3.14.3.2.29":
511 internal bool VerifySignature (RSA rsa
)
513 RSAPKCS1SignatureDeformatter v
= new RSAPKCS1SignatureDeformatter (rsa
);
514 string hashName
= GetHashNameFromOID (m_signaturealgo
);
515 if (hashName
== null)
516 throw new CryptographicException ("Unsupported hash algorithm: " + m_signaturealgo
);
518 v
.SetHashAlgorithm (hashName
);
519 return v
.VerifySignature (this.Hash
, this.Signature
);
522 public bool VerifySignature (AsymmetricAlgorithm aa
)
525 throw new ArgumentNullException ("aa");
528 return VerifySignature (aa
as RSA
);
530 return VerifySignature (aa
as DSA
);
532 throw new NotSupportedException ("Unknown Asymmetric Algorithm " + aa
.ToString ());
535 public bool CheckSignature (byte[] hash
, string hashAlgorithm
, byte[] signature
)
537 RSACryptoServiceProvider r
= (RSACryptoServiceProvider
) RSA
;
538 return r
.VerifyHash (hash
, hashAlgorithm
, signature
);
541 public bool IsSelfSigned
{
543 if (m_issuername
== m_subject
)
544 return VerifySignature (RSA
);
550 public ASN1
GetIssuerName ()
555 public ASN1
GetSubjectName ()
560 protected X509Certificate (SerializationInfo info
, StreamingContext context
)
562 Parse ((byte[]) info
.GetValue ("raw", typeof (byte[])));
565 [SecurityPermission (SecurityAction
.Demand
, SerializationFormatter
= true)]
566 public virtual void GetObjectData (SerializationInfo info
, StreamingContext context
)
568 info
.AddValue ("raw", m_encodedcert
);
569 // note: we NEVER serialize the private key
572 static byte[] PEM (string type
, byte[] data
)
574 string pem
= Encoding
.ASCII
.GetString (data
);
575 string header
= String
.Format ("-----BEGIN {0}-----", type
);
576 string footer
= String
.Format ("-----END {0}-----", type
);
577 int start
= pem
.IndexOf (header
) + header
.Length
;
578 int end
= pem
.IndexOf (footer
, start
);
579 string base64
= pem
.Substring (start
, (end
- start
));
580 return Convert
.FromBase64String (base64
);