source: trunk/CrypP2P/Helper/PAPCertificate.cs @ 1374

Last change on this file since 1374 was 1374, checked in by Paul Lelgemann, 12 years ago

+ Added P2P connection to CrypP2P based on PeerToPeerBase plugin (using default settings)
+ Added MD5Collider to CoreDeveloper solution

File size: 20.0 KB
Line 
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using System.Text;
5using System.Security.Cryptography.X509Certificates;
6using System.IO;
7using Cryptool.PluginBase;
8
9/*
10 * EVERYTHING UNTESTED AND NOT READY!!!
11 */
12
13namespace Cryptool.P2P.Helper
14{
15    public static class PAPCertificate
16    {
17        #region Variables
18
19        /// <summary>
20        /// You can only use the P@P-network by using this user name!!!
21        /// All certificates, which are necessary for using the P@P-P2P-Network, are
22        /// registered with this user name.
23        /// </summary>
24        public const string CERTIFIED_PEER_NAME = "CrypTool2"; //"pap0001"; //"CT_PAP_User";
25        public const string CERTIFICATE_DIRECTORY = "pap_certificates";
26
27        private static string sCertPath;
28        /// <summary>
29        /// only set the application path - without certificate
30        /// directory or a specific certificate file name!
31        /// </summary>
32        public static string CertPath
33        {
34            get { return sCertPath; }
35            private set
36            {
37                string combindedCertPath = Path.Combine(value, CERTIFICATE_DIRECTORY);
38                if (Directory.Exists(combindedCertPath))
39                    sCertPath = combindedCertPath;
40                else
41                    throw (new DirectoryNotFoundException("Path: " + combindedCertPath + " not found"));
42            }
43        }
44
45        //private const string CERT_USER_SUBJECT = "CN=pap0001@pap.de@pap0001@.*, O=Peers@Play, S=None, C=NA";
46        private const string CERT_USER_FILE_NAME = "CrypTool2.pfx"; //"pap0001.p12"; //ct2_user_pap.cer
47        private const string CERT_USER_PASSWORD = "ct2"; //"test"; //CT2p@pC3rt1f1cat10n
48        private const string CERT_USER_ISSUER = "P@P project";
49        private const string CERT_USER_SERIAL = "1E"; //"0D";
50
51        private const string CERT_PAP_FILE_NAME = "pap.cer";
52        private const string CERT_PAP_PASSWORD = "test";
53        private const string CERT_PAP_ISSUER = "P@P project";
54        private const string CERT_PAP_SERIAL = "37cbb8163698c48845e9d5c80e3496b1";
55
56        private const string CERT_OPA_FILE_NAME = "opa.vs.uni-due.de.cer";
57        private const string CERT_OPA_PASSWORD = "";
58        private const string CERT_OPA_ISSUER = "opa.vs.uni-due.de";
59        private const string CERT_OPA_SERIAL = "2FB799C15BAE2FB74FDB72DD0329196A";
60
61        private const string CERT_SERVER_CA_FILE_NAME = "ServerCA.cer";
62        private const string CERT_SERVER_CA_PASSWORD = "";
63        private const string CERT_SERVER_CA_ISSUER = "PAP Server CA";
64        private const string CERT_SERVER_CA_SERIAL = "23440A89B21FA7B84ADDB59DA494F79B";
65
66        private const X509FindType PAP_FIND_TYPE = X509FindType.FindBySerialNumber;
67        // here have to be at least two certificate (PAP Server CA, P@P project)
68        private const StoreName CERT_STORE_ROOT = StoreName.Root;
69        private const StoreLocation CERT_STORE_LOCATION = StoreLocation.CurrentUser;
70        // here has to be one certificate (User certificate)
71        private const StoreName CERT_STORE_USER = StoreName.My;
72
73        #endregion
74
75        #region VERY PAP-SPECIFIC CERTIFICATE METHODS
76
77        public enum PAP_Certificates
78        {
79            Server_CA,
80            Opa,
81            Pap,
82            User
83        }
84
85        /// <summary>
86        /// Checks if all neccessary certificates were installed, otherwise trying to
87        /// install all missing certificates. Returns true if everything worked fine.
88        /// </summary>
89        /// <param name="sPath">only the path of the running application, not the
90        /// certificate direction or a specific filename!!! Everything else will
91        /// be combined internally!!!</param>
92        /// <returns></returns>
93        public static bool CheckAvailabilityAndInstallMissingCertificates(string sPath)
94        {
95            List<PAP_Certificates> lstMissingCerts = new List<PAP_Certificates>();
96            lstMissingCerts = CheckAvailabilityOfPAPCertificates(sPath);
97            return InstallMissingCertificates(lstMissingCerts, sPath);
98        }
99
100        /// <summary>
101        /// Checks if all neccessary certificates were installed, otherwise it returns a
102        /// list with all missing certificates
103        /// </summary>
104        /// <param name="sPath">only the path of the running application, not the
105        /// certificate direction or a specific filename!!! Everything else will
106        /// be combined internally!!!</param>
107        /// <returns></returns>
108        public static List<PAP_Certificates> CheckAvailabilityOfPAPCertificates(string sPath)
109        {
110            CertPath = sPath;
111            List<PAP_Certificates> retLst = new List<PAP_Certificates>();
112            X509Certificate2Collection certColl;
113
114            /* BEGIN: Checking availablity of the three root certificates */
115
116            // Wacker 27.02.2010: Removed checking for Server and OPA (operator-SSL) certificates; These are only needed if
117            // we want to get a new certificate online - a feature which is not implmented now. When this feature is
118            // implemented, just uncommentd the following lines
119
120            //certColl = FindCertificates(CERT_STORE_ROOT, CERT_STORE_LOCATION,
121            //    PAP_FIND_TYPE, CERT_SERVER_CA_SERIAL, true);
122            //if (certColl.Count == 0)
123            //    retLst.Add(PAP_Certificates.Server_CA);
124
125            //certColl = FindCertificates(CERT_STORE_ROOT, CERT_STORE_LOCATION,
126            //    PAP_FIND_TYPE, CERT_OPA_SERIAL, true);
127            //if (certColl.Count == 0)
128            //    retLst.Add(PAP_Certificates.Opa);
129
130            certColl = FindCertificates(CERT_STORE_ROOT, CERT_STORE_LOCATION,
131                PAP_FIND_TYPE, CERT_PAP_SERIAL, true);
132            if (certColl.Count == 0)
133                retLst.Add(PAP_Certificates.Pap);
134            /* END: Checking availablity of the three root certificates */
135
136            // Check user certificate availability
137            certColl = FindCertificates(CERT_STORE_USER, CERT_STORE_LOCATION,
138                PAP_FIND_TYPE, CERT_USER_SERIAL, true);
139            if (certColl.Count == 0)
140                retLst.Add(PAP_Certificates.User);
141
142            return retLst;
143        }
144
145        /// <summary>
146        /// Installs all certificates, which are specified in the parameter list.
147        /// Returns true, if everything works fine
148        /// </summary>
149        /// <param name="installList">a list of all certificates, which you want to install</param>
150        /// <param name="sPath">only the path of the running application, not the
151        /// certificate direction or a specific filename!!! Everything else will
152        /// be combined internally!!!</param>
153        /// <returns></returns>
154        public static bool InstallMissingCertificates(List<PAP_Certificates> installList, string sPath)
155        {
156            bool intermediateResult = true;
157            bool actualResult = true;
158
159            CertPath = sPath;
160
161            foreach (PAP_Certificates papCert in installList)
162            {
163                switch (papCert)
164                {
165                    case PAP_Certificates.Server_CA:
166                        intermediateResult = InstallCertificate(CERT_STORE_ROOT, CERT_STORE_LOCATION,
167                            Path.Combine(CertPath, CERT_SERVER_CA_FILE_NAME), CERT_SERVER_CA_PASSWORD);
168                        break;
169                    case PAP_Certificates.Opa:
170                        intermediateResult = InstallCertificate(CERT_STORE_ROOT, CERT_STORE_LOCATION,
171                            Path.Combine(CertPath, CERT_OPA_FILE_NAME), CERT_OPA_PASSWORD);
172                        break;
173                    case PAP_Certificates.Pap:
174                        intermediateResult = InstallCertificate(CERT_STORE_ROOT, CERT_STORE_LOCATION,
175                            Path.Combine(CertPath, CERT_PAP_FILE_NAME), CERT_PAP_PASSWORD);
176                        break;
177                    case PAP_Certificates.User:
178                        intermediateResult = InstallCertificate(CERT_STORE_USER, CERT_STORE_LOCATION,
179                            Path.Combine(CertPath, CERT_USER_FILE_NAME), CERT_USER_PASSWORD);
180                        break;
181                    default:
182                        break;
183                }
184                actualResult = actualResult && intermediateResult;
185            }
186            return actualResult;
187        }
188
189        #endregion
190
191        #region Common Certificate Operations (Find and Install Certs)
192
193        /// <summary>
194        /// Searches for certificates in the given Store with the given FindType and value
195        /// </summary>
196        /// <param name="storeName">Name of the store</param>
197        /// <param name="storeLocation">Location of the store</param>
198        /// <param name="findType">choose which attribute of the installed cert's should be compare with the given value</param>
199        /// <param name="findValue">value, which should be exactly in the chosen FindType of an installed certificate</param>
200        /// <param name="onlyValidCerts">only valid certificates (not outdated, revocated, etc.) will be considered.</param>
201        /// <returns>a list of all certificates, who satisfy the search attributes</returns>
202        private static X509Certificate2Collection FindCertificates(StoreName storeName, StoreLocation storeLocation, X509FindType findType, object findValue, bool onlyValidCerts)
203        {
204            X509Certificate2Collection findedCertCol = null;
205            X509Store store = new X509Store(storeName, storeLocation);
206            try
207            {
208                store.Open(OpenFlags.ReadOnly);
209                findedCertCol = store.Certificates.Find(findType, findValue, onlyValidCerts);
210            }
211            catch (Exception)
212            {
213
214                throw;
215            }
216            finally
217            {
218                store.Close();
219            }
220
221            return findedCertCol;
222        }
223
224        /// <summary>
225        /// Installs a given certificate if it is already valid and not installed yet
226        /// </summary>
227        /// <param name="storeName">Name of the store</param>
228        /// <param name="storeLocation">Location of the store</param>
229        /// <param name="sCertPath">Whole certification path and filename</param>
230        /// <param name="sCertPassword">if necessary, you have to declare a password. Otherwise use ""</param>
231        private static bool InstallCertificate(StoreName storeName, StoreLocation storeLocation, string sCertPath, string sCertPassword)
232        {
233            bool ret = false;
234
235            if (File.Exists(sCertPath))
236            {
237                X509Store store = new X509Store(storeName, storeLocation);
238                try
239                {
240                    /* Verification of certifates failed every time - no idea why */
241                    //if (cert.Verify())
242                    //{
243                    X509Certificate2 cert = new X509Certificate2(sCertPath, sCertPassword);
244                    store.Open(OpenFlags.ReadWrite);
245                    store.Add(cert);
246                    store.Close();
247                    ret = true;
248                    //}
249                    //else
250                    //{
251                    //    throw (new Exception("Installing Certificate " + cert.SubjectName.Name + " wasn't possible, because Certificate isn't valid anymore"));
252                    //}
253                }
254                catch (Exception ex)
255                {
256                    throw (new Exception("Installation of " + sCertPath + " certificate wasn't possible", ex));
257                }
258                finally
259                {
260                    store.Close();
261                }
262            }
263            return ret;
264        }
265
266        #endregion
267
268        #region (currently not used) PAP specific stuff (eMail Address registered certificates, etc.)
269
270        /// <summary>
271        /// Will search for the root certificate in the windows certificate store.
272        /// </summary>
273        /// <returns>The root certificate or null on error</returns>
274        public static X509Certificate2Collection getRootCertificate()
275        {
276            X509Certificate2Collection root = FindCertificates(CERT_STORE_ROOT, CERT_STORE_LOCATION, X509FindType.FindBySerialNumber, CERT_PAP_SERIAL, true);
277            return root;
278        }
279
280        /// <summary>
281        /// Gives back an X509Certificate2 from an given email address (hashed value)
282        /// </summary>
283        /// <param name="email">the email</param>
284        /// <returns>the searched certificate</returns>
285        public static X509Certificate2 GetCertificateWithMail(String email)
286        {
287            try
288            {
289                String fileName = GetHash(email);
290                X509Certificate2Collection col = FindCertificates(StoreName.My, StoreLocation.CurrentUser, X509FindType.FindByIssuerName, CERT_PAP_ISSUER, true);
291                foreach (X509Certificate2 cert in col)
292                {
293                    if (cert.Subject.Contains(fileName))
294                    {
295                        return cert;
296                    }
297                }
298                return null;
299            }
300            catch
301            {
302                return null;
303            }
304        }
305
306        /// <summary>
307        /// Creates an crypted string from an ordinary
308        /// </summary>
309        /// <param name="str">the ordinary string</param>
310        /// <returns>an coded string</returns>
311        public static String GetHash(String str)
312        {
313            str = str.ToLower();
314            System.Security.Cryptography.SHA1 sec = new System.Security.Cryptography.SHA1CryptoServiceProvider();
315            Encoding encoding = Encoding.Unicode;
316            byte[] insertion = encoding.GetBytes(str);
317            byte[] hash = sec.ComputeHash(insertion);
318            String result = BitConverter.ToString(hash);//Convert.ToBase64String(hash);
319            result = result.Substring(result.Length / 2 + 1);
320            return result;
321        }
322
323        #endregion
324
325        #region PickCertificate (currently not used)
326        /// <summary>
327        /// Let the user choose a certificate from given location and store name
328        /// </summary>
329        /// <param name="location">The location</param>
330        /// <param name="name">The store name</param>
331        public static void PickCertificate(StoreLocation location, StoreName name)
332        {
333            X509Certificate2 MyCertificate;
334            X509Store store = new X509Store(name, location);
335            try
336            {
337                store.Open(OpenFlags.ReadOnly);
338
339                // Pick a certificate from the store
340                MyCertificate = X509Certificate2UI.SelectFromCollection(store.Certificates, "P@P certificates", "Please select your certificate", X509SelectionFlag.SingleSelection)[0];
341
342                // Comment next line to enable selection of an invalid certificate
343                ValidateCert(MyCertificate);
344
345                //MyCertificateSerialNumber = MyCertificate.SerialNumber;
346            }
347            catch (Exception ex)
348            {
349                MyCertificate = null;
350                throw (new Exception("Certificate not valid", ex));
351            }
352            finally { store.Close(); }
353        }
354        #endregion
355
356        #region ValidateCert (currently not used)
357        /// <summary>
358        /// Validates a certificate according to P@P-rules
359        /// </summary>
360        /// <exception cref="SNALCertificateNotValidException"></exception>
361        /// <exception cref="ArgumentNullException"></exception>
362        /// <param name="cert">Certificate to validate</param>
363        public static void ValidateCert(X509Certificate2 cert)
364        {
365            if (cert == null)
366            {
367                throw new ArgumentNullException("cert");
368            }
369
370            X509Chain chain = new X509Chain();
371
372            // check entire chain for revocation
373            chain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain;
374
375            // TODO: Check Online
376            chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
377
378            // timeout for online revocation list
379            chain.ChainPolicy.UrlRetrievalTimeout = new TimeSpan(0, 0, 30);
380
381            // TODO: Revocation unknown allowed
382            chain.ChainPolicy.VerificationFlags = X509VerificationFlags.NoFlag;
383
384            // Check chain
385            chain.Build(cert);
386
387            // If there was an error or no root CA is given throw exception
388            if (chain.ChainStatus.Length != 0 || chain.ChainElements.Count != 2)
389            {
390                throw new Exception("Certificates chain not valid!");
391            }
392
393            // Check root certificate
394            if (chain.ChainElements[1].Certificate.SerialNumber.ToLower() != CERT_PAP_SERIAL.ToLower())
395            {
396                throw new Exception("Certificates root CA not valid!");
397            }
398        }
399        #endregion
400
401        /// <summary>
402        /// Checks if all certificates for using the pap p2p system are installed.
403        /// Otherwise it tries to install the missing certificates. If all operations
404        /// succeed, return value is true. Only when value is true, you can try
405        /// to initialize the PAP System.
406        /// </summary>
407        /// <returns>If all operations succeed, return value is true. Only when value
408        /// is true, you can try to initialize the PAP System.</returns>
409        public static bool CheckAndInstallPAPCertificates()
410        {
411            bool retValue = false;
412
413            // get exe directory, because there resides the certificate directory
414            System.Reflection.Assembly assemb = System.Reflection.Assembly.GetEntryAssembly();
415            string applicationDir = System.IO.Path.GetDirectoryName(assemb.Location);
416            // check if all necessary certs are installed
417            P2PManager.Instance.GuiLogMessage("Check installation of all certificates, which are necessary to run the p2p system", NotificationLevel.Info);
418            List<PAPCertificate.PAP_Certificates> lstMissingCerts = PAPCertificate.CheckAvailabilityOfPAPCertificates(applicationDir);
419            if (lstMissingCerts.Count == 0)
420            {
421                //GuiLogMessage("All neccessary p2p certificates are installed.", NotificationLevel.Info);
422                retValue = true;
423            }
424            else
425            {
426                StringBuilder sbMissingCerts = new StringBuilder();
427                for (int i = 0; i < lstMissingCerts.Count; i++)
428                {
429                    sbMissingCerts.AppendLine(Enum.GetName(typeof(PAPCertificate.PAP_Certificates), lstMissingCerts[i]));
430                }
431                P2PManager.Instance.GuiLogMessage("Following certificates are missing. They will be installed now.\n" + sbMissingCerts.ToString(), NotificationLevel.Info);
432
433                // try/catch neccessary because the CT-Editor doesn't support the whole exception display process (e.g. shows only "unknown error.")
434                try
435                {
436                    if (PAPCertificate.InstallMissingCertificates(lstMissingCerts, applicationDir))
437                    {
438                        P2PManager.Instance.GuiLogMessage("Installation of all missing certificates was successful.", NotificationLevel.Info);
439                        retValue = true;
440                    }
441                    else
442                    {
443                        P2PManager.Instance.GuiLogMessage("No/not all missing certificates were installed successful.", NotificationLevel.Error);
444                    }
445                }
446                catch (Exception ex)
447                {
448                    P2PManager.Instance.GuiLogMessage("Error occured while installing certificates. Exception: " + ex.ToString(), NotificationLevel.Error);
449                }
450            }
451            return retValue;
452        }
453    }
454}
Note: See TracBrowser for help on using the repository browser.