Skip to content

Commit 30a8acd

Browse files
blackspherefollowerqdot
authored andcommitted
Adding an 'ignore cert errors' flag to the WSClient
This sets the ignore subject and ignore unknown certificates flags on WebSocket4Net. Unfortunatly our WebSocketServer library does not allow for a X509Chain to be provided and WebSocket4Net requires the chain to be valid (even if the root is self signed). To avoid this breaking us further, the cert generation now skips the CA and only gnerates a single self-signed cert. For upgrade purposes, any existing CA+Cert combos wil be removed. Fixes #316
1 parent e36fec9 commit 30a8acd

4 files changed

Lines changed: 136 additions & 11 deletions

File tree

Buttplug.Apps.ExampleClientGUI/ExampleClientPanel.xaml.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ private async void Connect()
6666
{
6767
if (_client != null)
6868
{
69-
await _client.Connect(new Uri(AdressTextBox.Text));
69+
await _client.Connect(new Uri(AdressTextBox.Text), true);
7070
await _client.RequestDeviceList();
7171

7272
foreach (var dev in _client.getDevices())

Buttplug.Client.Test/ButtPlugClientTests.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,5 +62,40 @@ public async void TestConnection()
6262
await client.Disconnect();
6363
server.StopServer();
6464
}
65+
66+
[Fact]
67+
public async void TestSSLConnection()
68+
{
69+
var server = new ButtplugWebsocketServer();
70+
server.StartServer(this, 12346, true, true);
71+
72+
var client = new ButtplugTestClient("Test client");
73+
await client.Connect(new Uri("wss://localhost:12346/buttplug"), true);
74+
75+
var msgId = client.nextMsgId;
76+
var res = await client.SendMsg(new Core.Messages.Test("Test string", msgId));
77+
Assert.True(res != null);
78+
Assert.True(res is Core.Messages.Test);
79+
Assert.True(((Core.Messages.Test)res).TestString == "Test string");
80+
Assert.True(((Core.Messages.Test)res).Id == msgId);
81+
82+
// Check ping is working
83+
Thread.Sleep(400);
84+
85+
msgId = client.nextMsgId;
86+
res = await client.SendMsg(new Core.Messages.Test("Test string", msgId));
87+
Assert.True(res != null);
88+
Assert.True(res is Core.Messages.Test);
89+
Assert.True(((Core.Messages.Test)res).TestString == "Test string");
90+
Assert.True(((Core.Messages.Test)res).Id == msgId);
91+
92+
Assert.True(client.nextMsgId > 4);
93+
94+
await client.RequestDeviceList();
95+
96+
// Shut it down
97+
await client.Disconnect();
98+
server.StopServer();
99+
}
65100
}
66101
}

Buttplug.Client/ButtplugWSClient.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ public ButtplugWSClient(string aClientName)
103103
Disconnect().Wait();
104104
}
105105

106-
public async Task Connect(Uri aURL)
106+
public async Task Connect(Uri aURL, bool aIgnoreSSLErrors = false)
107107
{
108108
if (_ws != null && (_ws.State == WebSocketState.Connecting || _ws.State == WebSocketState.Open))
109109
{
@@ -122,6 +122,12 @@ public async Task Connect(Uri aURL)
122122
_ws.Closed += ClosedHandler;
123123
_ws.Error += ErrorHandler;
124124

125+
if (aIgnoreSSLErrors)
126+
{
127+
_ws.Security.AllowNameMismatchCertificate = true;
128+
_ws.Security.AllowUnstrustedCertificate = true;
129+
}
130+
125131
_ws.Open();
126132

127133
_connecting = true;

Buttplug.Components.WebsocketServer/CertUtils.cs

Lines changed: 93 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,85 @@ private static X509Certificate2 GenerateSelfSignedCertificate(string subject, X5
102102
return x509;
103103
}
104104

105+
private static X509Certificate2 GenerateSelfSignedCertificate(string subject)
106+
{
107+
const int keyStrength = 2048;
108+
109+
// Generating Random Numbers
110+
var randomGenerator = new CryptoApiRandomGenerator();
111+
var random = new SecureRandom(randomGenerator);
112+
113+
// The Certificate Generator
114+
var certificateGenerator = new X509V3CertificateGenerator();
115+
116+
// Serial Number
117+
certificateGenerator.SetSerialNumber(BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(long.MaxValue), random));
118+
119+
// Subject Public Key
120+
var keyPairGenerator = new RsaKeyPairGenerator();
121+
var keyGenerationParameters = new KeyGenerationParameters(random, keyStrength);
122+
keyPairGenerator.Init(keyGenerationParameters);
123+
var subjectKeyPair = keyPairGenerator.GenerateKeyPair();
124+
certificateGenerator.SetPublicKey(subjectKeyPair.Public);
125+
126+
// Subject DN
127+
certificateGenerator.SetSubjectDN(new X509Name("CN=" + subject));
128+
129+
// Subject Alternative Name
130+
var subjectAlternativeNames = new List<Asn1Encodable>()
131+
{
132+
new GeneralName(GeneralName.DnsName, Environment.MachineName),
133+
new GeneralName(GeneralName.DnsName, "localhost"),
134+
new GeneralName(GeneralName.IPAddress, "127.0.0.1"),
135+
};
136+
137+
if (subject != "localhost" && subject != Environment.MachineName)
138+
{
139+
subjectAlternativeNames.Add(new GeneralName(GeneralName.DnsName, subject));
140+
}
141+
142+
certificateGenerator.AddExtension(X509Extensions.SubjectAlternativeName.Id, false, new DerSequence(subjectAlternativeNames.ToArray()));
143+
144+
// Issuer
145+
certificateGenerator.SetIssuerDN(new X509Name("CN=" + subject));
146+
certificateGenerator.AddExtension(X509Extensions.SubjectKeyIdentifier.Id, false, new SubjectKeyIdentifier(SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(subjectKeyPair.Public)));
147+
148+
// Valid For
149+
var notBefore = DateTime.UtcNow.Date;
150+
var notAfter = notBefore.AddYears(2);
151+
certificateGenerator.SetNotBefore(notBefore);
152+
certificateGenerator.SetNotAfter(notAfter);
153+
154+
// Add basic constraint
155+
certificateGenerator.AddExtension(X509Extensions.BasicConstraints.Id, true, new BasicConstraints(false));
156+
157+
certificateGenerator.AddExtension(X509Extensions.ExtendedKeyUsage.Id, false, new ExtendedKeyUsage(new[] { KeyPurposeID.IdKPServerAuth }));
158+
159+
// Signature Algorithm
160+
const string signatureAlgorithm = "SHA256WithRSA";
161+
var signatureFactory = new Asn1SignatureFactory(signatureAlgorithm, subjectKeyPair.Private);
162+
163+
// selfsign certificate
164+
var certificate = certificateGenerator.Generate(signatureFactory);
165+
166+
// correcponding private key
167+
var info = PrivateKeyInfoFactory.CreatePrivateKeyInfo(subjectKeyPair.Private);
168+
169+
// merge into X509Certificate2
170+
var x509 = new X509Certificate2(certificate.GetEncoded());
171+
var seq = (Asn1Sequence)Asn1Object.FromByteArray(info.ParsePrivateKey().GetDerEncoded());
172+
if (seq.Count != 9)
173+
{
174+
// throw new PemException("malformed sequence in RSA private key");
175+
}
176+
177+
var rsa = RsaPrivateKeyStructure.GetInstance(seq);
178+
var rsaparams = new RsaPrivateCrtKeyParameters(
179+
rsa.Modulus, rsa.PublicExponent, rsa.PrivateExponent, rsa.Prime1, rsa.Prime2, rsa.Exponent1, rsa.Exponent2, rsa.Coefficient);
180+
x509.PrivateKey = ToDotNetKey(rsaparams); // x509.PrivateKey = DotNetUtilities.ToRSA(rsaparams);
181+
return x509;
182+
}
183+
105184
private static AsymmetricAlgorithm ToDotNetKey(RsaPrivateCrtKeyParameters privateKey)
106185
{
107186
var cspParams = new CspParameters
@@ -186,18 +265,23 @@ public static X509Certificate2 GetCert(string app, string hostname = "localhost"
186265
var appPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), app);
187266
var caPfx = Path.Combine(appPath, "ca.pfx");
188267
var certPfx = Path.Combine(appPath, "cert.pfx");
189-
if (!File.Exists(caPfx) || !File.Exists(certPfx))
268+
269+
// Patch release rework of cert handling: our websocket server doesn't acept a chain!
270+
if (File.Exists(caPfx))
190271
{
191-
AsymmetricCipherKeyPair caKeyPair = null;
192-
var caCert = GenerateCACertificate("CN=" + app + "CA", ref caKeyPair);
193-
var clientCert = GenerateSelfSignedCertificate(hostname, caCert, caKeyPair);
194-
var p12ca = new X509Certificate2(caCert.Export(X509ContentType.Pfx), (string)null).Export(X509ContentType.Pfx);
272+
File.Delete(caPfx);
273+
if (File.Exists(certPfx))
274+
{
275+
File.Delete(certPfx);
276+
}
277+
}
278+
279+
if (!File.Exists(certPfx))
280+
{
281+
var clientCert = GenerateSelfSignedCertificate(hostname);
195282
var p12cert = clientCert.Export(X509ContentType.Pfx);
196283
Directory.CreateDirectory(appPath);
197-
var w = File.OpenWrite(caPfx);
198-
w.Write(p12ca, 0, p12ca.Length);
199-
w.Close();
200-
w = File.OpenWrite(certPfx);
284+
var w = File.OpenWrite(certPfx);
201285
w.Write(p12cert, 0, p12cert.Length);
202286
w.Close();
203287
}

0 commit comments

Comments
 (0)