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 build it ourselve
128 _chain
= new X509CertificateCollection ();
129 X509Certificate x
= leaf
;
130 X509Certificate tmp
= x
;
131 while ((x
!= null) && (!x
.IsSelfSigned
)) {
132 tmp
= x
; // last valid
134 x
= FindCertificateParent (x
);
136 // find a trusted root
137 _root
= FindCertificateRoot (tmp
);
140 // chain supplied - still have to check signatures!
141 int last
= _chain
.Count
;
143 if (IsParent (leaf
, _chain
[0])) {
145 for (; i
< last
; i
++) {
146 if (!IsParent (_chain
[i
-1], _chain
[i
]))
150 _root
= FindCertificateRoot (_chain
[last
- 1]);
154 // is the leaf a root ? (trusted or untrusted)
155 _root
= FindCertificateRoot (leaf
);
159 // validate the chain
160 if ((_chain
!= null) && (_status
== X509ChainStatusFlags
.NoError
)) {
161 foreach (X509Certificate x
in _chain
) {
162 // validate dates for each certificate in the chain
163 // note: we DO NOT check for nested date/time
169 if (!IsValid (leaf
)) {
170 // switch status code if the failure is expiration
171 if (_status
== X509ChainStatusFlags
.NotTimeNested
)
172 _status
= X509ChainStatusFlags
.NotTimeValid
;
176 if ((_root
!= null) && !IsValid (_root
)) {
180 return (_status
== X509ChainStatusFlags
.NoError
);
187 _status
= X509ChainStatusFlags
.NoError
;
188 roots
= null; // this force a reload
196 private bool IsValid (X509Certificate cert
)
198 if (!cert
.IsCurrent
) {
199 // FIXME: nesting isn't very well implemented
200 _status
= X509ChainStatusFlags
.NotTimeNested
;
204 // TODO - we should check for CRITICAL but unknown extensions
205 // X509ChainStatusFlags.InvalidExtension
206 #if (!NET_1_0 && !INSIDE_CORLIB)
207 if (ServicePointManager
.CheckCertificateRevocationList
) {
208 // TODO - check revocation (CRL, OCSP ...)
209 // X509ChainStatusFlags.RevocationStatusUnknown
210 // X509ChainStatusFlags.Revoked
216 private X509Certificate
FindCertificateParent (X509Certificate child
)
218 foreach (X509Certificate potentialParent
in certs
) {
219 if (IsParent (child
, potentialParent
))
220 return potentialParent
;
225 private X509Certificate
FindCertificateRoot (X509Certificate potentialRoot
)
227 if (potentialRoot
== null) {
228 _status
= X509ChainStatusFlags
.PartialChain
;
232 // if the trusted root is in the chain
233 if (IsTrusted (potentialRoot
)) {
234 return potentialRoot
;
237 // if the root isn't in the chain
238 foreach (X509Certificate root
in TrustAnchors
) {
239 if (IsParent (potentialRoot
, root
)) {
244 // is it a (untrusted) root ?
245 if (potentialRoot
.IsSelfSigned
) {
246 _status
= X509ChainStatusFlags
.UntrustedRoot
;
247 return potentialRoot
;
250 _status
= X509ChainStatusFlags
.PartialChain
;
254 private bool IsTrusted (X509Certificate potentialTrusted
)
256 return TrustAnchors
.Contains (potentialTrusted
);
259 private bool IsParent (X509Certificate child
, X509Certificate parent
)
261 if (child
.IssuerName
!= parent
.SubjectName
)
264 // parent MUST have the Basic Constraint CA=true (except for trusted roots)
265 // see why at http://www.microsoft.com/technet/security/bulletin/MS02-050.asp
266 if ((parent
.Version
> 2) && (!IsTrusted (parent
))) {
267 // TODO: we do not support pathLenConstraint
268 X509Extension ext
= parent
.Extensions
["2.5.29.19"];
270 BasicConstraintsExtension bc
= new BasicConstraintsExtension (ext
);
271 if (!bc
.CertificateAuthority
)
272 _status
= X509ChainStatusFlags
.InvalidBasicConstraints
;
275 _status
= X509ChainStatusFlags
.InvalidBasicConstraints
;
278 if (!child
.VerifySignature (parent
.RSA
)) {
279 _status
= X509ChainStatusFlags
.NotSignatureValid
;