Commit 60f30298 60f3029879f5e8cb6d697300a0ab537a4fb998d5 by Mick Smith

Senaste version från LIL-GIT 2017-09-12. MICK

git-tfs-id: [https://tfs.rsv.se/tfs/LoadTestCollection]$/VSTT-Plugins;C1190
1 parent 61003c77
...@@ -18,10 +18,46 @@ using System.ComponentModel; ...@@ -18,10 +18,46 @@ using System.ComponentModel;
18 using Microsoft.VisualStudio.TestTools.WebTesting.Rules; 18 using Microsoft.VisualStudio.TestTools.WebTesting.Rules;
19 using System.Text.RegularExpressions; 19 using System.Text.RegularExpressions;
20 using System.IO; 20 using System.IO;
21 using System.IO.Compression;
21 using Microsoft.VisualStudio.TestTools.LoadTesting; 22 using Microsoft.VisualStudio.TestTools.LoadTesting;
22 23
23 namespace LIL_VSTT_Plugins 24 namespace LIL_VSTT_Plugins
24 { 25 {
26 [DisplayName("Zip File Upload"), Description("Creates an ZIP archive of each of the files to be uploaded using the files name and adding .zip. Warning, uses %TEMP% for temp storage.")]
27 public class ZipFileUploadBeforePost : WebTestRequestPlugin
28 {
29 Queue<String> deleteDirs = new Queue<string>();
30 public override void PreRequest(object sender, PreRequestEventArgs e)
31 {
32 if (e.Request.Body.GetType() == typeof(FormPostHttpBody)) {
33 FormPostHttpBody body = (FormPostHttpBody)e.Request.Body;
34 foreach (FormPostParameter param in body.FormPostParameters) {
35 if(param.GetType() == typeof(FileUploadParameter))
36 {
37 FileUploadParameter fparam = (FileUploadParameter)param;
38 String tempDir = Path.GetTempPath() + "\\" + Guid.NewGuid().ToString();
39 Directory.CreateDirectory(tempDir);
40 deleteDirs.Enqueue(tempDir);
41 Directory.CreateDirectory(tempDir + @"\ZipDir");
42 File.Copy(fparam.FileName, tempDir + @"\ZipDir\" + Path.GetFileName(fparam.FileName));
43 ZipFile.CreateFromDirectory(tempDir + @"\ZipDir", tempDir + "\\" + Path.GetFileName(fparam.FileName) + ".zip");
44
45 fparam.FileName = tempDir + "\\" + Path.GetFileName(fparam.FileName) + ".zip";
46 fparam.FileUploadName = fparam.FileUploadName + ".zip";
47 }
48 }
49 }
50 base.PreRequest(sender, e);
51 }
52
53 public override void PostRequest(object sender, PostRequestEventArgs e)
54 {
55 foreach (String dir in deleteDirs) Directory.Delete(dir, true);
56 deleteDirs.Clear();
57 base.PostRequest(sender, e);
58 }
59 }
60
25 [DisplayName("Set Request Think Time"), Description("Changes the thinktime on requests with a set thinktime over 0 to the value of the ThinkTime context parameter")] 61 [DisplayName("Set Request Think Time"), Description("Changes the thinktime on requests with a set thinktime over 0 to the value of the ThinkTime context parameter")]
26 public class SetRequestThinkTime : WebTestPlugin 62 public class SetRequestThinkTime : WebTestPlugin
27 { 63 {
...@@ -208,6 +244,9 @@ namespace LIL_VSTT_Plugins ...@@ -208,6 +244,9 @@ namespace LIL_VSTT_Plugins
208 [DisplayName("File Name"), Description("The file name to use for logging")] 244 [DisplayName("File Name"), Description("The file name to use for logging")]
209 public String Filename { get; set; } 245 public String Filename { get; set; }
210 246
247 [DisplayName("Autogenerate File Name"), Description("Automatically generate a filename using the test name and user id from the load test")]
248 public bool autoFilename { get; set; }
249
211 [DisplayName("Write header"), DefaultValue(false), Description("Writes the parameter names as a header. Will write a new header for each user (dont use in loadtest)")] 250 [DisplayName("Write header"), DefaultValue(false), Description("Writes the parameter names as a header. Will write a new header for each user (dont use in loadtest)")]
212 public bool useHeader { get; set; } 251 public bool useHeader { get; set; }
213 252
...@@ -221,6 +260,11 @@ namespace LIL_VSTT_Plugins ...@@ -221,6 +260,11 @@ namespace LIL_VSTT_Plugins
221 260
222 public override void PostWebTest(object sender, PostWebTestEventArgs e) 261 public override void PostWebTest(object sender, PostWebTestEventArgs e)
223 { 262 {
263 String completeFileName = Filename + e.WebTest.Context.WebTestUserId + ".log";
264 if (autoFilename)
265 {
266 completeFileName = e.WebTest.Name + e.WebTest.Context.WebTestUserId + ".log";
267 }
224 if ((e.WebTest.Outcome == Outcome.Pass && logPassed) || (e.WebTest.Outcome == Outcome.Fail && logFailed)) { 268 if ((e.WebTest.Outcome == Outcome.Pass && logPassed) || (e.WebTest.Outcome == Outcome.Fail && logFailed)) {
225 string row = ""; 269 string row = "";
226 string hrow = ""; 270 string hrow = "";
...@@ -240,10 +284,10 @@ namespace LIL_VSTT_Plugins ...@@ -240,10 +284,10 @@ namespace LIL_VSTT_Plugins
240 } 284 }
241 if (header && useHeader) 285 if (header && useHeader)
242 { 286 {
243 File.AppendAllText(Filename + e.WebTest.Context.WebTestUserId + ".log", hrow + "\r\n"); 287 File.AppendAllText(completeFileName, hrow + "\r\n");
244 header = false; 288 header = false;
245 } 289 }
246 File.AppendAllText(Filename + e.WebTest.Context.WebTestUserId + ".log", row + "\r\n"); 290 File.AppendAllText(completeFileName, row + "\r\n");
247 base.PostWebTest(sender, e); 291 base.PostWebTest(sender, e);
248 } 292 }
249 } 293 }
......
...@@ -13,6 +13,10 @@ ...@@ -13,6 +13,10 @@
13 <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> 13 <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
14 <FileAlignment>512</FileAlignment> 14 <FileAlignment>512</FileAlignment>
15 <TargetFrameworkProfile /> 15 <TargetFrameworkProfile />
16 <SccProjectName>SAK</SccProjectName>
17 <SccLocalPath>SAK</SccLocalPath>
18 <SccAuxPath>SAK</SccAuxPath>
19 <SccProvider>SAK</SccProvider>
16 </PropertyGroup> 20 </PropertyGroup>
17 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> 21 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
18 <DebugSymbols>true</DebugSymbols> 22 <DebugSymbols>true</DebugSymbols>
...@@ -41,6 +45,8 @@ ...@@ -41,6 +45,8 @@
41 <Reference Include="Microsoft.VisualStudio.QualityTools.WebTestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" /> 45 <Reference Include="Microsoft.VisualStudio.QualityTools.WebTestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
42 <Reference Include="System" /> 46 <Reference Include="System" />
43 <Reference Include="System.Core" /> 47 <Reference Include="System.Core" />
48 <Reference Include="System.IO.Compression" />
49 <Reference Include="System.IO.Compression.FileSystem" />
44 <Reference Include="System.Xml.Linq" /> 50 <Reference Include="System.Xml.Linq" />
45 <Reference Include="System.Data.DataSetExtensions" /> 51 <Reference Include="System.Data.DataSetExtensions" />
46 <Reference Include="Microsoft.CSharp" /> 52 <Reference Include="Microsoft.CSharp" />
......
1 ""
2 {
3 "FILE_VERSION" = "9237"
4 "ENLISTMENT_CHOICE" = "NEVER"
5 "PROJECT_FILE_RELATIVE_PATH" = ""
6 "NUMBER_OF_EXCLUDED_FILES" = "0"
7 "ORIGINAL_PROJECT_FILE_PATH" = ""
8 "NUMBER_OF_NESTED_PROJECTS" = "0"
9 "SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER"
10 }
...@@ -52,6 +52,31 @@ namespace LIL_VSTT_Plugins ...@@ -52,6 +52,31 @@ namespace LIL_VSTT_Plugins
52 } 52 }
53 53
54 /// <summary> 54 /// <summary>
55 /// LoadTest Context Copy
56 /// </summary>
57 [DisplayName("Personnummer Generator")]
58 [Description("(C) Copyright 2017 LIGHTS IN LINE AB\r\nGenererar personnummer och sätter dem som parameter till testernas context.")]
59 public class LoadTestPnumGen : ILoadTestPlugin
60 {
61 //store the load test object.
62 LoadTest mLoadTest;
63
64 public void Initialize(LoadTest loadTest)
65 {
66 mLoadTest = loadTest;
67
68 //connect to the TestStarting event.
69 mLoadTest.TestStarting += new EventHandler<TestStartingEventArgs>(mLoadTest_TestStarting);
70 }
71
72
73 void mLoadTest_TestStarting(object sender, TestStartingEventArgs e)
74 {
75 //TODO
76 }
77 }
78
79 /// <summary>
55 /// Service Manager Plugin 80 /// Service Manager Plugin
56 /// </summary> 81 /// </summary>
57 [DisplayName("Service Manager Config")] 82 [DisplayName("Service Manager Config")]
...@@ -457,6 +482,7 @@ namespace LIL_VSTT_Plugins ...@@ -457,6 +482,7 @@ namespace LIL_VSTT_Plugins
457 string input = null; 482 string input = null;
458 int lineNum = 0; 483 int lineNum = 0;
459 int dataNum = 0; 484 int dataNum = 0;
485 char[] trim = { ' ', '\x00', '\t', '\x20' };
460 while ((input = re.ReadLine()) != null) 486 while ((input = re.ReadLine()) != null)
461 { 487 {
462 // Ignore blank lines and empty lines (just whitespace) or lines with only blank/empty/whitespace columns 488 // Ignore blank lines and empty lines (just whitespace) or lines with only blank/empty/whitespace columns
...@@ -466,7 +492,7 @@ namespace LIL_VSTT_Plugins ...@@ -466,7 +492,7 @@ namespace LIL_VSTT_Plugins
466 if (lineNum == 1 && myHasColName == true) 492 if (lineNum == 1 && myHasColName == true)
467 { 493 {
468 // First line is column names 494 // First line is column names
469 myColNames = input.TrimEnd(); 495 myColNames = input.TrimEnd(trim);
470 } 496 }
471 else 497 else
472 { 498 {
...@@ -477,13 +503,13 @@ namespace LIL_VSTT_Plugins ...@@ -477,13 +503,13 @@ namespace LIL_VSTT_Plugins
477 else ifAgentId = dataNum + 1; 503 else ifAgentId = dataNum + 1;
478 if (ifAgentId == agentId) 504 if (ifAgentId == agentId)
479 { 505 {
480 myParams.Add(input.TrimEnd()); 506 myParams.Add(input.TrimEnd(trim));
481 } 507 }
482 dataNum++; 508 dataNum++;
483 } 509 }
484 else 510 else
485 { 511 {
486 myParams.Add(input.TrimEnd()); 512 myParams.Add(input.TrimEnd(trim));
487 } 513 }
488 } 514 }
489 } 515 }
......
...@@ -20,6 +20,7 @@ using System.IO; ...@@ -20,6 +20,7 @@ using System.IO;
20 using System.ComponentModel; 20 using System.ComponentModel;
21 using System.Text.RegularExpressions; 21 using System.Text.RegularExpressions;
22 using System.Security.Cryptography.X509Certificates; 22 using System.Security.Cryptography.X509Certificates;
23 using System.Diagnostics;
23 24
24 namespace LIL_VSTT_Plugins 25 namespace LIL_VSTT_Plugins
25 { 26 {
...@@ -233,6 +234,54 @@ namespace LIL_VSTT_Plugins ...@@ -233,6 +234,54 @@ namespace LIL_VSTT_Plugins
233 } 234 }
234 235
235 /// <summary> 236 /// <summary>
237 /// Slå på URL encode på query string parametrar
238 /// </summary>
239 [DisplayName("URL Encode Query String Parameter")]
240 [Description("(C) Copyright 2011 LIGHTS IN LINE AB\r\nTvingar en URL Encode på angiven Query String parameter i alla request")]
241 public class URLEncodeQueryStringParameter : WebTestPlugin
242 {
243 [DisplayName("Query String Parameter Name")]
244 [Description("Name of the query string parameter to URL encode before each request")]
245 public String paramName { get; set; }
246 public override void PreRequest(object sender, PreRequestEventArgs e)
247 {
248 if (e.Request.HasQueryStringParameters)
249 {
250 if (e.Request.QueryStringParameters.Contains(paramName))
251 {
252 foreach (QueryStringParameter qsp in e.Request.QueryStringParameters) {
253 if (qsp.Name.Equals(paramName))
254 {
255 qsp.UrlEncode = true;
256 }
257 }
258 }
259 }
260 }
261 }
262
263 /// <summary>
264 /// Loggar alla transaktioners svarstider som context parametrar
265 /// </summary>
266 [DisplayName("Transaction Response Times to Context")]
267 [Description("(C) Copyright 2016 LIGHTS IN LINE AB\r\nLoggar alla transaktioners svarstider som context parametrar")]
268 public class TransactionsToContext : WebTestPlugin
269 {
270 public override void PostTransaction(object sender, PostTransactionEventArgs e)
271 {
272 base.PostTransaction(sender, e);
273 if (!e.WebTest.Context.ContainsKey(e.TransactionName))
274 {
275 e.WebTest.Context.Add(e.TransactionName, e.Duration.TotalMilliseconds.ToString());
276 }
277 else
278 {
279 e.WebTest.Context[e.TransactionName] = e.Duration.TotalMilliseconds.ToString();
280 }
281 }
282 }
283
284 /// <summary>
236 /// Ignorerar status koder under 500. 285 /// Ignorerar status koder under 500.
237 /// </summary> 286 /// </summary>
238 [DisplayName("Ignore 4xx status codes")] 287 [DisplayName("Ignore 4xx status codes")]
...@@ -306,16 +355,70 @@ namespace LIL_VSTT_Plugins ...@@ -306,16 +355,70 @@ namespace LIL_VSTT_Plugins
306 [Description("Default inte påslaget. Om servern inte stödjer TLS1.2 kommer SSL handskakningen att avbrytas och requestet failar. Kräver .NET 4.5 samt att TLS1.2 är aktiverat i SChannel (använd bifogad schannel_high.reg om det inte är påslaget på äldre windows versioner)")] 355 [Description("Default inte påslaget. Om servern inte stödjer TLS1.2 kommer SSL handskakningen att avbrytas och requestet failar. Kräver .NET 4.5 samt att TLS1.2 är aktiverat i SChannel (använd bifogad schannel_high.reg om det inte är påslaget på äldre windows versioner)")]
307 public bool useTls12 { get; set; } 356 public bool useTls12 { get; set; }
308 357
358 [DisplayName("Apply Override"), DefaultValue(false)]
359 [Description("Ändrar proxy inställningarna för detta webtest enligt detta plugin")]
360 [Category("Web Proxy")]
361 public bool proxyOverride { get; set; }
362
363 [DisplayName("Proxy URI"), DefaultValue("http://host:port")]
364 [Description("Anger proxyserverns URI (http://host:port)")]
365 [Category("Web Proxy")]
366 public string proxyURI { get; set; }
367
368 [DisplayName("Bypass Local"), DefaultValue(false)]
369 [Description("True för att inte använda proxy på lokala hostnamn, i.e. utan domännamn")]
370 [Category("Web Proxy")]
371 public bool proxyBypassLocal { get; set; }
372
373 [DisplayName("Bypass RegExp"), DefaultValue("")]
374 [Description("Sätter ett reguljärt uttryck för URI (hostnamn) som INTE ska gå via proxyn")]
375 [Category("Web Proxy")]
376 public string proxyBypass { get; set; }
377
378 [DisplayName("User Name"), DefaultValue("")]
379 [Description("Sätter användarnamn för proxyn")]
380 [Category("Web Proxy")]
381 public string proxyUser { get; set; }
382
383 [DisplayName("User Password"), DefaultValue("")]
384 [Description("Sätter swedbanks proxy (temp lösning)")]
385 [Category("Web Proxy")]
386 public string proxyPass { get; set; }
387
388 System.Net.WebProxy myProxy = null;
389
309 public override void PreWebTest(object sender, PreWebTestEventArgs e) 390 public override void PreWebTest(object sender, PreWebTestEventArgs e)
310 { 391 {
311 base.PreWebTest(sender, e); 392 base.PreWebTest(sender, e);
393
394 if (proxyOverride)
395 {
396 if (myProxy == null)
397 {
398 myProxy = new System.Net.WebProxy();
399 if(!String.IsNullOrWhiteSpace(proxyURI)) myProxy.Address = new Uri(proxyURI);
400 myProxy.BypassProxyOnLocal = proxyBypassLocal;
401 if (!String.IsNullOrWhiteSpace(proxyBypass)) myProxy.BypassList = new string[] { proxyBypass };
402 if (!String.IsNullOrWhiteSpace(proxyUser)) myProxy.Credentials = new System.Net.NetworkCredential(proxyUser, proxyPass);
403 }
404 // Change the webtests proxy setting
405 e.WebTest.Proxy = "lil";
406 e.WebTest.WebProxy = myProxy;
407 // Set context parameters
408 e.WebTest.Context["proxyOverride"] = proxyOverride;
409 e.WebTest.Context["proxyURI"] = proxyURI;
410 e.WebTest.Context["proxyBypassLocal"] = proxyBypassLocal;
411 e.WebTest.Context["proxyBypass"] = proxyBypass;
412 e.WebTest.Context["proxyUser"] = proxyUser;
413 e.WebTest.Context["proxyPass"] = proxyPass;
414 }
415
312 System.Net.ServicePointManager.Expect100Continue = exp100; 416 System.Net.ServicePointManager.Expect100Continue = exp100;
313 System.Net.ServicePointManager.MaxServicePointIdleTime = maxIdle; 417 System.Net.ServicePointManager.MaxServicePointIdleTime = maxIdle;
314 System.Net.ServicePointManager.SetTcpKeepAlive(keepAlive, timeOut, interVal); 418 System.Net.ServicePointManager.SetTcpKeepAlive(keepAlive, timeOut, interVal);
315 System.Net.ServicePointManager.UseNagleAlgorithm = useNagle; 419 System.Net.ServicePointManager.UseNagleAlgorithm = useNagle;
316 if(useTls12) System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12; 420 if(useTls12) System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12;
317 } 421 }
318
319 } 422 }
320 423
321 /// <summary> 424 /// <summary>
...@@ -558,21 +661,21 @@ namespace LIL_VSTT_Plugins ...@@ -558,21 +661,21 @@ namespace LIL_VSTT_Plugins
558 /// WebTest Client Certificate 661 /// WebTest Client Certificate
559 /// </summary> 662 /// </summary>
560 [DisplayName("Client Certificate")] 663 [DisplayName("Client Certificate")]
561 [Description("(C) Copyright 2016 LIGHTS IN LINE AB\r\nSätter webtestet att använda ett specifikt client cert för SSL. Certifikatet behöver inte installeras i certstore först.")] 664 [Description("(C) Copyright 2016 LIGHTS IN LINE AB\r\nSätter webtestet att använda ett specifikt client cert för SSL. Certifikatet installeras automatiskt i Windows User Certificate Store.")]
562 public class ClientCertificatePlugin : WebTestPlugin 665 public class ClientCertificatePlugin : WebTestPlugin
563 { 666 {
564 [DisplayName("Certificate Path")] 667 [DisplayName("Certificate Path")]
565 [Description("Sökvägen till certifikatfilen (.P12/.PFX/.PEM med privat nyckel)")] 668 [Description("Sökvägen till certifikatfilen (P12/PFX/PEM med privat nyckel eller CER/DER utan privat nyckel)")]
566 [DefaultValue("")] 669 [DefaultValue("")]
567 public string pCertificatePath { get; set; } 670 public string pCertificatePath { get; set; }
568 671
569 [DisplayName("Certificate Path Parameter")] 672 [DisplayName("Certificate Path Parameter")]
570 [Description("Ange namn på parameter som ska användas för sökvägen till certifikatfilen (.P12/.PFX/.PEM). Om parametern saknas eller är tom används Certificate Path")] 673 [Description("Ange namn på parameter som ska användas för sökvägen till certifikatfilen. Om parametern saknas eller är tom används Certificate Path")]
571 [DefaultValue("")] 674 [DefaultValue("")]
572 public string pCertificatePathParameter { get; set; } 675 public string pCertificatePathParameter { get; set; }
573 676
574 [DisplayName("Certificate Password")] 677 [DisplayName("Certificate Password")]
575 [Description("Ange lösenordet för att öppna certifikatfilen")] 678 [Description("Ange lösenordet för att öppna skyddade/krypterade filer")]
576 [DefaultValue("")] 679 [DefaultValue("")]
577 public string pCertificatePassword { get; set; } 680 public string pCertificatePassword { get; set; }
578 681
...@@ -598,19 +701,25 @@ namespace LIL_VSTT_Plugins ...@@ -598,19 +701,25 @@ namespace LIL_VSTT_Plugins
598 701
599 private bool haveCert = false; 702 private bool haveCert = false;
600 private X509Certificate2 myClientCertAndKey; 703 private X509Certificate2 myClientCertAndKey;
704 private Regex p12RegExp = new Regex(@"p12$|pfx$",RegexOptions.IgnoreCase);
705 private Regex cerRegExp = new Regex(@"cer$|der$", RegexOptions.IgnoreCase);
706 private Regex pemRegExp = new Regex(@"pem$", RegexOptions.IgnoreCase);
601 707
602 public override void PreWebTest(object sender, PreWebTestEventArgs e) 708 public override void PreWebTest(object sender, PreWebTestEventArgs e)
603 { 709 {
710 Stopwatch sw = new Stopwatch();
711 sw.Start();
712
604 base.PreWebTest(sender, e); 713 base.PreWebTest(sender, e);
605 String certPath, certPass; 714 String certPath, certPass;
606 // Ladda in certifikatet och sätt CertPolicy 715 // Ladda in certifikatet och sätt CertPolicy
607 716
608 if (!String.IsNullOrWhiteSpace(pCertificatePathParameter) && e.WebTest.Context.ContainsKey(pCertificatePathParameter) && !String.IsNullOrWhiteSpace(e.WebTest.Context[pCertificatePathParameter].ToString()) ) 717 if (!String.IsNullOrWhiteSpace(pCertificatePathParameter) && e.WebTest.Context.ContainsKey(pCertificatePathParameter) && !String.IsNullOrWhiteSpace(e.WebTest.Context[pCertificatePathParameter].ToString()) )
609 { 718 {
610 certPath = e.WebTest.Context[pCertificatePathParameter].ToString(); 719 certPath = e.WebTest.Context[pCertificatePathParameter].ToString().Trim();
611 } else 720 } else
612 { 721 {
613 certPath = pCertificatePath; 722 certPath = pCertificatePath.Trim();
614 } 723 }
615 724
616 if (!String.IsNullOrWhiteSpace(pCertificatePasswordParameter) && e.WebTest.Context.ContainsKey(pCertificatePasswordParameter) && !String.IsNullOrWhiteSpace(e.WebTest.Context[pCertificatePasswordParameter].ToString())) 725 if (!String.IsNullOrWhiteSpace(pCertificatePasswordParameter) && e.WebTest.Context.ContainsKey(pCertificatePasswordParameter) && !String.IsNullOrWhiteSpace(e.WebTest.Context[pCertificatePasswordParameter].ToString()))
...@@ -629,11 +738,11 @@ namespace LIL_VSTT_Plugins ...@@ -629,11 +738,11 @@ namespace LIL_VSTT_Plugins
629 return; 738 return;
630 } 739 }
631 740
632 // Check what type of container we have. All files are treated as PEM unless the extension is .pfx or .p12 741 // Check what type of container we have. All files are treated as PEM unless the extension matches our winX509regExp regular expression (see above)
633 // Read certificate and private key depending on type 742 // Read certificate and private key depending on type
634 if (certPath.ToLower().EndsWith(".pfx") || certPath.ToLower().EndsWith(".p12")) 743 if (p12RegExp.IsMatch(certPath))
635 { 744 {
636 if (pDebug) e.WebTest.AddCommentToResult("Certificate file is treated as PFX/PKCS12"); 745 if (pDebug) e.WebTest.AddCommentToResult("Certificate file is treated as PFX/P12");
637 try 746 try
638 { 747 {
639 myClientCertAndKey = new X509Certificate2(certPath, certPass, X509KeyStorageFlags.PersistKeySet); 748 myClientCertAndKey = new X509Certificate2(certPath, certPass, X509KeyStorageFlags.PersistKeySet);
...@@ -643,9 +752,21 @@ namespace LIL_VSTT_Plugins ...@@ -643,9 +752,21 @@ namespace LIL_VSTT_Plugins
643 e.WebTest.AddCommentToResult("Error during loading of certificate: " + certPath + " Message: " + ex.Message); 752 e.WebTest.AddCommentToResult("Error during loading of certificate: " + certPath + " Message: " + ex.Message);
644 return; 753 return;
645 } 754 }
646 } else 755 } else if (cerRegExp.IsMatch(certPath))
647 { 756 {
648 if (pDebug) e.WebTest.AddCommentToResult("Certificate file is treated as PEM/PKCS8"); 757 if (pDebug) e.WebTest.AddCommentToResult("Certificate file is treated as CER/DER without private key");
758 try
759 {
760 myClientCertAndKey = new X509Certificate2(certPath, certPass);
761 }
762 catch (Exception ex)
763 {
764 e.WebTest.AddCommentToResult("Error during loading of certificate: " + certPath + " Message: " + ex.Message);
765 return;
766 }
767 } else if (pemRegExp.IsMatch(certPath))
768 {
769 if (pDebug) e.WebTest.AddCommentToResult("Certificate file is treated as OpenSSL encoded PEM");
649 770
650 // Use Bouncy Castle to read the certificate and key, then convert to .NET X509Certificate2 and X509Certificate 771 // Use Bouncy Castle to read the certificate and key, then convert to .NET X509Certificate2 and X509Certificate
651 String text; 772 String text;
...@@ -698,19 +819,24 @@ namespace LIL_VSTT_Plugins ...@@ -698,19 +819,24 @@ namespace LIL_VSTT_Plugins
698 } 819 }
699 keyTextBeginPos = text.IndexOf("-----BEGIN", keyTextEndPos); 820 keyTextBeginPos = text.IndexOf("-----BEGIN", keyTextEndPos);
700 } 821 }
701 if (bcKey == null || bcCert == null) 822 if (bcCert == null)
702 { 823 {
703 e.WebTest.AddCommentToResult("Error: PEM file has to contain both certificate and private key"); 824 e.WebTest.AddCommentToResult("Error: PEM file has to contain an x509 certificate");
704 return; 825 return;
705 } 826 }
706 try { 827 try {
707 myClientCertAndKey = new X509Certificate2(Org.BouncyCastle.Security.DotNetUtilities.ToX509Certificate(bcCert)); 828 myClientCertAndKey = new X509Certificate2(Org.BouncyCastle.Security.DotNetUtilities.ToX509Certificate(bcCert));
708 myClientCertAndKey.PrivateKey = Org.BouncyCastle.Security.DotNetUtilities.ToRSA(bcKey.Private as Org.BouncyCastle.Crypto.Parameters.RsaPrivateCrtKeyParameters); 829 if(bcKey != null) myClientCertAndKey.PrivateKey = Org.BouncyCastle.Security.DotNetUtilities.ToRSA(bcKey.Private as Org.BouncyCastle.Crypto.Parameters.RsaPrivateCrtKeyParameters);
709 } catch (Exception ex) 830 } catch (Exception ex)
710 { 831 {
711 e.WebTest.AddCommentToResult("Error during loading of PEM file: " + certPath + " Message: " + ex.Message); 832 e.WebTest.AddCommentToResult("Error during loading of PEM file: " + certPath + " Message: " + ex.Message);
712 return; 833 return;
713 } 834 }
835 } else
836 {
837 // Unknown or unsuported format
838 e.WebTest.AddCommentToResult("Error during loading of file: " + certPath + " Message: Unsupported format");
839 return;
714 } 840 }
715 841
716 // Check that we have a certificate 842 // Check that we have a certificate
...@@ -721,16 +847,17 @@ namespace LIL_VSTT_Plugins ...@@ -721,16 +847,17 @@ namespace LIL_VSTT_Plugins
721 } 847 }
722 else 848 else
723 { 849 {
724 if (pDebug) e.WebTest.AddCommentToResult("Certificate File " + certPath + " loaded successfully."); 850 if (pDebug) e.WebTest.AddCommentToResult("Certificate File " + certPath + " loaded successfully in " + sw.ElapsedMilliseconds + "ms");
725 } 851 }
726 852
727 // Check that it seems okey 853 // Check that it seems okey
728 if (string.IsNullOrWhiteSpace(myClientCertAndKey.GetCertHashString())) 854 if (string.IsNullOrWhiteSpace(myClientCertAndKey.Thumbprint))
729 { 855 {
730 if (pDebug) e.WebTest.AddCommentToResult("Certificate File " + certPath + " contains no SHA1 hash. Not using it."); 856 if (pDebug) e.WebTest.AddCommentToResult("Certificate File " + certPath + " contains no Thumbprint. Not using it.");
731 return; 857 return;
732 } 858 }
733 if (pDebug) e.WebTest.AddCommentToResult("Loaded client certificate for Subject: [" + myClientCertAndKey.Subject + "] Issued by: [" + myClientCertAndKey.Issuer + "] Expires: [" + myClientCertAndKey.GetExpirationDateString() + "]"); 859 if (pDebug) e.WebTest.AddCommentToResult("Subject: [" + myClientCertAndKey.Subject + "]");
860 if (pDebug) e.WebTest.AddCommentToResult("Issued by: [" + myClientCertAndKey.Issuer + "] Expires: [" + myClientCertAndKey.GetExpirationDateString() + "]");
734 861
735 // Check if the certificate is trusted (i.e. chain can be validated) 862 // Check if the certificate is trusted (i.e. chain can be validated)
736 bool myCertTrusted = false; 863 bool myCertTrusted = false;
...@@ -747,24 +874,19 @@ namespace LIL_VSTT_Plugins ...@@ -747,24 +874,19 @@ namespace LIL_VSTT_Plugins
747 // Check if it is expired or about to expire 874 // Check if it is expired or about to expire
748 if(myClientCertAndKey.NotAfter < DateTime.Now) 875 if(myClientCertAndKey.NotAfter < DateTime.Now)
749 { 876 {
750 e.WebTest.AddCommentToResult("Warning: Client Certificate has expired. Might not be trusted on server. Expired " + myClientCertAndKey.NotAfter.ToString()); 877 e.WebTest.AddCommentToResult("Warning: Client Certificate has expired. Might not be trusted on server.");
751 } else if (myClientCertAndKey.NotBefore > DateTime.Now) 878 } else if (myClientCertAndKey.NotBefore > DateTime.Now)
752 { 879 {
753 e.WebTest.AddCommentToResult("Warning: Client Certificate is not valid yet. Might not be trusted on server. Valid " + myClientCertAndKey.NotBefore.ToString()); 880 e.WebTest.AddCommentToResult("Warning: Client Certificate is not valid yet. Might not be trusted on server. Valid on " + myClientCertAndKey.NotBefore.ToString());
754 } else if (myClientCertAndKey.NotAfter < DateTime.Now.AddDays(14)) 881 } else if (myClientCertAndKey.NotAfter < DateTime.Now.AddDays(14))
755 { 882 {
756 e.WebTest.AddCommentToResult("Warning: Client Certificate will expire in less than 14 days. Better renew it soon. Expires " + myClientCertAndKey.NotAfter.ToString()); 883 e.WebTest.AddCommentToResult("Warning: Client Certificate will expire in less than 14 days. Better renew it soon.");
757 } 884 }
758 885
759 // Check if we have a private key 886 // Check if we have a private key
760 if (!myClientCertAndKey.HasPrivateKey) 887 if (myClientCertAndKey.HasPrivateKey)
761 { 888 {
762 // Cant use it without private key 889 if (pDebug) e.WebTest.AddCommentToResult("Certificate HAS PRIVATE KEY in file");
763 e.WebTest.AddCommentToResult("Error: Certificate HAS NO PRIVATE KEY, cannot use it without one.");
764 return;
765 } else
766 {
767 if (pDebug) e.WebTest.AddCommentToResult("Certificate HAS PRIVATE KEY");
768 } 890 }
769 891
770 // Check that the certificate exists in the cert store 892 // Check that the certificate exists in the cert store
...@@ -772,31 +894,69 @@ namespace LIL_VSTT_Plugins ...@@ -772,31 +894,69 @@ namespace LIL_VSTT_Plugins
772 cuStore.Open(OpenFlags.ReadWrite); 894 cuStore.Open(OpenFlags.ReadWrite);
773 if(cuStore.Certificates.Contains(myClientCertAndKey)) { 895 if(cuStore.Certificates.Contains(myClientCertAndKey)) {
774 if (pDebug) e.WebTest.AddCommentToResult("Certificate already INSTALLED in Current User Windows Certificate Store"); 896 if (pDebug) e.WebTest.AddCommentToResult("Certificate already INSTALLED in Current User Windows Certificate Store");
897 // Try to load the key from store if we dont have it and verify that it belongs to the certificate
898 if(!myClientCertAndKey.HasPrivateKey)
899 {
900 X509Certificate2Collection certCol = cuStore.Certificates.Find(X509FindType.FindByThumbprint, myClientCertAndKey.Thumbprint, false);
901 if(certCol == null || certCol.Count == 0)
902 {
903 e.WebTest.AddCommentToResult("Error: Certificate could not be loaded from store using it's thumbprint which is very strange. Aborting");
904 return;
905 } else
906 {
907 if(certCol.Count > 1)
908 {
909 if (pDebug) e.WebTest.AddCommentToResult("Certificates thumbprint has more than one match in the Windows User Certificate Store, using the first one.");
910 }
911 X509Certificate2 cert = certCol[0];
912 if (!cert.HasPrivateKey)
913 {
914 e.WebTest.AddCommentToResult("Error: Certificate does not have a corresponding private key in the Windows User Certificate Store. Can not use it for SSL.");
915 return;
916 } else
917 {
918 if (pDebug) e.WebTest.AddCommentToResult("Certificate was found in Windows User Certificate Store using thumbprint and HAS a corresponding PRIVATE KEY");
919 }
920 }
921 }
775 } else 922 } else
776 { 923 {
777 if (pDebug) e.WebTest.AddCommentToResult("Certificate is NOT INSTALLED"); 924 if (pDebug) e.WebTest.AddCommentToResult("Certificate is NOT INSTALLED");
778 if(pInstallTrusted && myCertTrusted || pInstallUntrusted) 925 if (myClientCertAndKey.HasPrivateKey)
779 { 926 {
780 // Try to install certificate 927 if (pInstallTrusted && myCertTrusted || pInstallUntrusted)
781 if (myCertTrusted || !myCertTrusted)
782 { 928 {
929 // Try to install certificate
783 // Install in user store 930 // Install in user store
784 try { 931 try
932 {
785 myClientCertAndKey.FriendlyName = "VSTT"; 933 myClientCertAndKey.FriendlyName = "VSTT";
786 cuStore.Add(myClientCertAndKey); 934 cuStore.Add(myClientCertAndKey);
787 if (pDebug) e.WebTest.AddCommentToResult("Certificate HAS BEEN INSTALLED in the Current User Windows Certificate Store with Friendly Name: VSTT"); 935 if (pDebug) e.WebTest.AddCommentToResult("Certificate HAS BEEN INSTALLED in the Current User Windows Certificate Store with Friendly Name: VSTT");
788 } catch (Exception ex) 936 }
937 catch (Exception ex)
789 { 938 {
790 e.WebTest.AddCommentToResult("Error: COULD NOT INSTALL in the Current User Windows Certificate Store, Message: " + ex.Message); 939 e.WebTest.AddCommentToResult("Error: COULD NOT INSTALL in the Current User Windows Certificate Store, Message: " + ex.Message);
791 return; 940 return;
792 } 941 }
793 } 942 }
943 else
944 {
945 e.WebTest.AddCommentToResult("Error: COULD NOT INSTALL the certificate since you selected NOT to install untrusted certificates.");
946 return;
947 }
948 } else
949 {
950 e.WebTest.AddCommentToResult("Error: COULD NOT INSTALL the certificate since the file did not contain a private key and cannot be used without one.");
951 return;
794 } 952 }
795 } 953 }
796 954
797 // Set the PreRequest method to add the certificate on requests 955 // Set the PreRequest method to add the certificate on requests
798 haveCert = true; 956 haveCert = true;
799 if (pDebug) e.WebTest.AddCommentToResult("Certificate will be ADDED TO REQUESTS"); 957 if (pDebug) e.WebTest.AddCommentToResult("Certificate will be ADDED TO REQUESTS");
958 sw.Stop();
959 if (pDebug) e.WebTest.AddCommentToResult("Certificate processing done in " + sw.ElapsedMilliseconds + "ms");
800 } 960 }
801 961
802 public override void PreRequest(object sender, PreRequestEventArgs e) 962 public override void PreRequest(object sender, PreRequestEventArgs e)
...@@ -811,7 +971,7 @@ namespace LIL_VSTT_Plugins ...@@ -811,7 +971,7 @@ namespace LIL_VSTT_Plugins
811 e.WebTest.Context["Client Certificate"] = myClientCertAndKey.Subject; 971 e.WebTest.Context["Client Certificate"] = myClientCertAndKey.Subject;
812 } else 972 } else
813 { 973 {
814 e.WebTest.Context["Client Certificate"] = "No certificate was added"; 974 e.WebTest.Context["Client Certificate"] = "No certificate was specified in this request. Windows will try to automatically choose an installed client certificate if requested by the server.";
815 } 975 }
816 } 976 }
817 } 977 }
......