вторник, 4 февраля 2025 г.

Про брутфорс, шифрование, подпись

Клиент-серверные приложения постоянно общаются друг с другом по сети обычно по HTTP(S) протоколу. Запрос - ответ. Часто в запросе передаются некие параметры к серверу: логин/пароль, сумма денег, номер страницы, номер товара и прочее. Сервер получает эти параметры, отправляет их в базу данных, получает результат и формирует ответ для отправившего запрос пользователя.
Выглядит всё красиво, логично и... безопасно?
А что если...
А что если у вас некий Вася Пупкин плохо написал серверную часть (бэкенд)?
Он принимает на веру все данные, которые ему отправил пользователь за чистую монету и с этими данными дальше работает.
Допустим, Вася написал у себя на сервере такой код:
- получить с формы данных сайта сумму для списания денег со счёта пользователя и записать это значение в переменную amount;
- найти в базе данных пользователя с указанным userID;
- вычесть эту сумму у пользователя. Например, так:
  SET user_amount = user_amount - @amount FROM users WHERE user_id = @userID
Выглядит логично.
Но.
Наш Вася не делает никаких проверок на вводимые данные.
Про SQL-Injection мы поговорим позже, сейчас мы о другом.
А что, если пользователь (который теперь уже в наших глазах становится злоумышленником), вместо отправки вам 10 долларов введёт -10 (минус десять) делларов?
Что тогда произойдёт?
Давайте снова посмотрим на код Васи Пупкина:
SET user_amount = user_amount - (-10) From users ...
Понимаете теперь? 
Минус на минус даёт плюс! Злоумышленнику вместо списания 10 долларов они наоборот начислятся! И он станет на 10 долларов богаче. Ну почему на 10? Что мешает вместо 10 долларов ввести 100 или... Ну дальше вы сами мечтайте...
Как от этого защититься?
Ну, первое правило - это фильтрация всех входных данных. Нельзя вводить ничего кроме цифр, нельзя вводить отрицательные числа, спецсимволы, ввести ограничение на большие числа, и на маленькие тоже. Проверяйте всё.
Это нужно делать как на стороне фронтенда (сайта), так и на стороне бэкенда, когда вы получаете данные от клиента. Не забывайте, что клиент-злоумышленник может обойти первичную защиту на фронтенде и вы априори не должны доверять тем данным, которые к вам приходят.

Подпись и MITM.

Ну хорошо. У нас все защиты и проверки стоят, всё замечательно.
Тётя Клава приходит на ваш сайт, вводит сумму для оплаты товара на 1 доллар, но у неё со счёта списывается 100 долларов. И при этом эти деньги уходят какому-то другому пользователю этого сервиса! Как такое возможно?
Поверьте, что такое часто было в конце 1990-ч - начале 2000-х годов, когда на сайтах не было HTTPS и была возможность MITM-атак (Man-In-The-Middle).
Пока не изобрели HTTPS платёжные системы защищались с помощью цифровой подписи.
Давайте разберёмся, как же украли деньги у тёти Клавы.
Представим ситуацию, что тётя Клава пришла в свою любимую кафешку с бесплатным вайфаем, подключилась к нему и сёрфит в интернетике. Но в этой кафешке есть злой хакер-админ, который видит всё, что пишет и отправляет наша Клава в интернет (не забывайте, у нас нет безопасного HTTPS, есть только HTTP). Но мало того - он может подменять все данные, которые она отправляет в сеть, и подменять то, что она получает. Допустим, тётя Клава делает покупку на 1 доллар, а злой-хакер заменяет 1 на 100 долларов и в итоге с её счёта списывается 100 долларов вместо 1.
Умные программисты придумали способ как от этой проблемы защититься.
И придумали цифровую подпись.
... дальше про хэши
про коллизии
про брутфорс
ограничение на вводимые данные, защита от радужных таблиц

Week 05, Game 05 - Pairs

четверг, 23 января 2025 г.

четверг, 16 января 2025 г.

Week 03, Game 03 - Tetris

 Пошла третья неделя 2005 года, пишу вторую игру на движке YAGEC (Yet Another Mobile Engine - Canvas).

Теперь это "Tetris"

среда, 8 января 2025 г.

Week 02, Game 02 - Sea Battle

 08.01.2025

Пошла вторая неделя, пишу вторую игру на движке YAGEC (Yet Another Mobile Engine - Canvas).

Теперь это "Морской Бой" - "Sea Battle"


воскресенье, 5 января 2025 г.

Project 2025 - "One Week - One Game", Week 01, Game 01 - Checkers

 Запускаю челлендж на 2025 - одна мобильная игра на Android в неделю!

Пошла первая неделя. И это будет игра "Шашки"


понедельник, 14 декабря 2020 г.

Редактируемое поле в React Native

export type EditOrderScreenState = {
comment: string;
isEditing: number;
address: string;
dataChanged: boolean;
}

export default class EditOrderScreen extends Component<EditOrderScreenProps, EditOrderScreenState> {

order: OfficeRequest;

state: EditOrderScreenState = {
comment: "",
isEditing: 0,
address: "Address",
dataChanged: false,
}
...
<TouchableOpacity style={listStyles.listItem}
onPress={() => this.setState({dataChanged: false, isEditing: 1})}>
<Text>Адрес: </Text>
{this.state.isEditing === 1 ?
<TextInput
value={this.state.address}
onChangeText={(value) => {
this.setState({address: value})
}}
autoFocus
onBlur={() => {
this.setState({dataChanged: true, isEditing: 0})
}}
/> :
<Text onPress={() => this.setState({
dataChanged: false,
isEditing: 1
})}>{this.state.address}</Text>
}
</TouchableOpacity>
Вызов из другого окна:
\<View style={{width: '100%', paddingHorizontal: 16, paddingVertical: 16, paddingTop: 50}}>
    <AppButton onPress={() => {
console.log("Edit PVZ");
this.props.navigation.navigate('EditOrder', {order: this.order});
}} title={'Редактировать'} size={'full'} type={'primary'}/>
</View>

вторник, 27 августа 2013 г.

Android: проблема с доступом к сети в разных версиях API

Решение:

if (android.os.Build.VERSION.SDK_INT > 9) {
            StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
            StrictMode.setThreadPolicy(policy);
        }

Данный кусок кода ставим в onCreate.
И не забываем про <uses-permission android:name="android.permission.INTERNET" /> в манифесте.

вторник, 23 июля 2013 г.

Подводные камни в Java

Иногда бывает засада в простых вещах, но которые сложно отловить и отдебажить.
Например, byte:

Java byte     -128..127       8 bits
C#   byte     0..255             8 bits

Это надо учитывать при битовых операциях и при работе с шестнадцатеричными числами.
Скажем, 0xd3 в C# - это 211, а в Java -83

Ещё одна вещь, которая может содержать потенциальную ошибку заключается в том, что если в Java-коде у вас число начинается с 0, то оно преобразуется в восьмеричное число (кто придумал этот бред?). Поэтому, если вы напишете 0123, (предполагая, что это будет десятичное 123), то будьте уверены, что это это будет восьмеричное 123, которое в десятичном виде является числом 173.

понедельник, 6 мая 2013 г.

Правильно читаем кириллицу в UTF-8 из JSON-ответа. Десериализация JSON

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Web;
using System.Xml;

....

[DataContract]
        public class iiiText
        {
            [DataMember]
            public String value;
            [DataMember]
            public String delay;
            [DataMember]
            public String status;
            [DataMember]
            public String showRate;
        }

        [DataContract]
        public class iiiAnimation
        {
            [DataMember]
            public String type;
            [DataMember]
            public String duration;
            [DataMember]
            public String isFaded;
        }

        [DataContract]
        public class iiiResult
        {
            [DataMember]
            public iiiText text;
            [DataMember]
            public iiiAnimation animation;
            [DataMember]
            public String navigation;
            [DataMember]
            public String token;
            [DataMember]
            public String showExpSys;
            [DataMember]
            public String rubric;
        }

        [DataContract]
        public class iiiClass
        {
            [DataMember]
            public iiiResult result;
            [DataMember]
            public String id;
        }

        public class JSONHelper
        {
            public static T Deserialise(string json)
            {
                using (var ms = new MemoryStream(Encoding.Unicode.GetBytes(json)))
                {
                    var serialiser = new DataContractJsonSerializer(typeof(T));
                    return (T)serialiser.ReadObject(ms);
                }
            }

        }


static void Main(string[] args)
        {
            String ChatBotId = "xxxxxx-xxxx-xxx-xxx";

            while (true)
            {
                String message = Console.ReadLine();
                String url =
                    String.Format(
                        "https://...{0}...{1}..."
                        , ChatBotId
                        , HttpUtility.UrlEncode(message));
                HttpWebRequest request = (HttpWebRequest) HttpWebRequest.Create(url);
                HttpWebResponse response = (HttpWebResponse) request.GetResponse();
                StreamReader responseReader = new StreamReader(response.GetResponseStream(),
                                                               Encoding.GetEncoding("windows-1251"));
                string html = responseReader.ReadToEnd();

                Regex r = new Regex(@".+?\((.+?)\)$");
                Match m = r.Match(html);
                if (m.Success)
                    html = m.Groups[1].Value;

                var answerData = JSONHelper.Deserialise(html);
                Console.WriteLine("{0}", answerData.result.text.value);
            }
        }

вторник, 2 апреля 2013 г.

Переводим PHP-сайт сайт на ASP.NET MVC без изменения вида ссылок

Предположим, что у нас есть PHP-сайт со страницей http://site.com/res.php?anyparameters и мы хотим настроить маршрутизацию в ASP.NET MVC4 так, чтобы эта php-ссылка осталась без изменений (у клиентов уже есть софт, настроенный на работу с этим урлом). При этом в GET-запросе может быть параметр с именем "action" ("action=addfunds", "action=logout",...)
Тогда делаем следующее

1. В методе RegisterRoutes класса RouteConfig прописываем следующие URL-паттерны:

routes.MapRoute(
                name: "ApiDocs",
                url: "api",
                defaults: new { controller = "Page", action = "Api" }
            );

 routes.MapRoute(
                name: "pull_php",
                url: "pull.php",
                defaults: new { controller = "Method", action = "Pull", key = UrlParameter.Optional }
            );

routes.MapRoute(
                name: "pull",
                url: "pull",
                defaults: new { controller = "Method", action = "Pull", key = UrlParameter.Optional }
            );

routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}",
                defaults: new {controller = "Page", action = "Main"});

2. В контроллере Method пишем
[HttpGet]
        public ActionResult Pull(String key, String id)
        {
            var apiAction = Request["action"];
            ...
            return View(result);
        }

Вот и всё.

вторник, 26 февраля 2013 г.

RSA-шифрование в Android (Java)

В одном своём проекте я решил реализовать безопасную переписку между двумя пользователями используя мобильное приложение на Android.
Задача сводилась к тому, чтобы пользователь мог создать себе пару ключей (публичный/приватный), иметь возможность сохранить их в виде Base64-строки (для передачи по http и простого хранения в виде текстовой строки), а также чтобы можно было восстановить эти ключи, сохранённые в Base64 и использовать их для шифрования.


Пример использования класса Crypto (Я-Alex, у меня есть пара ключей public/private; Ustas-мой собеседник, передающий сообщение мне)

Юстас - Алексу
--------------
на стороне Юстаса (необходимо предварительно получить открытый(публичный) ключ Алекса):
Crypto Ustas = new Crypto();
Ustas.GenerateKeys();
String toAlex = Ustas.RSAEncrypt("Привет!", Alex.publicBase64);

на стороне Алекса:
Crypto Alex = new Crypto();
Alex.GenerateKeys();
String fromUstas = Alex.RSADecrypt(toAlex, Alex.privateBase64);


класс Crypto.java:
------------------
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;

import android.util.Base64;
import android.util.Log;

public class Crypto
{
    KeyPairGenerator kpg;
    KeyPair kp ;
    int keySize = 1024;
    RSAPublicKey key;

    public String publicBase64;
    public String privateBase64;
   
    public void GenerateKeys() throws NoSuchAlgorithmException
    {
        kpg = KeyPairGenerator.getInstance("RSA");
        kpg.initialize(1024);
        kp = kpg.genKeyPair();
        PublicKey publicKey = kp.getPublic();
        PrivateKey privateKey = kp.getPrivate();

        byte[] publicBytes = publicKey.getEncoded();
        publicBase64 = Base64.encodeToString(publicBytes, Base64.DEFAULT);

        byte[] privateBytes = privateKey.getEncoded();
        privateBase64 = Base64.encodeToString(privateBytes, Base64.DEFAULT);
    }
   
    public String RSAEncrypt(final String plain, String publicKeyBase64) throws NoSuchAlgorithmException, NoSuchPaddingException,
        InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidKeySpecException {
       
        PublicKey publicKey;
        byte[] publicBytes = Base64.decode(publicKeyBase64, Base64.DEFAULT);
        publicKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(publicBytes));

        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] encryptedBytes = cipher.doFinal(plain.getBytes());

        String encrypted = Base64.encodeToString(encryptedBytes, Base64.DEFAULT);
        return encrypted;
    }

    public String RSADecrypt(final String encrypted, String privateKeyBase64) throws NoSuchAlgorithmException, NoSuchPaddingException,
        InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidKeySpecException {
   
        byte[] privateBytes = Base64.decode(privateKeyBase64, Base64.DEFAULT);
        PrivateKey privateKey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(privateBytes));
       
        byte[] encryptedBytes = Base64.decode(encrypted, Base64.DEFAULT);
        Cipher cipher1 = Cipher.getInstance("RSA");
        cipher1.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] decryptedBytes = cipher1.doFinal(encryptedBytes);
        String decrypted = new String(decryptedBytes);
        return decrypted;
    }
   
}

Как выложить свой проект на github

I. Подготовка
    1. Зарегистрироваться на github
    2. Создать проект
    3. Установить git

II.
1.
github: new repository
git config --global user.name "my_username"
git config --global user.email "my@email.com"
git config --global core.autocrlf true
git config --global core.safecrlf true

2. Устанавливаем TortoiseGit
3. На папке проекта ПКМ - Add... и добавляем все файлы проекта в репозиторий.
4. Выполняем команды (ПКМ на папке проекта - Git Bash Here):

$ git remote add origin https://github.com/my_username/my_project.git
$ git push -u origin master

пятница, 8 июня 2012 г.

Шифрованная синхронизация файлов.

Будем для этих целей пользоваться сервисами BoxCryptor и Dropbox. Для начала устанавливаем Dropbox, указываем ему место, где он будет хранить файлы. Пусть это будет, например, папка D:\Dropbox\ Затем устанавливаем BoxCryptor, создаём шифрованный диск и указываем, что размещаться он будет внутри папки, находящейся под присмотром Dropbox. У нас это будет D:\Dropbox\BoxCryptor. Здесь же будет создан xml-файл .encfs6, который будет хранить публичный ключ. BoxCryptor запросит пароль, необходимый для шифрования/дешифрования ваших файлов. После этого у нас в системе появится виртуапьный диск (скажем, Y:\). Этим хранилищем можно пользоваться как обычным диском - сохранять файлы, создавать проекты, репозитории, хранить документы, переписку и т.д. Весь контент, перемещённый на этот диск, будет физически сохраняться в папке D:\Dropbox\BoxCryptor предварительно проходя стадию шифрования. При этом шифруются даже имена файлов! А Dropbox будет тут же, при малейшем изменении файлов, синхронизировать их со своим хранилищем в облаке. То есть, в облако дропбокса будут отправляться зишифрованные файлы. на другой машине, где мы захотим подключиться к нашему криптоконтейнеру, мы также сначала устанавливаем Dropbox. Указываем ему, где будем сохранять шифрованный контент. Например, в F:\Dropbox\ Делаем синхронизацию, ждём загрузки файла с ключами .encfs6.xml Затем ставим BoxCryptor, и говорим ему, что надо подключиться к хранилищу, размещённому в дропбоксовском хранилище (F:\Dropbox). BoxCryptor запрашивает ваш пароль к бокскриптору и монтирует диск. Всё, можно пользоваться и не опасаться, что ваши данные куда-то утекут. И при этом мы получаем полную синхронизацию файлов, давая возможность безопасно работать с вашим хранилищем в любой точке мира, где есть интернет.

четверг, 15 декабря 2011 г.

Поиск во всех таблицах в MS SQL

CREATE PROC SearchAllTables
(
@SearchStr nvarchar(100)
)
AS
BEGIN

-- Copyright © 2002 Narayana Vyas Kondreddi. All rights reserved.
-- Purpose: To search all columns of all tables for a given search string
-- Written by: Narayana Vyas Kondreddi
-- Site: http://vyaskn.tripod.com
-- Tested on: SQL Server 7.0, SQL Server 2000, SQL server 2005
-- Date modified: 28th July 2002 22:50 GMT


CREATE TABLE #Results (ColumnName nvarchar(370), ColumnValue nvarchar(3630))

SET NOCOUNT ON

DECLARE @TableName nvarchar(256), @ColumnName nvarchar(128), @SearchStr2 nvarchar(110)
SET @TableName = ''
SET @SearchStr2 = QUOTENAME('%' + @SearchStr + '%','''')

WHILE @TableName IS NOT NULL
BEGIN
SET @ColumnName = ''
SET @TableName =
(
SELECT MIN(QUOTENAME(TABLE_SCHEMA) + '.' + QUOTENAME(TABLE_NAME))
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE = 'BASE TABLE'
AND QUOTENAME(TABLE_SCHEMA) + '.' + QUOTENAME(TABLE_NAME) > @TableName
AND OBJECTPROPERTY(
OBJECT_ID(
QUOTENAME(TABLE_SCHEMA) + '.' + QUOTENAME(TABLE_NAME)
), 'IsMSShipped'
) = 0
)

WHILE (@TableName IS NOT NULL) AND (@ColumnName IS NOT NULL)
BEGIN
SET @ColumnName =
(
SELECT MIN(QUOTENAME(COLUMN_NAME))
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = PARSENAME(@TableName, 2)
AND TABLE_NAME = PARSENAME(@TableName, 1)
AND DATA_TYPE IN ('char', 'varchar', 'nchar', 'nvarchar')
AND QUOTENAME(COLUMN_NAME) > @ColumnName
)

IF @ColumnName IS NOT NULL
BEGIN
INSERT INTO #Results
EXEC
(
'SELECT ''' + @TableName + '.' + @ColumnName + ''', LEFT(' + @ColumnName + ', 3630)
FROM ' + @TableName + ' (NOLOCK) ' +
' WHERE ' + @ColumnName + ' LIKE ' + @SearchStr2
)
END
END
END

SELECT ColumnName, ColumnValue FROM #Results
END

понедельник, 31 октября 2011 г.

OCR-распознавалка китайских иероглифов


Сделал такую вот распознавалку китайских иероглифов.
Пишете от руки иероглиф и по мере рисования выбираете наиболее похожий из списка.

пятница, 14 мая 2010 г.

среда, 14 апреля 2010 г.

База данных адресов с географическими координатами

Имеется в наличии база данных по адресам г. Москвы с их привязкой к географическим координатам.
В базе 88557 объектов с определёнными координатами.
Пример из базы (10 случайных адресов):


Приобрести базу можно здесь
Как это можно использовать?
Можно написать процедуру, которая будет показывать объекты вблизи заданной точки (по адресу)

ALTER PROCEDURE [dbo].[GetNearestPoints]
@street nvarchar(150),
@home varchar(50),
@dist float
AS
BEGIN
SET NOCOUNT ON;
 
DECLARE @x float, @y float
DECLARE @dx float, @dy float
DECLARE @dx1km float, @dy1km float
 
SELECT @x=h.h_coordx, @y=h.h_coordy FROM houses h
INNER JOIN streets s ON h.h_streetid = s.s_id
AND s.s_name = @street
WHERE h.h_address = @home
 
SELECT @dx1km = 1/(108.307 * cos(@y*3.14159/180)), @dy1km = 1.0/111
SELECT @dx = @dist * @dx1km/2, @dy = @dist * @dy1km
 
SELECT s.s_name AS street,
h.h_address AS home,
h.h_coordx AS x,
h.h_coordy AS y
FROM houses h
INNER JOIN streets s ON s.s_id = h.h_streetid
WHERE h.h_coordx>=@x-@dx AND h.h_coordx<=@x+@dx
AND h.h_coordy>=@y-@dy AND h.h_coordy<=@y+@dy
ORDER BY s_name, h.h_coordx, h.h_coordy
 
END
 


Или в районе, ограниченном окружностью

ALTER PROCEDURE [dbo].[GetNearestPointsR]
@street varchar(150),
@home varchar(50),
@dist float
AS
BEGIN
SET NOCOUNT ON;
 
CREATE TABLE #t (street varchar(50), home varchar(50), x float, y float)
 
DECLARE @x float, @y float, @phi float
DECLARE @xx float, @yy float
DECLARE @x2 float, @y2 float
DECLARE @dx float, @dy float
DECLARE @dx1km float, @dy1km float
SELECT @x=h.h_coordx, @y=h.h_coordy FROM houses h
INNER JOIN streets s ON h.h_streetid = s.s_id
AND s.s_name = @street
WHERE h.h_address = @home
 
SELECT @dx1km = 1/(108.307 * cos(@y*3.14159/180)), @dy1km = 1.0/111
SELECT @dx = @dist * @dx1km/2, @dy = @dist * @dy1km
 
DECLARE @counter int
SET @counter = 0
WHILE @counter < 3
BEGIN
SET @counter = @counter + 1
SELECT @phi = (@counter*45/2)*3.14/180
SELECT @xx = @dx*cos(@phi), @yy = @dy*sin(@phi)
 
INSERT INTO #t (street, home, x, y)
SELECT s.s_name, h.h_address, h.h_coordx, h.h_coordy FROM houses h
INNER JOIN streets s ON s.s_id = h.h_streetid
WHERE h.h_coordx>=@x-@xx AND h.h_coordx<=@x+@xx
AND h.h_coordy>=@y-@yy AND h.h_coordy<=@y+@yy
ORDER BY s_name, h.h_coordx, h.h_coordy
 
END
 
SELECT MAX(street) AS street, MAX(home) AS home, x, y FROM #t GROUP BY x, y ORDER BY x, y
 
DROP TABLE #T
 
END
 

вторник, 16 марта 2010 г.

Использование утилиты LogParser

Для анализа производительности методов строим график с помощью логпарсера.

logparser "SELECT TO_INT(SUBSTR(Field9, ADD(INDEX_OF(Field9, ''), 8), SUB(SUB(INDEX_OF(Field9, ''), INDEX_OF(Field9, '')),8))) as method, AVG(TO_REAL(SUBSTR(Field9, ADD(INDEX_OF(Field9, ''), 16), SUB(SUB(INDEX_OF(Field9, ''), INDEX_OF(Field9, '')), 16)))) as duration INTO methods_avg.gif FROM c:\logs\vkontakte150310.csv WHERE Field9 LIKE '%%%%' GROUP BY method ORDER BY method" -i:csv -headerRow:off -o:chart -chartType:PieExploded3D -chartTitle:"Среднее время выполнения методов" -legend:on -values:on

суббота, 14 ноября 2009 г.

Конвертация символа в нужную кодировку

//char -> windows-1251
byte[] data = new byte[1];
data[0] = (byte)(trans & 0xFF);
path += System.Text.Encoding.GetEncoding(1251).GetString(data);