From 2746e44d62b031d0981d2f6b1bc3c7d2a34d64ec Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Sun, 6 Nov 2022 21:18:24 +0700 Subject: Add some dotnet ECDsa interop methods --- crypto/src/security/DotNetUtilities.cs | 118 +++++++++++++++++------- crypto/test/src/security/test/TestDotNetUtil.cs | 32 ++++++- 2 files changed, 117 insertions(+), 33 deletions(-) (limited to 'crypto') diff --git a/crypto/src/security/DotNetUtilities.cs b/crypto/src/security/DotNetUtilities.cs index 3a7c5f0cb..08853e45e 100644 --- a/crypto/src/security/DotNetUtilities.cs +++ b/crypto/src/security/DotNetUtilities.cs @@ -5,9 +5,12 @@ using System.Runtime.Versioning; using System.Security.Cryptography; using SystemX509 = System.Security.Cryptography.X509Certificates; +using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.Pkcs; using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Asn1.X9; using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Generators; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Math; using Org.BouncyCastle.Utilities; @@ -50,23 +53,11 @@ namespace Org.BouncyCastle.Security public static AsymmetricCipherKeyPair GetDsaKeyPair(DSAParameters dp) { - DsaValidationParameters validationParameters = (dp.Seed != null) - ? new DsaValidationParameters(dp.Seed, dp.Counter) - : null; - - DsaParameters parameters = new DsaParameters( - new BigInteger(1, dp.P), - new BigInteger(1, dp.Q), - new BigInteger(1, dp.G), - validationParameters); - - DsaPublicKeyParameters pubKey = new DsaPublicKeyParameters( - new BigInteger(1, dp.Y), - parameters); + DsaPublicKeyParameters pubKey = GetDsaPublicKey(dp); DsaPrivateKeyParameters privKey = new DsaPrivateKeyParameters( new BigInteger(1, dp.X), - parameters); + pubKey.Parameters); return new AsymmetricCipherKeyPair(pubKey, privKey); } @@ -93,6 +84,62 @@ namespace Org.BouncyCastle.Security parameters); } +#if NETCOREAPP1_0_OR_GREATER || NET47_OR_GREATER || NETSTANDARD1_6_OR_GREATER + public static AsymmetricCipherKeyPair GetECDsaKeyPair(ECDsa ecDsa) + { + return GetECKeyPair("ECDSA", ecDsa.ExportParameters(true)); + } + + public static ECPublicKeyParameters GetECDsaPublicKey(ECDsa ecDsa) + { + return GetECPublicKey("ECDSA", ecDsa.ExportParameters(false)); + } + + public static AsymmetricCipherKeyPair GetECKeyPair(string algorithm, ECParameters ec) + { + ECPublicKeyParameters pubKey = GetECPublicKey(algorithm, ec); + + ECPrivateKeyParameters privKey = new ECPrivateKeyParameters( + pubKey.AlgorithmName, + new BigInteger(1, ec.D), + pubKey.Parameters); + + return new AsymmetricCipherKeyPair(pubKey, privKey); + } + + public static ECPublicKeyParameters GetECPublicKey(string algorithm, ECParameters ec) + { + X9ECParameters x9 = GetX9ECParameters(ec.Curve); + if (x9 == null) + throw new NotSupportedException("Unrecognized curve"); + + return new ECPublicKeyParameters( + algorithm, + GetECPoint(x9.Curve, ec.Q), + new ECDomainParameters(x9)); + } + + private static Math.EC.ECPoint GetECPoint(Math.EC.ECCurve curve, ECPoint point) + { + return curve.CreatePoint(new BigInteger(1, point.X), new BigInteger(1, point.Y)); + } + + private static X9ECParameters GetX9ECParameters(ECCurve curve) + { + if (!curve.IsNamed) + throw new NotSupportedException("Only named curves are supported"); + + Oid oid = curve.Oid; + if (oid != null) + { + string oidValue = oid.Value; + if (oidValue != null) + return ECKeyPairGenerator.FindECCurveByOid(new DerObjectIdentifier(oidValue)); + } + return null; + } +#endif + public static AsymmetricCipherKeyPair GetRsaKeyPair(RSA rsa) { return GetRsaKeyPair(rsa.ExportParameters(true)); @@ -100,17 +147,11 @@ namespace Org.BouncyCastle.Security public static AsymmetricCipherKeyPair GetRsaKeyPair(RSAParameters rp) { - BigInteger modulus = new BigInteger(1, rp.Modulus); - BigInteger pubExp = new BigInteger(1, rp.Exponent); - - RsaKeyParameters pubKey = new RsaKeyParameters( - false, - modulus, - pubExp); + RsaKeyParameters pubKey = GetRsaPublicKey(rp); RsaPrivateCrtKeyParameters privKey = new RsaPrivateCrtKeyParameters( - modulus, - pubExp, + pubKey.Modulus, + pubKey.Exponent, new BigInteger(1, rp.D), new BigInteger(1, rp.P), new BigInteger(1, rp.Q), @@ -137,17 +178,18 @@ namespace Org.BouncyCastle.Security public static AsymmetricCipherKeyPair GetKeyPair(AsymmetricAlgorithm privateKey) { - if (privateKey is DSA) - { - return GetDsaKeyPair((DSA)privateKey); - } + if (privateKey is DSA dsa) + return GetDsaKeyPair(dsa); - if (privateKey is RSA) - { - return GetRsaKeyPair((RSA)privateKey); - } +#if NETCOREAPP1_0_OR_GREATER || NET47_OR_GREATER || NETSTANDARD1_6_OR_GREATER + if (privateKey is ECDsa ecDsa) + return GetECDsaKeyPair(ecDsa); +#endif - throw new ArgumentException("Unsupported algorithm specified", "privateKey"); + if (privateKey is RSA rsa) + return GetRsaKeyPair(rsa); + + throw new ArgumentException("Unsupported algorithm specified", nameof(privateKey)); } #if NET5_0_OR_GREATER @@ -244,6 +286,18 @@ namespace Org.BouncyCastle.Security return BigIntegers.AsUnsignedByteArray(size, n); } + // TODO Why do we use CspParameters instead of just RSA.Create in methods below? +// private static RSA CreateRSA(RSAParameters rp) +// { +//#if NETCOREAPP2_0_OR_GREATER || NET472_OR_GREATER || NETSTANDARD2_1_OR_GREATER +// return RSA.Create(rp); +//#else +// var rsa = RSA.Create(); +// rsa.ImportParameters(rp); +// return rsa; +//#endif +// } + #if NET5_0_OR_GREATER [SupportedOSPlatform("windows")] #endif diff --git a/crypto/test/src/security/test/TestDotNetUtil.cs b/crypto/test/src/security/test/TestDotNetUtil.cs index b83a94a36..5d0177ead 100644 --- a/crypto/test/src/security/test/TestDotNetUtil.cs +++ b/crypto/test/src/security/test/TestDotNetUtil.cs @@ -22,6 +22,36 @@ namespace Org.BouncyCastle.Security.Tests [TestFixture] public class TestDotNetUtilities { +//#if NETCOREAPP1_0_OR_GREATER || NET47_OR_GREATER || NETSTANDARD1_6_OR_GREATER +#if NET6_0_OR_GREATER + [Test] + public void TestECDsaInterop() + { + byte[] data = new byte[1024]; + + for (int i = 0; i < 10; ++i) + { + var ecDsa = ECDsa.Create(ECCurve.NamedCurves.nistP256); + byte[] sig1 = ecDsa.SignData(data, HashAlgorithmName.SHA256); + + AsymmetricCipherKeyPair kp = DotNetUtilities.GetECDsaKeyPair(ecDsa); + Assert.IsNotNull(kp.Private); + Assert.IsNotNull(kp.Public); + ISigner signer = SignerUtilities.GetSigner("SHA256withPLAIN-ECDSA"); + + signer.Init(false, kp.Public); + signer.BlockUpdate(data, 0, data.Length); + Assert.IsTrue(signer.VerifySignature(sig1)); + + signer.Init(true, kp.Private); + signer.BlockUpdate(data, 0, data.Length); + byte[] sig2 = signer.GenerateSignature(); + + Assert.IsTrue(ecDsa.VerifyData(data, sig2, HashAlgorithmName.SHA256)); + } + } +#endif + //#if NET5_0_OR_GREATER #if NET6_0_OR_GREATER [SupportedOSPlatform("windows")] @@ -29,7 +59,7 @@ namespace Org.BouncyCastle.Security.Tests [Test] public void TestRsaInterop() { - for (int i = 0; i < 100; ++i) + for (int i = 0; i < 10; ++i) { RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(512); RSAParameters rp = rsa.ExportParameters(true); -- cgit 1.4.1