Skip to content

Commit beb77ec

Browse files
authored
Merge pull request #1418 from PepperDash/fix/devtools-fixex
Fix/devtools fixex
2 parents 17adac6 + b318e7f commit beb77ec

2 files changed

Lines changed: 62 additions & 24 deletions

File tree

src/PepperDash.Core/Logging/DebugWebsocketSink.cs

Lines changed: 12 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,10 @@
1515
using Org.BouncyCastle.Crypto;
1616
using Org.BouncyCastle.Crypto.Generators;
1717
using Org.BouncyCastle.Crypto.Operators;
18-
using Org.BouncyCastle.Crypto.Parameters;
1918
using Org.BouncyCastle.Math;
2019
using Org.BouncyCastle.Pkcs;
2120
using Org.BouncyCastle.Security;
2221
using Org.BouncyCastle.X509;
23-
using System.Security.Cryptography;
2422
using Serilog.Formatting;
2523
using Serilog.Formatting.Json;
2624

@@ -244,9 +242,10 @@ private static X509Certificate2 LoadOrRecreateCert(string certPath, string certP
244242

245243
/// <summary>
246244
/// Loads a PKCS#12 file written by BouncyCastle and returns an <see cref="X509Certificate2"/> with
247-
/// private key attached via <see cref="RSACryptoServiceProvider"/>.
248-
/// Using BouncyCastle's own reader avoids the .NET/Mono PFX parser, which can reject
249-
/// BouncyCastle-generated archives on the Crestron runtime.
245+
/// private key attached.
246+
/// The PFX is parsed and re-encoded by BouncyCastle (ensuring format compatibility), then passed as
247+
/// raw bytes to <see cref="X509Certificate2"/> so neither <c>RSACryptoServiceProvider</c> nor the
248+
/// <c>EphemeralKeySet</c> flag (unsupported on the Crestron/Mono runtime) is needed.
250249
/// </summary>
251250
private static X509Certificate2 LoadCertFromBouncyCastle(string certPath, string certPassword)
252251
{
@@ -258,24 +257,16 @@ private static X509Certificate2 LoadCertFromBouncyCastle(string certPath, string
258257
var store = new Pkcs12StoreBuilder().Build();
259258
store.Load(stream, passwordChars);
260259

261-
foreach (string alias in store.Aliases)
260+
// Re-encode through BouncyCastle to guarantee PKCS#12 format compatibility,
261+
// then hand raw bytes to X509Certificate2 — no RSACryptoServiceProvider needed.
262+
using (var ms = new MemoryStream())
262263
{
263-
if (!store.IsKeyEntry(alias)) continue;
264+
store.Save(ms, passwordChars, new SecureRandom());
265+
var cert = new X509Certificate2(ms.ToArray(), certPassword);
264266

265-
var keyEntry = store.GetKey(alias);
266-
var certChain = store.GetCertificateChain(alias);
267-
if (certChain == null || certChain.Length == 0) continue;
268-
269-
// Build X509Certificate2 from raw DER — no PFX parsing by .NET needed.
270-
var cert = new X509Certificate2(certChain[0].Certificate.GetEncoded());
271-
272-
// Attach the private key via RSACryptoServiceProvider (available on all target runtimes).
273-
var rsaParams = DotNetUtilities.ToRSAParameters(
274-
(RsaPrivateCrtKeyParameters)keyEntry.Key);
275-
var rsa = new RSACryptoServiceProvider();
276-
rsa.PersistKeyInCsp = false;
277-
rsa.ImportParameters(rsaParams);
278-
cert.PrivateKey = rsa;
267+
if (!cert.HasPrivateKey)
268+
throw new InvalidOperationException(
269+
string.Format("Certificate loaded from '{0}' does not contain a private key and cannot be used as a server certificate.", certPath));
279270

280271
return cert;
281272
}
@@ -285,8 +276,6 @@ private static X509Certificate2 LoadCertFromBouncyCastle(string certPath, string
285276
{
286277
Array.Clear(passwordChars, 0, passwordChars.Length);
287278
}
288-
289-
throw new InvalidOperationException("No key entry found in PKCS#12 store: " + certPath);
290279
}
291280

292281
private void Start(int port, string certPath = "", string certPassword = "")

src/PepperDash.Essentials.Core/Web/RequestHandlers/LoginRequestHandler.cs

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11

22
using System;
3+
using System.Collections.Generic;
34
using Crestron.SimplSharp.CrestronAuthentication;
45
using Crestron.SimplSharp.WebScripting;
56
using Newtonsoft.Json;
@@ -91,7 +92,19 @@ protected override void HandlePost(HttpCwsContext context)
9192
context.Response.StatusDescription = "OK";
9293
context.Response.ContentType = "application/json";
9394
context.Response.ContentEncoding = System.Text.Encoding.UTF8;
94-
context.Response.Write(JsonConvert.SerializeObject(new { Token = token }, Formatting.Indented), false);
95+
context.Response.Write(JsonConvert.SerializeObject(
96+
new
97+
{
98+
Token = new LoginResponse
99+
{
100+
UserName = token.UserName,
101+
Access = token.Access,
102+
State = token.State,
103+
Groups = token.Groups,
104+
ADConnect = token.ADConnect,
105+
Valid = token.Valid
106+
}
107+
}, Formatting.Indented), false);
95108
context.Response.End();
96109
}
97110
catch (System.Exception ex)
@@ -121,4 +134,40 @@ public class LoginRequest
121134
/// </summary>
122135
public string Password { get; set; }
123136
}
137+
138+
/// <summary>
139+
/// Represents a LoginResponse
140+
/// </summary>
141+
internal class LoginResponse
142+
{
143+
/// <summary>
144+
/// Gets or sets the username.
145+
/// </summary>
146+
public string UserName { get; set; }
147+
148+
/// <summary>
149+
/// Gets or sets the access level.
150+
/// </summary>
151+
public Authentication.UserAuthenticationLevelEnum Access { get; set; }
152+
153+
/// <summary>
154+
/// Gets or sets the token authenticated state.
155+
/// </summary>
156+
public Authentication.eTokenAuthenticatedState State { get; set; }
157+
158+
/// <summary>
159+
/// Gets or sets the list of groups.
160+
/// </summary>
161+
public List<string> Groups { get; set; }
162+
163+
/// <summary>
164+
/// Gets or sets the active directory connection flag.
165+
/// </summary>
166+
public int ADConnect { get; set; }
167+
168+
/// <summary>
169+
/// Gets or sets the valid flag indicating whether the token is valid.
170+
/// </summary>
171+
public bool Valid { get; set; }
172+
}
124173
}

0 commit comments

Comments
 (0)