Pokazywanie postów oznaczonych etykietą hash. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą hash. Pokaż wszystkie posty

sobota, 26 kwietnia 2014

[C#|Visual Studio] C#: Cryptography

Podstawowe trzy obszary zastosowań kryptografii, którymi są: wyliczanie haszy, kryptografia symetryczna oraz kryptografia asymetryczna zostały w bardzo dobry sposób zaimplementowane w dot Necie w namespace System.Security.Cryptography. Podstawowe pojęcia związane z kryptografią to: plaintext - wiadomość do zaszyfrowania, szyfrowanie - proces obfuskacji danych, deszyfracja - proces odtwarzania oryginalnych danych oraz ciphertext - dane w postaci zaszyfrowanej. Różne techniki kryprograficzne zapewniają:
  • poufność - dane nie mogą być przeczytane przez nieodpowiednie osoby
  • integralność - możliwość weryfikacji, czy dane były modyfikowane
  • uwierzytelnianie - potwierdzenie tożsamości użytkownika
  • niezaprzeczalność - autor wiadomości nie może się jej wyprzeć

Hashing:

Funkcje haszujące konwertują wejście o zmiennej długości w wyjściowy ciąg bajtów o stałej długości tworząc skrót (nazywany także hashem) z wiadomości. Operacja taka jest nieodwracalna i charakteryzuje się tym, że małe zmiany ciągu wejściowego powodują duże zmiany w ciągu wyjściowym. Haszowanie stosuje się w celu zapewnienia integralności. W .NET mamy dostępne 6 klas dziedziczących po abstrakcyjnej HashAlgorithm. Wybór powinien być kompromisem pomiędzy długością klucza (ataki typu bruteforce), a szybkością wykonywania.

Console.WriteLine("Please enter secret message:");
var msg = Console.ReadLine();
var msgBytes = Encoding.UTF8.GetBytes(msg);

HashAlgorithm[] algorithms = { 
                                 new MD5Cng(),
                                 new MD5CryptoServiceProvider(),
                                 new SHA1Managed(), 
                                 new SHA256Managed(),
                                 new SHA384Managed(),
                                 new SHA512Managed(),
                                 new RIPEMD160Managed()
                             };

var watch = new Stopwatch();
foreach (var hashAlgorithm in algorithms)
{
    watch.Start();
    byte[] hash = null;
    for (int i = 0; i < 100000; i++)
    {
        hash = hashAlgorithm.ComputeHash(msgBytes);
    }
    watch.Stop();
    Console.WriteLine("{0} ({1} bit): {2} ms ", 
        hashAlgorithm.GetType().Name, 8*hash.Length, watch.ElapsedMilliseconds);
    watch.Reset();
    Console.WriteLine(BitConverter.ToString(hash).Replace("-", ""));
}

Uzyskane wyniki:

Please enter secret message:
Hello world!

MD5Cng (128 bit): 933 ms
86FB269D190D2C85F6E0468CECA42A20

MD5CryptoServiceProvider (128 bit): 812 ms
86FB269D190D2C85F6E0468CECA42A20

SHA1Managed (160 bit): 257 ms
D3486AE9136E7856BC42212385EA797094475802

SHA256Managed (256 bit): 375 ms
C0535E4BE2B79FFD93291305436BF889314E4A3FAEC05ECFFCBB7DF31AD9E51A

SHA384Managed (384 bit): 2696 ms
86255FA2C36E4B30969EAE17DC34C772CBEBDFC58B58403900BE87614EB1A34B8780263F255EB5E65CA9BBB8641CCCFE

SHA512Managed (512 bit): 3323 ms
F6CDE2A0F819314CDDE55FC227D8D7DAE3D28CC556222A0A8AD66D91CCAD4AAD6094F517A2182360C9AACF6A3DC323162CB6FD8CDFFEDB0FE038F55E85FFB5B6

RIPEMD160Managed (160 bit): 423 ms
7F772647D88750ADD82D8E1A7A3E5C0902A346A3

Symmetric Algorithms:

Algorytmy symetryczne, to takie, w których do procesu szyfrowania i deszyfrowania używany jest ten sam klucz. Klucz taki musi być przechowywany w odpowiednio bezpiecznych warunkach i mieć odpowiednią długość, żeby uniknąć ataków typu brute-force. Obecnie powszechnie używanym algorytmem jest AES (Rijndael z min.128 bitowymi kluczami). Im dłuższy klucz, tym dłuższe szyfrowanie. Jeżeli chcemy wygenerować sobie klucz, najlepiej skorzystać z klasy RNGCryptoServiceProvider zapewniającej dużo lepszą "losowość", niż pozostałe narzędzia do generacji liczb pseudolosowych. Algorytm Rijndael często używany jest w trybie CBC, gdzie wynik kolejnej rundy szyfrowania zależy od rundy poprzedniej. Stąd obok klucza musimy znać wektor wejściowy (przed pierwszą rundą). Wektor taki - wektor IV (Initialization Vector) podaje się obok klucza przy dystrybucji. Konstruktor klasy RijndaelManaged wygeneruje taki wektor.

private static void Symmetric(byte[] msgBytes)
{
    var key = new byte[256/8];
    RNGCryptoServiceProvider provider = new RNGCryptoServiceProvider();
    provider.GetBytes(key);

    var cipher = CreateCipher();

    cipher.Key = key;
    var encryptor = cipher.CreateEncryptor();
    var result = encryptor.TransformFinalBlock(msgBytes, 0, msgBytes.Length);

    Console.WriteLine("Secret key: " + BytesToString(key));
    Console.WriteLine("IV: " + BytesToString(cipher.IV));
    Console.WriteLine("Encrypted message: " + BytesToString(result));

    var cipher2 = CreateCipher();
    cipher2.Key = key;
    cipher2.IV = cipher.IV;

    var decryptor = cipher.CreateDecryptor();
    var originalMessage = decryptor.TransformFinalBlock(result, 0, result.Length);

    var s1 = BytesToString(originalMessage);
    var s2 = BytesToString(msgBytes);

    Console.WriteLine("Are messages equal ? " + s1.Equals(s2));
}

private static RijndaelManaged CreateCipher()
{
    RijndaelManaged cipher = new RijndaelManaged();
    cipher.KeySize = 256;
    cipher.BlockSize = 256;
    cipher.Padding = PaddingMode.ISO10126;
    cipher.Mode = CipherMode.CBC;
    return cipher;
}

Asymmetric algorithms:

Kryptografia asymetryczna nazywana także kryptografią klucza publicznego polega na tym, że mamy wygenerowaną parę kluczy: publiczny i prywatny. Klucze te uzupełniają się, to znaczy, że wiadomość zaszyfrowaną kluczem prywatnym można odszyfrować kluczem publicznym i na odwrót. Operacje takie są od 100 do 1000 razy wolniejsze od kryptografii symetrycznej, dlatego często algorytmów asymetrycznych używa się tylko do przesłania symetrycznego klucza sesji. Najpopularniejszy algorytm to RSA. Para kluczy przechowywana jest w kontenerze, przeważnie w systemie operacyjnym, ale można ją też eksportować do pliku XML. Wczytujemy ją z Xml metodę FromString lub podając nazwę kontenera systemowego przy użyciu klasy CspParameters.

CspParameters cp = new CspParameters();
cp.KeyContainerName = "SampleKeys";
cp.Flags = CspProviderFlags.UseMachineKeyStore;
RSACryptoServiceProvider cipher = new RSACryptoServiceProvider(cp);
var bytes = cipher.Encrypt(msgBytes, true);

Console.WriteLine("Encrypted message: " + BytesToString(bytes));
Console.WriteLine(cipher.ToXmlString(includePrivateParameters:true));

var s1 = BytesToString(msgBytes);
var s2 = BytesToString(cipher.Decrypt(bytes, true));

Console.WriteLine("Are messages equal ? " + s1.Equals(s2));

poniedziałek, 3 marca 2014

[NoSQL] Redis: Typy danych, funkcjonalności

Redis wspiera 5 typów danych:

1. STRING


Podstawowy typ danych, który może reprezentować całe obiekty zserializowane do stringów,  może też być liczbą.

Podstawowe operacje na stringach:

redis 127.0.0.1:6379> set user:1 "{'name': 'Joe', 'email': 'joe@joe.com'}"
OK
redis 127.0.0.1:6379> get user:1
"{'name': 'Joe', 'email': 'joe@joe.com'}"
redis 127.0.0.1:6379> set user:id 1
OK
redis 127.0.0.1:6379> incr user:id
(integer) 2
redis 127.0.0.1:6379> append user:1 " extra data"
(integer) 50
redis 127.0.0.1:6379> get user:1
"{'name': 'Joe', 'email': 'joe@joe.com'} extra data"
redis 127.0.0.1:6379> getrange user:1 5 9
"e': '"
redis 127.0.0.1:6379> mset order:1 "o1 data" order:2 "o2 data"
OK
redis 127.0.0.1:6379> mget order:1 order:2
1) "o1 data"
2) "o2 data"
redis 127.0.0.1:6379> strlen user:1
(integer) 50

Operacje set i get służą do wstawiania / pobierania wartości pod podany klucz. Podobnie można wykorzystać operacje mset i mget, do których można podać wiele par klucz - wartość i wstawić je do bazy w obrębie jednej operacji. Wartości, które Redis rozpozna jako numeryczne, można inkrementować operacją incr. Kolejne operacje dotyczące typowo ciągów znaków to: append - doklejanie stringa na koniec wartości znajdującej się pod podanym kluczem, getrange - pobranie podzbioru znaków spod podanego klucza oraz strlen, czyli operacja zwracająca długość stringa znajdującego się pod podaną wartością. 

2. LISTA


Zbiór danych posortowanych po kolejności, w jakiej zostały dodane. Podstawowe operacje na listach:


redis 127.0.0.1:6379> lpush mylist "a"
(integer) 1
redis 127.0.0.1:6379> lpush mylist "b"
(integer) 2
redis 127.0.0.1:6379> lpush mylist "c"
(integer) 3
redis 127.0.0.1:6379> lrange mylist 0 2
1) "c"
2) "b"
3) "a"
redis 127.0.0.1:6379> ltrim mylist 0 1
OK
redis 127.0.0.1:6379> lrange mylist 0 2
1) "c"
2) "b"
redis 127.0.0.1:6379> lindex mylist 1
"b"
redis 127.0.0.1:6379> lindex mylist 0
"c"
redis 127.0.0.1:6379> lpop mylist
"c"
redis 127.0.0.1:6379> lrange mylist 0 2
1) "b"
redis 127.0.0.1:6379> rpush mylist "e"
(integer) 2
redis 127.0.0.1:6379> lrange mylist 0 2
1) "b"
2) "e"

Dane dodajemy operatorem lpush. Pobieranie odbywa się po zakresie za pomocą lrange gdzie podajemy zakres indeksów. Element spod konkretnego indeksu pobieramy przez lindex. Lista może działać jako kolejka, gdzie dodajemy dane z prawej strony (rpush) i pobieramy z lewej (lpop).

3. SET


Kolekcja unikalnych stringów. Mamy do dyspozycji operacje z teorii zbiorów. Podstawowe operacje na setach:

redis 127.0.0.1:6379> sadd names "joe" "bob" "mary"
(integer) 3
redis 127.0.0.1:6379> scard names
(integer) 3
redis 127.0.0.1:6379> smembers names
1) "bob"
2) "mary"
3) "joe"
redis 127.0.0.1:6379> sadd names2 "joe" "tim" "mary"
(integer) 3
redis 127.0.0.1:6379> sdiff names names2
1) "bob"
redis 127.0.0.1:6379> sinter names names2
1) "mary"
2) "joe"

Elementy do zbioru dodajemy za pomocą operatora sadd. Liczność zbioru możemy podejrzeć za pomocą scard, a jego elementy pobieramy przez smembers. Przykładowe operacje na zbiorach to różnica (sdiff) i przecięcie (sinter).

4. HASH


Typ danych mapujący stringowe pola na stringowe wartości. Redis jest zoptymalizowany pod kątem hashy z niewielką liczbą wartości.

redis 127.0.0.1:6379> hset user:1:h name "joe"
(integer) 1
redis 127.0.0.1:6379> hset user:1:h email "joe@joe.com"
(integer) 1
redis 127.0.0.1:6379> hgetall user:1:h
1) "name"
2) "joe"
3) "email"
4) "joe@joe.com"
redis 127.0.0.1:6379> hmget user:1:h name email
1) "joe"
2) "joe@joe.com"
redis 127.0.0.1:6379> hkeys user:1:h
1) "name"
2) "email"
redis 127.0.0.1:6379> hvals user:1:h
1) "joe"
2) "joe@joe.com"

Dane do hasha o nazwie user:1:h  pod pole name wstawiamy poleceniem hset. Wartości możemy pobrać na kilka sposobów. Jawnie podając pola (hmget), pobierając wszystkie (hvals). Same nazwy pól pobieramy poleceniem hkeys, a klucze i wartości komendą hgetall.

5. SORTED SET


Zbiory unikalnych danych posortowane po rankingu.

redis 127.0.0.1:6379> zadd hs 120 "joe" 100 "bob" 100 "mary" 90 "tim"
(integer) 4
redis 127.0.0.1:6379> zrank hs "bob"
(integer) 1

Dodajemy dane jako pary ranking - klucz poleceniem zadd, natomiast ranking konkretnego klucza (indeks) pobieramy za pomocą zrank.

Pozostałe funkcjonalności:


Redis może pracować jako message bus poprzez proste subskrybowanie / publikowanie wiadomości.

subscribe greetings //klient 1
publish greetings "hello redis" //klient 2
psubscribe greet* // subskrybuje na wszystkie kanały spełniające pattern
unsubscribe / punsubscribe

Ciekawą funkcjonalnością niekoniecznie spotykaną w bazach NoSQL jest zaimplementowana transakcyjność. Transakcję otwieramy poleceniem multi, po którym podajemy operacje mające się wykonać w obrębie transakcji. Kończymy poleceniem exec.
Z poziomu aplikacji klienckiej możemy także włączyć tryb monitorowania operacji, jakie wykonywane są na bazie poleceniem monitor. 

Redis przechowuje dane w pamięci z opcją "persisted to disk". Klient komunikuje się z serwerem po TCP wykorzystując specjalny protokół Redis-a. Baza domyślnie robi snapshoty danych na dysk co określoną liczbę zmian wartości. Plik redis.conf przechowuje konfigurację w sposób tekstowy. Można tam ustawić, co ile sekund lub ile zmian kluczy ma się robić snapshot. Przykładowo:

save 300 10 (co najmniej 300 sekund i 10 zmian kluczy)

Replikacja: działa na zasadzie master / slave(s) - slave'y pozostają domyślnie w trybie read only.

Prosta autentykacja polega na tym, że klient podaje hasło, które jest ustawione w pliku redis.conf na serwerze.