2005.01_Pyro i OpenSSL–bezpieczny komunikator internetowy_[Programowanie].pdf
(
863 KB
)
Pobierz
439110635 UNPDF
dla programistów
Pyro i OpenSSL
– bezpieczny
komunikator internetowy
Marek Sawerwain
tne informacje poprzez Inter-
net, to wprowadzenie szy-
frowania danych w naszym
aplikacjach rozproszonych jest bardzo
ważnym zagadnieniem. Istnieje wiele
bibliotek wspomagających nas w tym
temacie. Jedną z najbardziej znanych jest
OpenSSL
. Jest ona dostępna we wszyst-
kich większych dystrybucjach Linuk-
sa. Odpowiada za realizację bezpiecz-
nych kanałów komunikacyjnych w opar-
ciu o protokół SSL i jest wykorzystywana
przez wiele programów, w tym przeglą-
darki WWW.
Zastosowanie biblioteki
OpenSSL
naj-
lepiej pokazać na przykładzie. W nume-
rze Linux+ 10/2003 prezentowałem prosty
komunikator na wzór bardzo popularne-
go Gadu-Gadu. Jedną z niedoskonało-
ści tamtego programu była komunikacja
bez jakiegokolwiek szyfrowania danych.
W tym artykule postaram się pokazać,
jak małym kosztem zmienić ten stan
rzeczy.
opisu interfejsu pochodzących z progra-
mu Glade.
Do poprawnego działania będzie
wymagał, tak jak poprzednio, dwóch uru-
chomionych usług Pyro: serwera nazw
(usługę uruchamiamy poprzez wydanie
polecenia
ns
) oraz serwera zdarzeń (usługę
należy uruchomić wydając polecenie
es
).
Warto przedstawić sposób działa-
nia naszej aplikacji w postaci schema-
tu, który znajduje się na Rysunku 1. Jak
widać, mamy trzy specjalne serwery. Ser-
wery zdarzeń i nazw należą do systemu
Pyro, ale główny serwer, nazwany ser-
werem rozmów, musimy napisać samo-
dzielnie, a raczej poprawić kod, doda-
jąc obsługę SSL, bo przecież serwer już
wcześniej napisaliśmy.
Typowy serwer Pyro
Program komunikatora to wbrew pozo-
rom niewielka aplikacja. Łączny kod źró-
dłowy serwera i klienta to tylko 7 kB
tekstu źródłowego w Pythonie. Zanim
wprowadzimy obsługę SSL do komunika-
tora, chciałbym przedstawić typową apli-
kację Pyro.
Listing 1 zawiera kod klasy o nazwie
test_obj
. Poprzez Pyro chcemy udostęp-
nić tę klasę w sieci tak, aby inny użyt-
kownicy Pythona mogli ją wykorzy-
stać we własnych programach. Pierwszą
czynnością jest definicja klasy (Listing 1),
którą zapisujemy w oddzielnym pliku
o nazwie
test_obj.py
.
Istnieje kilka sposobów na napisanie
serwera. Poniżej zaprezentuję sposób, który
został użyty w komunikatorze. Pierwszym
elementem w implementacji serwera jest
dołączenie odpowiednich pakietów:
Na płycie CD/DVD
Na płycie CD/DVD znajdują się
wykorzystywane biblioteki, kod
źródłowy programu oraz wszyst-
kie listingi z artykułu.
Kilka postanowień
na początek
Najważniejszym zadaniem będzie doda-
nie do komunikatora obsługi szyfrowa-
nego kanału. Uprzedzając dalszą część
artykułu, już teraz mogę zdradzić, iż jest
to zadanie łatwe do wykonania. Głównie
dlatego, iż Pyro znakomicie współpracu-
je z biblioteką OpenSSL.
Aby w naszym komunikatorze znala-
zła się obsługa SSL, wymagana jest jesz-
cze instalacja jednego pakietu o nazwie
M2Crypt
. Kilka uwag na ten temat zawiera
ramka
Kompilacja i instalacja pakietów
.
Pozostałe założenia naszego progra-
mu pozostają niezmienione. Nadal jest
to program napisany w Pythonie. Będzie
korzystał z biblioteki GTK+ oraz z plików
O autorze
Autor zajmuje się tworzeniem
oprogramowania dla WIN32
i Linuksa. Zainteresowania:
teoria języków programowania
oraz dobra literatura.
Kontakt z autorem:
autorzy@lpmagazine.org
import Pyro.core
import Pyro.naming
import test_obj
70
styczeń 2005
G
dy planujemy przesyłać isto-
python/pyro/ssl
dla programistów
Kompilacja oraz
instalacja pakietów
Nasza aplikacja to dość mały program.
Wielkość jest wynikiem zastosowania
kilku bibliotek, które pozwalają zaim-
plementować całkiem zgrabną apli-
kację minimalną ilością kodu. Z tego
powodu, aby nasz komunikator zadzia-
łał, potrzebny jest język programowa-
nia Python oraz pakiet OpenSSL. Sam
język, jak również bibliotekę, znajdzie-
my bez problemów w każdej dystrybucji
Linuksa. Autorzy biblioteki Pyro zale-
cają najnowszą wersję Pythona, naj-
lepiej 2.3.3, oraz wersję OpenSSL nie
starszą niż 0.9.7 (jeśli w dystrybucji jest
starsza wersja, to koniecznie trzeba
dokonać aktualizacji tego pakietu).
Biblioteki Pyro raczej nie znajdzie-
my, ale zanim przystąpimy do jej insta-
lacji, należy zainstalować inną biblio-
tekę o nazwie
M2Crypt
. Będzie ona
obsługiwać połączenia szyfrowane
SSL. Biblioteka M2Crypto ma również
swoje wymagania i do poprawnej kom-
pilacji wymaga pakietu SWIG w wersji
przynajmniej 1.3.21. Pakiet SWIG jest
oferowany przez niektóre dystrybucje,
ale gdy pojawi się konieczność jego
kompilacji, to dzięki skryptowi
coni-
gure
sprawdza się ona do trzech pole-
ceń:
Rysunek 1.
Schemat działania komunikatora
Dwa pierwsze dotyczą Pyro, ale ostatni
pakiet to nasza klasa, którą mamy zamiar
udostępnić. W tym celu w serwerze
definiujemy specjalną klasę pośrednią
w następujący sposób:
ma zainstalowanej sieci i jest dostępny tylko
adres lokalny
127.0.0.1
.
Następnym krokiem w implementa-
cji serwera jest uzyskanie obiektu ser-
wera nazw:
class test_obj_class
S
(Pyro.core.ObjBase, test_obj.test_obj):
def __init__(self):
Pyro.core.ObjBase.__init__(self)
ns=Pyro.naming.NameServerLocator().getNS()
Ostatnim obiektem do utworzenia jest
daemon
:
# ./configure –prefix=/katalog
# make
# make install
Następnie uruchomiamy serwer metodą
initServer
:
daemon=Pyro.core.Daemon()
Konieczną operacją jest podanie mu
obiektu serwera nazw i to wykonujemy
w następujący sposób:
Pyro.core.initServer()
Proces kompilacji oraz instalacji pakie-
tu M2Crypto (jak również Pyro) sprowa-
dza się do dwóch poleceń:
W przeciwieństwie do wcześniejszego
kodu serwera komunikatora, określa-
my jeszcze kilka dodatkowych informa-
cji. Na początku określamy ilość infor-
macji, które będą zapisywane do pliku
zdefiniowanego za pomocą następnej
linii:
daemon.useNameServer(ns)
# python setup.py build
# python setup.py install
Ostatnie czynności w serwerze polegają
na podłączeniu naszego obiektu. W pierw-
szym parametrze wykorzystujemy klasę
pomocniczą
test_obj_class
, a w drugim
ciąg znaków reprezentujący nasz obiekt,
czyli
test_obj
. Na koniec możemy wywo-
łać metodę
requestLoop
, co spowoduje
uruchomienie serwera. Kod tych dwóch
czynności przedstawia się następująco:
Pamiętajmy, aby wcześniej zainsta-
lować wszystkie pakiety bezpośred-
nio związane z interpreterem Pytho-
na oraz pakiety związane z biblioteką
OpenSSL.
Nasz komunikator wymaga jeszcze
jednej biblioteki do poprawnego działa-
nia, a mianowicie
PyGTK+
, gdyż wyko-
rzystujemy
GTK+
oraz pakiet
libgla-
de
. Wiele dystrybucji, np.
Aurox
,
Man-
drakelinux
czy
Fedora Core
, zawiera-
ją odpowiednie pakiety – wystarczy je
tylko doinstalować.
Pyro.config.PYRO_TRACELEVEL=3
Pyro.config.PYRO_LOGFILE='server_log'
Później określamy komputer, na którym
został uruchamiany serwer nazw:
Pyro.config.PYRO_NS_HOSTNAME='localhost'
uri=daemon.connect(test_obj_class(), "test_obj")
daemon.requestLoop()
Ustalenie adresu serwera nazw oraz serwe-
ra zdarzeń (
Pyro.config.PYRO_ES_HOSTNAME
)
jest szczególnie istotne, gdy będziemy testo-
wać program na komputerze, na którym nie
Klient Pyro
W programie klienta, podobnie jak w ser-
werze, na początek dołączamy potrzeb-
ne pakiety:
www.lpmagazine.org
71
dla programistów
Listing 1.
Postać klasy test_obj
import Pyro.util
import Pyro.core
class
test_obj
:
Następnie inicjujemy klienta:
def
m1
(
s
,
string
):
print
"Metoda m1 parametr:"
,
string
return
"Długość ciągu znaków: "
+
str
(
len
(
string
))
def
m2
(
s
,
number
):
print
"Metoda m2 parametr:"
,
number
return
"Kwadrat liczby:"
+str(
number
*
number
)
Pyro.core.initClient()
Później ustalamy parametry pomocnicze,
takie jak adres komputera, na którym
znajduje się serwer nazw.
Gdy to zrobimy, możemy uzyskać
referencję do zdalnego obiektu. Wyko-
rzystujemy metodę
getProxyForURI
:
Listing 2.
Pełny kod serwera oparty o protokół SSL
#! /usr/bin/env python
test = Pyro.core.getProxyForURI
S
("PYRONAME://test_obj")
import
sys
import
Pyro
.
core
import
Pyro
.
naming
import
Pyro
.
util
import
Pyro
.
protocol
Po tych czynnościach metody ze zdalne-
go obiektu wywołujemy tak samo, jak
przy lokalnych obiektach:
print test.m1("Abcdef")
print test.m2(5)
from
Pyro
.
errors
import
PyroError
,
NamingError
from
Pyro
.
protocol
import
getHostname
Dodajemy obsługę SSL
do serwera
Jak widać, napisanie nieskompliko-
wanego serwera i klienta przy zasto-
sowaniu Pyro to dość łatwe zada-
nie. Kod źródłowy biblioteki zawie-
ra wiele przykładów, które polecam
przejrzeć czytelnikom, którzy w tym
momencie zainteresowali się tą biblio-
teką. Naszym głównym zadaniem jest
jednak komunikacja za pomocą proto-
kołu SSL.
Listing 2 zawiera pełny kod serwera
udostępniającego obiekt
test_obj
przy
zastosowaniu protokołu SSL. Postać ser-
wera jest bardzo podobna do poprzed-
niego przykładu, w którym komunika-
cja jest jawna.
Pierwszą różnicą są pakiety, które
dołączamy. Istotny jest
Pyro.protocol
,
używany w klasie
printCertValidator
.
Kolejna różnica kryje się w tworzeniu
obiektu
daemon
. W argumencie konstruk-
tora podajemy wartość
PYROSSL
:
import
test_obj
class
test_obj_class
(
Pyro
.
core
.
ObjBase
,
test_obj
.
test_obj
):
def
__init__
(
self
):
Pyro
.
core
.
ObjBase
.
__init__
(
self
)
class
printCertValidator
(
Pyro
.
protocol
.
BasicSSLValidator
):
def
checkCertificate
(
self
,
cert
):
if
cert
is
None
:
return
(
0
,
3
)
print
"Cert Subject: %s"
%
cert
.
get_subject
()
return
(
1
,
0
)
##### main program #####
Pyro
.
core
.
initServer
()
Pyro
.
config
.
PYRO_TRACELEVEL
=
3
Pyro
.
config
.
PYRO_NS_HOSTNAME
=
'localhost'
Pyro
.
config
.
PYRO_LOGFILE
=
'server_log'
ns
=
Pyro
.
naming
.
NameServerLocator
()
.
getNS
()
daemon
=
Pyro
.
core
.
Daemon
(
prtcol
=
'PYROSSL'
)
daemon
.
setNewConnectionValidator
(
printCertValidator
())
daemon
.
useNameServer
(
ns
)
daemon=Pyro.core.Daemon(prtcol='PYROSSL')
W ten sposób nakazujemy stosowanie
protokołu SSL.
Następnie podłączamy klasę
print-
CertValidator
, której zadaniem jest
sprawdzenie poprawności certyfikatu:
uri
=
daemon
.
connect
(
test_obj_class
()
,
"test_obj"
)
print
"Server is ready. Let's go!!!"
daemon
.
requestLoop
()
daemon.setNewConnectionValidator
S
(printCertValidator())
72
styczeń 2005
python/pyro/ssl
dla programistów
Kolejne czynności pozostają niezmie-
nione w porównaniu do zwykłego ser-
wera.
Zajmijmy się teraz klasą
printCert-
Validator
. Zawiera ona jedną metodę
–
checkCertificate
. Musi ona zwrócić
pewne określone wartości. Jeśli chcemy
zaakceptować certyfikat, to jako war-
tość powrotną zwracamy parę
(1,0)
.
W przypadku odrzucenia certyfikatu,
za pomocą słowa
return
musimy
przekazać parę
(0, kod_błędu)
. Przez
kod_błędu
rozumiemy jedną z poniż-
szych wartości:
Listing 3.
Postać plików z certyikatami dla protokołu SSL
Plik z głównym certyikatem:
-----BEGIN CERTIFICATE-----
MIIDkjCCAvugAwIBAgIBA i tak dalej ....
-----END CERTIFICATE-----
Plik
client.pem
:
-----BEGIN CERTIFICATE-----
MIIDvzCCAyigAwIBAgIBATANBgkqh i tak dalej ....
-----END CERTIFICATE-----
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQC7ixGOs2Sq i tak dalej ...
-----END RSA PRIVATE KEY-----
Plik
server.pem
:
-----BEGIN CERTIFICATE-----
MIIDvzCCAyigAwIBAgIBATANBgkqh i tak dalej ....
-----END CERTIFICATE-----
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQC7ixGOs2Sq i tak dalej ...
-----END RSA PRIVATE KEY-----
•
Pyro.constants.DENIED_UNSPECIFIED
– dowolny powód błędu;
•
Pyro.constants.DENIED_SERVERTOOBUSY
– serwer jest zbyt zajęty;
•
Pyro.constants.DENIED_HOSTBLOCKED
– blokada komputera;
•
Pyro.constants.DENIED_SECURITY
– błąd związany z bezpieczeństwem.
Program klienta
W programie serwera najważniej-
szą zmianą było dodanie w kon-
struktorze obiektu
daemon
informacji
o tym, że będziemy korzystać z pro-
tokołu SSL. W kliencie nie musimy
wykonywać żadnych zmian! Bibliote-
ka Pyro samodzielnie wykryje fakt, że
serwer korzysta z protokołu SSL i klient
samoczynnie przełączy się na komu-
nikację poprzez SSL. Z tego powodu
uzyskanie referencji do obiektu nadal
możemy wykonywać w ten sposób:
test1 = Pyro.core.getProxyForURI
S
("PYRONAME://test_obj")
Na Listingu 2, gdy nie ma certyfikatu,
zwracamy parę
(0,3)
, co oznacza błąd
związany z bezpieczeństwem.
Jak widzimy, dodanie obsługi SSL
wymaga dołączenia jednej klasy oraz
kosmetycznych zmian w kodzie. Oprócz
serwera, musimy wygenerować certyfi-
katy dla serwera i klienta. Informacje o
tym, jak tego dokonać, podam w dal-
szej części artykułu. Teraz zajmiemy się
klientem.
Jeśli jednak chcemy jawnie określić
komputer oraz fakt użycia przez nas pro-
tokołu SSL, to możemy w wywołaniu
metody
getProxyForURI
podać pełne
dane w następujący sposób:
test2 = Pyro.core.getProxyForURI
("PYROLOCSSL://localhost/test_obj")
Ostatni składnik w adresie to nazwa obie-
ktu, którą podaliśmy w drugim argu-
mencie metody
connect
podczas rejestro-
wania naszej klasy w serwerze.
Jak widać, siłą Pyro jest automaty-
zacja wielu operacji. Istotne zmiany,
które należy wprowadzić, aby zwięk-
szyć bezpieczeństwo aplikacji Pyro,
wykonujemy tylko po stronie serwe-
ra. Uzbrojeni w tę wiedzę w bardzo
łatwy sposób możemy dokonać zmian
w naszym komunikatorze.
Rysunek 2.
Nasza aplikacja podczas rozmowy
Generowanie certyikatów
Bezpieczeństwo komunikacji ściśle zależy
od utworzonych certyfikatów. Z tego
powodu, zanim zaczniemy modyfiko-
wać nasz program, wygenerujemy sto-
sowne certyfikaty. Nie jest to trudne,
ale musimy znać obsługę programu
openssl
. Możemy ułatwić sobie zada-
nie, jeśli będziemy korzystać ze skryp-
tu pomocniczego, dostępnego w kodzie
źródłowym pakietu OpenSSL. Potrzeb-
ny skrypt można odnaleźć wtedy
www.lpmagazine.org
73
dla programistów
Rysunek 3.
Strona domowa PYRO
z pliku
newcert.pem
do pliku
o nazwie
client.pem
, a następnie dołą-
czamy do niego opis klucza prywatnego
z pliku
newkey.pem
.
Z certyfikatem przeznaczonym dla
serwera postępujemy w podobny
sposób, ale nie generujemy już cer-
tyfikatu autoryzacji (jest wspólny dla
klienta i serwera), lecz następny cer-
tyfikat żądania. Podczas wypełnia-
nia pól informacyjnych warto zwrócić
uwagę na to, aby pole
Common Name
posiadało inną wartość dla klienta
i dla serwera.
Po zakończeniu operacji tworze-
nia plików
client.pem
oraz
server.pem
brakuje nam jeszcze jednego pliku,
a mianowicie głównego certyfikatu. Znaj-
duje się on w katalogu
demoCA
pod nazwą
cacert.
Wystarczy ten plik przekopiować do
katalogu, w którym znajdują się pozostałe
pliki naszej aplikacji. Zmieniamy nazwę pliku
z certyfikatem na
ca.pem
oraz kasujemy
początkowe informacje o certyfikacie aż do
linii rozpoczynającej certyfikat:
w katalogu
Apps
. Pojawia się on w dwóch
wersjach: dla języka Perl, jako plik
o nazwie
CA.pl
, oraz jako zwykły skrypt
BASH-a –
CA.sh
. My będziemy korzystać
z tego drugiego (jego kopia znajduje się
również na płycie CD/DVD).
Pierwszym krokiem jest utworzenie
certyfikatu autoryzacji, czyli pliku CA.
Gdy korzystamy z pomocy skryptu
CA.sh
,
wystarczy wydać krótkie polecenie:
Otrzymaliśmy wszystkie niezbędne
dane, aby utworzyć certyfikat dla
klienta. Niestety, musimy samodziel-
nie przenieść pewne dane z plików
wygenerowanych przez skrypt
CA.sh
i program
openssl
. Kopiujemy cześć
opisującą certyfikat (Listing 3 przed-
stawia ogólną postać tych plików)
-----BEGIN CERTIFICATE-----
W ten sposób dysponujemy trzema pli-
kami, które są niezbędne do bezpiecz-
nej komunikacji pomiędzy serwerem
a klientem.
Listing 4.
Najważniejsze fragmenty programu serwera
# ./CA.sh -newca
#! /usr/bin/python
import
sys
import
Pyro
.
core
import
Pyro
.
naming
from
Pyro
.
EventService
.
Clients
import
Publisher
Skrypt utworzy kilka katalogów oraz plik
z certyfikatem (znajdzie się on w katalo-
gu
demoCA
). Zostaniemy również popro-
szeni o wypełnienie kilku pól.
Następnie generujemy certyfikat żąda-
# identyczna postać jak na Listingu 2
class
printCertValidator
(
Pyro
.
protocol
.
BasicSSLValidator
):
class
TalkSrv
(
Pyro.core.ObjBase, Publisher
):
nia:
# ./CA.sh -newreq
Pyro
.
core
.
initServer
()
Pyro
.
config
.
PYRO_TRACELEVEL
=
3
Pyro
.
config
.
PYRO_NS_HOSTNAME
=
'localhost'
P
yro.config.PYRO_LOGFILE
=
'server_log'
Tutaj ponownie zostaniemy poproszeni
o podanie kilku informacji. Po zakoń-
czeniu procesu powstanie plik o nazwie
newreq.pem
. Następnym krokiem jest
podpis certyfikatu żądania:
ns
=
Pyro
.
naming
.
NameServerLocator
()
.
getNS
()
daemon
=
Pyro
.
core
.
Daemon
(
prtcol
=
'PYROSSL'
)
daemon
.
setNewConnectionValidator
(
printCertValidator
())
daemon
.
useNameServer
(
ns
)
# ./CA.sh -sign
Powstanie plik o nazwie
newcert.pem
.
Teraz możemy odczytać frazę klucza
i w tym celu wydajemy polecenie:
uri
=
daemon
.
connect
(
TalkSrv
()
,
"TalkSrv"
)
print
"TalkServer is ready."
daemon
.
requestLoop
()
# openssl rsa < newreq.pem > newkey.pem
74
styczeń 2005
Plik z chomika:
SOLARIX33
Inne pliki z tego folderu:
2006.01_Koder plików w formacie OGG_[Programowanie].pdf
(722 KB)
2007.06_Piękno fraktali_[Programowanie].pdf
(1778 KB)
2008.11_GanttProject_[Programowanie].pdf
(1014 KB)
2007.04_USB Device Explorer_[Programowanie].pdf
(1134 KB)
2006.09_QT, PyQT – szybkie tworzenie baz danych_[Programowanie].pdf
(1319 KB)
Inne foldery tego chomika:
Administracja
Aktualnosci
Audio
Bazy Danych
Bezpieczenstwo
Zgłoś jeśli
naruszono regulamin