This post was most recently updated on September 19th, 2021.
3 min read.Recently, I ran into a problem that I definitely caused myself. I had a certificate, that was created to authenticate an app against Azure AD. The encryption key for the .pfx file was, however, lost.
I could just create a new certificate, configure the app service to use that, and replace the old certificate… But that’s manual labor and where’s the fun in that?
I prefer a solution that included a bit more coding!
After a bit of googling, I found a nice, programmatic solution to simply brute-force the private key by trying every single key in existence. Not very elegant, but I suppose it should do the trick?
The bad thing was that the sample code was fairly old – around 10 years old .NET 4 code, to be exact.
It still worked, however! The executable I compiled from the solution was able to try around 20k passwords per minute. That’s a bit low compared with the benchmarks the author posts (from 2012!), but I suppose my laptop’s single-core performance of unoptimized code (debug build) IS going to be fairly bad.
Anyway – I wanted to take a look at modernizing the app a little since I already had the code on my machine and everything! 😅
The modernization process
It’s remarkable, how much of the .NET world is backward compatible, but there are some rabbit holes that you don’t want to dive into. One of them is the old .csproj -file format. As the old file looks somewhat like this:
There’s so much that would probably need to be changed to get this thing running in .NET Core or.NET5, and I don’t understand half of the properties. Hell, the format is likely years older than my career as a .NET developer (I migrated from PHP and Java in 2011)!
So in short, I decided to nope that upgrade path out of the window and start with a fresh project file.
.NET5 supports Windows Forms, so simply creating a new WinForms project in .NET5 and copy-pasting the code was the easiest way to proceed.
The code itself is remarkably transparent and quite simple. And simply making it multithreaded took about half an hour of back-and-forth (because I’ll be damned if I remember how to do anything without fiddling with it for a while), and I ended up using Tasks (and ThreadPool) for multithreading – because it’s easy and I don’t need to worry about a maximum number of threads, pooling, or actually pretty much anything at all.
So, this is what I added (and made the method async):
Task.Run(() => {
if (TestPassword(pathToCertBx.Text, pw, logPathBx.Text, i))
{ password_found = true; }
});
I had to add or change 5 lines. Or 6, if you count adding a using-statement for System.Threading… 😅
Conclusion
After my code changes, the tool was able to try around 160-200k passwords per minute and was using around 50-75% of the CPU power of my machine with 20-30 threads. That’s an 8-10 times improvement, while still leaving the machine completely usable. Not insignificant, but also not quite good enough.
With a run-of-the-mill developer laptop, cracking a password of proper complexity will take months. And while a lot of people will use “aaaa” as their password for dev artifacts, I’m more of a “correct horse battery staple”* person – which means I’d be done quicker by creating a new certificate and delivering it to Microsoft’s Data Center in Dublin myself, even if I had to sneak over a few borders by foot.
Still, it was useful to see how much the cracking would be improved by simple tweaks to the code.
The code – both the original solution and the slightly modified version I created – are available in this GitHub repository:
https://github.com/koskila/CertificatePasswordRecovery
In the end, I created a new certificate and went on with the configuration of the app service – but this was an interesting detour for an hour or two :)
References & footnotes
- https://7thzero.com/blog/certificate-password-recovery-tool
- https://security.stackexchange.com/questions/119229/recover-password-from-pfx-file
- https://github.com/koskila/CertificatePasswordRecovery
* My actual passwords are machine-generated, this is a joke, don’t @ me.
- “Performing cleanup” – Excel is stuck with an old, conflicted file and will never recover. - November 12, 2024
- How to add multiple app URIs for your Entra app registration? - November 5, 2024
- How to access Environment Secrets with GitHub Actions? - October 29, 2024