• Home
  • STRONA ISOLUTION.PL
  • LOGOWANIE
  • POMOC

Hibernate + Jasypt = przezroczyste szyfrowanie w bazach danych

Marek Grądzki | 29/08/2008

Jasypt (Java Simplified Encryption) jest biblioteką Javy, która pozwala na dodanie w prosty sposób szyfrowania w naszej aplikacji. Sama biblioteka nie implementuje żadnych algorytmów kryptograficznych, dostarcza natomiast proste i funkcjonalne API, które znacznie ułatwia korzystanie z JCA.

Szczególną cechą biblioteki jest wsparcie integracji z wieloma technologiami (Spring, Hibernate, Wicket, Jboss Seam). W artykule opiszę jak w połączeniu z Hibernate uzyskać szyfrowanie bazy danych, przezroczyste dla użytkownika i aplikacji internetowej.

1. Kryptografia w Javie

Standardowo mechanizmy kryptograficzne w Javie dostępne są za pośrednictwem api JCA (Java Cryptography Architecture). Jest to tylko zbiór interfejsów, implementacji konkretnych algorytmów dostarczają dostawcy bezpieczeństwa (security providers). Standardowe api zmusza programistów do wnikania w szczegóły działania algorytmów kryptograficznych (pobieranie instancji algorytmów, konfiguracja instancji, inicjalizacja oraz wykonanie algorytmu).

Jasypt pozwala programistom, nie będącym ekspertami w dziedzinie kryptografii, wykorzystywać szyfrowanie w swoich aplikacjach. Dostarcza proste i funkcjonalne api, które może posłużyć do:

  • tworzenia skrótów wiadomości (przydatne m.in. do przechowywania haseł)
  • szyfrowania tekstów, liczb i danych binarnych przy użyciu hasła (Password Based Encrytpion).

Dostępne są dwa interfejsy – prosty (Easy), dla programistów nie mających większej wiedzy o kryptografii lub/i nie chcących wnikać w sposób działania biblioteki:

  • skróty wiadomości (org.jasypt.util.digest.*)
  • przechowywanie haseł (org.jasypt.util.password.*)
  • szyfrowanie tekstu (org.jasypt.util.text.*)
  • szyfrowanie liczb (org.jasypt.util.numeric.*)
  • szyfrowanie danych binarnych (org.jasypt.util.binary.*)

oraz drugi, bardziej zaawansowany (Standard), pozwalający na wybór algorytmów i konfigurację ich działania:

  • skróty wiadomości (org.jasypt.digest.*)
  • szyfrowanie (org.jasypt.encryption.*)

Przykład przy użyciu prostego interfejsu (domyślnie szyfrowane przy użyciu PBEWithMD5AndDES):


BasicTextEncryptor textEncryptor = new BasicTextEncryptor();
textEncryptor.setPassword("qw123456789");
String myEncryptedText = textEncryptor.encrypt("Wiadomość.");
String plainText = textEncryptor.decrypt(myEncryptedText);

Bardziej zaawansowane szyfrowanie (choć interfejs nadal dużo prostszy niż JCA):


StandardPBEStringEncryptor textEncryptor = new StandardPBEStringEncryptor();
textEncryptor.setProvider(new BouncyCastleProvider());
textEncryptor.setAlgorithm("PBEWITHSHA256AND256BITAES-CBC-BC");
textEncryptor.setPassword("qw123456789");
textEncryptor.setKeyObtentionIterations(1000);
String encryptedText = textEncryptor.encrypt("Wiadomość");
String plainText = textEncryptor.decrypt(encryptedText);

W drugim przykładzie użyto darmowego providera BouncyCastle (charakteryzuje się większą liczbą zaimplementowanych algorytmów niż standardowy SunJCE i otwartym kodem źródłowym).

2. Spring + Hibernate + Jasypt

Jasypt można zintegrować z Hibernate wykorzystując w mapowaniach typy użytkownika (implementacje org.hibernate.usertype.UserType) dostarczone przez bibliotekę.

Typ Javy Typ SQL Typ Jasypt (org.jasypt.hibernate.type)
String VARCHAR, CLOB, TEXT EncryptedStringType
byte[] VARBINARY, BLOB EncryptedBinaryType
Byte VARCHAR, CLOB, TEXT EncryptedByteAsStringType
Short VARCHAR, CLOB, TEXT EncryptedShortAsStringType
Integer VARCHAR, CLOB, TEXT EncryptedIntegerAsStringType
Long VARCHAR, CLOB, TEXT EncryptedLongAsStringType
BigInteger NUMERIC, NUMBER EncryptedBigIntegerType
BigInteger VARCHAR, CLOB, TEXT EncryptedBigIntegerAsStringType
Float VARCHAR, CLOB, TEXT EncryptedFloatAsStringType
Double VARCHAR, CLOB, TEXT EncryptedDoubleAsStringType
BigDecimal NUMERIC, NUMBER EncryptedBigDecimalType
BigDecimal VARCHAR, CLOB, TEXT EncryptedBigDecimalAsStringType
Boolean VARCHAR, CLOB, TEXT EncryptedBoleanAsStringType
Date VARCHAR, CLOB, TEXT EncryptedDateAsStringType
Calendar VARCHAR, CLOB, TEXT EncryptedCalendarAsStringType

Użycie tych typów zapewni, że pola zostaną zapisane w postaci zaszyfrowanej. Po odczycie (HibernateTemplate.get()) zostaną automatycznie odszyfrowane, bez potrzeby dodawania kodu, który to zrealizuje. Takie zabezpieczenie pozwoli ochronić poufne dane przed osobami mającymi dostęp do bazy danych (np. administratorami baz danych).

Szyfrowanie w Jasypt zaimplementowane jest w oparciu o standard PKCS #5 (Password-Based Cryptography). Klucz algorytmu szyfrującego otrzymujemy przez wielokrotne wykonanie skrótu ciągu znaków, składającego się z frazy kluczowej (hasła użytkownika) poprzedzonej pewną liczbą bajtów (tzw. sól). Największe bezpieczeństwo otrzymujemy, gdy sól jest losowa. Wtedy wynik szyfrowania danego tekstu przy użyciu stałego klucza jest zawsze inny. Wprowadza to pewne ograniczenia w użyciu Hibernate. Pola zaszyfrowane z wykorzystaniem losowej soli nie mogę być częścią klauzuli WHERE w zapytaniach HQL. Aby móc porównywać ze sobą zaszyfrowane wartości, wynik szyfrowania danego tekstu musi być stały dla danego klucza, sól musi być więc stała.

Fragment mapowania:


<property name="accountNumber" type="encryptedString" length="128" />
<property name="address" type="comparableEncryptedString" length="172" />

Aby móc używać tych typów w mapowaniach potrzeba odrobiny konfiguracji. Poniżej definicja typów Hibernate (ta konfiguracja umożliwia szyfrowanie tylko typu String, dla każdego typu z tabelki powyżej należy dopisać analogiczną konfigurację):


<typedef name="comparableEncryptedString" class="org.jasypt.hibernate.type.EncryptedStringType">
    <param name="encryptorRegisteredName">jasyptStringHibernateEncryptor</param>
</typedef>

<typedef name=”encryptedString” class=”org.jasypt.hibernate.type.EncryptedStringType”>
    <param name=”encryptorRegisteredName”>secureJasyptStringHibernateEncryptor</param>
</typedef>

Dla każdego typu Hibernate należy wskazać komponent odpowiedzialny za szyfrowanie. Właściwość registeredName powinna odpowiadać parametrowi encryptorRegisteredName z definicji typu Hibernate. Poniżej konfiguracja beanów springowych:


<bean id="jasyptStringHibernateEncryptor"
    class="org.jasypt.hibernate.encryptor.HibernatePBEStringEncryptor"
    lazy-init="false">
    <property name="registeredName" value="jasyptStringHibernateEncryptor" />
    <property name="config" ref="stringPBEConfig" />
</bean>

<bean id=”secureJasyptStringHibernateEncryptor”
    class=”org.jasypt.hibernate.encryptor.HibernatePBEStringEncryptor”
    lazy-init=”false”>
    <property name=”registeredName” value=”secureJasyptStringHibernateEncryptor” />
    <property name=”config” ref=”secureStringPBEConfig” />
</bean>

Każdy komponent szyfrujący musi posiadać swoją konfigurację. Poniżej dwie przykładowe konfiguracje definiujące: algorytm szyfrujący, ilość iteracji funkcji skrótu potrzebnych do uzyskania klucza szyfrującego, frazę klucza, dostawcę bezpieczeństwa oraz generator soli. Komponent szyfrujący, przypisany do typu comparableEncryptedString używa konfiguracji ze stałą solą. Dzięki temu będzie można wyszukiwać obiektów na podstawie pól tego typu.


<bean id="secureStringPBEConfig" class="org.jasypt.encryption.pbe.config.SimpleStringPBEConfig">
    <property name="algorithm" value="PBEWITHSHA-256AND256BITAES-CBC-BC" />
    <property name="keyObtentionIterations" value="1000" />
    <property name="password" value="qw123" />
    <property name="provider" ref="bcProvider" />
    <property name="saltGenerator" ref="randomSaltGenerator" />
</bean>

<bean id=”stringPBEConfig” class=”org.jasypt.encryption.pbe.config.SimpleStringPBEConfig”>
    <property name=”algorithm” value=”PBEWITHSHA-256AND256BITAES-CBC-BC” />
    <property name=”keyObtentionIterations” value=”1000″ />
    <property name=”password” value=”qw123″ />
    <property name=”provider” ref=”bcProvider” />
    <property name=”saltGenerator” ref=”fixedStringSaltGenerator” />
</bean>

Poniżej konfiguracje generatorów soli i dostawcy bezpieczeństwa:


<bean id="bcProvider" class="org.bouncycastle.jce.provider.BouncyCastleProvider" />

<bean id=”randomSaltGenerator” class=”org.jasypt.salt.RandomSaltGenerator” />

<bean id=”fixedStringSaltGenerator” class=”org.jasypt.salt.FixedStringSaltGenerator”>
    <property name=”salt” value=”p5u+?drach@c2eZ-” />
</bean>

Przechowywanie hasła w pliku XML nie jest najbezpieczniejszym rozwiązaniem, dlatego Jasypt udostępnia inne klasy konfiguracyjne. Szczególnie warte uwagi są EnvironmentStringPBEConfig, która umożliwia definiowania hasła jako zmiennej środowiskowej lub właściwości wirtualnej maszyny Javy oraz WebPBEConfig, która pozwala na ustawienie hasła za pośrednictwem interfejsu webowego (serwletu definiowanego w web.xml). Poniżej przykład konfiguracji klasy WebPBEConfig (najbezpieczniejszego sposobu konfiguracji hasła dostępnego w Jasypt):


<bean id="webPBEConfig" class="org.jasypt.encryption.pbe.config.WebPBEConfig">
    <property name="name" value="webPBEConfig" />
    <property name="validationWord" value="abc321" />
    <property name="algorithm" value="PBEWITHSHA-256AND256BITAES-CBC-BC" />
    <property name="keyObtentionIterations" value="1000" />
    <property name="provider" ref="bcProvider" />
    <property name="saltGenerator" ref="fixedStringSaltGenerator" />
</bean>

Dodatkowo w pliku web.xml należy dodać:


<servlet>
    <servlet-name>webPBEConfigServlet</servlet-name>
    <servlet-class>org.jasypt.web.pbeconfig.WebPBEConfigServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>

<lservlet-mapping>
    <servlet-name>webPBEConfigServlet</servlet-name>
    <url-pattern>/webPBEConfig.do</url-pattern>
</servlet-mapping>

<filter>
    <filter-name>webPBEConfigFilter</filter-name>
    <filter-class>org.jasypt.web.pbeconfig.WebPBEConfigFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>webPBEConfigFilter</filter-name>
    <servlet-name>strutsActionServlet</servlet-name>
</filter-mapping>

Filtr webPBEConfigFilter blokuje dostęp do aplikacji do momentu ustawienia hasła. Po otwarciu strony /webPBEConfig.do, ujrzymy formularz do wprowadzenia hasła do szyfrowania:

okno wprowadzenia hasła

Validation word to dodatkowe zabezpieczenie, które uniemożliwi ustawienie hasła przypadkowej osobie. Hasło jest ustawiane jeden raz i nie może być zmieniane w trakcie działania aplikacji.

3. Wyliczanie długości kolumn

Wprowadzając szyfrowanie w bazie danych należy pamiętać, że zaszyfrowane kolumny mogą zająć więcej miejsca. Wszystko zależy od wybranego algorytmu oraz kodowania. Pierwszy narzut powoduje samo szyfrowanie – ilość bajtów danych wynikowych jest wielokrotnością długości bloku algorytmu (w naszym przypadku 16). Dodatkowy narzut powoduje użycie losowej soli, która dołączana jest do zaszyfrowanej kolumny (w naszym przypadku 16 bajtów), co pozwala na późniejsze odszyfrowanie danych. Stała sól jest zapisywana jedynie w konfiguracji aplikacji i nie powoduje zwiększenia rozmiaru zaszyfrowanej kolumny. Największy narzut powoduje kodowanie zaszyfrowanych danych w bazie danych. Aby uniknąć problemów z przenośnością, Jasypt pozwala na kodowanie zaszyfrowanych kolumn w systemie heksadecymalnym jako znaki ASCII (po zakodowaniu tekst jest dwa razy dłuższy) lub jako Base64 (kodowanie to daje średnio 30% więcej znaków).

Poniżej zamieszczam tabelki pomocne w ustaleniu długości pól zaszyfrowanych w bazie danych (dla szyfru AES). Zakładam że tekst niezaszyfrowany kodowany jest w UTF-8 natomiast zaszyfrowany w Base64. Jeśli tekst niezaszyfrowany nie zawiera polskich znaków diakrytycznych, każdy znak mieści się w jednym bajcie. Jeśli zawiera polskie znaki, zakładam najgorsza możliwość – wszystkie znaki zajmują po 2 bajty.

a) Losowa sól

Ilość znaków Ilość znaków w BD (tekst en) Ilość znaków w BD (tekst pl)
7 44 44
8 44 64
15 44 64
16 64 88
31 64 108
32 88 128
63 108 192
64 128 216
127 192 364
128 218 384
255 364 704
x wz1.PNG wz2.PNG

b) Stała sól

Ilość znaków Ilość znaków w BD (tekst en) Ilość znaków w BD (tekst pl)
7 24 24
8 24 44
15 24 44
16 44 64
31 44 88
32 64 108
63 88 172
64 108 192
127 172 344
128 192 364
255 344 364
x wz3.PNG wz4.PNG

c) Wymagane długości pól dla innych typów danych wspieranych przez Jasypt.

Typ Java Typ Jasypt (org.jasypt.hibernate.type) Stała sól Zmienna sól
Date EncryptedDateAsStringType 24 znaki 44 znaki
Boolean EncryptedStringType 24 znaki 44 znaki
Long EncryptedStringType 44 znaki 64 znaki
Double EncryptedStringType 44 znaki 64 znaki
Float EncryptedStringType 24 znaki 44 znaki
Short EncryptedStringType 24 znaki 44 znaki
Integer EncryptedStringType 24 znaki 44 znaki
BigDecimal EncryptedBigDecimalAsStringType tj. teksty bez znaków diakrytycznych
BigDecimal (numeric) EncryptedBigDecimalType 48 cyfr (jeśli <36 cyfr przed szyfrowaniem) 87 cyfr (jeśli <36 cyfr przed szyfrowaniem)
BigInteger EncryptedBigIntegerAsStringType tj. teksty bez znaków diakrytycznych
BigInteger EncryptedBigIntegerType 48 cyfr (jeśli <36 cyfr przed szyfrowaniem) 87 cyfr (jeśli <36 cyfr przed szyfrowaniem)

4. Szyfrowanie całej bazy danych

Istnieją sytuacje, w których konieczne może być zaszyfrowanie/odszyfrowanie całej bazy danych. Pierwsza sytuacja zachodzi na przykład, gdy wprowadzamy szyfrowanie na późnym etapie projektu. Druga, jeśli konieczny jest dostęp do niezaszyfrowanych danych za pomocą narzędzi nie korzystających z Hibernate (bazę można odszyfrować, użyć narzędzia a następnie znów zaszyfrować). Konieczność odszyfrowania bazy danych może wynikać także z przyjętej polityki bezpieczeństwa. Polityka powinna zakładać okresowe zmiany klucza szyfrującego. Zmiana klucza będzie wymagała odszyfrowania całej bazy starym kluczem i zaszyfrowania nowym.

Zaszyfrowanie/odszyfrowanie całej bazy danych można łatwo zaimplementować, tworząc dwa mapowania: jedno dla z typami szyfrującymi a drugą bez. Następnie należy przy użyciu Hibernate odczytać dane z jednej bazy i zapisać do drugiej. Tworzenie mapowań można zautomatyzować np. przy użyciu XSLT. Mając mapowanie z typami szyfrującymi, łatwo można utworzyć wersję bez szyfrowania. Niestety transformacja w drugą stronę nie jest możliwa bez dodatkowych danych (nie wiemy które kolumny mają być szyfrowane i w jaki sposób). Aby to osiągnąć, należałoby dodać dodatkowe atrybuty do DTD Hibernate (gdyby Hibernate używał XML Schema do definicji mapowań byłoby to dużo prostsze).

Poniżej przykład klasy realizującej to zadanie (do poprawnego działania potrzebna jest konfiguracja dwóch źródeł danych i ustawienie odpowiednich zależności):


import java.util.Iterator;
import java.util.List;
import org.hibernate.ReplicationMode;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.mapping.RootClass;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.orm.hibernate3.LocalSessionFactoryBean;

public class DatabaseCopyUtilities {
    private LocalSessionFactoryBean sourceLocalSessionFactoryBean;
    private SessionFactory sourceDatabaseSessionFactory;
    private SessionFactory targetDatabaseSessionFactory;

    @SuppressWarnings(”unchecked”)
    public void copy() {
        Session sourceDatabaseSession = sourceDatabaseSessionFactory.openSession();
        Session targetDatabaseSession = targetDatabaseSessionFactory.openSession();
        Iterator i = sourceLocalSessionFactoryBean.getConfiguration().getClassMappings();
        while (i.hasNext()) {
            RootClass rc = (RootClass)i.next();
            List l1 = sourceDatabaseSession.createQuery(”from ” + rc.getClassName()).list();
            for (Object o : l1) {
                targetDatabaseSession.replicate(o, ReplicationMode.OVERWRITE);
                targetDatabaseSession.flush();
            }
        }
        sourceDatabaseSession.close();
        targetDatabaseSession.close();
    }

    @Required
    public void setSourceLocalSessionFactoryBean(LocalSessionFactoryBean sourceLocalSessionFactoryBean) {
        this.sourceLocalSessionFactoryBean = sourceLocalSessionFactoryBean;
    }

    @Required
    public void setSourceDatabaseSessionFactory(SessionFactory sourceDatabaseSessionFactory) {
        this.sourceDatabaseSessionFactory = sourceDatabaseSessionFactory;
    }

    @Required
    public void setTargetDatabaseSessionFactory(SessionFactory targetDatabaseSessionFactory) {
        this.targetDatabaseSessionFactory = targetDatabaseSessionFactory;
    }

}

5. Podsumowanie

Artykuł nie omawia wszystkich funkcjonalności oferowanych przez Jasypt. Bardzo istotnym zagadnieniem, o którym nie napisałem, jest przechowywanie haseł (np. za pomocą których użytkownicy logują się do systemu). Ze względów bezpieczeństwa, nie powinno się przechowywać haseł w postaci zaszyfrowanej (ktoś, kto zna klucz szyfrujący, mógłby odczytać hasła). Do bazy danych należy zapisywać skróty haseł (wyniki nieodwracalnego przekształcenia). Api Jasypt ułatwia to zadanie dzięki integracji ze Spring Security. Zainteresowanych odsyłam na stronę projektu.

Więcej informacji:

http://www.jasypt.org/
http://www.bouncycastle.org/
http://java.sun.com/javase/6/docs/technotes/guides/security/crypto/CryptoSpec.html
http://www.ietf.org/rfc/rfc2898.txt
http://pl.wikipedia.org/wiki/AES
http://pl.wikipedia.org/wiki/Sha1
http://pl.wikipedia.org/wiki/Base64

Categories
Aplikacje Web, Bazy danych, Spring
Comments rss
Comments rss
Trackback
Trackback

« Git? GWT (AJAX) for Isolution Workflow »

9 responses

Wspaniały temat - szyfrowanie danych z prezentacją Jasypt, o którym

Jacek Laskowski Windows XP Mozilla Firefox 3.0.1 | 02/09/2008

Wspaniały temat - szyfrowanie danych z prezentacją Jasypt, o którym nigdy wcześniej nie słyszałem (nawet wzmianki!). Mam pytanie odnośnie uwagi (gdyby Hibernate używał XML Schema do definicji mapowań byłoby to dużo prostsze). Mógłbyś rozwinąć temat? W jaki sposób XSD uprościłby temat? I jak wygląda sprawa wydajności takiego rozwiązania. Robiłeś jakiekolwiek badania w temacie?

Kilka literówek jakie znalazłem po drodze:
1/ Jboss Steam - JBoss Seam jak mniemam
2/ przezroczyste - przeźroczyste
3/ przenaszalność - przenośność

Jacek

Dziękuję za komentarz. Przepraszam za literówki (już poprawiłem). Jeśli chodzi

Marek Grądzki Linux Mozilla Firefox 3.0.1 | 10/09/2008

Dziękuję za komentarz. Przepraszam za literówki (już poprawiłem). Jeśli chodzi o uwagi merytoryczne: w momencie pisania artykułu wydawało mi się, że zastosowanie XSD pozwoli na dodanie do elementu atrybutów z innej przestrzeni nazw tak, aby dokument był poprawny strukturalnie (valid). Po dokładniejszemu przyjrzeniu się problemowi, doszedłem do wniosku, że znaczące ułatwienia możliwe są tylko w przypadku, gdy typ został zaprojektowany w sposób umożliwiający łatwe rozszerzanie (np. zawiera xs:anyAttribute).
Jeśli XSD nie pozwala na rozszerzanie, można stworzyć własny schemat (importujący schemat oryginalny) i użyć xs:extension. Nie jest to rozwiązanie idealne, ale na pewno lepsze niż utworzenie lokalnej kopi schematu i jego późniejsza modyfikacja. Jak dotąd nie udało mi się wymyślić lepszego rozwiązania. Może powinien wypowiedzieć się ktoś, kto ma większe doświadczenie odnośnie XML-a …
Testów wydajnościowych jeszcze nie przeprowadzałem (mają dość wysoki priorytet na mojej liście rzeczy do zrobienia). Na wydajność może mieć wpływ wiele czynników, dlatego jeszcze zastanawiam się nad sposobem przeprowadzenia testów. Istotne będą zapewne: wybór i konfiguracja algorytmów kryptograficznych (np. ilość iteracji funkcji skrótu) oraz efektywność implementacji algorytmów przez providera. Myślę, że narzut spowodowany wprowadzeniem dodatkowej warstwy przez Jasypt będzie dużo mniej istotny.

Добрый день! < a href="http://sotkashop.ru/contacts/ thomas@sotkashop.ru" >...< /a >... С уважением,...

Freeman | 16/06/2010

Добрый день! < a href=”http://sotkashop.ru/contacts/ thomas@sotkashop.ru” >…< /a >…

С уважением,…

< blockquote >< a href="http://pillspot.org/">Pillspot.org. Canadian Health&Care.Special Internet Prices.Best quality

HUGH | 04/07/2010

< blockquote >< a href=”http://pillspot.org/”>Pillspot.org. Canadian Health&Care.Special Internet Prices.Best quality drugs.No prescription online pharmacy. High quality pills. Order drugs online< /a >…

Buy:Cialis Super Active+.Super Active ED Pack.Zithromax.Propecia.Viagra Super Active+.Cialis Professional.Viagra.Cialis Soft Tabs.VPXL.Tramadol.Viagra Soft Tabs.Levitra.Soma.Viagra Professional.Maxaman.Viagra Super Force.Cialis….

Counters http://jaltecyat.AUTOPARTSVILLE.INFO/tag/Counters in nc/ : nc... Counters...

nc Ubuntu Linux Mozilla Firefox 3.0.6 | 29/08/2010

Counters http://jaltecyat.AUTOPARTSVILLE.INFO/tag/Counters in nc/ : nc…

Counters…

Range http://kphilipsh6bd.03GMCPARTS.US/tag/Cooktops+top+Range/ : top... Cooktops...

top Windows XP Internet Explorer 6.0 | 29/08/2010

Range http://kphilipsh6bd.03GMCPARTS.US/tag/Cooktops+top+Range/ : top…

Cooktops…

sterling http://nwheelchairyuxeqy.AWESOMEBABYCLOTHES.INFO/tag/sterling+wristbands+one+a/ : one... sterling...

sterling Ubuntu Linux Mozilla Firefox 3.0.6 | 29/08/2010

sterling http://nwheelchairyuxeqy.AWESOMEBABYCLOTHES.INFO/tag/sterling+wristbands+one+a/ : one…

sterling…

10 http://xstainlesszhw50m.03GMCPARTS.US/tag/gas+1810+10/ : 10... gas...

1810 Mac OS X Mozilla Firefox 3.0.6 | 29/08/2010

10 http://xstainlesszhw50m.03GMCPARTS.US/tag/gas+1810+10/ : 10…

gas…

Bay http://mhamptonw6gz.APTAUTOPARTS.INFO/tag/dark+Hampton+Bay/ : dark... Bay...

dark Windows Vista Mozilla Firefox 3.0.6 | 30/08/2010

Bay http://mhamptonw6gz.APTAUTOPARTS.INFO/tag/dark+Hampton+Bay/ : dark…

Bay…

Leave a comment

You can use these tags : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>

Navigation

  • Aplikacje Web
  • Architektura
  • Bazy danych
  • EJB
  • Narzędzia
  • Spring
  • Testowanie
  • UML
  • WEB Service
  • XML

Search

rss Comments rss valid xhtml 1.1 design by jide powered by Wordpress get firefox