Ο Μάνος αναφερόταν στο πως μπορείς να συνδυάσεις streams με διαφορετικές δυνατότητες για να πετύχεις το αποτέλεσμα που θέλεις. Μπορείς, π.χ. να συνδυάσεις ένα CryptoStream με ένα FileStream ή ένα NetworkStream για να κρυπτογραφήσεις τα δεδομένα, χωρίς να αλλάξει ο τρόπος με τον οποίο γράφεις ή διαβάζεις τα αρχεία. Στην απλούστερη περίπτωση μπορείς να δημιουργήσεις ένα stream το οποίο θα κρυπτογραφεί όσα γράφει με τον παρακάτω κώδικα:
public static void Encrypt(string fileName)
{
// Instantiate the crypto provider
RijndaelManaged rm = new RijndaelManaged();
// Set up information needed by the provider
byte[] Key = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14,
0x15, 0x16};
byte[] IV = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14,
0x15, 0x16};
// Create the encryption transformer
ICryptoTransform trans = rm.CreateEncryptor(Key, IV);
// Prepare the crypto stream for writing
FileStream file = new FileStream(fileName, FileMode.Create);
CryptoStreamMode mode = CryptoStreamMode.Write;
CryptoStream enc = new CryptoStream(file, trans, mode);
// Write to the crypto stream
StreamWriter writer = new StreamWriter(enc);
writer.WriteLine("Hello, encrypted world");
// Clean-up
writer.Close();
enc.Close();
file.Close();
}
Απλό έτσι? Πού είναι ο καιρός της VB6 και του CAPICOM (για όσους δεν το ξέρουν, βιβλιοθήκη για κρυπτογράφηση από το Platform SDK), που καθάριζες με μια .Encrypt! Με το CAPICOM όμως, είμασταν περιορισμένοι στον τρόπο υλοποίησης και κρυπτογράφησης που παρείχε η βιβλιοθήκη. Με το .NET μπορούμε να ρυθμίσουμε τον τρόπο με τον οποίο λειτουργεί η κρυπτογράφηση καθώς και να προσθέσουμε εύκολα νέους αλγόριθμους κρυπτογράφησης. Για παράδειγμα, ο αλγόριθμος που χρησιμοποιεί το παραπάνω παράδειγμα είναι ο αλγόριθμος που χρησιμοποιείται στο πρότυπο AES ο οποίος είναι πολύ ασφαλέστερος από τον TripleDES, τον οποίο και αντικαθιστά. Το τίμημα είναι η μεγαλύτερη πολυπλοκότητα του κώδικα.
Ευτυχώς, ο κώδικας δεν είναι δα και τόσο πάρα πολύ περίπλοκος. Χρειάζεται να κάνουμε τρία μηχανικά βήματα:
- Δημιουργούμε την κλάση του αλγόριθμου που θέλουμε να χρησιμοποιήσουμε, εδώ του RijndaelManaged.
- Δημιουργούμε μια κλάση Transformation μέσω της μεθόδου CreateEncryptor
- Δημιουργούμε ένα CryptoStream δίνοντας του το αρχικό μας stream και το Transformation.
Στο βήμα 2 όμως πρέπει να προσέξουμε 2 πράγματα. Η κλάση Transformation δημιουργείται δίνοντας σαν παραμέτρους δύο πίνακες από bytes. Ο ένας είναι το κλειδί και ο δεύτερος το πρώτο block που θα κρυπτογραφήσει το stream. Για να μπορέσει να γίνει η αποκρυπτογράφηση θα πρέπει να δώσουμε το ίδιο κλειδί ΚΑΙ το ίδιο αρχικό μπλοκ! Αντίθετα με το παράδειγμα, το αρχικό μπλοκ θα πρέπει να είναι όσο πιο τυχαίο γίνεται.
Το δεύτερο και πολύ σημαντικότερο που πρέπει να προσέξουμε, είναι ότι το κλειδί ΔΕΝ είναι σε καμμία περίπτωση το password που δίνουμε για να κρυπτογραφήσουμε το αρχείο! Αντιθέτως, πρέπει να είναι το hash του αρχικού password. Για επαρκή ασφάλεια, δεν χρησιμοποιείται μόνο ένα αλλά εκατοντάδες αλλεπάλληλα hashes.
Ευτυχώς, το .NET Framework περιέχει δύο κλάσεις, την Rfc2898 (στο .NET 2.0) και την PasswordDeriveBytes η οποία αναλαμβάνει να δημιουργήσει ένα κλειδί συγκεκριμμένου μεγέθους με παραμέτρους το password, τον αλγόριθμο κρυπτογράφησης και τον αριθμό των hash που θα χρησιμοποιηθούνε. Μερικοί αλγόριθμοι πάντως επιβάλλουν συγκεκριμένο αριθμό από hashes. Η ίδια κλάση μπορεί να χρησιμοποιηθεί για να δημιουργηθεί και το αρχικό μπλοκ (IV) από το password. Ελάχιστος προτεινόμενος αριθμός hash iterations: 1000!
Το MSDN περιγράφει αρκετά καλά τη θεωρία όλων αυτών, αλλά δεν έχει ένα ολοκληρωμένο παράδειγμα. Μπορείς να δεις όμως το "Simple Tool to Encrypt/Decrypt E-mail Messages" το οποίο κάνει όλα τα παραπάνω χρησιμοποιώντας MemoryStreams. Επίσης, δες το "Cryptography the .NET way" για μια περίληψη όλων των παραπάνω.
Ωραίααααααα. Όλα αυτά όμως είναι μόνο για symmetric encryption. Τί γίνεται αν θέλω να χρησιμοποιήσω public/private keys? Χμμ τα πράγματα εδώ γίνονται ενδιαφέροντα. Οι κλάσεις για assymetric encryption ΔΕΝ έχουν αντίστοιχα transformations! Γιατί αυτό? Γιατί οι αλγόριθμοι αυτοί είναι πολύ αργοί. Απίστευτα αργοί. Άστα-να-πάνε αργοί. Καλύτερη λύση είναι να τους χρησιμοποιήσει κανείς για να κρυπτογραφήσει το κλειδί και το IV ενός συμμετρικού αλγόριθμού, όπως ο RijndaelManaged. Στην περίπτωση αυτή όμως αρχίζουν άλλες συζητήσεις, όπως το πως διαχειριζόμαστε τα public/private keys, που θα τα βρούμε, πως τα ανταλλάσσουμε κλπ. Το MSDN ευτυχώς έχει αρκετά άρθρα και παραδείγματα για το θέμα.
Το .NET Framework 2.0 παρέχει και άλλες λύσεις όταν θέλουμε να προστατεύσουμε τα δεδομένα του χρήστη. Το Data Protection API επιτρέπει την κρυπτογράφηση δεδομένων έτσι ώστε να μπορούν να διαβαστούν μόνο από το χρήστη ή το process που έκανε την κρυπτογράφηση. Τα κλειδιά δημιουργούνται και αποθηκεύονται αυτόματα από το λειτουργικό, εξασφαλίζοντας έτσι ότι δεν μπορεί άλλος χρήστης να διαβάσει τα δεδομένα. Το άρθρο How To: Use Data Protection περιγράφει τις κλάσεις και τις δυνατότητες του DPAPI. Σημειωτέον, το DPAPI μπορούσε να χρησιμοποιηθεί και στο .NET 1.1 μέσω Interop. Το ωραίο με το DPAPI είναι ότι μπορεί να χρησιμοποιηθεί και για την προστασία δεδομένων στη μνήμη, τα οποία θα μπορούν να διαβαστούν μόνο από το Process που τα δημιούργησε.
Άλλη λύση είναι να χρησιμοποιήσουμε την κλάση SslStream για την ανταλλαγή κρυπτογραφημένων δεδομένων με κάποιο άλλο υπολογιστή.
Τέλος, μπορούμε να χρησιμοποιήσουμε όσους συνδυασμούς των παραπάνω μεθόδων θέλουμε. Π.χ. αν θέλουμε να μεταφέρουμε δεδομένα από ένα υπολογιστή σε ένα άλλο, μπορούμε να κάνουμε τα εξής:
- Φτιάχνουμε μια κλάση RijndaelManaged και καλούμε την CreateEncryptor ΧΩΡΙΣ παραμέτρους. Τα IV και Key θα δημιουργηθούν αυτόματα.
- Παίρνουμε τις τιμές των IV και Key από την κλάση μέσω των παραμέτρων IV και Key.
- Τις στέλνουμε στον άλλο υπολογιστή χρησιμοποιώντας ένα SslStream
- Στέλνουμε τα κρυπτογραφημένα αρχεία μέσω ενός κανονικού NetworkStream το οποίο το περιτυλίγουμε σε ένα CryptoStream.
Κλείνω προτείνοντας το βιβλίο "Programming .NET Security" των Freeman, Jones, ISBN 0-596-00442-7.
Παναγιώτης Καναβός, Freelancer
Twitter: http://www.twitter.com/pkanavos