static void Main(string[] args) { if (args.Length != 1) { Console.WriteLine("Pass the path to PRODINFO as the only argument"); return; } FileInfo prodinfoPath = new FileInfo(args[0]); if (!prodinfoPath.Exists) { Console.WriteLine($"Provided path \"{prodinfoPath.FullName}\" doesn't exist."); return; } if (OutputPfxInfo.Exists) { Console.WriteLine($"Output file {OutputPfxInfo.Name} already exists."); return; } Keyset k; if (GlobalProdSwitchKeysInfo.Exists) { k = ExternalKeyReader.ReadKeyFile(GlobalProdSwitchKeysInfo.FullName); } else if (LocalProdSwitchKeysInfo.Exists) { k = ExternalKeyReader.ReadKeyFile(LocalProdSwitchKeysInfo.FullName); } else { Console.WriteLine("Keys couldn't be found. Add to ~/.switch or working directory."); return; } k.DeriveKeys(); if (k.SslRsaKek.IsEmpty()) { Console.WriteLine("You are missing SslRsaKek in your keys file."); return; } Calibration cal0; byte[] certBytes; using (Stream prodinfoFile = prodinfoPath.OpenRead()) { prodinfoFile.Seek(0, SeekOrigin.Begin); cal0 = new Calibration(prodinfoFile); prodinfoFile.Seek(0x0AD0, SeekOrigin.Begin); // seek to certificate length byte[] buffer = new byte[0x4]; prodinfoFile.Read(buffer, 0, buffer.Length); // read cert length uint certLength = BitConverter.ToUInt32(buffer, 0); certBytes = new byte[certLength]; prodinfoFile.Seek(0x0AE0, SeekOrigin.Begin); // seek to cert (should be redundant?) prodinfoFile.Read(certBytes, 0, (int)certLength); // read actual cert } // extract enc private modulus byte[] counter = cal0.SslExtKey.Take(0x10).ToArray(); byte[] privateModulus = cal0.SslExtKey.Skip(0x10).ToArray(); // decrypt private modulus new Aes128CtrTransform(k.SslRsaKek, counter).TransformBlock(privateModulus); // import raw cert var certificate = new X509CertificateParser().ReadCertificate(certBytes); // import private modulus var privateParameter = certificate.RecoverPrivateParameter(privateModulus); // build PFX and add cert var store = new Pkcs12Store(); var certEntry = new X509CertificateEntry(certificate); store.SetCertificateEntry(certificate.SubjectDN.ToString(), certEntry); // add private key params to PFX AsymmetricKeyEntry privateKeyEntry = new AsymmetricKeyEntry(privateParameter); store.SetKeyEntry($"{certificate.SubjectDN}_key", privateKeyEntry, new[] { certEntry }); // output PFX with password using (Stream pfxStream = OutputPfxInfo.Create()) store.Save(pfxStream, "switch".ToCharArray(), new SecureRandom()); Console.WriteLine($"Wrote to {OutputPfxInfo.FullName}"); }
private static bool TryDumpCert(FileInfo prodinfo = null, Nand nand = null) { try { Calibration cal0; byte[] certBytes; using (Stream prodinfoFile = HACGUIKeyset.TempPRODINFOFileInfo.Create()) { // copy PRODINFO from local file Stream prodinfoStream = null; if (prodinfo != null) { prodinfoStream = prodinfo.OpenRead(); } else { prodinfoStream = nand.OpenProdInfo(); } prodinfoStream.CopyTo(prodinfoFile); prodinfoStream.Close(); prodinfoFile.Seek(0, SeekOrigin.Begin); cal0 = new Calibration(prodinfoFile); prodinfoFile.Seek(0x0AD0, SeekOrigin.Begin); // seek to certificate length byte[] buffer = new byte[0x4]; prodinfoFile.Read(buffer, 0, buffer.Length); // read cert length uint certLength = BitConverter.ToUInt32(buffer, 0); certBytes = new byte[certLength]; prodinfoFile.Seek(0x0AE0, SeekOrigin.Begin); // seek to cert (should be redundant?) prodinfoFile.Read(certBytes, 0, (int)certLength); // read actual cert } byte[] counter = cal0.SslExtKey.Take(0x10).ToArray(); byte[] privModulus = cal0.SslExtKey.Skip(0x10).ToArray(); // bit strange structure but it works new Aes128CtrTransform(HACGUIKeyset.Keyset.SslRsaKek, counter).TransformBlock(privModulus); // decrypt private modulus X509Certificate certificate = new X509CertificateParser().ReadCertificate(certBytes); AsymmetricKeyParameter privKey = certificate.RecoverPrivateParameter(privModulus); var store = new Pkcs12Store(); X509CertificateEntry certEntry = new X509CertificateEntry(certificate); store.SetCertificateEntry(certificate.SubjectDN.ToString(), certEntry); AsymmetricKeyEntry privKeyEntry = new AsymmetricKeyEntry(privKey); store.SetKeyEntry(certificate.SubjectDN.ToString() + "_key", privKeyEntry, new X509CertificateEntry[] { certEntry }); using (Stream pfxStream = HACGUIKeyset.GetClientCertificateByName(PickConsolePage.ConsoleName).Create()) store.Save(pfxStream, "switch".ToCharArray(), new SecureRandom()); return(true); } catch { return(false); } }