Skip to content

Instantly share code, notes, and snippets.

@fosterbuster
Last active March 2, 2021 19:44
Show Gist options
  • Save fosterbuster/56e1b0e45af01a6dc3c850634fdbad28 to your computer and use it in GitHub Desktop.
Save fosterbuster/56e1b0e45af01a6dc3c850634fdbad28 to your computer and use it in GitHub Desktop.
Kamstrup kem file decryptor for .NET
namespace kemdecrypt
{
using System;
using System.IO.Compression;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
using McMaster.Extensions.CommandLineUtils;
public class Program
{
public static Task<int> Main(string[] args)
{
return CommandLineApplication.ExecuteAsync<Program>(args);
}
[Option(Description = "File is zipped.", Inherited = false, LongName = "zip", ShortName = "z", ShowInHelpText = true)]
public bool IsZipped { get; }
[Option(Description = "Password for decrypting the kem file.", Inherited = false, LongName = "password", ShortName = "pass", ShowInHelpText = true)]
public string Password { get; private set; }
[Option(Description = "The path of the file.", Inherited = false, LongName = "path", ShortName = "p", ShowInHelpText = true)]
public string Path { get; }
private async Task<int> OnExecuteAsync(CommandLineApplication app)
{
if (string.IsNullOrEmpty(Path))
{
app.ShowHelp();
return 0;
}
byte[] encrypted;
// TODO check if it actually is a zip file, and so on.
if (IsZipped)
{
XElement element = await XElement
.LoadAsync(
ZipFile.OpenRead(Path).Entries.First( x=> x.Name.EndsWith(".kem"))
.Open(),
LoadOptions.None,
new CancellationToken()) ;
encrypted = Convert.FromBase64String(element.Value);
}
else
{
encrypted = Convert.FromBase64String(XElement.Load(Path).Value);
}
if (string.IsNullOrEmpty(Password))
{
Console.WriteLine("No password provided. Please input password now.");
Password = Prompt.GetPassword("Password: ");
}
var decryptor = new AesDecryptor(Encoding.UTF8.GetBytes(Password));
var decrypted = decryptor.Decrypt(encrypted);
// Kamstrup seems to pad the file with \b - We trim it away, since it is invalid xml.
var decoded = Encoding.UTF8.GetString(decrypted);
var parsed = decoded.Substring(0, decoded.LastIndexOf(">") + 1);
Console.WriteLine(XElement.Parse(parsed));
return 0;
}
}
}
namespace kemdecrypt
{
using System;
using System.Security.Cryptography;
public class AesDecryptor
{
private readonly byte[] _decryptionKey;
public AesDecryptor(byte[] decryptionKey)
{
if (decryptionKey == null)
{
throw new ArgumentNullException(nameof(decryptionKey));
}
// Password can be less than 16 bytes, so we pad some extra 0's to the end of the key to ensure it has the correct length.
_decryptionKey = new byte[16];
Buffer.BlockCopy(decryptionKey, 0, _decryptionKey, 0, decryptionKey.Length);
}
public byte[] Decrypt(byte[] payload)
{
using (var algorithm = new AesCryptoServiceProvider
{
Padding = PaddingMode.None,
Mode = CipherMode.CBC,
IV = _decryptionKey, // initialization vector is the same as the key.
Key = _decryptionKey
})
{
return algorithm.CreateDecryptor().TransformFinalBlock(payload, 0, payload.Length);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment