Permalink
Browse files

Initial commit

  • Loading branch information...
0 parents commit 9066f69ce02e00500f560f137bd3829217b518c7 @augustl committed Jun 29, 2012
Showing with 293 additions and 0 deletions.
  1. +7 −0 README.md
  2. +77 −0 certificate_authority.rb
  3. +36 −0 encryption.rb
  4. +29 −0 encryption_asymmetric.rb
  5. +65 −0 file_serialization.rb
  6. +5 −0 generate_keypair.rb
  7. +32 −0 signatures.rb
  8. +42 −0 signatures_asymmetric.rb
@@ -0,0 +1,7 @@
+# Ruby OpenSSL Cheat Sheet
+
+A collection of use cases with examples for Ruby's OpenSSL bindings.
+
+# Feel very free to contribute
+
+Make a pull request on http://github.com/augustl/ruby-openssl-cheat-sheet or e-mail me on august@augustl.com.
@@ -0,0 +1,77 @@
+# This is not for the faint of heart.
+#
+# TODO: Add a ton of explanation here.
+
+require "openssl"
+
+# Generate your CA first. A CA is not special, it's just a private key and
+# a certificate like any other. The only difference is that the certificate
+# is used to sign other certificates.
+
+ca_passphrase = "verysecret"
+ca_keypair = OpenSSL::PKey::RSA.new(2048)
+File.open("/tmp/ca.pem", "w+") do |f|
+ f.write ca_keypair.to_pem(OpenSSL::Cipher.new("AES-128-CBC"), ca_passphrase)
+end
+
+ca_cert = OpenSSL::X509::Certificate.new
+ca_cert.not_before = Time.now
+ca_cert.subject = OpenSSL::X509::Name.new([
+ ["C", "NO"],
+ ["ST", "Oslo"],
+ ["L", "Oslo"],
+ ["O", "August Lilleaas"]
+ ])
+# All issued certs will be unusuable after this time.
+ca_cert.not_after = Time.now + 1000000000 # 40 or so years
+ca_cert.serial = 1
+ca_cert.public_key = ca_keypair.public_key
+
+File.open("/tmp/ca.crt", "w+") do |f|
+ f.write ca_cert.to_pem
+end
+
+# Your CA is now ready to go. You can sign other certificates with it.
+
+# Let's create a certificate and sign it with out CA. Since a certificate
+# can be boiled down to "a public key with metadata and expiration date",
+# we need a keypair for our new cert (just like we did for the CA cert above).
+our_cert_keypair = OpenSSL::PKey::RSA.new(2048)
+
+# The signing request is what you typically ship to the certificate authority.
+# This file contains the public key of your keypair, and lets the CA issue a
+# cert to you without ever sending them your private key, and without you ever
+# seeing their private key.
+our_cert_req = OpenSSL::X509::Request.new
+our_cert_req.subject = OpenSSL::X509::Name.new([
+ ["C", "NO"],
+ ["ST", "Oslo"],
+ ["L", "Oslo"],
+ ["O", "August Lilleaas"],
+ ["CN", "*.augustl.com"]
+ ])
+our_cert_req.public_key = our_cert_keypair.public_key
+our_cert_req.sign our_cert_keypair, OpenSSL::Digest::SHA1.new
+
+
+# The CN (Common Name) is what browsers use to validate which domain names the
+# certificate is valid for. If you aren't going to use the certificate for web
+# servers, the contents of the CN field is of no technical significance. It's
+# just metadata.
+
+# At this point you typically save the our_cert_req.to_pem to a file, and send
+# it to the CA. Since we're both the issuer and the requester, we'll do it inline
+# right away for convenience, instead of via files.
+
+our_cert = OpenSSL::X509::Certificate.new
+our_cert.subject = our_cert_req.subject
+our_cert.issuer = ca_cert.subject
+our_cert.not_before = Time.now
+our_cert.not_after = Time.now + 100000000 # 3 or so years.
+our_cert.serial = 123 # Should be an unique number, the CA probably has a database.
+our_cert.public_key = our_cert_req.public_key
+our_cert.sign ca_keypair, OpenSSL::Digest::SHA1.new
+
+# You now have a certificate signed by another certificate, in other words you
+# created your own certificate authority. Congrats! Use our_cert.to_pem and write
+# it to a file in order to ship it to the certificate requester.
@@ -0,0 +1,36 @@
+# Symmetric crypto lets you scramble some data with one shared key. It is faster
+# than asymmetric crypto, but has the downside that you can only share the data
+# with trusted parties, so the key has to be pre-shared somehow, unlike async
+# crypto.
+
+require "openssl"
+
+secret = "fd5d148867091d7595c388ac0dc50bb465052b764c4db8b4b4c3448b52ee0b33df16975830acca82"
+data = "This is some data"
+
+# You can list available chiphers.
+#p OpenSSL::Cipher.ciphers
+
+# The chiphers take the format name-keylength-mode
+cipher = OpenSSL::Cipher.new("AES-128-CBC")
+
+# An alternative way of creating the object would be
+cipher = OpenSSL::Cipher::AES.new(128, :CBC)
+
+# The API is very imperative, as it binds pretty directly to the underlying C
+# libraries. This call sets the object in encryption mode.
+cipher.encrypt
+
+cipher.key = secret
+encrypted = cipher.update(data) + cipher.final
+p encrypted
+# => ...some unreadable binary stuff...
+
+# Time to decrypt it. We need to create a new object.
+decipher = OpenSSL::Cipher::AES.new(128, :CBC)
+decipher.decrypt
+decipher.key = secret
+
+decrypted = decipher.update(encrypted) + decipher.final
+p decrypted
+# => "This is some data"
@@ -0,0 +1,29 @@
+# Asymmetric crypto is awesome.
+#
+# TODO: Add lengthy explanation of how it can be used to verify identity
+# and all sorts of awesomeness.
+
+require "openssl"
+
+data = "Some private data is here."
+
+keypair = OpenSSL::PKey::RSA.new(2048)
+pub_key = keypair.public_key
+
+# You can encrypt with the private key and decrypt with the public key
+encrypted = keypair.private_encrypt(data)
+p encrypted
+# => ...some unreadable binary stuff...
+
+decrypted = pub_key.public_decrypt(encrypted)
+p decrypted
+# => "Some private data is here."
+
+# And vice versa
+encrypted = pub_key.public_encrypt(data)
+p encrypted
+# => ...some unreadable binary stuff...
+
+decrypted = keypair.private_decrypt(encrypted)
+p decrypted
+# => "Some private data is here."
@@ -0,0 +1,65 @@
+# Many of the keys you can generate with ruby are serializable to disk. This is
+# useful for writing ruby scripts that creates plain OpenSSL (and GnuTLS, and ..)
+# files that anyone can use.
+
+require "openssl"
+
+keypair = OpenSSL::PKey::RSA.new(2048)
+pub_key = keypair.public_key
+
+# PEM format
+File.open("/tmp/out.pem", "w+") do |f|
+ f.write keypair.to_pem
+end
+
+File.open("/tmp/out-pub.pem", "w+") do |f|
+ f.write pub_key.to_pem
+end
+
+# DER format
+File.open("/tmp/out.der", "w+") do |f|
+ f.write keypair.to_der
+end
+
+File.open("/tmp/out-pub.der", "w+") do |f|
+ f.write pub_key.to_der
+end
+
+# Reading the files is easy. Ruby will automatically detect the format.
+
+# The keypair for the private key contains both. The public key is just a
+# function of the private key.
+got = OpenSSL::PKey::RSA.new(File.read("/tmp/out.der"))
+p got.private? # => true
+p got.public? # => true
+
+# The public key does not contain the private key, obviously.
+got = OpenSSL::PKey::RSA.new(File.read("/tmp/out-pub.der"))
+p got.private? # => false
+p got.public? # => true
+
+
+# You probably want to encrypt the private key on disk, though. That's easy too.
+p File.read("/tmp/out.pem")
+# => -----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQ ......etc
+
+pk_passphrase = "secretsauce"
+File.open("/tmp/out.pem", "w+") do |f|
+ # You can list available chiphers.
+ #p OpenSSL::Cipher.ciphers
+ f.write keypair.to_pem(OpenSSL::Cipher.new("AES-128-CBC"), pk_passphrase)
+end
+p File.read("/tmp/out.pem")
+# => "-----BEGIN RSA PRIVATE KEY-----\nProc-Type: 4,ENCRYPTED\nDEK-Info: AES-128-CBC,C323774 ......etc
+
+begin
+ got = OpenSSL::PKey::RSA.new(File.read("/tmp/out.pem"), "wrong passphrase")
+rescue OpenSSL::PKey::RSAError
+ # Yup, that won't work
+end
+
+got = OpenSSL::PKey::RSA.new(File.read("/tmp/out.pem"), pk_passphrase)
+p got.private?
+# => true
+p got.to_pem
+# => -----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQ ......etc
@@ -0,0 +1,5 @@
+require "openssl"
+
+######################
+# Generate a keypair #
+######################
@@ -0,0 +1,32 @@
+# Use a secret to create a signature for any data.
+#
+# This method is commonly used for secure cookies. Rails does this, for
+# example. The cookie data is public. A method like the one described here
+# is used to create a signature based on a secret when the cookie is created.
+# The actual cookie contains the cookie data itself, and the signature. When
+# the framework receives a cookie, it creates a new signature for the cookie.
+# If either the cookie data or the signature data changed, you know someone
+# tampered with the cookie. The security lies in that only the ones that know
+# the secret are able to create the correct signature.
+
+require "openssl"
+
+data = "This is some data"
+secret = "cfa4f9980a2209f91145d912082dcbfd28484e4fe846df404ef417b57aae740b880820fe357b99e7"
+signature = OpenSSL::HMAC.digest("sha1", secret, data)
+
+payload = {:data => data, :signature => signature}
+
+# Now send the payload to anyone.
+
+# If the payload is not tampered with:
+p payload[:signature] == OpenSSL::HMAC.digest("sha1", secret, payload[:data])
+# => true
+
+# If someone tampers with the signature or data, you'll know, because the
+# signatures won't match
+
+p payload[:signature] + "altered" == OpenSSL::HMAC.digest("sha1", secret, payload[:data])
+# => false
+p payload[:signature] == OpenSSL::HMAC.digest("sha1", secret, payload[:data] + "altered")
+# => false
@@ -0,0 +1,42 @@
+# Asymmetric digital signatures is a great way to verify integrity and
+# authenticity of data. Create a keypair, send the public key to your
+# receivers, and use this method to create a digital signature. By combining
+# the data and the public key, you can verify that the signature was created
+# by the owner of the private key.
+
+require "openssl"
+
+data = "A small brown fox."
+
+########################
+# Sign a piece of data #
+########################
+
+digest = OpenSSL::Digest::SHA256.new
+# To list available digests:
+#p OpenSSL::Digest.constants
+
+keypair = OpenSSL::PKey::DSA.new(2048)
+# Or
+keypair = OpenSSL::PKey::RSA.new(2048)
+
+signature = keypair.sign(digest, data)
+
+#########################
+# Verify the signature #
+#########################
+
+# The public key is just a function of the private key. You can lose the
+# public key. As long as you have the private key, you can just generate
+# the public key again, it doesn't change.
+pub_key = keypair.public_key
+
+# You can read the file from disk, you probably didn't have the keypair available post creation like we have in this example.
+#pub_key = OpenSSL::PKey::RSA.new(File.read("/tmp/ruby-ssl-cheatsheet/our.pub"))
+
+p pub_key.verify(OpenSSL::Digest::SHA256.new, signature, data)
+# => true
+p pub_key.verify(OpenSSL::Digest::SHA256.new, signature, data + "altered")
+# => false
+p pub_key.verify(OpenSSL::Digest::SHA256.new, "altered" + signature, data)
+# => false

0 comments on commit 9066f69

Please sign in to comment.