2. @nahi - Twitter, Github
Software Engineer at https://www.treasuredata.com
OSS developer and enthusiast;
committer of CRuby and JRuby
Information Security Specialist
6. (D) S for external C
[F] Encryption in S
[G] Encryption in C
[E] authentication
(C) S for internal C
(B) C for external S
7 Implementation Patterns
(A) C for internal S
(A)
(A)
(B)
(B)
(C)
(D)
[F]
[G]
[E]
[E]
Orange: Implementation target
Gray: External system
7. (D) S for external C
[F] Encryption in S
[G] Encryption in C
[E] authentication
(C) S for internal C
(B) C for external S
7 Implementation Patterns
(A) C for internal S
(A)
(A)
(B)
(B)
(C)
(D)
[F]
[G]
[E]
[E]
Orange: Implementation target
Gray: External system
8. … in Ruby
(A) C for internal S
(B) C for external S
(C) S for internal C
(D) S for external C
[E] authentication
[F] Encryption in S
[G] Encryption in C
(A)
(A)
(B)
(B)
(C)
[E]
[F]
[G]
(D)
[E]
Blue: Acceptable
Orange: Pitfalls
Red: No way
9. Protected communication
Fixed server authentication
➔ SSL configuration:
CBC, SSLv3.0, compression,
TLSv1.0, RC4, DHE1024, …
➔ Fails for wrong endpoint
(A) C for internal S
(A)
(A)
17. Protected communication
Restricted client authentication
➔ SSL configuration
➔ Server key management
➔ Certificate rotation
➔ Fails for unexpected clients
(C) S for internal C
(C)
19. Protected communication
Client authentication
➔ SSL configuration
➔ Server key management
➔ Certificate rotation
➔ Fails for unexpected clients
➔ Recovery from key compromise
You have better solutions (Apache, Nginx, ELB, …)
(D) S for external C
(C)
(D)
20. Client authentication
On unprotected network
➔ Cipher algorithm
➔ Tamper detection
➔ Constant time operation
Use well-known library
[E] authentication
[E]
[E]
21. Data protection at rest
➔ Cipher algorithm
➔ Encryption key management
◆ Storage
◆ Usage authn / authz
◆ Usage auditing
◆ Rotation
➔ Tamper detection
➔ Processing throughput / latency
[F] Encryption in S / [G] in C
[F]
[G]
22. require 'aws-sdk'
class KMSEncryptor
CTX = { 'purpose' => 'odrk05 demonstration' }
GCM_IV_SIZE = 12; GCM_TAG_SIZE = 16
def initialize(region, key_id)
@region, @key_id = region, key_id
@kms = Aws::KMS::Client.new(region: @region)
end
def generate_data_key
resp = @kms.generate_data_key_without_plaintext(
key_id: @key_id, encryption_context: CTX, key_spec: 'AES_128'
)
resp.ciphertext_blob
end
def with_key(wrapped_key)
key = nil
begin
key = @kms.decrypt(
ciphertext_blob: wrapped_key, encryption_context: CTX
).plaintext
yield key
ensure
# TODO: confirm that key is deleted from memory
key.tr!("0-xff".force_encoding('BINARY'), "0")
end
end
23. def encrypt(wrapped_key, plaintext)
with_key(wrapped_key) do |key|
cipher = OpenSSL::Cipher::Cipher.new('aes-128-gcm')
iv = OpenSSL::Random.random_bytes(GCM_IV_SIZE)
cipher.encrypt; cipher.key = key;cipher.iv = iv
iv + cipher.update(plaintext) + cipher.final
end
end
def decrypt(wrapped_key, ciphertext)
with_key(wrapped_key) do |key|
iv, data = ciphertext.unpack("a#{GCM_IV_SIZE}a*")
auth_tag = data.slice!(data.bytesize - GCM_TAG_SIZE, GCM_TAG_SIZE)
cipher = OpenSSL::Cipher::Cipher.new('aes-128-gcm')
cipher.decrypt; cipher.key = key; cipher.iv = iv
cipher.auth_tag = auth_tag
cipher.update(data) + cipher.final
end
end
end
encryptor = KMSEncryptor.new('ap-northeast-1', 'alias/nahi-test-tokyo')
# generate key for each data, customer, or something
wrapped_key = encryptor.generate_data_key
plaintext = File.read(__FILE__)
ciphertext = encryptor.encrypt(wrapped_key, plaintext)
# save wrapped_key and ciphertext in DB, File or somewhere
# restore wrapped_key and ciphertext from DB, File or somewhere
puts encryptor.decrypt(wrapped_key, ciphertext)
jruby-openssl does not
support aes-gcm…
-> next page
24. if defined?(JRuby)
require 'java'
java_import 'javax.crypto.Cipher'
java_import 'javax.crypto.SecretKey'
java_import 'javax.crypto.spec.SecretKeySpec'
java_import 'javax.crypto.spec.GCMParameterSpec'
class KMSEncryptor
# Overrides
def encrypt(wrapped_key, plaintext)
with_key(wrapped_key) do |key|
cipher = Cipher.getInstance('AES/GCM/PKCS5Padding')
iv = OpenSSL::Random.random_bytes(GCM_IV_SIZE)
spec = GCMParameterSpec.new(GCM_TAG_SIZE * 8, iv.to_java_bytes)
cipher.init(1, SecretKeySpec.new(key.to_java_bytes, 0, key.bytesize, 'AES'), spec)
ciphertext = String.from_java_bytes(
cipher.doFinal(plaintext.to_java_bytes), Encoding::BINARY)
iv + ciphertext
end
end
# Overrides
def decrypt(wrapped_key, ciphertext)
with_key(wrapped_key) do |key|
cipher = Cipher.getInstance('AES/GCM/PKCS5Padding')
iv, data = ciphertext.unpack("a#{GCM_IV_SIZE}a*")
spec = GCMParameterSpec.new(GCM_TAG_SIZE * 8, iv.to_java_bytes)
cipher.init(2, SecretKeySpec.new(key.to_java_bytes, 0, key.bytesize, 'AES'), spec)
String.from_java_bytes(cipher.doFinal(data.to_java_bytes), Encoding::BINARY)
end
end
end
end
aes-128-gcm in JRuby!
25. … in Ruby
(A) C for internal S
(B) C for external S
(C) S for internal C
(D) S for external C
[E] authentication
[F] Encryption in S
[G] Encryption in C
(A)
(A)
(B)
(B)
(C)
[E]
[F]
[G]
(D)
[E]
Blue: Acceptable
Orange: Pitfalls
Red: No way