Fungerande PEM inläsning, behöver dock testas så att privata nyckeln i cert stor…
…e faktiskt går att använda efter installation.
Showing
6 changed files
with
142 additions
and
32 deletions
... | @@ -8,6 +8,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution | ... | @@ -8,6 +8,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution |
8 | LICENSE = LICENSE | 8 | LICENSE = LICENSE |
9 | LIL_VSTT_Plugins.vsmdi = LIL_VSTT_Plugins.vsmdi | 9 | LIL_VSTT_Plugins.vsmdi = LIL_VSTT_Plugins.vsmdi |
10 | Local.testsettings = Local.testsettings | 10 | Local.testsettings = Local.testsettings |
11 | Notes.md = Notes.md | ||
11 | README.md = README.md | 12 | README.md = README.md |
12 | EndProjectSection | 13 | EndProjectSection |
13 | EndProject | 14 | EndProject | ... | ... |
... | @@ -55,6 +55,12 @@ | ... | @@ -55,6 +55,12 @@ |
55 | <ItemGroup> | 55 | <ItemGroup> |
56 | <None Include="Registry\Schannel_high_withclient.reg" /> | 56 | <None Include="Registry\Schannel_high_withclient.reg" /> |
57 | </ItemGroup> | 57 | </ItemGroup> |
58 | <ItemGroup> | ||
59 | <ProjectReference Include="..\crypto\crypto.Net45.csproj"> | ||
60 | <Project>{45473847-8af8-4baf-b768-442c6875b8cf}</Project> | ||
61 | <Name>crypto.Net45</Name> | ||
62 | </ProjectReference> | ||
63 | </ItemGroup> | ||
58 | <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> | 64 | <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> |
59 | <!-- To modify your build process, add your task inside one of the targets below and uncomment it. | 65 | <!-- To modify your build process, add your task inside one of the targets below and uncomment it. |
60 | Other similar extension points exist, see Microsoft.Common.targets. | 66 | Other similar extension points exist, see Microsoft.Common.targets. | ... | ... |
... | @@ -549,12 +549,12 @@ namespace LIL_VSTT_Plugins | ... | @@ -549,12 +549,12 @@ namespace LIL_VSTT_Plugins |
549 | public class ClientCertificatePlugin : WebTestPlugin | 549 | public class ClientCertificatePlugin : WebTestPlugin |
550 | { | 550 | { |
551 | [DisplayName("Certificate Path")] | 551 | [DisplayName("Certificate Path")] |
552 | [Description("Sökvägen till certifikatfilen (P12/PFX med privat nyckel)")] | 552 | [Description("Sökvägen till certifikatfilen (.P12/.PFX/.PEM med privat nyckel)")] |
553 | [DefaultValue("")] | 553 | [DefaultValue("")] |
554 | public string pCertificatePath { get; set; } | 554 | public string pCertificatePath { get; set; } |
555 | 555 | ||
556 | [DisplayName("Certificate Path Parameter")] | 556 | [DisplayName("Certificate Path Parameter")] |
557 | [Description("Ange namn på parameter som ska användas för sökvägen till certifikatfilen (P12/PFX). Om parametern saknas eller är tom används Certificate Path")] | 557 | [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")] |
558 | [DefaultValue("")] | 558 | [DefaultValue("")] |
559 | public string pCertificatePathParameter { get; set; } | 559 | public string pCertificatePathParameter { get; set; } |
560 | 560 | ||
... | @@ -585,6 +585,7 @@ namespace LIL_VSTT_Plugins | ... | @@ -585,6 +585,7 @@ namespace LIL_VSTT_Plugins |
585 | 585 | ||
586 | private bool haveCert = false; | 586 | private bool haveCert = false; |
587 | private X509Certificate myClientCert; | 587 | private X509Certificate myClientCert; |
588 | private X509Certificate2 myClientCertAndKey; | ||
588 | 589 | ||
589 | public override void PreWebTest(object sender, PreWebTestEventArgs e) | 590 | public override void PreWebTest(object sender, PreWebTestEventArgs e) |
590 | { | 591 | { |
... | @@ -616,35 +617,114 @@ namespace LIL_VSTT_Plugins | ... | @@ -616,35 +617,114 @@ namespace LIL_VSTT_Plugins |
616 | return; | 617 | return; |
617 | } | 618 | } |
618 | 619 | ||
619 | try { | 620 | // Check what type of container we have. All files are treated as PEM unless the extension is .pfx or .p12 |
620 | myClientCert = new X509Certificate(certPath, certPass); | 621 | // Read certificate and private key depending on type |
621 | } catch (Exception ex) | 622 | if (certPath.ToLower().EndsWith(".pfx") || certPath.ToLower().EndsWith(".p12")) |
622 | { | 623 | { |
623 | e.WebTest.AddCommentToResult("Error during loading of certificate: " + certPath + " Message: " + ex.Message); | 624 | if (pDebug) e.WebTest.AddCommentToResult("Certificate file is treated as PFX/PKCS12"); |
624 | return; | 625 | try |
626 | { | ||
627 | myClientCertAndKey = new X509Certificate2(certPath, certPass, X509KeyStorageFlags.PersistKeySet); | ||
628 | myClientCert = new X509Certificate(myClientCertAndKey); | ||
629 | } | ||
630 | catch (Exception ex) | ||
631 | { | ||
632 | e.WebTest.AddCommentToResult("Error during loading of certificate: " + certPath + " Message: " + ex.Message); | ||
633 | return; | ||
634 | } | ||
635 | } else | ||
636 | { | ||
637 | if (pDebug) e.WebTest.AddCommentToResult("Certificate file is treated as PEM/PKCS8"); | ||
638 | |||
639 | // Use Bouncy Castle to read the certificate and key, then convert to .NET X509Certificate2 and X509Certificate | ||
640 | String text; | ||
641 | try { | ||
642 | text = File.ReadAllText(certPath); | ||
643 | } catch (Exception ex) | ||
644 | { | ||
645 | e.WebTest.AddCommentToResult("Error opening PEM file: " + certPath + " Message: " + ex.Message); | ||
646 | return; | ||
647 | } | ||
648 | |||
649 | // Find the key and certificate in file and load them | ||
650 | int keyTextBeginPos = text.IndexOf("-----BEGIN"); | ||
651 | int keyTextEndPos = text.IndexOf("-----END"); | ||
652 | Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair bcKey = null; | ||
653 | Org.BouncyCastle.X509.X509Certificate bcCert = null; | ||
654 | |||
655 | while (keyTextBeginPos != -1) | ||
656 | { | ||
657 | text = text.Substring(keyTextBeginPos); | ||
658 | object obj; | ||
659 | try | ||
660 | { | ||
661 | Org.BouncyCastle.OpenSsl.PemReader bc = new Org.BouncyCastle.OpenSsl.PemReader(new StringReader(text), new PasswordHelper(certPass)); | ||
662 | obj = bc.ReadObject(); | ||
663 | } | ||
664 | catch (Org.BouncyCastle.Crypto.InvalidCipherTextException ex) | ||
665 | { | ||
666 | e.WebTest.AddCommentToResult("Error during reading of PEM file: " + certPath + " Wrong password? Message: " + ex.Message); | ||
667 | return; | ||
668 | } | ||
669 | catch (Exception ex) | ||
670 | { | ||
671 | e.WebTest.AddCommentToResult("Error during reading of PEM file: " + certPath + " Message: " + ex.GetType().FullName + ":" + ex.Message); | ||
672 | return; | ||
673 | } | ||
674 | if (obj != null) | ||
675 | { | ||
676 | if (pDebug) e.WebTest.AddCommentToResult("Found PEM Object of type " + obj.GetType().FullName); | ||
677 | if(obj is Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair) | ||
678 | { | ||
679 | // We have a keypair | ||
680 | bcKey = (Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair)obj; | ||
681 | } | ||
682 | if(obj is Org.BouncyCastle.X509.X509Certificate) | ||
683 | { | ||
684 | // We have a certificate | ||
685 | bcCert = (Org.BouncyCastle.X509.X509Certificate)obj; | ||
686 | } | ||
687 | } | ||
688 | keyTextBeginPos = text.IndexOf("-----BEGIN", keyTextEndPos); | ||
689 | } | ||
690 | if (bcKey == null || bcCert == null) | ||
691 | { | ||
692 | e.WebTest.AddCommentToResult("Error: PEM file has to contain both certificate and private key"); | ||
693 | return; | ||
694 | } | ||
695 | try { | ||
696 | myClientCert = Org.BouncyCastle.Security.DotNetUtilities.ToX509Certificate(bcCert); | ||
697 | myClientCertAndKey = new X509Certificate2(myClientCert); | ||
698 | myClientCertAndKey.PrivateKey = Org.BouncyCastle.Security.DotNetUtilities.ToRSA(bcKey.Private as Org.BouncyCastle.Crypto.Parameters.RsaPrivateCrtKeyParameters); | ||
699 | } catch (Exception ex) | ||
700 | { | ||
701 | e.WebTest.AddCommentToResult("Error during loading of PEM file: " + certPath + " Message: " + ex.Message); | ||
702 | return; | ||
703 | } | ||
625 | } | 704 | } |
626 | 705 | ||
627 | if(myClientCert == null) { | 706 | // Check that we have a certificate |
707 | if (myClientCert == null) | ||
708 | { | ||
628 | if (pDebug) e.WebTest.AddCommentToResult("Certificate File " + certPath + " could not be loaded."); | 709 | if (pDebug) e.WebTest.AddCommentToResult("Certificate File " + certPath + " could not be loaded."); |
629 | return; | 710 | return; |
630 | } else | 711 | } |
712 | else | ||
631 | { | 713 | { |
632 | if (pDebug) e.WebTest.AddCommentToResult("Certificate File " + certPath); | 714 | if (pDebug) e.WebTest.AddCommentToResult("Certificate File " + certPath + " loaded successfully."); |
633 | } | 715 | } |
634 | 716 | ||
717 | // Check that it seems okey | ||
635 | if (string.IsNullOrWhiteSpace(myClientCert.GetCertHashString())) | 718 | if (string.IsNullOrWhiteSpace(myClientCert.GetCertHashString())) |
636 | { | 719 | { |
637 | if (pDebug) e.WebTest.AddCommentToResult("Certificate File " + certPath + " contains no SHA1 hash. Not using it."); | 720 | if (pDebug) e.WebTest.AddCommentToResult("Certificate File " + certPath + " contains no SHA1 hash. Not using it."); |
638 | return; | 721 | return; |
639 | } | 722 | } |
640 | |||
641 | if (pDebug) e.WebTest.AddCommentToResult("Loaded client certificate for Subject: [" + myClientCert.Subject + "] Issued by: [" + myClientCert.Issuer + "] Expires: [" + myClientCert.GetExpirationDateString() + "]"); | 723 | if (pDebug) e.WebTest.AddCommentToResult("Loaded client certificate for Subject: [" + myClientCert.Subject + "] Issued by: [" + myClientCert.Issuer + "] Expires: [" + myClientCert.GetExpirationDateString() + "]"); |
642 | 724 | ||
643 | // Check if the certificate is trusted (i.e. chain can be validated) | 725 | // Check if the certificate is trusted (i.e. chain can be validated) |
644 | bool myCertTrusted = false; | 726 | bool myCertTrusted = false; |
645 | X509Certificate2 cer = new X509Certificate2(certPath, certPass, X509KeyStorageFlags.PersistKeySet); | 727 | if (myClientCertAndKey.Verify()) |
646 | cer.FriendlyName = "VSTT"; | ||
647 | if (cer.Verify()) | ||
648 | { | 728 | { |
649 | if (pDebug) e.WebTest.AddCommentToResult("Certificate is TRUSTED"); | 729 | if (pDebug) e.WebTest.AddCommentToResult("Certificate is TRUSTED"); |
650 | myCertTrusted = true; | 730 | myCertTrusted = true; |
... | @@ -654,19 +734,20 @@ namespace LIL_VSTT_Plugins | ... | @@ -654,19 +734,20 @@ namespace LIL_VSTT_Plugins |
654 | myCertTrusted = false; | 734 | myCertTrusted = false; |
655 | } | 735 | } |
656 | 736 | ||
657 | if(cer.NotAfter < DateTime.Now) | 737 | // Check if it is expired or about to expire |
738 | if(myClientCertAndKey.NotAfter < DateTime.Now) | ||
658 | { | 739 | { |
659 | e.WebTest.AddCommentToResult("Warning: Client Certificate has expired. Might not be trusted on server. Expired " + cer.NotAfter.ToString()); | 740 | e.WebTest.AddCommentToResult("Warning: Client Certificate has expired. Might not be trusted on server. Expired " + myClientCertAndKey.NotAfter.ToString()); |
660 | } else if (cer.NotBefore > DateTime.Now) | 741 | } else if (myClientCertAndKey.NotBefore > DateTime.Now) |
661 | { | 742 | { |
662 | e.WebTest.AddCommentToResult("Warning: Client Certificate is not valid yet. Might not be trusted on server. Valid " + cer.NotBefore.ToString()); | 743 | e.WebTest.AddCommentToResult("Warning: Client Certificate is not valid yet. Might not be trusted on server. Valid " + myClientCertAndKey.NotBefore.ToString()); |
663 | } else if (cer.NotAfter < DateTime.Now.AddDays(14)) | 744 | } else if (myClientCertAndKey.NotAfter < DateTime.Now.AddDays(14)) |
664 | { | 745 | { |
665 | e.WebTest.AddCommentToResult("Warning: Client Certificate will expire in less than 14 days. Better renew it soon. Expires " + cer.NotAfter.ToString()); | 746 | e.WebTest.AddCommentToResult("Warning: Client Certificate will expire in less than 14 days. Better renew it soon. Expires " + myClientCertAndKey.NotAfter.ToString()); |
666 | } | 747 | } |
667 | 748 | ||
668 | // Check if we have a private key | 749 | // Check if we have a private key |
669 | if (!cer.HasPrivateKey) | 750 | if (!myClientCertAndKey.HasPrivateKey) |
670 | { | 751 | { |
671 | // Cant use it without private key | 752 | // Cant use it without private key |
672 | e.WebTest.AddCommentToResult("Error: Certificate HAS NO PRIVATE KEY, cannot use it without one."); | 753 | e.WebTest.AddCommentToResult("Error: Certificate HAS NO PRIVATE KEY, cannot use it without one."); |
... | @@ -679,8 +760,8 @@ namespace LIL_VSTT_Plugins | ... | @@ -679,8 +760,8 @@ namespace LIL_VSTT_Plugins |
679 | // Check that the certificate exists in the cert store | 760 | // Check that the certificate exists in the cert store |
680 | X509Store cuStore = new X509Store(); | 761 | X509Store cuStore = new X509Store(); |
681 | cuStore.Open(OpenFlags.ReadWrite); | 762 | cuStore.Open(OpenFlags.ReadWrite); |
682 | if(cuStore.Certificates.Contains(cer)) { | 763 | if(cuStore.Certificates.Contains(myClientCertAndKey)) { |
683 | if (pDebug) e.WebTest.AddCommentToResult("Certificate is INSTALLED"); | 764 | if (pDebug) e.WebTest.AddCommentToResult("Certificate already INSTALLED in Current User Windows Certificate Store"); |
684 | } else | 765 | } else |
685 | { | 766 | { |
686 | if (pDebug) e.WebTest.AddCommentToResult("Certificate is NOT INSTALLED"); | 767 | if (pDebug) e.WebTest.AddCommentToResult("Certificate is NOT INSTALLED"); |
... | @@ -691,11 +772,12 @@ namespace LIL_VSTT_Plugins | ... | @@ -691,11 +772,12 @@ namespace LIL_VSTT_Plugins |
691 | { | 772 | { |
692 | // Install in user store | 773 | // Install in user store |
693 | try { | 774 | try { |
694 | cuStore.Add(cer); | 775 | myClientCertAndKey.FriendlyName = "VSTT"; |
695 | if (pDebug) e.WebTest.AddCommentToResult("Certificate HAS BEEN INSTALLED in the Windows Certificate Store"); | 776 | cuStore.Add(myClientCertAndKey); |
777 | if (pDebug) e.WebTest.AddCommentToResult("Certificate HAS BEEN INSTALLED in the Current User Windows Certificate Store with Friendly Name: VSTT"); | ||
696 | } catch (Exception ex) | 778 | } catch (Exception ex) |
697 | { | 779 | { |
698 | e.WebTest.AddCommentToResult("Error: COULD NOT INSTALL in the Windows Certificate Store, Message: " + ex.Message); | 780 | e.WebTest.AddCommentToResult("Error: COULD NOT INSTALL in the Current User Windows Certificate Store, Message: " + ex.Message); |
699 | return; | 781 | return; |
700 | } | 782 | } |
701 | } | 783 | } |
... | @@ -724,6 +806,19 @@ namespace LIL_VSTT_Plugins | ... | @@ -724,6 +806,19 @@ namespace LIL_VSTT_Plugins |
724 | } | 806 | } |
725 | } | 807 | } |
726 | 808 | ||
809 | public class PasswordHelper : Org.BouncyCastle.OpenSsl.IPasswordFinder | ||
810 | { | ||
811 | private string pwd = ""; | ||
812 | public PasswordHelper(String password) | ||
813 | { | ||
814 | this.pwd = password; | ||
815 | } | ||
816 | public char[] GetPassword() | ||
817 | { | ||
818 | return pwd.ToArray(); | ||
819 | } | ||
820 | } | ||
821 | |||
727 | /* | 822 | /* |
728 | /// <summary> | 823 | /// <summary> |
729 | /// WebTest Plugin Template | 824 | /// WebTest Plugin Template | ... | ... |
Notes.md
0 → 100644
... | @@ -43,7 +43,10 @@ | ... | @@ -43,7 +43,10 @@ |
43 | </Request> | 43 | </Request> |
44 | </Items> | 44 | </Items> |
45 | <ContextParameters> | 45 | <ContextParameters> |
46 | <ContextParameter Name="CertFile" Value="U:\projekt\MjukaCertifikat\Interna certifikat_2016\P12\Auth - FN10007 EN10007.p12" /> | 46 | <ContextParameter Name="PEM-Key" Value="U:\projekt\MjukaCertifikat\Interna certifikat_2016\PEM\Auth_FN10007_EN10007-key.pem" /> |
47 | <ContextParameter Name="PEM-Cert" Value="U:\projekt\MjukaCertifikat\Interna certifikat_2016\PEM\Auth_FN10007_EN10007-cert.pem" /> | ||
48 | <ContextParameter Name="PEM" Value="U:\projekt\MjukaCertifikat\Interna certifikat_2016\PEM\Auth_FN10007_EN10007.pem" /> | ||
49 | <ContextParameter Name="PFX" Value="U:\projekt\MjukaCertifikat\Interna certifikat_2016\P12\Auth - FN10007 EN10007.p12" /> | ||
47 | </ContextParameters> | 50 | </ContextParameters> |
48 | <ValidationRules> | 51 | <ValidationRules> |
49 | <ValidationRule Classname="Microsoft.VisualStudio.TestTools.WebTesting.Rules.ValidateResponseUrl, Microsoft.VisualStudio.QualityTools.WebTestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" DisplayName="Response URL" Description="Validates that the response URL after redirects are followed is the same as the recorded response URL. QueryString parameters are ignored." Level="Low" ExectuionOrder="BeforeDependents" /> | 52 | <ValidationRule Classname="Microsoft.VisualStudio.TestTools.WebTesting.Rules.ValidateResponseUrl, Microsoft.VisualStudio.QualityTools.WebTestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" DisplayName="Response URL" Description="Validates that the response URL after redirects are followed is the same as the recorded response URL. QueryString parameters are ignored." Level="Low" ExectuionOrder="BeforeDependents" /> |
... | @@ -68,10 +71,10 @@ | ... | @@ -68,10 +71,10 @@ |
68 | <WebTestPlugin Classname="LIL_VSTT_Plugins.ClientCertificatePlugin, LIL_VSTT_Plugins, Version=1.3.0.0, Culture=neutral, PublicKeyToken=null" DisplayName="Client Certificate" Description="(C) Copyright 2016 LIGHTS IN LINE AB
Sätter webtestet att använda ett specifikt client cert för SSL. Certifikatet behöver inte installeras i certstore först."> | 71 | <WebTestPlugin Classname="LIL_VSTT_Plugins.ClientCertificatePlugin, LIL_VSTT_Plugins, Version=1.3.0.0, Culture=neutral, PublicKeyToken=null" DisplayName="Client Certificate" Description="(C) Copyright 2016 LIGHTS IN LINE AB
Sätter webtestet att använda ett specifikt client cert för SSL. Certifikatet behöver inte installeras i certstore först."> |
69 | <RuleParameters> | 72 | <RuleParameters> |
70 | <RuleParameter Name="pCertificatePath" Value="" /> | 73 | <RuleParameter Name="pCertificatePath" Value="" /> |
71 | <RuleParameter Name="pCertificatePathParameter" Value="CertFile" /> | 74 | <RuleParameter Name="pCertificatePathParameter" Value="PEM" /> |
72 | <RuleParameter Name="pCertificatePassword" Value="abcd1234" /> | 75 | <RuleParameter Name="pCertificatePassword" Value="abcd1234" /> |
73 | <RuleParameter Name="pCertificatePasswordParameter" Value="" /> | 76 | <RuleParameter Name="pCertificatePasswordParameter" Value="" /> |
74 | <RuleParameter Name="pDebug" Value="False" /> | 77 | <RuleParameter Name="pDebug" Value="True" /> |
75 | <RuleParameter Name="pInstallTrusted" Value="True" /> | 78 | <RuleParameter Name="pInstallTrusted" Value="True" /> |
76 | <RuleParameter Name="pInstallUntrusted" Value="True" /> | 79 | <RuleParameter Name="pInstallUntrusted" Value="True" /> |
77 | </RuleParameters> | 80 | </RuleParameters> | ... | ... |
1 | #if !(NETCF_1_0 || SILVERLIGHT || PORTABLE) | ||
2 | |||
3 | using System; | 1 | using System; |
4 | using System.Security.Cryptography; | 2 | using System.Security.Cryptography; |
5 | using SystemX509 = System.Security.Cryptography.X509Certificates; | 3 | using SystemX509 = System.Security.Cryptography.X509Certificates; |
... | @@ -242,4 +240,3 @@ namespace Org.BouncyCastle.Security | ... | @@ -242,4 +240,3 @@ namespace Org.BouncyCastle.Security |
242 | } | 240 | } |
243 | } | 241 | } |
244 | 242 | ||
245 | #endif | ... | ... |
-
Please register or sign in to post a comment