2016-01-28 31 views
6

Używam języka Ruby o nazwie Open SSL bindings do szyfrowania AES-256. Mogę zaszyfrować niepusty ciąg. Jednak podczas próby zaszyfrowania pustego ciągu Ruby podnosi wyjątek, narzucając, że dane nie mogą być puste. Jak mogę zaszyfrować pusty ciąg za pomocą powiązań OpenSSL Ruby?Szyfruj pusty ciąg znaków

kod do odtworzenia problemu

require "openssl" 

KEY = OpenSSL::Cipher::Cipher.new("aes-256-cbc").random_key 

def encrypt(plaintext) 
    cipher = OpenSSL::Cipher::Cipher.new("aes-256-cbc") 
    cipher.encrypt 
    iv = cipher.random_iv 
    cipher.iv = iv 
    cipher.key = KEY 
    ciphertext = cipher.update(plaintext) # <- ArgumentError here 
    ciphertext << cipher.final 
    [iv, ciphertext] 
end 

def decrypt(iv, ciphertext) 
    cipher = OpenSSL::Cipher::Cipher.new("aes-256-cbc") 
    cipher.decrypt 
    cipher.iv = iv 
    cipher.key = KEY 
    plaintext = cipher.update(ciphertext) 
    plaintext << cipher.final 
    plaintext 
end 

p decrypt(*encrypt("foo")) # "foo" 

p decrypt(*encrypt("")) 
# /tmp/foo.rb:11:in `update': data must not be empty (ArgumentError) 
#   from /tmp/foo.rb:11:in `encrypt' 
#   from /tmp/foo.rb:27:in `<main>' 

Wersje

  • ruby-2.2.2p95
  • OpenSSL :: wersja jest "1.1.0"
  • Microsoft SQL Server 2014 (12.0.2000.8)

Dlaczego chcę szyfrować puste ciągi?

Piszę program ETL do migracji danych z jednej bazy danych do bazy danych SqlServer. Niektóre kolumny ze źródłowej bazy danych muszą być zaszyfrowane przed zapisaniem ich w docelowej bazie danych. Kolumny źródłowe mogą zawierać dowolne dane, w tym puste ciągi. Kolumny docelowe są zwykle niedozwolone. Kolumny docelowe zostaną odszyfrowane przez kod .net.

Cel nr 1: Brak informacji o zaszyfrowanym polu, w tym o tym, czy istnieje, czy nie, powinien być możliwy do odzyskania bez prawidłowego odszyfrowania go. Zaszyfrowany pusty ciąg powinien być nieodróżnialny od innych zaszyfrowanych danych.

Cel # 2: kod .net, który odszyfruje te wartości, nie musi specjalnie traktować pustych ciągów.

Jeśli mogę uzyskać openssl do szyfrowania pustych ciągów, osiągnę oba te cele.

Obejście - Nie szyfrować pustych strun

ja po prostu nie mógł szyfrować pustych strun, przekazując je przez.

def encrypt(plaintext) 
    return plaintext if plaintext.empty? 
    ... 
end 

def decrypt(iv, ciphertext) 
    return ciphertext if ciphertext.empty? 
    ... 
end 

ten ma wady, gdyż informacje odsłaniających, a także wymaga kodu współpracującego być zapisane na boku NET.

Obejście - Dodaj pewnej stałej do tekstu jawnego

mogę dodać jakiś stały ciąg tekstu jawnego przed szyfrowaniem i usunąć go po odszyfrowaniu:

PLAINTEXT_SUFFIX = " " 

def encrypt(plaintext) 
    plaintext += PLAINTEXT_SUFFIX 
    ... 
end 

def decrypt(iv, ciphertext) 
    ... 
    plaintext.chomp(PLAINTEXT_SUFFIX) 
end 

Ukrywa czy dane istnieje, czy nie, ale nadal wymaga współpracującego kodu .net.

+1

OpenSSL obsługuje dopełnienie PKCS # 7, więc to powinno działać. Jest to prawdopodobnie lepiej dopasowane do raportu o błędzie. Przy okazji, czy próbowałeś po prostu pominąć 'cipher.update', jeśli tekst jawny jest pusty? "szyfrowanie.final" może być w tym przypadku wystarczające. –

+0

@ArtjomB. Próbowałem pominąć wywołanie aktualizacji # Cipher. To nie zadziałało, gdy próbowałem go wcześniej z powodu błędu ID10T, niż nie zauważyłem w tym czasie. Okazuje się, że to rozwiązanie. Czy mógłbyś dodać to jako odpowiedź? –

+0

Możesz dodać sam. Nie mam pojęcia o rubinach i nie chcę się uczyć w tym szczególnym czasie. :) –

Odpowiedz

7

As suggested by @ArtjomB, to tak proste, jak nie dzwonienie pod numer Cipher#update z pustym ciągiem znaków. Wartość zwrócona przez Cipher#final następnie poprawnie szyfruje pusty ciąg znaków.

require "openssl" 

KEY = OpenSSL::Cipher::Cipher.new("aes-256-cbc").random_key 

def encrypt(plaintext) 
    cipher = OpenSSL::Cipher::Cipher.new("aes-256-cbc") 
    cipher.encrypt 
    iv = cipher.random_iv 
    cipher.iv = iv 
    cipher.key = KEY 
    ciphertext = "" 
    ciphertext << cipher.update(plaintext) unless plaintext.empty? 
    ciphertext << cipher.final 
    [iv, ciphertext] 
end 

def decrypt(iv, ciphertext) 
    cipher = OpenSSL::Cipher::Cipher.new("aes-256-cbc") 
    cipher.decrypt 
    cipher.iv = iv 
    cipher.key = KEY 
    plaintext = cipher.update(ciphertext) 
    plaintext << cipher.final 
end 

p decrypt(*encrypt("foo")) # "foo" 
p decrypt(*encrypt(""))  # "" 
2

Jeśli możesz używać funkcji szyfrowania dostarczonych przez DBMS, wówczas MySQL AES_ENCRYPT wydaje się być w stanie zaszyfrować pusty ciąg.

Na przykład:

UPDATE some_table 
    SET some_column = AES_ENCRYPT('',UNHEX('F3229A0B371ED2D9441B830D21A390C3')); 

To AES-128 domyślnie, ja domyślam się, że będzie problem, jak trzeba AES-256. Ponadto, nie wiesz, który DBMS używasz i czy ten DBMS ma funkcje szyfrowania.

+0

Nie myślałem o korzystaniu z bazy danych do szyfrowania. Docelowy DB to SqlServer 2014. –

+1

@WayneConrad Niewiele wiadomo o tym serwerze, ale wygląda na to, że obsługuje szyfrowanie AES-256. Być może będziesz musiał przetestować go pod kątem pustego szyfrowania ciągów. –

+0

@WayneConrad Na marginesie, ze względu na twoją stronę profilu SO - odkryłem, że było coś o nazwie [Rozszerzenia Rubiego] (http://extensions.rubyforge.org/rdoc/index.html), które miało pierwsze wersje 'Symbolu # to_proc', który został zasymilowany w Railsach, a następnie w rdzeniu Ruby. Robiłem jakieś dochodzenie o tym, czy naprawdę użyłeś '[day, dollar] .each (&: another)' in 2003.: D –

Powiązane problemy