diff --git a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs index 4b95c19e..efff22ef 100644 --- a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs +++ b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs @@ -27,6 +27,7 @@ using System.Collections.Generic; using System.Collections; using IEC61850.Common; +using IEC61850.TLS; /// /// IEC 61850 API for the libiec61850 .NET wrapper library @@ -558,9 +559,6 @@ namespace IEC61850 /// public class IedServer { - [DllImport ("iec61850", CallingConvention=CallingConvention.Cdecl)] - static extern IntPtr IedServer_create(IntPtr modelRef); - [DllImport ("iec61850", CallingConvention=CallingConvention.Cdecl)] static extern IntPtr IedServer_createWithConfig(IntPtr modelRef, IntPtr tlsConfiguration, IntPtr serverConfiguratio); @@ -789,19 +787,30 @@ namespace IEC61850 private Dictionary clientConnections = new Dictionary (); - public IedServer(IedModel iedModel) + + + public IedServer(IedModel iedModel, IedServerConfig config = null) { - self = IedServer_create(iedModel.self); + IntPtr nativeConfig = IntPtr.Zero; + + if (config != null) + nativeConfig = config.self; + + self = IedServer_createWithConfig (iedModel.self, IntPtr.Zero, nativeConfig); } - public IedServer(IedModel iedModel, IedServerConfig config) + public IedServer(IedModel iedModel, TLSConfiguration tlsConfig, IedServerConfig config = null) { IntPtr nativeConfig = IntPtr.Zero; + IntPtr nativeTLSConfig = IntPtr.Zero; if (config != null) nativeConfig = config.self; - self = IedServer_createWithConfig (iedModel.self, IntPtr.Zero, nativeConfig); + if (tlsConfig != null) + nativeTLSConfig = tlsConfig.GetNativeInstance (); + + self = IedServer_createWithConfig (iedModel.self, nativeTLSConfig, nativeConfig); } // causes undefined behavior @@ -850,7 +859,7 @@ namespace IEC61850 /// Start MMS server public void Start () { - Start(102); + Start(-1); } /// diff --git a/dotnet/IEC61850forCSharp/TLS.cs b/dotnet/IEC61850forCSharp/TLS.cs index b7d67dc0..69a273b0 100644 --- a/dotnet/IEC61850forCSharp/TLS.cs +++ b/dotnet/IEC61850forCSharp/TLS.cs @@ -139,7 +139,6 @@ namespace IEC61850 public void SetOwnCertificate(string filename) { if (TLSConfiguration_setOwnCertificateFromFile (self, filename) == false) { - Console.WriteLine ("Failed to read certificate from file!"); throw new CryptographicException ("Failed to read certificate from file"); } } @@ -149,7 +148,6 @@ namespace IEC61850 byte[] certBytes = cert.GetRawCertData (); if (TLSConfiguration_setOwnCertificate (self, certBytes, certBytes.Length) == false) { - Console.WriteLine ("Failed to set certificate!"); throw new CryptographicException ("Failed to set certificate"); } } @@ -157,7 +155,6 @@ namespace IEC61850 public void AddAllowedCertificate(string filename) { if (TLSConfiguration_addAllowedCertificateFromFile (self, filename) == false) { - Console.WriteLine ("Failed to read allowed certificate from file!"); throw new CryptographicException ("Failed to read allowed certificate from file"); } } @@ -167,7 +164,6 @@ namespace IEC61850 byte[] certBytes = cert.GetRawCertData (); if (TLSConfiguration_addAllowedCertificate (self, certBytes, certBytes.Length) == false) { - Console.WriteLine ("Failed to add allowed certificate!"); throw new CryptographicException ("Failed to add allowed certificate"); } } @@ -175,7 +171,6 @@ namespace IEC61850 public void AddCACertificate(string filename) { if (TLSConfiguration_addCACertificateFromFile (self, filename) == false) { - Console.WriteLine ("Failed to read CA certificate from file!"); throw new CryptographicException ("Failed to read CA certificate from file"); } } @@ -185,7 +180,6 @@ namespace IEC61850 byte[] certBytes = cert.GetRawCertData (); if (TLSConfiguration_addCACertificate (self, certBytes, certBytes.Length) == false) { - Console.WriteLine ("Failed to add CA certificate!"); throw new CryptographicException ("Failed to add CA certificate"); } } @@ -193,7 +187,6 @@ namespace IEC61850 public void SetOwnKey (string filename, string password) { if (TLSConfiguration_setOwnKeyFromFile (self, filename, password) == false) { - Console.WriteLine ("Failed to read own key from file!"); throw new CryptographicException ("Failed to read own key from file"); } } @@ -203,7 +196,6 @@ namespace IEC61850 byte[] certBytes = key.Export (X509ContentType.Pkcs12); if (TLSConfiguration_setOwnKey (self, certBytes, certBytes.Length, password) == false) { - Console.WriteLine ("Failed to set own key!"); throw new CryptographicException ("Failed to set own key"); } } diff --git a/dotnet/dotnet.sln b/dotnet/dotnet.sln index 067ccaf9..ba1be6f4 100644 --- a/dotnet/dotnet.sln +++ b/dotnet/dotnet.sln @@ -42,6 +42,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "goose_subscriber", "goose_s EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "sv_subscriber", "sv_subscriber\sv_subscriber.csproj", "{44651D2D-3252-4FD5-8B8B-5552DBE1B499}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "tls_server_example", "tls_server_example\tls_server_example.csproj", "{B63F7A81-1D3A-4F2F-A7C2-D6F77E5BD307}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -96,6 +98,10 @@ Global {9E29B4CE-EE5F-4CA6-85F6-5D1FF8B27BF8}.Debug|Any CPU.Build.0 = Debug|Any CPU {9E29B4CE-EE5F-4CA6-85F6-5D1FF8B27BF8}.Release|Any CPU.ActiveCfg = Release|Any CPU {9E29B4CE-EE5F-4CA6-85F6-5D1FF8B27BF8}.Release|Any CPU.Build.0 = Release|Any CPU + {B63F7A81-1D3A-4F2F-A7C2-D6F77E5BD307}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B63F7A81-1D3A-4F2F-A7C2-D6F77E5BD307}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B63F7A81-1D3A-4F2F-A7C2-D6F77E5BD307}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B63F7A81-1D3A-4F2F-A7C2-D6F77E5BD307}.Release|Any CPU.Build.0 = Release|Any CPU {C351CFA4-E54E-49A1-86CE-69643535541A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C351CFA4-E54E-49A1-86CE-69643535541A}.Debug|Any CPU.Build.0 = Debug|Any CPU {C351CFA4-E54E-49A1-86CE-69643535541A}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/dotnet/tls_server_example/Program.cs b/dotnet/tls_server_example/Program.cs new file mode 100644 index 00000000..e7aa0cfc --- /dev/null +++ b/dotnet/tls_server_example/Program.cs @@ -0,0 +1,85 @@ +using System; +using System.Threading; +using System.Security.Cryptography.X509Certificates; + +using IEC61850.Server; +using IEC61850.Common; +using IEC61850.TLS; + +namespace tls_server_example +{ + class MainClass + { + public static void Main (string[] args) + { + bool running = true; + + /* run until Ctrl-C is pressed */ + Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) { + e.Cancel = true; + running = false; + }; + + IedModel iedModel = ConfigFileParser.CreateModelFromConfigFile ("model.cfg"); + + if (iedModel == null) { + Console.WriteLine ("No valid data model found!"); + return; + } + + DataObject spcso1 = (DataObject)iedModel.GetModelNodeByShortObjectReference ("GenericIO/GGIO1.SPCSO1"); + + TLSConfiguration tlsConfig = new TLSConfiguration (); + + tlsConfig.SetOwnCertificate (new X509Certificate2 ("server.cer")); + + tlsConfig.SetOwnKey ("server-key.pem", null); + + // Add a CA certificate to check the certificate provided by the server - not required when ChainValidation == false + tlsConfig.AddCACertificate (new X509Certificate2 ("root.cer")); + + // Check if the certificate is signed by a provided CA + tlsConfig.ChainValidation = true; + + // Check that the shown server certificate is in the list of allowed certificates + tlsConfig.AllowOnlyKnownCertificates = false; + + IedServer iedServer = new IedServer (iedModel, tlsConfig); + + iedServer.SetControlHandler (spcso1, delegate(DataObject controlObject, object parameter, MmsValue ctlVal, bool test) { + bool val = ctlVal.GetBoolean(); + + if (val) + Console.WriteLine("received binary control command: on"); + else + Console.WriteLine("received binary control command: off"); + + return ControlHandlerResult.OK; + }, null); + + iedServer.Start (); + Console.WriteLine ("Server started"); + + GC.Collect (); + + DataObject ggio1AnIn1 = (DataObject)iedModel.GetModelNodeByShortObjectReference ("GenericIO/GGIO1.AnIn1"); + + DataAttribute ggio1AnIn1magF = (DataAttribute)ggio1AnIn1.GetChild ("mag.f"); + DataAttribute ggio1AnIn1T = (DataAttribute)ggio1AnIn1.GetChild ("t"); + + float floatVal = 1.0f; + + while (running) { + floatVal += 1f; + iedServer.UpdateTimestampAttributeValue (ggio1AnIn1T, new Timestamp (DateTime.Now)); + iedServer.UpdateFloatAttributeValue (ggio1AnIn1magF, floatVal); + Thread.Sleep (100); + } + + iedServer.Stop (); + Console.WriteLine ("Server stopped"); + + iedServer.Destroy (); + } + } +} diff --git a/dotnet/tls_server_example/Properties/AssemblyInfo.cs b/dotnet/tls_server_example/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..638df635 --- /dev/null +++ b/dotnet/tls_server_example/Properties/AssemblyInfo.cs @@ -0,0 +1,27 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle ("tls_server_example")] +[assembly: AssemblyDescription ("")] +[assembly: AssemblyConfiguration ("")] +[assembly: AssemblyCompany ("")] +[assembly: AssemblyProduct ("")] +[assembly: AssemblyCopyright ("mzillgit")] +[assembly: AssemblyTrademark ("")] +[assembly: AssemblyCulture ("")] + +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. + +[assembly: AssemblyVersion ("1.0.*")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] + diff --git a/dotnet/tls_server_example/model.cfg b/dotnet/tls_server_example/model.cfg new file mode 100644 index 00000000..6f332425 --- /dev/null +++ b/dotnet/tls_server_example/model.cfg @@ -0,0 +1,237 @@ +MODEL(simpleIO){ +LD(GenericIO){ +LN(LLN0){ +DO(Mod 0){ +DA(stVal 0 12 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +DA(ctlModel 0 12 4 0 0)=0; +} +DO(Beh 0){ +DA(stVal 0 12 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +} +DO(Health 0){ +DA(stVal 0 3 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +} +DO(NamPlt 0){ +DA(vendor 0 20 5 0 0); +DA(swRev 0 20 5 0 0); +DA(d 0 20 5 0 0); +DA(configRev 0 20 5 0 0); +DA(ldNs 0 20 11 0 0); +} +DS(Events){ +DE(GGIO1$ST$SPCSO1$stVal); +DE(GGIO1$ST$SPCSO2$stVal); +DE(GGIO1$ST$SPCSO3$stVal); +DE(GGIO1$ST$SPCSO4$stVal); +} +DS(AnalogValues){ +DE(GGIO1$MX$AnIn1); +DE(GGIO1$MX$AnIn2); +DE(GGIO1$MX$AnIn3); +DE(GGIO1$MX$AnIn4); +} +RC(EventsRCB01 Events 0 Events 1 24 111 50 1000); +RC(AnalogValuesRCB01 AnalogValues 0 AnalogValues 1 24 111 50 1000); +LC(EventLog Events GenericIO/LLN0$EventLog 19 0 0 1); +LC(GeneralLog - - 19 0 0 1); +LOG(GeneralLog); +LOG(EventLog); +GC(gcbEvents events Events 2 0 -1 -1 ){ +PA(4 273 4096 010ccd010001); +} +GC(gcbAnalogValues analog AnalogValues 2 0 -1 -1 ){ +PA(4 273 4096 010ccd010001); +} +} +LN(LPHD1){ +DO(PhyNam 0){ +DA(vendor 0 20 5 0 0); +} +DO(PhyHealth 0){ +DA(stVal 0 3 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +} +DO(Proxy 0){ +DA(stVal 0 0 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +} +} +LN(GGIO1){ +DO(Mod 0){ +DA(stVal 0 12 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +DA(ctlModel 0 12 4 0 0)=0; +} +DO(Beh 0){ +DA(stVal 0 12 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +} +DO(Health 0){ +DA(stVal 0 3 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +} +DO(NamPlt 0){ +DA(vendor 0 20 5 0 0); +DA(swRev 0 20 5 0 0); +DA(d 0 20 5 0 0); +} +DO(AnIn1 0){ +DA(mag 0 27 1 1 0){ +DA(f 0 10 1 1 0); +} +DA(q 0 23 1 2 0); +DA(t 0 22 1 0 0); +} +DO(AnIn2 0){ +DA(mag 0 27 1 1 101){ +DA(f 0 10 1 1 0); +} +DA(q 0 23 1 2 0); +DA(t 0 22 1 0 102); +} +DO(AnIn3 0){ +DA(mag 0 27 1 1 0){ +DA(f 0 10 1 1 0); +} +DA(q 0 23 1 2 0); +DA(t 0 22 1 0 0); +} +DO(AnIn4 0){ +DA(mag 0 27 1 1 0){ +DA(f 0 10 1 1 0); +} +DA(q 0 23 1 2 0); +DA(t 0 22 1 0 0); +} +DO(SPCSO1 0){ +DA(stVal 0 0 0 1 0); +DA(q 0 23 0 2 0); +DA(Oper 0 27 12 0 0){ +DA(ctlVal 0 0 12 0 0); +DA(origin 0 27 12 0 0){ +DA(orCat 0 12 12 0 0); +DA(orIdent 0 13 12 0 0); +} +DA(ctlNum 0 6 12 0 0); +DA(T 0 22 12 0 0); +DA(Test 0 0 12 0 0); +DA(Check 0 24 12 0 0); +} +DA(ctlModel 0 12 4 0 0)=1; +DA(t 0 22 0 0 0); +} +DO(SPCSO2 0){ +DA(stVal 0 0 0 1 0); +DA(q 0 23 0 2 0); +DA(Oper 0 27 12 0 0){ +DA(ctlVal 0 0 12 0 0); +DA(origin 0 27 12 0 0){ +DA(orCat 0 12 12 0 0); +DA(orIdent 0 13 12 0 0); +} +DA(ctlNum 0 6 12 0 0); +DA(T 0 22 12 0 0); +DA(Test 0 0 12 0 0); +DA(Check 0 24 12 0 0); +} +DA(ctlModel 0 12 4 0 0)=1; +DA(t 0 22 0 0 0); +} +DO(SPCSO3 0){ +DA(stVal 0 0 0 1 0); +DA(q 0 23 0 2 0); +DA(Oper 0 27 12 0 0){ +DA(ctlVal 0 0 12 0 0); +DA(origin 0 27 12 0 0){ +DA(orCat 0 12 12 0 0); +DA(orIdent 0 13 12 0 0); +} +DA(ctlNum 0 6 12 0 0); +DA(T 0 22 12 0 0); +DA(Test 0 0 12 0 0); +DA(Check 0 24 12 0 0); +} +DA(ctlModel 0 12 4 0 0)=1; +DA(t 0 22 0 0 0); +} +DO(SPCSO4 0){ +DA(stVal 0 0 0 1 0); +DA(q 0 23 0 2 0); +DA(Oper 0 27 12 0 0){ +DA(ctlVal 0 0 12 0 0); +DA(origin 0 27 12 0 0){ +DA(orCat 0 12 12 0 0); +DA(orIdent 0 13 12 0 0); +} +DA(ctlNum 0 6 12 0 0); +DA(T 0 22 12 0 0); +DA(Test 0 0 12 0 0); +DA(Check 0 24 12 0 0); +} +DA(ctlModel 0 12 4 0 0)=1; +DA(t 0 22 0 0 0); +} +DO(Ind1 0){ +DA(stVal 0 0 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +} +DO(Ind2 0){ +DA(stVal 0 0 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +} +DO(Ind3 0){ +DA(stVal 0 0 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +} +DO(Ind4 0){ +DA(stVal 0 0 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +} +} +LN(PDUP1){ +DO(Beh 0){ +DA(stVal 0 12 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +} +DO(Mod 0){ +DA(stVal 0 12 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +DA(ctlModel 0 12 4 0 0)=0; +} +DO(Str 0){ +DA(general 0 0 0 1 0); +DA(dirGeneral 0 12 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +} +DO(Op 0){ +DA(general 0 0 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +} +DO(OpDlTmms 0){ +DA(setVal 0 3 2 1 0); +} +DO(RsDlTmms 0){ +DA(setVal 0 3 2 1 0); +} +} +} +} diff --git a/dotnet/tls_server_example/root.cer b/dotnet/tls_server_example/root.cer new file mode 100644 index 00000000..87683444 Binary files /dev/null and b/dotnet/tls_server_example/root.cer differ diff --git a/dotnet/tls_server_example/server-key.pem b/dotnet/tls_server_example/server-key.pem new file mode 100644 index 00000000..6fe35295 --- /dev/null +++ b/dotnet/tls_server_example/server-key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAu3Fjxb904UdyV/dDfvs8SFi6zJeNoMYjmpXm/LBcFSH3zGoK +wdcMovrUTED3Cc6Ww84AYpJ5MRMPTct7DfKJkWfSnkzOPmLldTSTv3RvzGVb4NzK +QqA5aSVDqAzPiP5RnFT6Q4KWRe69TMFxpw7zMXCJx9jDggqN1oojGGkmSgYGXnFd +Nc20Mujejh5pihgwnN4Y/bPFyxJwvIMj+D8qr9klhEmXKPTiX9UFd8oLkn9JCB6+ +SiHhNyFIo+Llossn1Q2hxCGty36fAhEWzpfBTaY510VLjie9y4q9GPTwxITDqSQd +xcX8IuvrbxX0DyEK507SMmTJmB9448eF9ZCWFQIDAQABAoIBAC80BuQtqslwrKLq +adz4d93gOmp7X/c07pJnXZwU7ZuEylp3+e2Gsm/4qq3pTkzx8ZWtsvsf19U774av +z3VbtrkfZDLpNKcRUKeLbgmw0NawT8r4zxaoMsz/zWHsl/bv1K2B2ORXZnCGBrXl +oTFo2mWA6bGiLNn6vm1grCXhlPreywyG/kFK3pi2VvkpvG3XZSI7mmZ0Dq/MD3nO +03oOZBqwwnMObfQQdhKE7646/+KgeuF/JsXaUH4bkHmtzYWyocWYMqpC0hjpNWlQ +cKuQ7t1kfmpsGD9aNW4+ND2ok9BdxIiC+rPXS9NDqZxoWLp+a8seU++uqk1l8RPq +tPE3LqECgYEAz1NmemNLiUsKvyemUvjp8+dJupzWtdV7fsnCbYhj/5gDA2UhFKCf +dP9xiHCdNe0797oAqHY7c3JhS4ug8haDy9aDIu5GG2DNYzjX/oYm4ywbCdRx+uEN +RcTw69FjSYVGkObmxWYszwsFybRasV6PYamg65qYR3FlvW2Td4Fndy8CgYEA53L/ +zHtBRQiNGJU9jfMHeX0bTtXIAt622Qn78jw0it/rhXWi2RwG2Cw5Q2aPRJ6uMt9F +yk1+GAPZcwYqwjq/nKRrl71Tn+KDWIk5rz1fNYRkaXtnMLs2MOogqoDTBshW0QBq +tnPrFNsaLKX6V92Az69wHjd2uwvLQLTvS/EuNfsCgYEAr3to/uhytAd3VirKRep3 +o0E+D5zWw1upxrwhPDK4aUuSKVp8sIfvz8iyoQiomE9vdZPTIMPKOEI1BgtuM9pI +vcyYfIVvg5bg4T3o3H9SBPB9BknyG6ZHZKl4PjGht0X+X4GBDM4Z2Tj8Mijcpsph +1AkOsrzMbZQWyEoqCnnWSHMCgYAFEHUcak4BTrCXqxxPsNOnCt/AF9lqhqkFkrxa +joqvxzqGDw7jJUPZEw6ltObJn5c8Mbp7NLrfl6X4aFgjK9npeYeJKHFd/DzXgRks +BnHA4Aa6cCLP5CjJZTYVxP/ZFCUiKZosJ9kq+ahW9cLGjWg2IyaW4qvMZ/OolMzv +onVaZQKBgQCir8u1vDsyA4JQXMytPHBJe27XaLRGULvteNydVB59Vt21a99o5gt1 +5B9gwWArZdZby3/KZiliNmzp8lMCrLJYjTL5WK6dbWdq92X5hCOofKPIjEcgHjhk +mvnAos3HeC83bJQtADXhw9jR7Vr6GJLM9HDcIgeIMzX7+BuqlMgaHA== +-----END RSA PRIVATE KEY----- diff --git a/dotnet/tls_server_example/server.cer b/dotnet/tls_server_example/server.cer new file mode 100644 index 00000000..957bdc30 Binary files /dev/null and b/dotnet/tls_server_example/server.cer differ diff --git a/dotnet/tls_server_example/tls_server_example.csproj b/dotnet/tls_server_example/tls_server_example.csproj new file mode 100644 index 00000000..012dd760 --- /dev/null +++ b/dotnet/tls_server_example/tls_server_example.csproj @@ -0,0 +1,58 @@ + + + + Debug + AnyCPU + {B63F7A81-1D3A-4F2F-A7C2-D6F77E5BD307} + Exe + tls_server_example + tls_server_example + v4.5 + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + true + + + full + true + bin\Release + prompt + 4 + true + + + + + + + + + + + + {C35D624E-5506-4560-8074-1728F1FA1A4D} + IEC61850.NET + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + \ No newline at end of file