Forum Xamarin.iOS

Elliptic Curve Cryptography

CaseCase USMember ✭✭✭

I need to use FIPS 140 compliant ECC cross platform. For a number of reasons, I can't use third-party libraries like BouncyCastle, so I am forced to use the underlying Crypto Service Provided on the Operating System. Obviously, I'll be using platform specific code for all of this.

I've got Windows, NetCore (for a Raspberry Pi), and Android (KNOX enabled Samsung device) working, but am having a hard time figuring out where/how to start for iOS.
The Android Device is a Samsung with KNOX and is using factory installed OpenSSL libraries. We've installed OpenSSL onto the Raspberry Pi and that is working as well. On Windows, the registry is set to use FIPS compliant algorithms and all is working. I

t's iOS I can't figure out!

note: None of this is designed for efficiency (yet), just focusing on getting it work first.

Some shared classes:

public class EcPrivateKey
{
    public byte[] Value;
    public byte[] ValueX;
    public byte[] ValueY;

    public EcPrivateKey(byte[] value, byte[] valueX, byte[] valueY)
    {
        Value = value;
        ValueX = valueX;
        ValueY = valueY;
    }
}

public class EcPublicKey
{
    public byte[] ValueX;
    public byte[] ValueY;

    public EcPublicKey() { }

    public EcPublicKey(byte[] x, byte[] y)
    {
        ValueX = x;
        ValueY = y;
    }
}

For UWP and NetCore

public (EcPublicKey, EcPrivateKey) EcGenerateKeys()
{
    using (var ec = ECDsa.Create(ECCurve.NamedCurves.nistP521))
    {
        var keys = ec.ExportExplicitParameters(true);
        var ecPublicKey = new EcPublicKey(keys.Q.X, keys.Q.Y);
        var ecPrivateKey = new EcPrivateKey(keys.D, keys.Q.X, keys.Q.Y);
        return (ecPublicKey, ecPrivateKey);
    }
}

public byte[] EcSign(byte[] data, EcPrivateKey key)
{
    var ecParameters = new ECParameters
    {
        D = key.Value,
        Q = new ECPoint
        {
            X = key.ValueX,
            Y = key.ValueY
        },
        Curve = ECCurve.NamedCurves.nistP521
    };
    using (var ec = ECDsa.Create(ecParameters))
    {
        return ec.SignData(data, HashAlgorithmName.SHA512);
    }
}

public bool EcVerify(byte[] data, byte[] signature, EcPublicKey key)
{
    var point = new ECPoint
    {
        X = key.ValueX,
        Y = key.ValueY
    };
    var ecParameters = new ECParameters
    {
        Q = point,
        Curve = ECCurve.NamedCurves.nistP521
    };
    using (var ec = ECDsa.Create(ecParameters))
    {
        return ec.VerifyData(data, signature, HashAlgorithmName.SHA512);
    }
}

For Android

public (EcPublicKey, EcPrivateKey) EcGenerateKeys()
{
    var keyPairGenerator = KeyPairGenerator.GetInstance("EC");
    keyPairGenerator.Initialize(new ECGenParameterSpec("secp521r1"));
    var keyPair = keyPairGenerator.GenerateKeyPair();
    var keyFactory = KeyFactory.GetInstance("EC");

    var publicSpec = Class.ForName("java.security.spec.ECPublicKeySpec");
    var publicKeySpec = (ECPublicKeySpec)keyFactory.GetKeySpec(keyPair.Public, publicSpec);
    var w = publicKeySpec.GetW();
    var wx = w.AffineX.ToByteArray();
    var wy = w.AffineY.ToByteArray();
    var ecPublicKey = new EcPublicKey(wx, wy);

    var privateSpec = Class.ForName("java.security.spec.ECPrivateKeySpec");
    var privateKeySpec = (ECPrivateKeySpec)keyFactory.GetKeySpec(keyPair.Private, privateSpec);
    var s = privateKeySpec.GetS();
    var ecPrivateKey = new EcPrivateKey(s.ToByteArray(), wx, wy);
    return (ecPublicKey, ecPrivateKey);
}

public byte[] EcSign(byte[] data, EcPrivateKey key)
{
    var algorithmParameters = AlgorithmParameters.GetInstance("EC");
    algorithmParameters.Init(new ECGenParameterSpec("secp521r1"));
    var algorithmSpec = Class.ForName("java.security.spec.ECParameterSpec");
    var ecParameterSpec = (ECParameterSpec) algorithmParameters.GetParameterSpec(algorithmSpec);
    var ecPrivateKeySpec = new ECPrivateKeySpec(new BigInteger(key.Value), ecParameterSpec);
    var keyFactory = KeyFactory.GetInstance("EC");
    var privateKey = keyFactory.GeneratePrivate(ecPrivateKeySpec); 
    var ecSignature = Signature.GetInstance("SHA512withECDSA");
    ecSignature.InitSign(privateKey);
    ecSignature.Update(data);
    return ecSignature.Sign();
}

public bool EcVerify(byte[] data, byte[] signature, EcPublicKey key)
{
    var algorithmParameters = AlgorithmParameters.GetInstance("EC");
    algorithmParameters.Init(new ECGenParameterSpec("secp521r1"));
    var algorithmSpec = Class.ForName("java.security.spec.ECParameterSpec");
    var ecParameterSpec = (ECParameterSpec) algorithmParameters.GetParameterSpec(algorithmSpec);
    var ecPublicKeySpec =
        new ECPublicKeySpec(new ECPoint(new BigInteger(key.ValueX), new BigInteger(key.ValueY)),
            ecParameterSpec);
    var keyFactory = KeyFactory.GetInstance("EC");
    var publicKey = keyFactory.GeneratePublic(ecPublicKeySpec);
    var ecSignature = Signature.GetInstance("SHA512withECDSA");
    ecSignature.InitVerify(publicKey);
    ecSignature.Update(data);
    return ecSignature.Verify(signature);
}
Tagged:

Best Answer

  • CaseCase USMember ✭✭✭
    Accepted Answer

    For anyone else looking at this, here's what I came up with. Again, not optimized, just working!

        public (EcPublicKey, EcPrivateKey) EcGenerateKeys()
        {
            var parameters = new SecKeyGenerationParameters
            {
                IsPermanent = false,
                KeySizeInBits = 521,
                KeyType = SecKeyType.ECSecPrimeRandom,
                PublicKeyAttrs = new SecKeyParameters
                {
                    IsPermanent = false
                },
                PrivateKeyAttrs = new SecKeyParameters
                {
                    IsPermanent = false
                }
            };
            SecKey.GenerateKeyPair(parameters.Dictionary, out var rawPublicKey, out var rawPrivateKey);
            var privateData = rawPrivateKey.GetExternalRepresentation(out var error);
            var privateStream = privateData.AsStream();
            byte[] privateBytes;
            using (var memoryStream = new MemoryStream())
            {
                privateStream.CopyTo(memoryStream);
                var temp = memoryStream.ToArray();
                privateBytes = new byte[temp.Length-1];
                Array.Copy(temp, 1, privateBytes, 0, temp.Length - 1);
            }
            var pointSize = privateBytes.Length / 3;
            var xBytes = new byte[pointSize];
            var yBytes = new byte[pointSize];
            var dBytes = new byte[pointSize];
            Array.Copy(privateBytes, 0, xBytes, 0, pointSize);
            Array.Copy(privateBytes, pointSize, yBytes, 0, pointSize);
            Array.Copy(privateBytes, 2 * pointSize, dBytes, 0, pointSize);
            var publicKey = new EcPublicKey(xBytes, yBytes);
            var privateKey = new EcPrivateKey(dBytes, xBytes, yBytes);
            return (publicKey, privateKey);
        }
    
        public byte[] EcSign(byte[] data, EcPrivateKey key)
        {
            var pointSize = key.Value.Length;
            var keyData = new byte[1 + 3 * pointSize];
            keyData[0] = 0x04;
            Array.Copy(key.ValueX, 0, keyData, 1, pointSize);
            Array.Copy(key.ValueY, 0, keyData, 1 + pointSize, pointSize);
            Array.Copy(key.Value, 0, keyData, 1 + 2 * pointSize, pointSize);
            var parameters = new SecKeyParameters
            {
                IsPermanent = false,
                CanSign = true
            };
            var keyReference = SecKey.Create(NSData.FromArray(keyData), SecKeyType.EC, SecKeyClass.Private, 521,
                parameters.Dictionary, out var error);
            var signature = keyReference.CreateSignature(SecKeyAlgorithm.EcdsaSignatureMessageX962Sha512, NSData.FromArray(data), out error);
            return signature.ToArray();
        }
    
        public bool EcVerify(byte[] data, byte[] signature, EcPublicKey key)
        {
            var pointSize = key.ValueX.Length;
            var keyData = new byte[1 + 2 * pointSize];
            keyData[0] = 0x04;
            Array.Copy(key.ValueX, 0, keyData, 1, pointSize);
            Array.Copy(key.ValueY, 0, keyData, 1 + pointSize, pointSize);
            var parameters = new SecKeyParameters
            {
                IsPermanent = false,
                CanVerify = true
            };
            var keyReference = SecKey.Create(NSData.FromArray(keyData), SecKeyType.EC, SecKeyClass.Public, 521,
                parameters.Dictionary, out var error);
            return keyReference.VerifySignature(SecKeyAlgorithm.EcdsaSignatureMessageX962Sha512, NSData.FromArray(data),
                NSData.FromArray(signature), out error);
        }
    

Answers

  • CaseCase USMember ✭✭✭
    Accepted Answer

    For anyone else looking at this, here's what I came up with. Again, not optimized, just working!

        public (EcPublicKey, EcPrivateKey) EcGenerateKeys()
        {
            var parameters = new SecKeyGenerationParameters
            {
                IsPermanent = false,
                KeySizeInBits = 521,
                KeyType = SecKeyType.ECSecPrimeRandom,
                PublicKeyAttrs = new SecKeyParameters
                {
                    IsPermanent = false
                },
                PrivateKeyAttrs = new SecKeyParameters
                {
                    IsPermanent = false
                }
            };
            SecKey.GenerateKeyPair(parameters.Dictionary, out var rawPublicKey, out var rawPrivateKey);
            var privateData = rawPrivateKey.GetExternalRepresentation(out var error);
            var privateStream = privateData.AsStream();
            byte[] privateBytes;
            using (var memoryStream = new MemoryStream())
            {
                privateStream.CopyTo(memoryStream);
                var temp = memoryStream.ToArray();
                privateBytes = new byte[temp.Length-1];
                Array.Copy(temp, 1, privateBytes, 0, temp.Length - 1);
            }
            var pointSize = privateBytes.Length / 3;
            var xBytes = new byte[pointSize];
            var yBytes = new byte[pointSize];
            var dBytes = new byte[pointSize];
            Array.Copy(privateBytes, 0, xBytes, 0, pointSize);
            Array.Copy(privateBytes, pointSize, yBytes, 0, pointSize);
            Array.Copy(privateBytes, 2 * pointSize, dBytes, 0, pointSize);
            var publicKey = new EcPublicKey(xBytes, yBytes);
            var privateKey = new EcPrivateKey(dBytes, xBytes, yBytes);
            return (publicKey, privateKey);
        }
    
        public byte[] EcSign(byte[] data, EcPrivateKey key)
        {
            var pointSize = key.Value.Length;
            var keyData = new byte[1 + 3 * pointSize];
            keyData[0] = 0x04;
            Array.Copy(key.ValueX, 0, keyData, 1, pointSize);
            Array.Copy(key.ValueY, 0, keyData, 1 + pointSize, pointSize);
            Array.Copy(key.Value, 0, keyData, 1 + 2 * pointSize, pointSize);
            var parameters = new SecKeyParameters
            {
                IsPermanent = false,
                CanSign = true
            };
            var keyReference = SecKey.Create(NSData.FromArray(keyData), SecKeyType.EC, SecKeyClass.Private, 521,
                parameters.Dictionary, out var error);
            var signature = keyReference.CreateSignature(SecKeyAlgorithm.EcdsaSignatureMessageX962Sha512, NSData.FromArray(data), out error);
            return signature.ToArray();
        }
    
        public bool EcVerify(byte[] data, byte[] signature, EcPublicKey key)
        {
            var pointSize = key.ValueX.Length;
            var keyData = new byte[1 + 2 * pointSize];
            keyData[0] = 0x04;
            Array.Copy(key.ValueX, 0, keyData, 1, pointSize);
            Array.Copy(key.ValueY, 0, keyData, 1 + pointSize, pointSize);
            var parameters = new SecKeyParameters
            {
                IsPermanent = false,
                CanVerify = true
            };
            var keyReference = SecKey.Create(NSData.FromArray(keyData), SecKeyType.EC, SecKeyClass.Public, 521,
                parameters.Dictionary, out var error);
            return keyReference.VerifySignature(SecKeyAlgorithm.EcdsaSignatureMessageX962Sha512, NSData.FromArray(data),
                NSData.FromArray(signature), out error);
        }
    
Sign In or Register to comment.