Клуб Фоток

API

17 июля 2009, 13:22

Может кто-нибудь перепишет RSA библиотеку с С на Java или C#?

10 комментариев
Подписаться на комментарии к посту
Budnyatsky Michel
17 июля 2009, 13:55
А что насчет скорости работы? При переводе с С на Яву неизбежна потеря производительности.
Производительность тут и не нужна. Я просто в С не силён, что б самому переписать. А этот костыль с RSA ставит крест на использование API.
Либо я чего-то не понимаю.
Budnyatsky Michel
17 июля 2009, 14:38
Вообще-то, если я все правильно понимаю, то RSA — это библиотека шифровки, а шифровка, особенно современная, требует повышенной производительности, так что не зря ее написали именно на С.
Производительность здесь действительно не играет роли, т.к. шифруются только пароли. Пробовал аналогичные алгоритмы под AS3, если не кодировать мегабайты – все летает.
Я портировал на C#, только под .NET 3.5 SP1.
поделишься? ;)

Конечно. Пожалуйста.

Про .NET 3.5 наврал. Нужна сборка из Mono - Mono.Security.dll

PublicKey.cs
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Mono.Math;

namespace YamRsa
{
  class PublicKey
  {
    private const int HEX = 16;

    public readonly BigInteger Module;
    public readonly BigInteger Exponent;

    public PublicKey(BigInteger m, BigInteger e)
    {
      Module = m;
      Exponent = e;
    }
    /// 
    /// Requires 0  integer*HEX + int.Parse(new string(new[] {@char}), NumberStyles.AllowHexSpecifier));
    }

    public override string ToString()
    {
      return string.Format("{0}#{1}", Module.ToString(HEX), Exponent.ToString(HEX));
    }
  }
}
CryptoProviderRSA.cs
using System;
using System.Linq;
using Mono.Math;

namespace YamRsa
{
  public class CryptoProviderRSA
  {
    private PublicKey _publicKey;

    public void ImportPublicKey(string key)
    {
      _publicKey = PublicKey.FromString(key);
    }

    private const int MAX_CRYPT_BITS = 1024;

    public byte[] Encrypt(byte[] data)
    {
	    // must ensure that any data block would be < key's modulus
	    // hence -1
	    int portionLen = (_publicKey.Module.BitCount() - 1) / 8;
	    var prevCrypted = new byte[portionLen];

      return Enumerable
        .Range(0, ((data.Length - 1) / portionLen) + 1)
        .Select(i => data.Skip(i * portionLen).Take(portionLen))
        .SelectMany(portion =>
       {
         var portbuf = portion.Select((b, i) => (byte)(b ^ prevCrypted[i])).ToArray();
         var cp = _publicKey.Encrypt(new BigInteger(portbuf)).GetBytes();
         var length = Math.Min(portbuf.Length, cp.Length);
         Array.Copy(cp, prevCrypted, length);
         Array.Clear(prevCrypted, length, prevCrypted.Length - length);
         return BitConverter.GetBytes((ushort)portbuf.Length)
           .Concat(BitConverter.GetBytes((ushort)cp.Length))
           .Concat(cp)
           .ToArray();
       })
       .ToArray();
    }
  }
}
Program.cs
using System;
using System.Linq;
using System.Text;

namespace YamRsa
{
  class Program
  {
    static void Main(string[] args)
    {
      var rsa = new CryptoProviderRSA();
      rsa.ImportPublicKey(args[0]);
      string data = @"";
      var encoded = rsa.Encrypt(Encoding.ASCII.GetBytes(data));
      Console.WriteLine(Convert.ToBase64String(encoded));
      Console.ReadLine();
    }
  }
}
В Program.cs должно быть      
 string data = @"";
Помогите пожалуйста переписать этот кусок кода из файла CryptoProviderRSA.cs без LINQ. Я пишу под .Net 2.0. Сам не могу разобраться никак.

return Enumerable
       .Range(0, ((data.Length - 1) / portionLen) + 1)
       .Select(i => data.Skip(i * portionLen).Take(portionLen))
       .SelectMany(portion =>
      {
        var portbuf = portion.Select((b, i) => (byte)(b ^ prevCrypted[i])).ToArray();
        var cp = _publicKey.Encrypt(new BigInteger(portbuf)).GetBytes();
        var length = Math.Min(portbuf.Length, cp.Length);
        Array.Copy(cp, prevCrypted, length);
        Array.Clear(prevCrypted, length, prevCrypted.Length - length);
        return BitConverter.GetBytes((ushort)portbuf.Length)
          .Concat(BitConverter.GetBytes((ushort)cp.Length))
          .Concat(cp)
          .ToArray();
      })
      .ToArray();
Переписал весь код под .Net Framework 2.0. Весь код исключительно в исходниках. Ведь мы с вами устаревшие люди :-D. Не хотим использовать .Net Framework 4.0. Там есть и Linq и BigInteger. Нам подавай живой код на самой первой и любимой платформе)))
Убрал весь код на Linq и всякие там var'ы.
Итак, вот оно!
class PublicKey
    {
        private const int HEX = 16;
        public readonly BigInteger Module;
        public readonly BigInteger Exponent;
        public PublicKey(BigInteger m, BigInteger e)
        {
            Module = m;
            Exponent = e;
        }
        public BigInteger Encrypt(BigInteger plain)
        {
            return plain.ModPow(Exponent, Module);
        }
        public static PublicKey FromString(string me)
        {
            string[] pair = me.Split(new[] { '#' }, 2);
            if (pair.Length != 2)
                throw new ArgumentException("Wrong format", "me");
            return new PublicKey(ParseHex(pair[0]), ParseHex(pair[1]));
        }
        private static BigInteger ParseHex(string hexString)
        {
            BigInteger result = new BigInteger();
            foreach (char c in hexString)
            {
                result = result * HEX + int.Parse(c.ToString(), NumberStyles.AllowHexSpecifier);
            }
            return result;
            //return hexString.Aggregate(new BigInteger(),
            //  (integer, c) => integer * HEX + int.Parse(c.ToString(), NumberStyles.AllowHexSpecifier));
        }
        /*Реализация ParseHex на Linq
        private static BigInteger ParseHex(string hexString)
        {
           return hexString.Aggregate(new BigInteger(),
              (integer, c) => integer * HEX + int.Parse(c.ToString(), NumberStyles.AllowHexSpecifier));
        }*/

        public override string ToString()
        {
            return string.Format("{0}#{1}", Module.ToString(HEX), Exponent.ToString(HEX));
        }
    }
    public class CryptoProviderRSA
    {
        private PublicKey _publicKey;
        public void ImportPublicKey(string key)
        {
            _publicKey = PublicKey.FromString(key);
        }
        private const int MAX_CRYPT_BITS = 1024;
        public byte[] Encrypt(byte[] data)
        {
            int portionLen = (_publicKey.Module.BitCount() - 1) / 8;
            byte[] prevCrypted = new byte[portionLen];
            System.Collections.Generic.List result = new List();
            int in_size = data.Length;
            while (in_size > 0)
            {
                int cur_size = in_size > portionLen ? portionLen : in_size;
                byte[] portbuf = new byte[cur_size];
                for (int i = 0; i < cur_size; i++)
                    portbuf[i] = (byte)(data[i] ^ prevCrypted[i]);
                byte[] cp = _publicKey.Encrypt(new BigInteger(portbuf)).GetBytes();
                int length = Math.Min(portbuf.Length, cp.Length);
                //for (int i = 0; i < portionLen; i++)
                //    prevCrypted[i] = i < length ? cp[i] : (byte)0;
                Array.Copy(cp, prevCrypted, length);
                Array.Clear(prevCrypted, length, prevCrypted.Length - length);

                byte[] p = BitConverter.GetBytes((ushort)portbuf.Length);
                byte[] c = BitConverter.GetBytes((ushort)cp.Length);
                byte[] temp = new byte[p.Length + c.Length + cp.Length];
                Array.Copy(p, 0, temp, 0, p.Length);
                Array.Copy(c, 0, temp, p.Length, c.Length);
                Array.Copy(cp, 0, temp, p.Length + c.Length, cp.Length);
                result.AddRange(temp);
                in_size -= cur_size;
            }
            return result.ToArray();
        }
        /*Реализация Encrypt на Linq
        public byte[] Encrypt(byte[] data)
        {
            int portionLen = (_publicKey.Module.BitCount() - 1) / 8;
            byte[] prevCrypted = new byte[portionLen];
            return Enumerable
              .Range(0, ((data.Length - 1) / portionLen) + 1)
              .Select(i => data.Skip(i * portionLen).Take(portionLen))
              .SelectMany(portion =>
              {
                  var portbuf = portion.Select((b, i) => (byte)(b ^ prevCrypted[i])).ToArray();
                  var cp = _publicKey.Encrypt(new BigInteger(portbuf)).GetBytes();
                  var length = Math.Min(portbuf.Length, cp.Length);
                  Array.Copy(cp, prevCrypted, length);
                  Array.Clear(prevCrypted, length, prevCrypted.Length - length);
                  return BitConverter.GetBytes((ushort)portbuf.Length)
                    .Concat(BitConverter.GetBytes((ushort)cp.Length))
                    .Concat(cp)
                    .ToArray();
              })
             .ToArray();

         * //int portionLen = (_publicKey.Module.BitCount() - 1) / 8;
            //byte[] prevCrypted = new byte[portionLen];
            //System.Collections.Generic.List result = new List();
            //for (int range = 0; range < ((data.Length - 1) / portionLen) + 1; range++)//Range
            //{
            //    byte[] take = new byte[portionLen];
            //    Array.Copy(data, range * portionLen, take, 0, portionLen);//Take
            //    byte[] portbuf = new byte[portionLen];
            //    for (int select = 0; select < portionLen; select++)
            //    {

            //    }
            //}
            //return result.ToArray();
        }*/
    }
Проблема с Mono библиотекой (Mono.Security.dll) решена использованием исходного кода относящегося именно к BigInterer.
Скачать исходный код BigInteger для .Net Framework 2.0
Добавляете прям целую папку с тамошними исходниками. Подключается через "using System.Math;". Я подумал, что BigInteger это ведь математический тип. Поэтому и вкрутил его в Math. Конфликтов с namespace не возникает, проверено.
Конечно можно работать с .Net Framework 4.0. Там есть BigInteger, но решил оставать на легковесном втором. Потому что и старые сервачки Windows Server 2003 и Windwos 2000 работают вроде как только со вторым. Ну и совместимость больше. Народ опять-таки под Mono сможет заюзать всё это дело.

На основе этого же кода уже начал разработку библиотеки на C# 2.0. Уже сделана авторизация и добавление альбома. Всё в объектном стиле, как и описывается в руководстве.
О более-менее работоспособных версиях буду сообщать здесь. И собственно выкладывать ссылки на них - тоже тут =)
До встречи!
P.S.: А Linq это жесть.. пол ночи разбирал с нуля эту технологию и по шагам переделывал код. Конечно лаконичный код. И быстрый... но разобраться в нём - чёрт ногу сломит)))