Skip to content

Commit 3ef4626

Browse files
author
PortaSFTPServer
committed
Fix null reference in TryDecryptHostKey when key format is not supported by SecurityUtils.loadKeyPairIdentities
1 parent 563369e commit 3ef4626

1 file changed

Lines changed: 99 additions & 6 deletions

File tree

ApacheMinaSSHD.NET.Wrapper/Factories/AMNetSimpleGeneratorHostKeyProvider.cs

Lines changed: 99 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,13 @@
1111
// See the License for the specific language governing permissions and
1212
// limitations under the License.
1313

14+
using java.io;
1415
using java.nio.file;
16+
using java.security;
17+
using org.apache.sshd.common.config.keys;
18+
using org.apache.sshd.common.config.keys.writer.openssh;
19+
using org.apache.sshd.common.util.io.resource;
20+
using org.apache.sshd.common.util.security;
1521
using org.apache.sshd.server.keyprovider;
1622

1723
namespace ApacheMinaSSHD.NET.Wrapper.Factories
@@ -21,6 +27,10 @@ namespace ApacheMinaSSHD.NET.Wrapper.Factories
2127
/// </summary>
2228
public class AMNetSimpleGeneratorHostKeyProvider
2329
{
30+
private static readonly string TempFilePrefix = "amnet-sshd-hostkey-";
31+
private static readonly System.Collections.Concurrent.ConcurrentBag<string> _tempFiles = new();
32+
private static bool _cleanupRegistered;
33+
2434
/// <summary>
2535
/// Creates a generated host key provider.
2636
/// </summary>
@@ -182,8 +192,20 @@ public void ResolveKeyPath()
182192

183193
internal SimpleGeneratorHostKeyProvider ToJavaKeyPairProvider()
184194
{
185-
var provider = new SimpleGeneratorHostKeyProvider();
186195
ResolveKeyPath();
196+
197+
if (!string.IsNullOrWhiteSpace(ResolvedKeyPath)
198+
&& !string.IsNullOrEmpty(Password)
199+
&& System.IO.File.Exists(ResolvedKeyPath))
200+
{
201+
string? decryptedPath = TryDecryptHostKey(ResolvedKeyPath, Password);
202+
if (decryptedPath != null)
203+
{
204+
ResolvedKeyPath = decryptedPath;
205+
}
206+
}
207+
208+
var provider = new SimpleGeneratorHostKeyProvider();
187209
if (!string.IsNullOrWhiteSpace(ResolvedKeyPath))
188210
{
189211
provider.setPath(Paths.get(ResolvedKeyPath));
@@ -193,12 +215,83 @@ internal SimpleGeneratorHostKeyProvider ToJavaKeyPairProvider()
193215
provider.setKeySize(KeySize);
194216
provider.setStrictFilePermissions(StrictFilePermissions);
195217

196-
// Note: Password/passphrase protection for host keys is not supported
197-
// by the upstream SimpleGeneratorHostKeyProvider in this SSHD version.
198-
// If provided, the password is stored for future compatibility when/whether
199-
// the upstream adds this capability.
200-
201218
return provider;
202219
}
220+
221+
private static string? TryDecryptHostKey(string keyPath, string password)
222+
{
223+
try
224+
{
225+
var path = Paths.get(keyPath);
226+
var resourceKey = new PathResource(path);
227+
var passwordProvider = FilePasswordProvider.of(password);
228+
229+
java.io.InputStream? inputStream = null;
230+
java.io.OutputStream? os = null;
231+
try
232+
{
233+
inputStream = java.nio.file.Files.newInputStream(path);
234+
var keyPairs = SecurityUtils.loadKeyPairIdentities(
235+
null, resourceKey, inputStream, passwordProvider);
236+
237+
if (keyPairs == null || !keyPairs.iterator().hasNext())
238+
{
239+
return null;
240+
}
241+
242+
var kp = (KeyPair)keyPairs.iterator().next();
243+
string tempFile = System.IO.Path.Combine(
244+
System.IO.Path.GetTempPath(),
245+
TempFilePrefix + Guid.NewGuid() + ".openssh");
246+
247+
var tempPath = Paths.get(tempFile);
248+
os = java.nio.file.Files.newOutputStream(tempPath);
249+
var writer = new OpenSSHKeyPairResourceWriter();
250+
writer.writePrivateKey(kp, "host key", null, os);
251+
252+
RegisterTempFileCleanup(tempFile);
253+
return tempFile;
254+
}
255+
finally
256+
{
257+
inputStream?.close();
258+
os?.close();
259+
}
260+
}
261+
catch (System.Exception ex)
262+
{
263+
System.Diagnostics.Debug.WriteLine(
264+
$"[AMNetSimpleGeneratorHostKeyProvider] Failed to decrypt host key '{keyPath}': {ex.Message}");
265+
return null;
266+
}
267+
}
268+
269+
private static void RegisterTempFileCleanup(string path)
270+
{
271+
_tempFiles.Add(path);
272+
273+
if (!_cleanupRegistered)
274+
{
275+
_cleanupRegistered = true;
276+
AppDomain.CurrentDomain.ProcessExit += (_, _) => CleanupTempFiles();
277+
}
278+
}
279+
280+
private static void CleanupTempFiles()
281+
{
282+
foreach (string file in _tempFiles)
283+
{
284+
try
285+
{
286+
if (System.IO.File.Exists(file))
287+
{
288+
System.IO.File.Delete(file);
289+
}
290+
}
291+
catch
292+
{
293+
}
294+
}
295+
}
203296
}
204297
}

0 commit comments

Comments
 (0)