2 // X509Chain.cs: X.509 Certificate Path
3 // This is a VERY simplified and minimal version
5 // Authenticode support
9 // Sebastien Pouliot <sebastien@ximian.com>
11 // (C) 2003 Motus Technologies Inc. (http://www.motus.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
;
36 using System
.Security
.Permissions
;
42 using Mono
.Security
.X509
.Extensions
;
44 namespace Mono
.Security
.X509
{
53 private X509CertificateCollection roots
;
54 private X509CertificateCollection certs
;
55 private X509Certificate _root
;
57 private X509CertificateCollection _chain
;
58 private X509ChainStatusFlags _status
;
64 certs
= new X509CertificateCollection ();
67 // get a pre-builded chain
68 public X509Chain (X509CertificateCollection chain
) : this ()
70 _chain
= new X509CertificateCollection ();
71 _chain
.AddRange (chain
);
76 public X509CertificateCollection Chain
{
77 get { return _chain; }
80 // the root of the specified certificate (may not be trusted!)
81 public X509Certificate Root
{
85 public X509ChainStatusFlags Status
{
86 get { return _status; }
89 public X509CertificateCollection TrustAnchors
{
92 roots
= new X509CertificateCollection ();
93 roots
.AddRange (X509StoreManager
.TrustedRootCertificates
);
98 [SecurityPermission (SecurityAction
.Demand
, Flags
=SecurityPermissionFlag
.ControlPolicy
)]
99 set { roots = value; }
104 public void LoadCertificate (X509Certificate x509
)
109 public void LoadCertificates (X509CertificateCollection collection
)
111 certs
.AddRange (collection
);
114 public X509Certificate
FindByIssuerName (string issuerName
)
116 foreach (X509Certificate x
in certs
) {
117 if (x
.IssuerName
== issuerName
)
123 public bool Build (X509Certificate leaf
)
125 _status
= X509ChainStatusFlags
.NoError
;
126 if (_chain
== null) {
127 // chain not supplied - we must built it ourselve
128 _chain
= new X509CertificateCollection ();
129 X509Certificate x
= leaf
;
130 X509Certificate tmp
= null;
131 while ((x
!= null) && (!x
.IsSelfSigned
)) {
132 tmp
= FindCertificateParent (x
);
135 x
= tmp
; // last valid
138 // find a trusted root
139 _root
= FindCertificateRoot (tmp
);
142 // chain supplied - still have to check signatures!
143 int last
= _chain
.Count
;
145 if (IsParent (leaf
, _chain
[0])) {
147 for (; i
< last
; i
++) {
148 if (!IsParent (_chain
[i
-1], _chain
[i
]))
152 _root
= FindCertificateRoot (_chain
[last
- 1]);
156 // is the leaf a root ? (trusted or untrusted)
157 _root
= FindCertificateRoot (leaf
);
161 // validate the chain
162 if ((_chain
!= null) && (_status
== X509ChainStatusFlags
.NoError
)) {
163 foreach (X509Certificate x
in _chain
) {
164 // validate dates for each certificate in the chain
165 // note: we DO NOT check for nested date/time
171 if (!IsValid (leaf
)) {
172 // switch status code if the failure is expiration
173 if (_status
== X509ChainStatusFlags
.NotTimeNested
)
174 _status
= X509ChainStatusFlags
.NotTimeValid
;
178 if ((_root
!= null) && !IsValid (_root
)) {
182 return (_status
== X509ChainStatusFlags
.NoError
);
189 _status
= X509ChainStatusFlags
.NoError
;
190 roots
= null; // this force a reload
198 private bool IsValid (X509Certificate cert
)
200 if (!cert
.IsCurrent
) {
201 // FIXME: nesting isn't very well implemented
202 _status
= X509ChainStatusFlags
.NotTimeNested
;
206 // TODO - we should check for CRITICAL but unknown extensions
207 // X509ChainStatusFlags.InvalidExtension
208 #if (!NET_1_0 && !INSIDE_CORLIB)
209 if (ServicePointManager
.CheckCertificateRevocationList
) {
210 // TODO - check revocation (CRL, OCSP ...)
211 // X509ChainStatusFlags.RevocationStatusUnknown
212 // X509ChainStatusFlags.Revoked
218 private X509Certificate
FindCertificateParent (X509Certificate child
)
220 foreach (X509Certificate potentialParent
in certs
) {
221 if (IsParent (child
, potentialParent
))
222 return potentialParent
;
227 private X509Certificate
FindCertificateRoot (X509Certificate potentialRoot
)
229 if (potentialRoot
== null) {
230 _status
= X509ChainStatusFlags
.PartialChain
;
234 // if the trusted root is in the chain
235 if (IsTrusted (potentialRoot
)) {
236 return potentialRoot
;
239 // if the root isn't in the chain
240 foreach (X509Certificate root
in TrustAnchors
) {
241 if (IsParent (potentialRoot
, root
)) {
246 // is it a (untrusted) root ?
247 if (potentialRoot
.IsSelfSigned
) {
248 _status
= X509ChainStatusFlags
.UntrustedRoot
;
249 return potentialRoot
;
252 _status
= X509ChainStatusFlags
.PartialChain
;
256 private bool IsTrusted (X509Certificate potentialTrusted
)
258 return TrustAnchors
.Contains (potentialTrusted
);
261 private bool IsParent (X509Certificate child
, X509Certificate parent
)
263 if (child
.IssuerName
!= parent
.SubjectName
)
266 // parent MUST have the Basic Constraint CA=true (except for trusted roots)
267 // see why at http://www.microsoft.com/technet/security/bulletin/MS02-050.asp
268 if ((parent
.Version
> 2) && (!IsTrusted (parent
))) {
269 // TODO: we do not support pathLenConstraint
270 X509Extension ext
= parent
.Extensions
["2.5.29.19"];
272 BasicConstraintsExtension bc
= new BasicConstraintsExtension (ext
);
273 if (!bc
.CertificateAuthority
)
274 _status
= X509ChainStatusFlags
.InvalidBasicConstraints
;
277 _status
= X509ChainStatusFlags
.InvalidBasicConstraints
;
280 if (!child
.VerifySignature (parent
.RSA
)) {
281 _status
= X509ChainStatusFlags
.NotSignatureValid
;