From b652e1adf3008e8f3a998464d537895e4e6b9247 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Thu, 20 Jul 2017 10:12:18 +0200 Subject: [PATCH] Split functionality into two packages (#2) * Trying to add des-ede3-cbc * split into 2 projects --- OpenSSL-X509Certificate2-Solution.sln | 16 +- examples/ConsoleApp/ConsoleApp.csproj | 25 +- examples/ConsoleApp/Program.cs | 2 + examples/ConsoleApp452/ConsoleApp452.csproj | 29 +- examples/ConsoleApp452/Demo.cs | 39 +- examples/ConsoleApp452/Program.cs | 2 + examples/ConsoleApp452/certificate_pub.crt | 21 - examples/ConsoleApp452/private.key | 28 -- examples/ConsoleApp452/private_rsa.key | 27 -- examples/{ConsoleApp => }/certificate_pub.crt | 0 examples/{ConsoleApp => }/private.key | 0 examples/{ConsoleApp => }/private_rsa.key | 0 examples/pwd_certificate_pub.crt | 21 + examples/pwd_private.key | 30 ++ examples/pwd_private_temp.key | 28 ++ .../BaseCertificateProvider.cs | 1 + .../CertificateFromFileProvider.cs | 11 +- .../OpenSSL.X509Certificate2Provider.csproj | 16 +- .../PrivateKeyDecoder.cs | 229 --------- .../IOpenSSLPrivateKeyDecoder.cs} | 12 +- .../OpenSSL.PrivateKeyDecoder.csproj | 53 +++ .../OpenSSLPrivateKeyDecoder.cs | 433 ++++++++++++++++++ .../SecureStringUtils.cs | 70 +++ .../TextUtil.cs | 2 +- 24 files changed, 751 insertions(+), 344 deletions(-) delete mode 100644 examples/ConsoleApp452/certificate_pub.crt delete mode 100644 examples/ConsoleApp452/private.key delete mode 100644 examples/ConsoleApp452/private_rsa.key rename examples/{ConsoleApp => }/certificate_pub.crt (100%) rename examples/{ConsoleApp => }/private.key (100%) rename examples/{ConsoleApp => }/private_rsa.key (100%) create mode 100644 examples/pwd_certificate_pub.crt create mode 100644 examples/pwd_private.key create mode 100644 examples/pwd_private_temp.key delete mode 100644 src/OpenSSL-X509Certificate2-Provider/PrivateKeyDecoder.cs rename src/{OpenSSL-X509Certificate2-Provider/IPrivateKeyDecoder.cs => OpenSSL.PrivateKeyDecoder/IOpenSSLPrivateKeyDecoder.cs} (52%) create mode 100644 src/OpenSSL.PrivateKeyDecoder/OpenSSL.PrivateKeyDecoder.csproj create mode 100644 src/OpenSSL.PrivateKeyDecoder/OpenSSLPrivateKeyDecoder.cs create mode 100644 src/OpenSSL.PrivateKeyDecoder/SecureStringUtils.cs rename src/{OpenSSL-X509Certificate2-Provider => OpenSSL.PrivateKeyDecoder}/TextUtil.cs (89%) diff --git a/OpenSSL-X509Certificate2-Solution.sln b/OpenSSL-X509Certificate2-Solution.sln index 72814ba..753e7ea 100644 --- a/OpenSSL-X509Certificate2-Solution.sln +++ b/OpenSSL-X509Certificate2-Solution.sln @@ -1,13 +1,20 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26430.15 +VisualStudioVersion = 15.0.26430.16 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenSSL.X509Certificate2Provider", "src\OpenSSL-X509Certificate2-Provider\OpenSSL.X509Certificate2Provider.csproj", "{CB7AD528-D24B-4D2E-AFBF-56FA40C48985}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{8A9B95AB-7BFA-43F8-A001-9873F18F9534}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{9BB037AA-C636-4732-9573-914F5BE29F89}" + ProjectSection(SolutionItems) = preProject + examples\certificate_pub.crt = examples\certificate_pub.crt + examples\private.key = examples\private.key + examples\private_rsa.key = examples\private_rsa.key + examples\pwd_certificate_pub.crt = examples\pwd_certificate_pub.crt + examples\pwd_private.key = examples\pwd_private.key + EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleApp", "examples\ConsoleApp\ConsoleApp.csproj", "{5E14342F-0878-41DA-A3D6-3A25E044CF17}" EndProject @@ -18,6 +25,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "solution items", "solution README.md = README.md EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenSSL.PrivateKeyDecoder", "src\OpenSSL.PrivateKeyDecoder\OpenSSL.PrivateKeyDecoder.csproj", "{095AE67B-6CE2-4002-86FC-33186D110EED}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -36,6 +45,10 @@ Global {0726E037-FD07-43DA-B746-BFBA1A932611}.Debug|Any CPU.Build.0 = Debug|Any CPU {0726E037-FD07-43DA-B746-BFBA1A932611}.Release|Any CPU.ActiveCfg = Release|Any CPU {0726E037-FD07-43DA-B746-BFBA1A932611}.Release|Any CPU.Build.0 = Release|Any CPU + {095AE67B-6CE2-4002-86FC-33186D110EED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {095AE67B-6CE2-4002-86FC-33186D110EED}.Debug|Any CPU.Build.0 = Debug|Any CPU + {095AE67B-6CE2-4002-86FC-33186D110EED}.Release|Any CPU.ActiveCfg = Release|Any CPU + {095AE67B-6CE2-4002-86FC-33186D110EED}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -44,5 +57,6 @@ Global {CB7AD528-D24B-4D2E-AFBF-56FA40C48985} = {8A9B95AB-7BFA-43F8-A001-9873F18F9534} {5E14342F-0878-41DA-A3D6-3A25E044CF17} = {9BB037AA-C636-4732-9573-914F5BE29F89} {0726E037-FD07-43DA-B746-BFBA1A932611} = {9BB037AA-C636-4732-9573-914F5BE29F89} + {095AE67B-6CE2-4002-86FC-33186D110EED} = {8A9B95AB-7BFA-43F8-A001-9873F18F9534} EndGlobalSection EndGlobal diff --git a/examples/ConsoleApp/ConsoleApp.csproj b/examples/ConsoleApp/ConsoleApp.csproj index 3e85ffd..c83e1ce 100644 --- a/examples/ConsoleApp/ConsoleApp.csproj +++ b/examples/ConsoleApp/ConsoleApp.csproj @@ -6,30 +6,31 @@ ../../images/certificate.ico - - - - - - - + + PreserveNewest + + + PreserveNewest + + PreserveNewest - - + + PreserveNewest - - + + PreserveNewest - + + \ No newline at end of file diff --git a/examples/ConsoleApp/Program.cs b/examples/ConsoleApp/Program.cs index 0e7c164..aa61c09 100644 --- a/examples/ConsoleApp/Program.cs +++ b/examples/ConsoleApp/Program.cs @@ -11,6 +11,8 @@ static void Main(string[] args) Console.WriteLine(new string('-', 80)); Demo.TestX509Certificate2WithRsa(); Console.WriteLine(new string('-', 80)); + //Demo.TestX509Certificate2WithEncryptedPrivateKey(); + Console.WriteLine(new string('-', 80)); Demo.TestPrivateKey(); Console.WriteLine(new string('-', 80)); Demo.TestPrivateRsaKey(); diff --git a/examples/ConsoleApp452/ConsoleApp452.csproj b/examples/ConsoleApp452/ConsoleApp452.csproj index 11408d6..da00710 100644 --- a/examples/ConsoleApp452/ConsoleApp452.csproj +++ b/examples/ConsoleApp452/ConsoleApp452.csproj @@ -53,22 +53,37 @@ - - + + certificate_pub.crt + PreserveNewest + + + private.key + PreserveNewest + + + private_rsa.key PreserveNewest - - + + + pwd_certificate_pub.crt PreserveNewest - - + + + pwd_private.key PreserveNewest - + + {cb7ad528-d24b-4d2e-afbf-56fa40c48985} OpenSSL.X509Certificate2Provider + + {095ae67b-6ce2-4002-86fc-33186d110eed} + OpenSSL.PrivateKeyDecoder + \ No newline at end of file diff --git a/examples/ConsoleApp452/Demo.cs b/examples/ConsoleApp452/Demo.cs index 50e7fed..b8f0642 100644 --- a/examples/ConsoleApp452/Demo.cs +++ b/examples/ConsoleApp452/Demo.cs @@ -4,6 +4,7 @@ using System.Security.Cryptography.X509Certificates; using System.Text; using System.Xml.Linq; +using OpenSSL.PrivateKeyDecoder; using OpenSSL.X509Certificate2Provider; namespace ConsoleApp452 @@ -74,13 +75,47 @@ public static void TestX509Certificate2WithRsa() Console.WriteLine("VerifyHash: {0}", verifyHashResult); } + public static void TestX509Certificate2WithEncryptedPrivateKey() + { + Console.WriteLine("TestX509Certificate2WithEncryptedPrivateKey"); + + // Generated using: + // openssl req -x509 -sha256 -days 365 -newkey rsa:2048 -keyout pwd_private_temp.key -out pwd_certificate_pub.crt + // openssl pkcs8 -topk8 -in pwd_private_temp.key -out pwd_private.key + // password = abc123 + string certificateText = File.ReadAllText("pwd_certificate_pub.crt"); + string privateKeyText = File.ReadAllText("pwd_private.key"); + + ICertificateProvider provider = new CertificateFromFileProvider(certificateText, privateKeyText, SecureStringUtils.Encrypt("abc123")); + X509Certificate2 certificate = provider.Certificate; + + Console.WriteLine("X509Certificate2:"); + Console.WriteLine(certificate); + Console.WriteLine(); + Console.WriteLine("PrivateKey:"); + RSACryptoServiceProvider cryptoServiceProvider = (RSACryptoServiceProvider)certificate.PrivateKey; + ShowRSAProperties(cryptoServiceProvider); + + var xml = XDocument.Parse(cryptoServiceProvider.ToXmlString(true)); + Console.WriteLine(xml.ToString()); + + // Sign the data + byte[] hello = new UTF8Encoding().GetBytes("Hello World"); + byte[] hashValue = cryptoServiceProvider.SignData(hello, CryptoConfig.MapNameToOID("SHA256")); + + RSACryptoServiceProvider publicKey = provider.PublicKey; + bool verifyHashResult = publicKey.VerifyData(hello, CryptoConfig.MapNameToOID("SHA256"), hashValue); + Console.WriteLine(); + Console.WriteLine("VerifyHash: {0}", verifyHashResult); + } + public static void TestPrivateKey() { Console.WriteLine("TestPrivateKey"); string privateKeyText = File.ReadAllText("private.key"); - IPrivateKeyDecoder decoder = new PrivateKeyDecoder(); + IOpenSSLPrivateKeyDecoder decoder = new OpenSSLPrivateKeyDecoder(); RSACryptoServiceProvider cryptoServiceProvider = decoder.Decode(privateKeyText); // Sign the data @@ -96,7 +131,7 @@ public static void TestPrivateRsaKey() string privateKeyText = File.ReadAllText("private_rsa.key"); - IPrivateKeyDecoder decoder = new PrivateKeyDecoder(); + IOpenSSLPrivateKeyDecoder decoder = new OpenSSLPrivateKeyDecoder(); RSACryptoServiceProvider cryptoServiceProvider = decoder.Decode(privateKeyText); // Sign the data diff --git a/examples/ConsoleApp452/Program.cs b/examples/ConsoleApp452/Program.cs index 8b9a030..15e2462 100644 --- a/examples/ConsoleApp452/Program.cs +++ b/examples/ConsoleApp452/Program.cs @@ -10,6 +10,8 @@ static void Main(string[] args) Console.WriteLine(new string('-', 80)); Demo.TestX509Certificate2WithRsa(); Console.WriteLine(new string('-', 80)); + //Demo.TestX509Certificate2WithEncryptedPrivateKey(); + Console.WriteLine(new string('-', 80)); Demo.TestPrivateKey(); Console.WriteLine(new string('-', 80)); Demo.TestPrivateRsaKey(); diff --git a/examples/ConsoleApp452/certificate_pub.crt b/examples/ConsoleApp452/certificate_pub.crt deleted file mode 100644 index 172d5ed..0000000 --- a/examples/ConsoleApp452/certificate_pub.crt +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDYDCCAkigAwIBAgIJAN+eFPJRWLQQMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV -BAYTAk5MMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX -aWRnaXRzIFB0eSBMdGQwHhcNMTcwNzEyMTgzNTM4WhcNMTgwNzEyMTgzNTM4WjBF -MQswCQYDVQQGEwJOTDETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 -ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB -CgKCAQEAvYyKS/SVrq4jfd0yimR7pqbgXV/CybK/qn+YNXcQQUCKadPNF62ajVFN -0rHOLY1RWlN3A18f82AGNkoRqYDLK3R2XywGRBxk+J+FKTVG8rXrE1QJPzd0bim6 -rkHErHHWCzGo/HNWdGMK5nzV8cfinn2CFZhgzg/XCl/xQlDGn/70bIsngy2KtmVN -0GBYdtQ7LQig1izF9g6zVUH8ZzTwfY/Sns5aDrChHGynABy4jxo5ZNc6F6w4AJ5F -1eU6SODo4b148+z5TNYM1I99b7jxDqUn/abYebLoqc3L8K581VMnhhRvP95JW9qX -zSDtI+7sxBfXroVW1QiCzgP9rMxqGQIDAQABo1MwUTAdBgNVHQ4EFgQUiLcCvE6M -tyeZBZ/z8NBaVDrM7AYwHwYDVR0jBBgwFoAUiLcCvE6MtyeZBZ/z8NBaVDrM7AYw -DwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAg+xjY//liDA2CvSl -FBoMDDQbLhjlYja0/JG/T8xp8G/PUOZE0TLt19393N1VlmijqbJ6vauNvaBaZJNf -y1V3ZttMFXjPPcyW05/ysHcPnrQ7xgCld6DLtrDB/5cTQic6vxl+eO2QPn3GSyUq -ITKACHf0QhiTe+4lmkYzAnJOxz0wV0s7JF8Atpsu6Kto7qJUQ46DmBadzssdwdWx -Y7EDuO56yA40v7wJuVoiwoAyYx17nWJU1WVGkdD9u0O/b0T90ANU4WxSxy8ml7/R -cjbq1mssvb6+oPBJaqrd25C0kq8AbS0MseXbXHGR7EZkpNeKczRCb/lQoldPo7Pz -x5nYvA== ------END CERTIFICATE----- diff --git a/examples/ConsoleApp452/private.key b/examples/ConsoleApp452/private.key deleted file mode 100644 index 86463ea..0000000 --- a/examples/ConsoleApp452/private.key +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC9jIpL9JWuriN9 -3TKKZHumpuBdX8LJsr+qf5g1dxBBQIpp080XrZqNUU3Ssc4tjVFaU3cDXx/zYAY2 -ShGpgMsrdHZfLAZEHGT4n4UpNUbytesTVAk/N3RuKbquQcSscdYLMaj8c1Z0Ywrm -fNXxx+KefYIVmGDOD9cKX/FCUMaf/vRsiyeDLYq2ZU3QYFh21DstCKDWLMX2DrNV -QfxnNPB9j9KezloOsKEcbKcAHLiPGjlk1zoXrDgAnkXV5TpI4OjhvXjz7PlM1gzU -j31vuPEOpSf9pth5suipzcvwrnzVUyeGFG8/3klb2pfNIO0j7uzEF9euhVbVCILO -A/2szGoZAgMBAAECggEAOcaEwPIkTcI2fz7VEOnf5sQmz09JCEG7ujR6KJHKGpmZ -lvhAI19/JGQL3JTly2yvx2B5fkWYb45tVk7tY0UmqHAvRIMexdQB0uaMqCrdHU3m -AVY0G1jD3HBLvqG9WPK501+nqotdZsxpniee7ruGj9ihtgNkPmsFmNv1pckJQerl -c7cvU6Y2eS3oczuw5KRu0UFVdhhuntAcUnzikzIID3qnMx08zbINVlkVxX+UHfDV -TXnCFlLJp39siiJ0YdRpiMiUKqKEVUJGvjOqeamKmB1ssgoILFMtEw8Wu0HFH/w9 -HhLrLaEm3RQ0sTbB/yU6UylDNHC6mKfwssJ7d63BIQKBgQD1X62zg+8A+Jb9TzD1 -Z4UJ3YLu12+jVLyEgey1q7a/QRKxhKu7V7Uf12xq8pWS/G+Vy5yjjl/GDa49UdMW -dJY8ynUuAdO9sB42HOb94J/hON+Nb1F2eJRnBz8Gaq+z4+SMoqoxRAtE+qU4y109 -vpJi06m6PP06QMpt/m8ikJ37iwKBgQDFwfa8wIgfVI+m6rsSrW8irfGrIiLhVSml -aU8PXnINifYfl8WHtkeJxhj+C2itPh/IBBHkWapbQxjJIv6TusDWkkhvMpo4VZW/ -84Ays3EtVyfclnL7z1pCZqGCpfnb4ynzpMXhtLhn88buXf2sCiA8h4DLjpkKT+3+ -Ut9psSG1awKBgQDyIaNH9RZBUA23E5FNaQTYUlUlGUSZ2UFaRNdwAm2IV096C2Bn -s0oRjMP42T8OV7pJCC6fGxyZVOCus/LoQs4KWQK/DqCCm6uEXIr41dMPLc80QzgQ -/zX6YSUpUNfnXSgoO1BU4z93pxqpa8yr5EWR7iqblBmShWL8ncnPQwQStwKBgQDE -dQZ7SQFxrn3vIm2qBmXLm+XjTsw78nCnBNEnwXmofY/mns37Hlg/RG77obXhtT+5 -YmGr6m/Urflnh75EYFjGURhob2rJ4CwePvbyZ1Vd56GQAF5GTh2qrW5x7hUkHq5E -f0KeEPzJOGI91130lTKlLzACCK18kx8pcobD5tZH9QKBgQDA4Xa0sBxCZR5rIp9X -FEukW10KM3wM3z8pubI+XN+ZT7s3C1f59dTBJgHsZQFJ/ibQ6kJobPTQNE8L+9aD -WyTEOQ6lWl+TYoKf576peyU7fOfWJUanJEpsBLruaCvhb8Gvuje+mwrwCy35STQV -KyyynGjIfF8Ex5r8q1K1Fv4i3w== ------END PRIVATE KEY----- diff --git a/examples/ConsoleApp452/private_rsa.key b/examples/ConsoleApp452/private_rsa.key deleted file mode 100644 index 219eaf1..0000000 --- a/examples/ConsoleApp452/private_rsa.key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpQIBAAKCAQEAvYyKS/SVrq4jfd0yimR7pqbgXV/CybK/qn+YNXcQQUCKadPN -F62ajVFN0rHOLY1RWlN3A18f82AGNkoRqYDLK3R2XywGRBxk+J+FKTVG8rXrE1QJ -Pzd0bim6rkHErHHWCzGo/HNWdGMK5nzV8cfinn2CFZhgzg/XCl/xQlDGn/70bIsn -gy2KtmVN0GBYdtQ7LQig1izF9g6zVUH8ZzTwfY/Sns5aDrChHGynABy4jxo5ZNc6 -F6w4AJ5F1eU6SODo4b148+z5TNYM1I99b7jxDqUn/abYebLoqc3L8K581VMnhhRv -P95JW9qXzSDtI+7sxBfXroVW1QiCzgP9rMxqGQIDAQABAoIBADnGhMDyJE3CNn8+ -1RDp3+bEJs9PSQhBu7o0eiiRyhqZmZb4QCNffyRkC9yU5ctsr8dgeX5FmG+ObVZO -7WNFJqhwL0SDHsXUAdLmjKgq3R1N5gFWNBtYw9xwS76hvVjyudNfp6qLXWbMaZ4n -nu67ho/YobYDZD5rBZjb9aXJCUHq5XO3L1OmNnkt6HM7sOSkbtFBVXYYbp7QHFJ8 -4pMyCA96pzMdPM2yDVZZFcV/lB3w1U15whZSyad/bIoidGHUaYjIlCqihFVCRr4z -qnmpipgdbLIKCCxTLRMPFrtBxR/8PR4S6y2hJt0UNLE2wf8lOlMpQzRwupin8LLC -e3etwSECgYEA9V+ts4PvAPiW/U8w9WeFCd2C7tdvo1S8hIHstau2v0ESsYSru1e1 -H9dsavKVkvxvlcuco45fxg2uPVHTFnSWPMp1LgHTvbAeNhzm/eCf4TjfjW9RdniU -Zwc/Bmqvs+PkjKKqMUQLRPqlOMtdPb6SYtOpujz9OkDKbf5vIpCd+4sCgYEAxcH2 -vMCIH1SPpuq7Eq1vIq3xqyIi4VUppWlPD15yDYn2H5fFh7ZHicYY/gtorT4fyAQR -5FmqW0MYySL+k7rA1pJIbzKaOFWVv/OAMrNxLVcn3JZy+89aQmahgqX52+Mp86TF -4bS4Z/PG7l39rAogPIeAy46ZCk/t/lLfabEhtWsCgYEA8iGjR/UWQVANtxORTWkE -2FJVJRlEmdlBWkTXcAJtiFdPegtgZ7NKEYzD+Nk/Dle6SQgunxscmVTgrrPy6ELO -ClkCvw6ggpurhFyK+NXTDy3PNEM4EP81+mElKVDX510oKDtQVOM/d6caqWvMq+RF -ke4qm5QZkoVi/J3Jz0MEErcCgYEAxHUGe0kBca597yJtqgZly5vl407MO/JwpwTR -J8F5qH2P5p7N+x5YP0Ru+6G14bU/uWJhq+pv1K35Z4e+RGBYxlEYaG9qyeAsHj72 -8mdVXeehkABeRk4dqq1uce4VJB6uRH9CnhD8yThiPddd9JUypS8wAgitfJMfKXKG -w+bWR/UCgYEAwOF2tLAcQmUeayKfVxRLpFtdCjN8DN8/KbmyPlzfmU+7NwtX+fXU -wSYB7GUBSf4m0OpCaGz00DRPC/vWg1skxDkOpVpfk2KCn+e+qXslO3zn1iVGpyRK -bAS67mgr4W/Br7o3vpsK8Ast+Uk0FSssspxoyHxfBMea/KtStRb+It8= ------END RSA PRIVATE KEY----- diff --git a/examples/ConsoleApp/certificate_pub.crt b/examples/certificate_pub.crt similarity index 100% rename from examples/ConsoleApp/certificate_pub.crt rename to examples/certificate_pub.crt diff --git a/examples/ConsoleApp/private.key b/examples/private.key similarity index 100% rename from examples/ConsoleApp/private.key rename to examples/private.key diff --git a/examples/ConsoleApp/private_rsa.key b/examples/private_rsa.key similarity index 100% rename from examples/ConsoleApp/private_rsa.key rename to examples/private_rsa.key diff --git a/examples/pwd_certificate_pub.crt b/examples/pwd_certificate_pub.crt new file mode 100644 index 0000000..7d38df5 --- /dev/null +++ b/examples/pwd_certificate_pub.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDYDCCAkigAwIBAgIJAKKi3yEvdrO3MA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQwHhcNMTcwNzE4MTk0NzMxWhcNMTgwNzE4MTk0NzMxWjBF +MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 +ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEA18Jgpg5YiBxa0yz3+LOtSPFkrPb3xG1LpNsly1j/RVi1dbDe9Vu411t2 +gtm1+QgRiQ/Gx4fTA0d11oIJ1+y22Azi2+6azk5CPmUtfWOILucbFAWSEL/yaFlj +RS288tKqWULG8tHvYUQDh2pvdnvahnZrpX0o8yjYsddznEpMuOauUDFsH+aaHRgC +niI9TxbRAATRTrTiBgxJO9ylxPML2YZ8DwZsqud1vx7EKIlVSD0SwroMNKx+BQLJ +SRtRJnsBQF/i4t/lwfNuBGcdKKAYw11Y6Rg5xj4Qa7gdzLOy6Evu/HTPkwDffGaS +hsnX6mPMmMCHv/s7u14z0yfhas/CmQIDAQABo1MwUTAdBgNVHQ4EFgQUDutBGZdA +DAwC5DZosc6XupVXKU8wHwYDVR0jBBgwFoAUDutBGZdADAwC5DZosc6XupVXKU8w +DwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEASsPjWmugQvUpOISD +MHTJodQnKX2OKWSixCfZ5Xnvy3291uT5ovEdiiCJKq/9MF0GXrH2mnRK1dPra4tv +pD4/8d7BB3pJETa6sKEgzbprDMieVkqSKluqqHy5Sq2OhgilhGvFXecY4eeCRGxx +UMaYwcR55KC8INUe348M1Ir2uneBQ71JVf29DBkCD0iO6vEoEFVX5iXR6MOipGnt +2qSDFuQYxPrhJoLt37npQki1YmmjqZOrzkPTLKe4ULlRB4E3FNJvJClKYdApC32F +Vio5QPujr9HaeCVdBpM/X1QrvCtPbrTYLsqngxqXdDm/B/+7XDdszzVAsAdj4N87 +DnrfEg== +-----END CERTIFICATE----- diff --git a/examples/pwd_private.key b/examples/pwd_private.key new file mode 100644 index 0000000..10f94f9 --- /dev/null +++ b/examples/pwd_private.key @@ -0,0 +1,30 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFHDBOBgkqhkiG9w0BBQ0wQTApBgkqhkiG9w0BBQwwHAQIQdleBLtvbPoCAggA +MAwGCCqGSIb3DQIJBQAwFAYIKoZIhvcNAwcECFguX8xRQT04BIIEyNgqKphZA5pG +baN005f+5Xea7g0d8IVPTzp5RpC6Ks+vORsa6sNjTuyfmaDyJP16ZaMf01mGlFIF +O2lHhxvrM25Z6sE9QbzHMXd6dcg5nwTvMgkG7oHoWkTwrh7nWwAKP6dOzFF0cDhH +NwNP7RVl87Jih5w297iVH0jctKHp1J90ND3WYCqDLH/6b82NDMLxNHZbepchypN+ +WrpBzrQvt2nrh7MHy/WkWm5RMOoqaB3kyRfmjnSdC9BWDDiv09LaRhT7uKU5jRP+ +6bZO7PQ6YRZmrIfJ4nZ/cMlmvo6Z1fQGc02ty9Ke/JSSETM67bSyjAl2X8ma6iJR +gvdwBTv2hLw1/W1bGL48bWvB8AicqW3VEb2bz36wp6qiW3ZhmIphmTtp/p9IYxcq +o2wkXuXqmykGVArD2E5G5apuIECT3Pj1VEL1IC1AdQJxIrGPJkkavJuN4PVqyAWg +HF1f/p2nnVrsq1T/pAl7eDIwapkyvS2iHlDcpWgVHoaILt8jwAzjDobSSoujkDQp +J/rCypm5ZFsj3Q1Vs2yqB0GCO5dXrknyKhe4C2jTB/riMNAXU/KBoEEKMPRbWJBW +o4MzMHy4eFPxehQfI/xou1GDJndz+PeuduBnA97aKdFhycgzaUbYqiSf8PI64zIr +eHrJLNyE0utzqEk80+tmkkDhVqYaWOmO938RB9T+mweBarn9H/HJh9qrjefQErF1 +DYnOCFaMyDdt2lfGWTTzseePayp3jOiuPyVLzlsEtIQl3kPJ5NQRarbWmZldLfRa +cGBB2QLTpM+Yh5pXOzViBHvw3y9kcEF9McZSnA635M/fKBVWjnarQ/VD+NXZWfs/ +qVmYaYG4R9UoSAQb4NjFWbRgXch0jz6lBnrq+vu0OBS0STqDZMOjafzIq+z0dat2 ++x6g8YEBeWp4JEM2e+iKKG10vFDYhxho/LwuekhOW4j8ZYS3/Q/XcqqSThID6Kp+ +MfohFYgJmYeaMeVxMlqDuIkgIQ8dp/d9Z6Y8xCBn57hQygx4u1GM2h3qp750J1Ym +9YQ+WmXTgxA60giYfDrkscxf1Q2+vv8AoMZpRHDnJeYpVIuuNpl7MyIUQJFtJ//g +38+sNKok4SglMytuouQ17lGeTu8n6KMAezh7kkM5tLWQtUFOuvZ8i94rc2qswY2S +aU1PgXHlnCiZ318sS4Wp+Mwae8CKHPhn3/uHfGiEupOZHbqIDfztYWn0/SbKJfKo +l9H8Kl3A0jtfVr4RAxtk324pvugjTtKLUmAQoYKMYdM+GYMyiUv7BaveibUrYnNp +8CqgmBMv8fHG7YVUjLB0DtMCfnDoIYQcI3UCnKzceQaQPZ4o259BXTZIxrwP2QMr +gbuT7iwSGLYKmkH7aklyB5CVSKWYPvi05864o784vUuViGfLVx5pdJdRgDCHBqA/ +ExbJdkvC6Ob/Y9eTvcbtNq/2XZreJHxTmBsk03hkxauAwf6FvsDkrRf2PCOZC21E +DcLOyRGRZ3Bp13Pz7b2eDgV9qSqbFA//ipKjZGI3dJwZ7U25952SFbU12nlm8PA1 +0w0Ms7HTW+PZ7l5aP6WxtZwnyy8c0VMMZyBqMo5XqhlUH9lij99LPclkQU/hlS+N +ybriOECRvexCaLMf0QS4og== +-----END ENCRYPTED PRIVATE KEY----- diff --git a/examples/pwd_private_temp.key b/examples/pwd_private_temp.key new file mode 100644 index 0000000..d3b07b1 --- /dev/null +++ b/examples/pwd_private_temp.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDXwmCmDliIHFrT +LPf4s61I8WSs9vfEbUuk2yXLWP9FWLV1sN71W7jXW3aC2bX5CBGJD8bHh9MDR3XW +ggnX7LbYDOLb7prOTkI+ZS19Y4gu5xsUBZIQv/JoWWNFLbzy0qpZQsby0e9hRAOH +am92e9qGdmulfSjzKNix13OcSky45q5QMWwf5podGAKeIj1PFtEABNFOtOIGDEk7 +3KXE8wvZhnwPBmyq53W/HsQoiVVIPRLCugw0rH4FAslJG1EmewFAX+Li3+XB824E +Zx0ooBjDXVjpGDnGPhBruB3Ms7LoS+78dM+TAN98ZpKGydfqY8yYwIe/+zu7XjPT +J+Fqz8KZAgMBAAECggEAY846vxXrxiq3qPAoVRdolVF07L3hpy+Xyk7FkOc+TmnW +pvaGbKFdQ+G/VJA5KVWeJvTxUuN1rRKlY/ELdKK/gfmUB4C8g6qX4h0Tiek82dV2 +JTDefHkOPU78KMgbm4fv3RKSzUhnn2rC5yVJIlnwoDqvWQIoqzK96qZriGSmUPEq +6ED0SATR/b/i7hLPNzZuR68tMGf690BuPbuMRxLYGIarR1qxVa4DUl1V4NY87gKd +k2R3SN22bzK6BjgFgSUFmmm2eVsWXIm1LJAPXHMWZp2geIG6uyrHAEeCEdtX0cnH +lMOrQC2bUVSYOOP7rKnrJi/+nifHMAVs9POMCnoHMQKBgQD5ZDej48PUJmaC/q0q +BP1lwLT3uhd5JAuzfQFO+S1pchatpCwNsfuiJ7UrPhn3/CV3BSl/10MGVWApF2PZ +eVqi7J7g6GxwHtG+FVsIdj94ENXyGkiIsbsOseW7T96Hbr3VetEprehyB9vLk3mj +1v2EinDiXobUMpBHtSaRfdfOawKBgQDdegLubdjZ3uXqYIyBjlmMxULn3zg3rOi5 +s0ouEmiER6D0THhpCO/wXeI/qDrkm6Rli1qd4loaykajoawE7JK3TU7mUoeX5A0r +eQ1DghayyaqSXOUUT5wVzDWacgu01qqQAqRbc1NOWufa3dzh0m6O/OOeXt4Mm2B5 +jrdurg6sCwKBgGcHqyDvIOENeD5X/qwkYZde9KR/YeEvLhJZ62d38/XjJ1FXSMfj +puSH6QXTYyEbL1Mrc8iX4t3D+bUgL6URyIsUnFKoUtxDLz2LTw+A4pm0wt6BU0P9 +wRzCC/nFdlaXPp/qXG0OpsAtVPWWmKNCV3whQZ+Tk7oopYiqYyOqTLS/AoGAMtw0 +FCDxrYa8ccheO9o+wk8CgFEIjfCNOSftHwtnKQlx8ugktqowv5gvvsKZU6pSZGdO +FffHDWPqOOWFpVPWlXWRat8E+GKMi7Eu1JPpNoGNRDNFABcQFwlgPel2ur6ZSJzy +tXWUaegCWoaclNdIQFnew52xjF+aFhAqG6apA+UCgYEAg0PkFhUOMvBxzXeZthq2 +W/JJ4ODXh3h6R2mVO9TDqBOHKSXWMksEqKxMg3y8aVQR1XQNIo8R0MdfTly9q6vi ++Kf0Rrxj2CswJjXcV9D2qQcm+2TRWmWH7zHceP2gxjSt2laUbf/sGcmdknW3yqVY +QaxlctJfxKXV0mP4cs6GMWo= +-----END PRIVATE KEY----- diff --git a/src/OpenSSL-X509Certificate2-Provider/BaseCertificateProvider.cs b/src/OpenSSL-X509Certificate2-Provider/BaseCertificateProvider.cs index 1f6b925..80745bc 100644 --- a/src/OpenSSL-X509Certificate2-Provider/BaseCertificateProvider.cs +++ b/src/OpenSSL-X509Certificate2-Provider/BaseCertificateProvider.cs @@ -1,5 +1,6 @@ using System; using JetBrains.Annotations; +using OpenSSL.PrivateKeyDecoder; namespace OpenSSL.X509Certificate2Provider { diff --git a/src/OpenSSL-X509Certificate2-Provider/CertificateFromFileProvider.cs b/src/OpenSSL-X509Certificate2-Provider/CertificateFromFileProvider.cs index 6674695..02c2e0a 100644 --- a/src/OpenSSL-X509Certificate2-Provider/CertificateFromFileProvider.cs +++ b/src/OpenSSL-X509Certificate2-Provider/CertificateFromFileProvider.cs @@ -1,6 +1,8 @@ -using System.Security.Cryptography; +using System.Security; +using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using JetBrains.Annotations; +using OpenSSL.PrivateKeyDecoder; namespace OpenSSL.X509Certificate2Provider { @@ -15,7 +17,8 @@ public class CertificateFromFileProvider : BaseCertificateProvider, ICertificate /// /// The certificate or public key text. /// The private (rsa) key text. - public CertificateFromFileProvider([NotNull] string certificateText, [NotNull] string privateKeyText) + /// The optional securePassword to decrypt the private key. + public CertificateFromFileProvider([NotNull] string certificateText, [NotNull] string privateKeyText, [CanBeNull] SecureString securePassword = null) { Certificate = new X509Certificate2(GetPublicKeyBytes(certificateText)); #if NETSTANDARD @@ -24,8 +27,8 @@ public CertificateFromFileProvider([NotNull] string certificateText, [NotNull] s PublicKey = (RSACryptoServiceProvider)Certificate.PublicKey.Key; #endif - IPrivateKeyDecoder decoder = new PrivateKeyDecoder(); - PrivateKey = decoder.Decode(privateKeyText); + IOpenSSLPrivateKeyDecoder decoder = new OpenSSLPrivateKeyDecoder(); + PrivateKey = decoder.Decode(privateKeyText, securePassword); #if !NETSTANDARD Certificate.PrivateKey = PrivateKey; diff --git a/src/OpenSSL-X509Certificate2-Provider/OpenSSL.X509Certificate2Provider.csproj b/src/OpenSSL-X509Certificate2-Provider/OpenSSL.X509Certificate2Provider.csproj index 57278fb..770f233 100644 --- a/src/OpenSSL-X509Certificate2-Provider/OpenSSL.X509Certificate2Provider.csproj +++ b/src/OpenSSL-X509Certificate2-Provider/OpenSSL.X509Certificate2Provider.csproj @@ -8,7 +8,7 @@ OpenSSL.X509Certificate2.Provider Parses OpenSSL public and private (rsa) key components and returns a X509Certificate2 with RSACryptoServiceProvider. Parses OpenSSL public and private (rsa) key components and returns a X509Certificate2 with RSACryptoServiceProvider. (based on http://www.jensign.com/opensslkey/opensslkey.cs) - 1.0.0.0 + 1.0.1.0 Stef Heyenrath true OpenSSL;X509Certificate2;certificate;certificates;private;public;RSA;X509;RSACryptoServiceProvider @@ -37,12 +37,6 @@ OpenSSL.X509Certificate2Provider - - NETSTANDARD @@ -51,8 +45,16 @@ + + + + + + + + \ No newline at end of file diff --git a/src/OpenSSL-X509Certificate2-Provider/PrivateKeyDecoder.cs b/src/OpenSSL-X509Certificate2-Provider/PrivateKeyDecoder.cs deleted file mode 100644 index 5b9276d..0000000 --- a/src/OpenSSL-X509Certificate2-Provider/PrivateKeyDecoder.cs +++ /dev/null @@ -1,229 +0,0 @@ -using System; -using System.IO; -using System.Security.Cryptography; -using JetBrains.Annotations; - -namespace OpenSSL.X509Certificate2Provider -{ - /// - /// PrivateKeyDecoder - /// - [PublicAPI] - public sealed class PrivateKeyDecoder : IPrivateKeyDecoder - { - // encoded OID sequence for PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1", including the sequence byte and terminal encoded null - private readonly byte[] SeqOID = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 }; - - private const string RSAPrivateKeyHeader = "-----BEGIN RSA PRIVATE KEY-----"; - private const string RSAPrivateKeyFooter = "-----END RSA PRIVATE KEY-----"; - private const string PrivateKeyHeader = "-----BEGIN PRIVATE KEY-----"; - private const string PrivateKeyFooter = "-----END PRIVATE KEY-----"; - - /// - /// Decode PrivateKey - /// - /// The private (rsa) key text. - /// RSACryptoServiceProvider - public RSACryptoServiceProvider Decode(string privateText) - { - string text = privateText.Trim(); - if (text.StartsWith(RSAPrivateKeyHeader) && text.EndsWith(RSAPrivateKeyFooter)) - { - byte[] data = TextUtil.ExtractBytes(text, RSAPrivateKeyHeader, RSAPrivateKeyFooter); - return DecodeRSAPrivateKey(data); - } - - if (text.StartsWith(PrivateKeyHeader) && text.EndsWith(PrivateKeyFooter)) - { - byte[] data = TextUtil.ExtractBytes(text, PrivateKeyHeader, PrivateKeyFooter); - return DecodePrivateKey(data); - } - - throw new NotSupportedException(); - } - - private RSACryptoServiceProvider DecodePrivateKey(byte[] pkcs8) - { - // read the asn.1 encoded SubjectPublicKeyInfo blob - var memoryStream = new MemoryStream(pkcs8); - int streamLength = (int)memoryStream.Length; - - using (var reader = new BinaryReader(memoryStream)) - { - ushort twobytes = reader.ReadUInt16(); - if (twobytes == 0x8130) // data read as little endian order (actual data order for Sequence is 30 81) - { - reader.ReadByte(); // advance 1 byte - } - else if (twobytes == 0x8230) - { - reader.ReadInt16(); // advance 2 bytes - } - else - { - return null; - } - - byte bt = reader.ReadByte(); - if (bt != 0x02) - { - return null; - } - - twobytes = reader.ReadUInt16(); - if (twobytes != 0x0001) - { - return null; - } - - byte[] seq = reader.ReadBytes(15); - if (!CompareByteArrays(seq, SeqOID)) // make sure Sequence for OID is correct - { - return null; - } - - bt = reader.ReadByte(); - if (bt != 0x04) // expect an Octet string - { - return null; - } - - bt = reader.ReadByte(); // read next byte, or next 2 bytes is 0x81 or 0x82; otherwise bt is the byte count - if (bt == 0x81) - { - reader.ReadByte(); - } - else if (bt == 0x82) - { - reader.ReadUInt16(); - } - - // at this stage, the remaining sequence should be the RSA private key - byte[] rsaprivkey = reader.ReadBytes((int)(streamLength - memoryStream.Position)); - return DecodeRSAPrivateKey(rsaprivkey); - } - } - - private RSACryptoServiceProvider DecodeRSAPrivateKey(byte[] privkey) - { - // decode the asn.1 encoded RSA private key - var memoryStream = new MemoryStream(privkey); - using (var reader = new BinaryReader(memoryStream)) - { - ushort twobytes = reader.ReadUInt16(); - if (twobytes == 0x8130) // data read as little endian order (actual data order for Sequence is 30 81) - { - reader.ReadByte(); // advance 1 byte - } - else if (twobytes == 0x8230) - { - reader.ReadInt16(); // advance 2 bytes - } - else - { - return null; - } - - twobytes = reader.ReadUInt16(); - if (twobytes != 0x0102) // version number - { - return null; - } - - byte bt = reader.ReadByte(); - if (bt != 0x00) - { - return null; - } - - // all private key components are Integer sequences - var rsaParameters = new RSAParameters(); - - int elems = GetIntegerSize(reader); - rsaParameters.Modulus = reader.ReadBytes(elems); - - elems = GetIntegerSize(reader); - rsaParameters.Exponent = reader.ReadBytes(elems); - - elems = GetIntegerSize(reader); - rsaParameters.D = reader.ReadBytes(elems); - - elems = GetIntegerSize(reader); - rsaParameters.P = reader.ReadBytes(elems); - - elems = GetIntegerSize(reader); - rsaParameters.Q = reader.ReadBytes(elems); - - elems = GetIntegerSize(reader); - rsaParameters.DP = reader.ReadBytes(elems); - - elems = GetIntegerSize(reader); - rsaParameters.DQ = reader.ReadBytes(elems); - - elems = GetIntegerSize(reader); - rsaParameters.InverseQ = reader.ReadBytes(elems); - - // create RSACryptoServiceProvider instance - var rsaCryptoServiceProvider = new RSACryptoServiceProvider(); - rsaCryptoServiceProvider.ImportParameters(rsaParameters); - return rsaCryptoServiceProvider; - } - } - - private bool CompareByteArrays(byte[] arrayA, byte[] arrayB) - { - if (arrayA.Length != arrayB.Length) - { - return false; - } - - int i = 0; - foreach (byte c in arrayA) - { - if (c != arrayB[i]) - { - return false; - } - i++; - } - - return true; - } - - private int GetIntegerSize(BinaryReader reader) - { - int count; - byte bt = reader.ReadByte(); - if (bt != 0x02) // expect integer - { - return 0; - } - bt = reader.ReadByte(); - - switch (bt) - { - case 0x81: - count = reader.ReadByte(); // data size in next byte - break; - case 0x82: - byte highbyte = reader.ReadByte(); - byte lowbyte = reader.ReadByte(); - byte[] modint = { lowbyte, highbyte, 0x00, 0x00 }; - count = BitConverter.ToInt32(modint, 0); - break; - default: - count = bt; // we already have the data size - break; - } - - while (reader.ReadByte() == 0x00) - { - // remove high order zeros in data - count -= 1; - } - - reader.BaseStream.Seek(-1, SeekOrigin.Current); // last ReadByte wasn't arrayA removed zero, so back up arrayA byte - return count; - } - } -} \ No newline at end of file diff --git a/src/OpenSSL-X509Certificate2-Provider/IPrivateKeyDecoder.cs b/src/OpenSSL.PrivateKeyDecoder/IOpenSSLPrivateKeyDecoder.cs similarity index 52% rename from src/OpenSSL-X509Certificate2-Provider/IPrivateKeyDecoder.cs rename to src/OpenSSL.PrivateKeyDecoder/IOpenSSLPrivateKeyDecoder.cs index 0bca16f..f2a6836 100644 --- a/src/OpenSSL-X509Certificate2-Provider/IPrivateKeyDecoder.cs +++ b/src/OpenSSL.PrivateKeyDecoder/IOpenSSLPrivateKeyDecoder.cs @@ -1,18 +1,20 @@ -using JetBrains.Annotations; +using System.Security; +using JetBrains.Annotations; using System.Security.Cryptography; -namespace OpenSSL.X509Certificate2Provider +namespace OpenSSL.PrivateKeyDecoder { /// - /// IPrivateKeyDecoder + /// IOpenSSLPrivateKeyDecoder /// - public interface IPrivateKeyDecoder + public interface IOpenSSLPrivateKeyDecoder { /// /// Decode PrivateKey /// /// The private (rsa) key text. + /// The optional password to decrypt this private key. /// RSACryptoServiceProvider - RSACryptoServiceProvider Decode([NotNull] string privateText); + RSACryptoServiceProvider Decode([NotNull] string privateText, [CanBeNull] SecureString securePassword = null); } } \ No newline at end of file diff --git a/src/OpenSSL.PrivateKeyDecoder/OpenSSL.PrivateKeyDecoder.csproj b/src/OpenSSL.PrivateKeyDecoder/OpenSSL.PrivateKeyDecoder.csproj new file mode 100644 index 0000000..5e8728a --- /dev/null +++ b/src/OpenSSL.PrivateKeyDecoder/OpenSSL.PrivateKeyDecoder.csproj @@ -0,0 +1,53 @@ + + + + Public Domain + Public Domain + OpenSSL PrivateKey Decoder + OpenSSL.PrivateKey.Decoder + OpenSSL.PrivateKeyDecoder + Parses an OpenSSL private (rsa) key component and returns a RSACryptoServiceProvider. + Parses an OpenSSL private (rsa) key component and returns a RSACryptoServiceProvider. (based on http://www.jensign.com/opensslkey/opensslkey.cs) + 1.0.0.0 + Stef Heyenrath + true + OpenSSL;private;privatekey;key;RSA;decoder;RSACryptoServiceProvider + Initial version + https://github.com/StefH/OpenSSL-X509Certificate2-Provider + https://raw.githubusercontent.com/StefH/OpenSSL-X509Certificate2-Provider/master/LICENSE + https://raw.githubusercontent.com/StefH/OpenSSL-X509Certificate2-Provider/master/images/certificate.png + git + https://github.com/StefH/OpenSSL-X509Certificate2-Provider + false + false + false + false + false + false + false + true + true + en-us + full + ../../images/certificate.ico + + + + net20;net35;net45;netstandard1.3 + OpenSSL.PrivateKeyDecoder + + + + NETSTANDARD + + + + + + + + + + + + \ No newline at end of file diff --git a/src/OpenSSL.PrivateKeyDecoder/OpenSSLPrivateKeyDecoder.cs b/src/OpenSSL.PrivateKeyDecoder/OpenSSLPrivateKeyDecoder.cs new file mode 100644 index 0000000..5abce2e --- /dev/null +++ b/src/OpenSSL.PrivateKeyDecoder/OpenSSLPrivateKeyDecoder.cs @@ -0,0 +1,433 @@ +using System; +using System.IO; +using System.Security; +using System.Security.Cryptography; +using JetBrains.Annotations; + +namespace OpenSSL.PrivateKeyDecoder +{ + /// + /// OpenSSLPrivateKeyDecoder + /// + [PublicAPI] + public sealed class OpenSSLPrivateKeyDecoder : IOpenSSLPrivateKeyDecoder + { + // 1.2.840.113549.1.1.1 - RSA encryption, including the sequence byte and terminal encoded null + private readonly byte[] OIDRSAEncryption = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 }; + + private readonly byte[] OIDpkcs5PBES2 = { 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x05, 0x0D }; + private readonly byte[] OIDpkcs5PBKDF2 = { 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x05, 0x0C }; + + // 1.2.840.113549.3.7 - DES-EDE3-CBC + private readonly byte[] OIDdesEDE3CBC = { 0x06, 0x08, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x03, 0x07 }; + + private const string RSAPrivateKeyHeader = "-----BEGIN RSA PRIVATE KEY-----"; + private const string RSAPrivateKeyFooter = "-----END RSA PRIVATE KEY-----"; + private const string PrivateKeyHeader = "-----BEGIN PRIVATE KEY-----"; + private const string PrivateKeyFooter = "-----END PRIVATE KEY-----"; + private const string PrivateEncryptedKeyHeader = "-----BEGIN ENCRYPTED PRIVATE KEY-----"; + private const string PrivateEncryptedKeyFooter = "-----END ENCRYPTED PRIVATE KEY-----"; + + /// + /// Decode PrivateKey + /// + /// The private (rsa) key text. + /// The optional securePassword to decrypt this private key. + /// RSACryptoServiceProvider + public RSACryptoServiceProvider Decode(string privateText, SecureString password = null) + { + string text = privateText.Trim(); + if (text.StartsWith(RSAPrivateKeyHeader) && text.EndsWith(RSAPrivateKeyFooter)) + { + byte[] data = TextUtil.ExtractBytes(text, RSAPrivateKeyHeader, RSAPrivateKeyFooter); + return DecodeRSAPrivateKey(data); + } + + if (text.StartsWith(PrivateKeyHeader) && text.EndsWith(PrivateKeyFooter)) + { + byte[] data = TextUtil.ExtractBytes(text, PrivateKeyHeader, PrivateKeyFooter); + return DecodePrivateKey(data); + } + + if (text.StartsWith(PrivateEncryptedKeyHeader) && text.EndsWith(PrivateEncryptedKeyFooter)) + { + throw new NotSupportedException("Not yet possible to decrypted a password protected key like : ENCRYPTED PRIVATE KEY"); + } + + throw new NotSupportedException(); + } + + private RSACryptoServiceProvider DecodeEncryptedPrivateKey(byte[] encpkcs8, SecureString securePassword) + { + var memoryStream = new MemoryStream(encpkcs8); + using (var binr = new BinaryReader(memoryStream)) + { + ushort twobytes = binr.ReadUInt16(); + switch (twobytes) + { + case 0x8130: + binr.ReadByte(); // advance 1 byte + break; + case 0x8230: + binr.ReadInt16(); // advance 2 bytes + break; + default: + return null; + } + + twobytes = binr.ReadUInt16(); // inner sequence + switch (twobytes) + { + case 0x8130: + binr.ReadByte(); + break; + case 0x8230: + binr.ReadInt16(); + break; + } + + byte[] seq = binr.ReadBytes(11); + if (!CompareByteArrays(seq, OIDpkcs5PBES2)) // is it a OIDpkcs5PBES2 ? + { + return null; + } + + twobytes = binr.ReadUInt16(); // inner sequence for pswd salt + switch (twobytes) + { + case 0x8130: + binr.ReadByte(); + break; + case 0x8230: + binr.ReadInt16(); + break; + } + + twobytes = binr.ReadUInt16(); // inner sequence for pswd salt + switch (twobytes) + { + case 0x8130: + binr.ReadByte(); + break; + case 0x8230: + binr.ReadInt16(); + break; + } + + seq = binr.ReadBytes(11); // read the Sequence OID + if (!CompareByteArrays(seq, OIDpkcs5PBKDF2)) // is it a OIDpkcs5PBKDF2 ? + { + return null; + } + + twobytes = binr.ReadUInt16(); + switch (twobytes) + { + case 0x8130: + binr.ReadByte(); + break; + case 0x8230: + binr.ReadInt16(); + break; + } + + byte bt = binr.ReadByte(); + if (bt != 0x04) // expect octet string for salt + { + return null; + } + + int saltsize = binr.ReadByte(); + byte[] salt = binr.ReadBytes(saltsize); + + bt = binr.ReadByte(); + if (bt != 0x02) // expect an integer for PBKF2 interation count + { + return null; + } + + int itbytes = binr.ReadByte(); // PBKD2 iterations should fit in 2 bytes. + int iterations; + switch (itbytes) + { + case 1: + iterations = binr.ReadByte(); + break; + case 2: + iterations = 256 * binr.ReadByte() + binr.ReadByte(); + break; + default: + return null; + } + + twobytes = binr.ReadUInt16(); + switch (twobytes) + { + case 0x8130: + binr.ReadByte(); + break; + case 0x8230: + binr.ReadInt16(); + break; + } + + byte[] seqdes = binr.ReadBytes(10); + if (!CompareByteArrays(seqdes, OIDdesEDE3CBC)) // is it a OIDdes-EDE3-CBC ? + { + return null; + } + + bt = binr.ReadByte(); + if (bt != 0x04) // expect octet string for IV + { + return null; + } + + int ivsize = binr.ReadByte(); + byte[] IV = binr.ReadBytes(ivsize); + + bt = binr.ReadByte(); + if (bt != 0x04) // expect octet string for encrypted PKCS8 data + { + return null; + } + + bt = binr.ReadByte(); + int encblobsize; + switch (bt) + { + case 0x81: + encblobsize = binr.ReadByte(); // data size in next byte + break; + case 0x82: + encblobsize = 256 * binr.ReadByte() + binr.ReadByte(); + break; + default: + encblobsize = bt; // we already have the data size + break; + } + + byte[] encryptedpkcs8 = binr.ReadBytes(encblobsize); + byte[] pkcs8 = DecryptPBDK2(encryptedpkcs8, salt, IV, securePassword, iterations); + if (pkcs8 == null) // probably a bad securePassword entered + { + return null; + } + + //----- With a decrypted pkcs #8 PrivateKeyInfo blob, decode it to an RSA --- + return DecodePrivateKey(pkcs8); + } + } + + /// + /// Uses PBKD2 to derive a 3DES key and decrypts data + /// + /// byte array + private byte[] DecryptPBDK2(byte[] edata, byte[] salt, byte[] IV, SecureString securePassword, int iterations) + { + byte[] psbytes = SecureStringUtils.Decrypt(securePassword); + + var rfc2898DeriveBytes = new Rfc2898DeriveBytes(psbytes, salt, iterations); + + var decAlg = TripleDES.Create(); + decAlg.Key = rfc2898DeriveBytes.GetBytes(24); + decAlg.IV = IV; + + var memoryStream = new MemoryStream(); + + using (var decrypt = new CryptoStream(memoryStream, decAlg.CreateDecryptor(), CryptoStreamMode.Write)) + { + decrypt.Write(edata, 0, edata.Length); + decrypt.Flush(); + //decrypt.Close(); // this is REQUIRED + // decrypt.Close(); // this is REQUIRED + } + + return memoryStream.ToArray(); + } + + private RSACryptoServiceProvider DecodePrivateKey(byte[] pkcs8) + { + // read the asn.1 encoded SubjectPublicKeyInfo blob + var memoryStream = new MemoryStream(pkcs8); + int streamLength = (int)memoryStream.Length; + + using (var reader = new BinaryReader(memoryStream)) + { + ushort twobytes = reader.ReadUInt16(); + if (twobytes == 0x8130) // data read as little endian order (actual data order for Sequence is 30 81) + { + reader.ReadByte(); // advance 1 byte + } + else if (twobytes == 0x8230) + { + reader.ReadInt16(); // advance 2 bytes + } + else + { + return null; + } + + byte bt = reader.ReadByte(); + if (bt != 0x02) + { + return null; + } + + twobytes = reader.ReadUInt16(); + if (twobytes != 0x0001) + { + return null; + } + + byte[] seq = reader.ReadBytes(15); + if (!CompareByteArrays(seq, OIDRSAEncryption)) // make sure Sequence for OID is correct + { + return null; + } + + bt = reader.ReadByte(); + if (bt != 0x04) // expect an Octet string + { + return null; + } + + bt = reader.ReadByte(); // read next byte, or next 2 bytes is 0x81 or 0x82; otherwise bt is the byte count + if (bt == 0x81) + { + reader.ReadByte(); + } + else if (bt == 0x82) + { + reader.ReadUInt16(); + } + + // at this stage, the remaining sequence should be the RSA private key + byte[] rsaprivkey = reader.ReadBytes((int)(streamLength - memoryStream.Position)); + return DecodeRSAPrivateKey(rsaprivkey); + } + } + + private RSACryptoServiceProvider DecodeRSAPrivateKey(byte[] privkey) + { + // decode the asn.1 encoded RSA private key + var memoryStream = new MemoryStream(privkey); + using (var reader = new BinaryReader(memoryStream)) + { + ushort twobytes = reader.ReadUInt16(); + if (twobytes == 0x8130) // data read as little endian order (actual data order for Sequence is 30 81) + { + reader.ReadByte(); // advance 1 byte + } + else if (twobytes == 0x8230) + { + reader.ReadInt16(); // advance 2 bytes + } + else + { + return null; + } + + twobytes = reader.ReadUInt16(); + if (twobytes != 0x0102) // version number + { + return null; + } + + byte bt = reader.ReadByte(); + if (bt != 0x00) + { + return null; + } + + // all private key components are Integer sequences + var rsaParameters = new RSAParameters(); + + int elems = GetIntegerSize(reader); + rsaParameters.Modulus = reader.ReadBytes(elems); + + elems = GetIntegerSize(reader); + rsaParameters.Exponent = reader.ReadBytes(elems); + + elems = GetIntegerSize(reader); + rsaParameters.D = reader.ReadBytes(elems); + + elems = GetIntegerSize(reader); + rsaParameters.P = reader.ReadBytes(elems); + + elems = GetIntegerSize(reader); + rsaParameters.Q = reader.ReadBytes(elems); + + elems = GetIntegerSize(reader); + rsaParameters.DP = reader.ReadBytes(elems); + + elems = GetIntegerSize(reader); + rsaParameters.DQ = reader.ReadBytes(elems); + + elems = GetIntegerSize(reader); + rsaParameters.InverseQ = reader.ReadBytes(elems); + + // create RSACryptoServiceProvider instance + var rsaCryptoServiceProvider = new RSACryptoServiceProvider(); + rsaCryptoServiceProvider.ImportParameters(rsaParameters); + return rsaCryptoServiceProvider; + } + } + + private bool CompareByteArrays(byte[] arrayA, byte[] arrayB) + { + if (arrayA.Length != arrayB.Length) + { + return false; + } + + int i = 0; + foreach (byte c in arrayA) + { + if (c != arrayB[i]) + { + return false; + } + i++; + } + + return true; + } + + private int GetIntegerSize(BinaryReader reader) + { + int count; + byte bt = reader.ReadByte(); + if (bt != 0x02) // expect integer + { + return 0; + } + bt = reader.ReadByte(); + + switch (bt) + { + case 0x81: + count = reader.ReadByte(); // data size in next byte + break; + case 0x82: + byte highbyte = reader.ReadByte(); + byte lowbyte = reader.ReadByte(); + byte[] modint = { lowbyte, highbyte, 0x00, 0x00 }; + count = BitConverter.ToInt32(modint, 0); + break; + default: + count = bt; // we already have the data size + break; + } + + while (reader.ReadByte() == 0x00) + { + // remove high order zeros in data + count -= 1; + } + + reader.BaseStream.Seek(-1, SeekOrigin.Current); // last ReadByte wasn't arrayA removed zero, so back up arrayA byte + return count; + } + } +} \ No newline at end of file diff --git a/src/OpenSSL.PrivateKeyDecoder/SecureStringUtils.cs b/src/OpenSSL.PrivateKeyDecoder/SecureStringUtils.cs new file mode 100644 index 0000000..d001efa --- /dev/null +++ b/src/OpenSSL.PrivateKeyDecoder/SecureStringUtils.cs @@ -0,0 +1,70 @@ +using System; +using System.Runtime.InteropServices; +using System.Security; +using JetBrains.Annotations; + +namespace OpenSSL.PrivateKeyDecoder +{ + /// + /// SecureString Utility methods + /// + public static class SecureStringUtils + { + /// + /// Converts a string to a SecureString. + /// + /// The string to encrypt. + /// SecureString + public static SecureString Encrypt(string input) + { + if (input == null) + { + throw new ArgumentNullException(nameof(input)); + } + + var secure = new SecureString(); + foreach (char c in input) + { + secure.AppendChar(c); + } + + secure.MakeReadOnly(); + return secure; + } + + /// + /// Decrypt a securestring into a byte array. + /// + /// The SecureString. + /// byte[] + public static byte[] Decrypt([NotNull] SecureString secure) + { + if (secure == null) + { + throw new ArgumentNullException(nameof(secure)); + } + + byte[] passwordData = new byte[secure.Length]; + + IntPtr unmanagedPassword = IntPtr.Zero; + try + { +#if NETSTANDARD + unmanagedPassword = SecureStringMarshal.SecureStringToGlobalAllocAnsi(secure); +#else + unmanagedPassword = Marshal.SecureStringToGlobalAllocAnsi(secure); +#endif + Marshal.Copy(unmanagedPassword, passwordData, 0, passwordData.Length); + } + finally + { + if (unmanagedPassword != IntPtr.Zero) + { + Marshal.ZeroFreeGlobalAllocAnsi(unmanagedPassword); + } + } + + return passwordData; + } + } +} \ No newline at end of file diff --git a/src/OpenSSL-X509Certificate2-Provider/TextUtil.cs b/src/OpenSSL.PrivateKeyDecoder/TextUtil.cs similarity index 89% rename from src/OpenSSL-X509Certificate2-Provider/TextUtil.cs rename to src/OpenSSL.PrivateKeyDecoder/TextUtil.cs index eab796b..40ccaeb 100644 --- a/src/OpenSSL-X509Certificate2-Provider/TextUtil.cs +++ b/src/OpenSSL.PrivateKeyDecoder/TextUtil.cs @@ -1,6 +1,6 @@ using System; -namespace OpenSSL.X509Certificate2Provider +namespace OpenSSL.PrivateKeyDecoder { internal static class TextUtil {