[Ruby] Common-Key-Verschlüsselungs-Benchmark-Test

Einführung

Beim Umgang mit vertraulichen Informationen ist es üblich, einfachen Text zu verschlüsseln, in einer Datenbank zu speichern und beim Abrufen zu entschlüsseln.

In meiner Arbeit untersuchte ich die Methoden zum Ver- und Entschlüsseln mit Ruby und hatte die Möglichkeit, mit jeder Methode einen Benchmark-Test durchzuführen, sodass ich den Inhalt zu diesem Zeitpunkt zusammenfasste.

Es werden zwei Methoden berücksichtigt: die Ruby-Standardbibliothek OpenSSL (eigene Implementierung) und der AWS Key Management Service (KMS).

Ihre eigene Implementierung ist wahrscheinlich rechenintensiv, und KMS ist wahrscheinlich netzwerkintensiv. Daher liegt der Fokus darauf, wie sich dies auswirken wird.

Verschlüsselungsmethode

Allgemeine Schlüsselverschlüsselung

Eine Methode, bei der Sender und Empfänger einen Schlüssel heimlich gemeinsam nutzen und einen gemeinsamen Schlüssel zum Ver- und Entschlüsseln verwenden. Wenn immer dieselben Daten durch dieselbe Verschlüsselung ersetzt werden, wird der Klartext aus der Häufigkeit abgeleitet. Stellen Sie daher den Initialisierungsvektor (oder das Salz) so ein, dass dieselben Daten durch eine andere Verschlüsselung ersetzt werden können. Diesmal habe ich den Initialisierungsvektor verwendet.

Bei Verwendung der Standard-Ruby-Bibliothek "OpenSSL :: Cipher" sieht es so aus.

def encrypt(plaintext, key, iv)
  enc = OpenSSL::Cipher.new('AES-256-CBC')
  enc.encrypt
  enc.key = key
  enc.iv = iv
  enc.update(plaintext) + enc.final
end

def decrypt(encrypted_data, key, iv)
  dec = OpenSSL::Cipher.new('AES-256-CBC')
  dec.decrypt
  dec.key = key
  dec.iv = iv
  decrypted_data = dec.update(encrypted_data) + dec.final

  #Die entschlüsselten Daten sind ASCII-Da es sich um 8 Bit handelt, korrigieren Sie die Codierung zwangsweise
  decrypted_data.force_encoding("UTF-8")
end

plaintext = "Zu verschlüsselnde Zeichenfolge"

key = "Gemeinsamer Schlüssel"
iv = "Initialisierungsvektor"

#Datenverschlüsselung
encrypted_data = encrypt(plaintext, key, iv)

#Entschlüsselung von Daten
decrypt(encrypted_data, key, iv)

Verschlüsselung mit öffentlichem Schlüssel

Eine Methode, bei der die Verschlüsselung mit einem öffentlichen Schlüssel und die Entschlüsselung mit einem privaten Schlüssel durchgeführt wird.

Bei Verwendung der Standard-Ruby-Bibliothek "OpenSSL :: Cipher" sieht es so aus.

def encrypt(plaintext, public_key)
  Base64.encode64(
    public_key.public_encrypt(
      data, 
      OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING
    )
  )
end

def decrypt(encrypted_data, private_key)
  decrypted_data = private_key.private_decrypt(
    Base64.decode64(encrypted_data), 
    OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING
  )

  #Die entschlüsselten Daten sind ASCII-Da es sich um 8 Bit handelt, korrigieren Sie die Codierung zwangsweise
  decrypted_data.force_encoding("UTF-8")
end

plaintext = "Zu verschlüsselnde Zeichenfolge"

public_key = OpenSSL::PKey::RSA.new(File.read(public_key_file))
private_key = OpenSSL::PKey::RSA.new(File.read(private_key_file))

#Datenverschlüsselung
encrypted_data = encrypt(plaintext, public_key)

#Entschlüsselung von Daten
decrypt(encrypted_data, private_key)

Überblick über die Benchmark

Da es sich bei dem zu verschlüsselnden Klartext um einen Langtext mit mehreren hundert Zeichen handelt, kann die Verschlüsselung mit öffentlichen Schlüsseln nicht verwendet werden (sie kann mit ein wenig Einfallsreichtum verwendet werden, wird jedoch nicht empfohlen). Daher haben wir uns für die Verwendung der allgemeinen Schlüsselverschlüsselung entschieden.

Verwenden Sie die Benchmark-Bibliothek zum Benchmarking https://docs.ruby-lang.org/ja/latest/class/Benchmark.html

require 'benchmark'

result = Benchmark.realtime do
  #Der zu messende Prozess wird hier beschrieben.
end

puts "#{result}s"

Vergleich

--Ruby Standardbibliothek OpenSSL

Bankbedingungen

--Messen Sie die Gesamtzahl der Sekunden bei 1000-maliger Ausführung --Messen Sie jeweils nur die Verschlüsselung und nur die Entschlüsselung

Verschlüsselungs-Benchmark-Skript

Ruby Standardbibliothek OpenSSL

require 'openssl'
require 'base64'
require 'benchmark'

def encrypt(plaintext, key, iv)
  enc = OpenSSL::Cipher.new('AES-256-CBC')
  enc.encrypt
  enc.key = key
  enc.iv = iv
  enc.update(comment) + enc.final
end

data = <<-EOS
Langer Satz ...
EOS

key = "Gemeinsamer Schlüssel"
iv = "Initialisierungsvektor"

result = Benchmark.realtime do
  1000.times do
    encrypt(plaintext, key, iv)
  end
end

KMS

require 'aws-sdk-s3'
require 'base64'
require 'benchmark'

class KMSClient
  REGION = 'ap-northeast-1'
  ALIAS_NAME = 'KMS-Aliasname'

  def initialize
    @client = Aws::KMS::Client.new(
      region: REGION,
      #Wenn Sie einen VPC-Endpunkt festgelegt haben, geben Sie diesen anstelle der Region an
      # endpoint: 'https://vpce-xxxxx.kms.ap-northeast-1.vpce.amazonaws.com',
      access_key_id: '',
      secret_access_key: '',
    )
    @alias = @client.list_aliases.aliases.find { |a| a.alias_name == ALIAS_NAME }
  end

  def encrypt(plaintext)
    ciphertext = @client.encrypt(
      key_id: @alias.target_key_id,
      plaintext: plaintext
    )

    Base64.encode64(ciphertext.ciphertext_blob)
  end
end

plaintext = <<-EOS
Langer Satz ...
EOS

client = KMSClient.new

result = Benchmark.realtime do
  1000.times do
    client.encrypt(plaintext)
  end
end

puts "#{result}s"

Entschlüsselungs-Benchmark-Skript

Ruby Standardbibliothek OpenSSL

require 'openssl'
require 'base64'
require 'benchmark'

def decrypt(encrypted_data, key, iv)
  dec = OpenSSL::Cipher.new('AES-256-CBC')
  dec.decrypt
  dec.key = key
  dec.iv = iv
  decrypted_data = dec.update(encrypted_data) + dec.final
  decrypted_data.force_encoding("UTF-8")
end

plaintext = <<-EOS
Langer Satz ...
EOS

key = "Gemeinsamer Schlüssel"
iv = "Initialisierungsvektor"

encrypted_data = encrypt(plaintext, key, iv)

result = Benchmark.realtime do
  1000.times do
    decrypt(encrypted_data, key, iv)
  end
end

puts "#{result}s"

KMS

require 'aws-sdk-s3'
require 'base64'
require 'benchmark'

class KMSClient
  REGION = 'ap-northeast-1'
  ALIAS_NAME = 'KMS-Aliasname'

  def initialize
    @client = Aws::KMS::Client.new(
      region: REGION,
      #Wenn Sie einen VPC-Endpunkt festgelegt haben, geben Sie diesen anstelle der Region an
      # endpoint: 'https://vpce-xxxxx.kms.ap-northeast-1.vpce.amazonaws.com',
      access_key_id: '',
      secret_access_key: '',
    )
    @alias = @client.list_aliases.aliases.find { |a| a.alias_name == ALIAS_NAME }
    p @alias
  end

  def encrypt(plaintext)
    ciphertext = @client.encrypt(
      key_id: @alias.target_key_id,
      plaintext: plaintext
    )

    Base64.encode64(ciphertext.ciphertext_blob)
  end

  def decrypt(ciphertext_blob)
    @client.decrypt(ciphertext_blob: Base64.decode64(ciphertext_blob)).plaintext
  end
end

plaintext = <<-EOS
Langer Satz ...
EOS

client = KMSClient.new

encrypted_data = client.encrypt(plaintext)

result = Benchmark.realtime do
  1000.times do
    client.decrypt(encrypted_data)
  end
end

puts "#{result}s"

Benchmark-Ergebnisse

Verschlüsselung

Methode Die Anzahl der Sekunden
Ruby Standardbibliothek OpenSSL 0.006588994991034269
KMS 8.035557514987886
KMS (VPC-Endpunkt) 7.766658762935549

Entschlüsselung

Methode Die Anzahl der Sekunden
Ruby Standardbibliothek OpenSSL 0.0037274740170687437
KMS 8.964495759923011
KMS (VPC-Endpunkt) 7.9086791928857565

Zusammenfassung

Schließlich scheint KMS aufgrund der erheblichen Netzwerkkosten langsam zu sein. Ich denke, dies ist das Ergebnis des Netzwerkzugriffs auf AWS bei jedem Aufruf der Verschlüsselungs- / Entschlüsselungsmethode. Das Einrichten eines VPC-Endpunkts und das Zulassen einer Verbindung innerhalb der VPC würde ihn ein wenig verbessern, aber es scheint seine eigene Implementierung immer noch nicht zu übertreffen. Wenn Sie jedoch den für die Verschlüsselung verwendeten Schlüssel verlängern, um die Sicherheit zu verbessern, erhöhen sich die Berechnungskosten, selbst wenn Sie ihn selbst implementieren. Daher müssen Sie in diesem Punkt anscheinend vorsichtig sein.

Recommended Posts

[Ruby] Common-Key-Verschlüsselungs-Benchmark-Test