serket 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2ad79717d0198ee41fa6b33200a5e3981b38e23f
4
+ data.tar.gz: 4a81d8df831e731c119852f48d41ba1a9b697b97
5
+ SHA512:
6
+ metadata.gz: 9fe9da5c68f650defa989dc9ff38b95e888919efce12c8095b1fd74135dd47aecb7e9b206b1f21dc035a0d20afb0551c1195e038f98ef64c54acab19f8d905eb
7
+ data.tar.gz: b422b32fbddeb028a070b758a4dec4d90406ed85b25a01aeb827ca175e6d778b66c1d26999ecd4c5cd23740e31aaacd4a46977b6bad90b7d5ead469378de3c6e
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/EncryptUtil.java ADDED
@@ -0,0 +1,125 @@
1
+ package org.adaptlab.chpir.android.survey;
2
+
3
+ import java.nio.charset.Charset;
4
+ import java.security.InvalidKeyException;
5
+ import java.security.KeyFactory;
6
+ import java.security.NoSuchAlgorithmException;
7
+ import java.security.NoSuchProviderException;
8
+ import java.security.PublicKey;
9
+ import java.security.Security;
10
+ import java.security.spec.InvalidKeySpecException;
11
+ import java.security.spec.X509EncodedKeySpec;
12
+
13
+ import javax.crypto.BadPaddingException;
14
+ import javax.crypto.Cipher;
15
+ import javax.crypto.IllegalBlockSizeException;
16
+ import javax.crypto.KeyGenerator;
17
+ import javax.crypto.NoSuchPaddingException;
18
+ import javax.crypto.SecretKey;
19
+ import javax.crypto.spec.SecretKeySpec;
20
+
21
+ import org.spongycastle.jce.provider.BouncyCastleProvider;
22
+
23
+ import android.util.Base64;
24
+ import android.util.Log;
25
+
26
+ /*
27
+ * Encrypts AES key with RSA using provided public key, and encrypts
28
+ * provided text with AES CBC.
29
+ *
30
+ * Returns string with delimited base 64 encoded iv, cipher key, and encrypted text.
31
+ *
32
+ * This is intended for using spongycastle with Android.
33
+ */
34
+ public class EncryptUtil {
35
+ private final static String TAG = "EncryptUtil";
36
+ private final static String ASYMMETRIC_ALGO = "RSA/None/PKCS1Padding";
37
+ private final static String SYMMETRIC_ALGO = "AES/CBC/PKCS5Padding";
38
+ private final static int SYMMETRIC_KEY_SIZE = 256;
39
+ private final static String PROVIDER = "SC";
40
+ private final static String FIELD_DELIMITER = "::"; // delimits iv, cipher key, and encrypted text
41
+ static {
42
+ Security.insertProviderAt(new BouncyCastleProvider(), 1);
43
+ }
44
+
45
+ private static PublicKey PUBLIC_KEY;
46
+ private final static String KEY_STRING = "" +
47
+ "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr19m7jjA3"
48
+ + "2IrF9io\ns9MWth119faF4Xc7clCtWWi9ncmMSBq0uQsASkpf/J"
49
+ + "VdVi91TZbzdzhInhhZ\nomMh+26r8oiAibRHRcxGRyCIUhEUZQu"
50
+ + "R8QeeaO5wugpg1zOYuYrevDH+MxuY\nebeAcqccW91ZQBDxsUKY"
51
+ + "jSiVIhm/Cmas/J/g9m+k+HKUGHREVzNZSRIToxuV\nc2DHVylWT"
52
+ + "0NPN14OUnOt4PHJZED/QwiiNFWo/UiovPkw1PjAC09gmV9sYmyK"
53
+ + "\nwu57oeCVvm6xbHkjO30an0NbaGrRFkR7wzLbVK+r8uTbkKPcm"
54
+ + "Mv0UPrG9hsg\nY4g5le8kVoDpdSYLIU7lc5LRDQIDAQAB"; // TEST public key
55
+
56
+ public static String encrypt(String text) {
57
+ if (text == null || text.isEmpty()) return "";
58
+
59
+ if (KEY_STRING == null || KEY_STRING.isEmpty()) {
60
+ throw new SecurityException("Public Key is not set for EncryptUtil");
61
+ }
62
+
63
+ try {
64
+ return encryptWithKey(text, generateAESKey());
65
+ } catch (NoSuchAlgorithmException nsae) {
66
+ Log.e(TAG, "NoSuchAlgorithmException: " + nsae);
67
+ } catch (InvalidKeyException ike) {
68
+ Log.e(TAG, "InvalidKeyException: " + ike);
69
+ } catch (NoSuchPaddingException nspe) {
70
+ Log.e(TAG, "NoSuchPaddingException: " + nspe);
71
+ } catch (IllegalBlockSizeException ibse) {
72
+ Log.e(TAG, "IllegalBlockSizeException: " + ibse);
73
+ } catch (BadPaddingException bpe) {
74
+ Log.e(TAG, "BadPaddingException: " + bpe);
75
+ } catch (NoSuchProviderException nspe) {
76
+ Log.e(TAG, "NoSuchProviderException: " + nspe);
77
+ }
78
+
79
+ return "";
80
+ }
81
+
82
+ /*
83
+ * Encrypt provided text with provided secret key. Secret key is used in the
84
+ * symmetric encryption algorithm.
85
+ *
86
+ * Returns format: IV::CIPHER_KEY::CIPHER_TEXT
87
+ */
88
+ private static String encryptWithKey(String text, SecretKey key) throws NoSuchAlgorithmException,
89
+ NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, NoSuchProviderException {
90
+
91
+ final Cipher symmetricCipher = Cipher.getInstance(SYMMETRIC_ALGO, PROVIDER);
92
+ symmetricCipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key.getEncoded(), SYMMETRIC_ALGO));
93
+ String cipherText = Base64.encodeToString(symmetricCipher.doFinal(text.getBytes()), Base64.DEFAULT);
94
+ String iv = Base64.encodeToString(symmetricCipher.getIV(), Base64.DEFAULT);
95
+
96
+ final Cipher asymmetricCipher = Cipher.getInstance(ASYMMETRIC_ALGO, PROVIDER);
97
+ asymmetricCipher.init(Cipher.ENCRYPT_MODE, getPublicKey());
98
+ String cipherKey = Base64.encodeToString(asymmetricCipher.doFinal(key.getEncoded()), Base64.DEFAULT);
99
+
100
+ return iv + FIELD_DELIMITER + cipherKey + FIELD_DELIMITER + cipherText;
101
+ }
102
+
103
+ private static PublicKey getPublicKey() throws NoSuchProviderException {
104
+ if (PUBLIC_KEY == null) {
105
+ try {
106
+ byte[] key = KEY_STRING.getBytes(Charset.forName("UTF-8"));
107
+ PUBLIC_KEY = KeyFactory.getInstance("RSA", PROVIDER).generatePublic(
108
+ new X509EncodedKeySpec(Base64.decode(key, Base64.DEFAULT)));
109
+ } catch (InvalidKeySpecException ikse) {
110
+ Log.e(TAG, "InvalidKeySpecException: " + ikse);
111
+ } catch (NoSuchAlgorithmException nsae) {
112
+ Log.e(TAG, "NoSuchAlgorithmException: " + nsae);
113
+ }
114
+ }
115
+
116
+ return PUBLIC_KEY;
117
+ }
118
+
119
+ private static SecretKey generateAESKey() throws NoSuchAlgorithmException, NoSuchProviderException {
120
+ KeyGenerator kgen = KeyGenerator.getInstance("AES", PROVIDER);
121
+ kgen.init(SYMMETRIC_KEY_SIZE);
122
+ return kgen.generateKey();
123
+ }
124
+ }
125
+
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in serket.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Michael Nipper
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,111 @@
1
+ # Serket
2
+
3
+ A gem for creating encrypted data using RSA and (by default) AES-256-CBC.
4
+
5
+ The envisioned use case for this is to encrypt data before saving it to a server or mobile device using a public key, and decrypting that data only when it is sent to another server that has the private key.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'serket'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install serket
20
+
21
+ ## Usage
22
+
23
+ To encrypt data, you must first tell serket where your public key is:
24
+
25
+ ```
26
+ Serket.configure do |config|
27
+ config.public_key_path = "path/to/public_key.pem"
28
+ end
29
+ ```
30
+
31
+ You can then use the FieldEncrypter class to encrypt some text:
32
+ ``
33
+ Serket::FieldEncrypter.encrypt("Hello out there!")
34
+ ``
35
+
36
+ By default, this will return a double-colon (::) delimited string. The first field is the initialization vector used for the symmetric encryption algorithm (by default, this is AES-256-CBC). The second field is the encrypted key for the symmetric algorithm. This key is encrypted using RSA, using the provided public key. The final field is the encrypted text ("Hello out there!" in this example).
37
+
38
+
39
+ To decrypt data, tell serket where to find your private key:
40
+ ```
41
+ Serket.configure do |config|
42
+ config.private_key_path = "path/to/private_key.pem"
43
+ end
44
+ ```
45
+
46
+ This expects the same format described for encryption, and is the inverse operation.
47
+
48
+ Quick Start:
49
+
50
+ ```
51
+ Serket.configure do |config|
52
+ config.public_key_path = "spec/resources/test_public_key.pem"
53
+ config.private_key_path = "spec/resources/test_private_key.pem"
54
+ end
55
+
56
+ encrypted = Serket::FieldEncrypter.new.encrypt("Hello out there!")
57
+ puts "#{encrypted} can be decrypted to #{Serket::FieldDecrypter.new.decrypt(encrypted)}"
58
+ ```
59
+
60
+ There are a few more configuration options.
61
+
62
+ format: :delimited (default), :json
63
+ symmetric_algorithm: AES-256-CBC (default)
64
+ delimiter: '::' (default)
65
+
66
+ These can all be modified in the configuration block, eg:
67
+
68
+ ```
69
+ Serket.configure do |config|
70
+ config.format = :json
71
+ end
72
+
73
+
74
+ Serket.configure do |config|
75
+ config.delimiter = '**'
76
+ end
77
+ ```
78
+
79
+ Note: trying to use a delimiter in the base64 character set throws an exception. This is because the iv/encrypted key/encrypted text are encoded in base64, and so it is a bad idea to use something in base64 as a delimiter.
80
+
81
+ There are also some helpers if you are using rails that make encryption/decryption straight forward. Assuming you have a model with a name field that you would like to encrypt before saving to the database, you could do so like this:
82
+
83
+ ```
84
+ class EncryptedModel < ActiveRecord::Base
85
+ extend Serket::EncryptedFields
86
+
87
+ encrypted_fields :name
88
+ end
89
+ ```
90
+
91
+ If you instead would like to decrypt a field before saving (for example, and encrypted value that is coming from an api), then you could do so like this:
92
+
93
+ ```
94
+ class DecryptedModel < ActiveRecord::Base
95
+ extend Serket::DecryptedFields
96
+
97
+ decrypted_fields :name
98
+ end
99
+ ```
100
+
101
+ This will automatically decrypt any values before saving assuming it matches your configurations.
102
+
103
+ You can see an example java client for use with Android in EncryptUtil.java
104
+
105
+ ## Contributing
106
+
107
+ 1. Fork it
108
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
109
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
110
+ 4. Push to the branch (`git push origin my-new-feature`)
111
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,11 @@
1
+ module Serket
2
+ class Configuration
3
+ attr_accessor :private_key_path, :public_key_path, :delimiter, :format, :symmetric_algorithm
4
+
5
+ def initialize
6
+ @delimiter = "::"
7
+ @format = :delimited
8
+ @symmetric_algorithm = 'AES-256-CBC'
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,13 @@
1
+ module Serket
2
+ module DecryptedFields
3
+ def decrypted_fields(*fields)
4
+ field_decrypter = FieldDecrypter.new
5
+
6
+ fields.each do |field|
7
+ define_method("#{field}=") do |value|
8
+ write_attribute(field, field_decrypter.decrypt(value))
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module Serket
2
+ module EncryptedFields
3
+ def encrypted_fields(*fields)
4
+ field_encrypter = FieldEncrypter.new
5
+
6
+ fields.each do |field|
7
+ define_method("#{field}=") do |value|
8
+ write_attribute(field, field_encrypter.encrypt(value))
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,52 @@
1
+ require 'openssl'
2
+ require 'base64'
3
+
4
+ module Serket
5
+ class FieldDecrypter
6
+ attr_accessor :field_delimiter, :private_key_filepath, :symmetric_algorithm
7
+
8
+ def initialize(options = {})
9
+ options ||= {}
10
+
11
+ @private_key_filepath = Serket.configuration.private_key_path
12
+ @field_delimiter = options[:field_delimiter] || Serket.configuration.delimiter
13
+ @symmetric_algorithm = options[:symmetric_algorithm] || Serket.configuration.symmetric_algorithm
14
+ @format = options[:format] || Serket.configuration.format
15
+ end
16
+
17
+ def decrypt(field)
18
+ return if field !~ /\S/
19
+ iv, encrypted_aes_key, encrypted_text = parse(field)
20
+ private_key = OpenSSL::PKey::RSA.new(File.read(private_key_filepath))
21
+ decrypted_aes_key = private_key.private_decrypt(Base64.decode64(encrypted_aes_key))
22
+ decrypt_data(iv, decrypted_aes_key, encrypted_text)
23
+ end
24
+
25
+ def field_delimiter=(delimiter)
26
+ if delimiter =~ /[A-Za-z0-9\/+]/
27
+ raise "This is not a valid delimiter! Must not be a character in Base64."
28
+ end
29
+
30
+ @field_delimiter = delimiter
31
+ end
32
+
33
+ private
34
+ def decrypt_data(iv, key, encrypted_text)
35
+ aes = OpenSSL::Cipher.new(symmetric_algorithm)
36
+ aes.decrypt
37
+ aes.key = key
38
+ aes.iv = Base64.decode64(iv)
39
+ aes.update(Base64.decode64(encrypted_text)) + aes.final
40
+ end
41
+
42
+ def parse(field)
43
+ case @format
44
+ when :delimited
45
+ field.split(field_delimiter)
46
+ when :json
47
+ parsed = JSON.parse(field)
48
+ [parsed['iv'], parsed['key'], parsed['message']]
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,59 @@
1
+ require 'openssl'
2
+ require 'base64'
3
+
4
+ module Serket
5
+ class FieldEncrypter
6
+ attr_accessor :field_delimiter, :public_key_filepath, :symmetric_algorithm
7
+
8
+ def initialize(options = {})
9
+ options ||= {}
10
+
11
+ @public_key_filepath = Serket.configuration.public_key_path
12
+ @field_delimiter = options[:field_delimiter] || Serket.configuration.delimiter
13
+ @symmetric_algorithm = options[:symmetric_algorithm] || Serket.configuration.symmetric_algorithm
14
+ @format = options[:format] || Serket.configuration.format
15
+ end
16
+
17
+ def encrypt(field)
18
+ aes = OpenSSL::Cipher.new(symmetric_algorithm)
19
+ aes_key = aes.random_key
20
+ iv = aes.random_iv
21
+ encrypt_data(iv, aes_key, field)
22
+ end
23
+
24
+ def field_delimiter=(delimiter)
25
+ if delimiter =~ /[A-Za-z0-9\/+]/
26
+ raise "This is not a valid delimiter! Must not be a character in Base64."
27
+ end
28
+
29
+ @field_delimiter = delimiter
30
+ end
31
+
32
+ private
33
+ def encrypt_data(iv, key, text)
34
+ public_key = OpenSSL::PKey::RSA.new(File.read(public_key_filepath))
35
+ encrypted_aes_key = public_key.public_encrypt(key)
36
+
37
+ aes = OpenSSL::Cipher.new(symmetric_algorithm)
38
+ aes.encrypt
39
+ aes.key = key
40
+ aes.iv = iv
41
+ encrypted_text = aes.update(text) + aes.final
42
+
43
+ parse(Base64.encode64(iv), Base64.encode64(encrypted_aes_key), Base64.encode64(encrypted_text))
44
+ end
45
+
46
+ def parse(iv, encrypted_key, encrypted_text)
47
+ case @format
48
+ when :delimited
49
+ [iv, field_delimiter, encrypted_key, field_delimiter, encrypted_text].join('')
50
+ when :json
51
+ hash = {}
52
+ hash['iv'] = iv
53
+ hash['key'] = encrypted_key
54
+ hash['message'] = encrypted_text
55
+ hash.to_json
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,3 @@
1
+ module Serket
2
+ VERSION = "0.0.1"
3
+ end
data/lib/serket.rb ADDED
@@ -0,0 +1,20 @@
1
+ require_relative "./serket/version"
2
+ require_relative "./serket/field_decrypter.rb"
3
+ require_relative "./serket/field_encrypter.rb"
4
+ require_relative "./serket/decrypted_fields.rb"
5
+ require_relative "./serket/encrypted_fields.rb"
6
+ require_relative "./serket/configuration.rb"
7
+
8
+ module Serket
9
+ class << self
10
+ attr_writer :configuration
11
+ end
12
+
13
+ def self.configuration
14
+ @configuration ||= Configuration.new
15
+ end
16
+
17
+ def self.configure
18
+ yield(configuration)
19
+ end
20
+ end
data/serket.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'serket/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "serket"
8
+ spec.version = Serket::VERSION
9
+ spec.authors = ["Michael Nipper"]
10
+ spec.email = ["mjn4406@gmail.com"]
11
+ spec.description = %q{Automatically encrypt and decrypt fields.}
12
+ spec.summary = %q{Automatically encrypt and decrypt fields using RSA and AES-256-CBC.}
13
+ spec.homepage = "https://github.com/mnipper/serket"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec"
24
+ end
@@ -0,0 +1,27 @@
1
+ -----BEGIN RSA PRIVATE KEY-----
2
+ MIIEpAIBAAKCAQEAr19m7jjA32IrF9ios9MWth119faF4Xc7clCtWWi9ncmMSBq0
3
+ uQsASkpf/JVdVi91TZbzdzhInhhZomMh+26r8oiAibRHRcxGRyCIUhEUZQuR8Qee
4
+ aO5wugpg1zOYuYrevDH+MxuYebeAcqccW91ZQBDxsUKYjSiVIhm/Cmas/J/g9m+k
5
+ +HKUGHREVzNZSRIToxuVc2DHVylWT0NPN14OUnOt4PHJZED/QwiiNFWo/UiovPkw
6
+ 1PjAC09gmV9sYmyKwu57oeCVvm6xbHkjO30an0NbaGrRFkR7wzLbVK+r8uTbkKPc
7
+ mMv0UPrG9hsgY4g5le8kVoDpdSYLIU7lc5LRDQIDAQABAoIBAG31n5BWvXhTEToO
8
+ exjljiP6LPBf9mn8XKW8uDSLW/kHWpILTK2JnFD4eV7iOHfFogNYVqe1/rJCClGr
9
+ Xq9MITwdIps1EktNXfNTDqaGVwdUTdmXMVgRyVSdFUNZ8rTDwgy2O/DHqL8Is90v
10
+ srRXAZMODL1cSFKZ04hiJErdPjHW8138/0H1bfUx/roMSdNXC50NrNXmrSaHGOfD
11
+ nV1O3ybe9mfd1iyOqHzRjL6O56N4sh3okrF/woSAAhNDaQ2byRNcbQdlX60Ss/m8
12
+ AHZH7a0Mr+2g5QJ8f9NWwoeUsQn5pSVF7hTOAL7y1WWTkqOhsXRNB/09/23idQqY
13
+ Bh9iVXECgYEA2mhCrm67NdTZybqLiOPRab+PwsQ1ursrkRnKFjhykdSIeXCbwCg6
14
+ NSbaquJRNAmLaf1Rmf+tx6GuxMTb7GQJU+XmKkjWKQ8YlFWgfcw5Qr4ROe2ES7wy
15
+ JVn3PeYEvBLVLavhlJQqDSadTCNWk+0NlMCsJvPU+2eKz6p+h8Nvb48CgYEAzY7l
16
+ YsuspzzM93v3js8n5mSsCEoFrPXGgIMrfbfydgB8GtqEcuEKH0nxwBkA2s8a7uom
17
+ oEPL4TxVIsZZm6XxPWXOrRQMTJ94/qJ4sRUz63xBn+7fINRkm6OUJJtnRrQpM9f7
18
+ Gix6fsu/aXoFz+Ai8djvLtydvxSMFnoL/oxgJ6MCgYBviAx9PSi27bIl1PBKgGRB
19
+ R0SnpkD10X0HBQ9w13SSSeFDeqDeuOw4llXWK+ph03nxLx5LsQhSSJuR+iGAjvlA
20
+ ccde0oEiyIW5whxKAU9AaQUs+sCzWDCXaGDcqCjEzi00vHBeymbK/mwXJHii48wq
21
+ qVWAMsYReCensp4YwFGYuwKBgQC7IMPnzXyufiYlgkAaTLGJBsqpfSFvlAqSAVc9
22
+ SpC6JbTVCWqb3gvF8h3W/6wMhY+CQbzKFw3qTG9AigsXK9jSSPT+EQslUePnAucW
23
+ ZjPuwx5Gx0Fu9ItmOGLrdGFvNyFvJcZczHLzLO5iygeydtu5CQCsy3/7bGwfJhn3
24
+ L7l1OwKBgQC0HtX6Z8oDLVRS9dykvpFY9l5ZAcgmn9l9JuG2QfaemMfEdM+LwX3m
25
+ UA1XEKED1rE37J1oE3RcB1hVKg46n+ncy9NEKxysBoKQJA7Ineti6DnjRR1eSNjJ
26
+ LVCabSkE+G4Nwf4rE0z/qsN+BfUcz9cwRJVDTN6mnOfH+u6cCEXqdw==
27
+ -----END RSA PRIVATE KEY-----
@@ -0,0 +1,9 @@
1
+ -----BEGIN PUBLIC KEY-----
2
+ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr19m7jjA32IrF9ios9MW
3
+ th119faF4Xc7clCtWWi9ncmMSBq0uQsASkpf/JVdVi91TZbzdzhInhhZomMh+26r
4
+ 8oiAibRHRcxGRyCIUhEUZQuR8QeeaO5wugpg1zOYuYrevDH+MxuYebeAcqccW91Z
5
+ QBDxsUKYjSiVIhm/Cmas/J/g9m+k+HKUGHREVzNZSRIToxuVc2DHVylWT0NPN14O
6
+ UnOt4PHJZED/QwiiNFWo/UiovPkw1PjAC09gmV9sYmyKwu57oeCVvm6xbHkjO30a
7
+ n0NbaGrRFkR7wzLbVK+r8uTbkKPcmMv0UPrG9hsgY4g5le8kVoDpdSYLIU7lc5LR
8
+ DQIDAQAB
9
+ -----END PUBLIC KEY-----
@@ -0,0 +1,15 @@
1
+ require "spec_helper"
2
+
3
+ describe Serket::DecryptedFields do
4
+ describe 'field decryption' do
5
+ before :each do
6
+ @encrypted_model = Serket::EncryptedModel.new
7
+ end
8
+
9
+ it "should decrypt an encrypted name" do
10
+ encrypted_name = 'bm5gkjwdfv6QBUqFPEnOaw==::CtQJyXdbuLeVzmIOIr0h/F9yh7xvz5KWUgxe/u4IkORGOW4KjU4bw+Wzve2vV1nLYUEWJJprr8sb+grm+Ao2sngNejiHzSkJKqZA/Pclw/Ok8KgHgN7olUz4BoCSdivIDRIT9ar06sNBrqOvLd4iGUlpMkpLdSJ69K08ebSvg5tED+PcK/oI6SJoVxRoUMYdYa9AfeIS9Ld5BgvhsaJgCKr089kfH2CzwpzlmRfdxb2qgyDXnk9PG/4WUEjjbamF/R74FNBdWkTLxZeLGdMImh87CQ6AOJ/v8l1JSzpPWwEjtmhTbFEzJPuA01tP5U5D07si0esJnab/B48iACEoLg==::iSmdDgnTzkEUv0yLbtFa8Q=='
11
+ @encrypted_model.name = encrypted_name
12
+ @encrypted_model.name.should == 'Kemba Walker'
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,16 @@
1
+ require "spec_helper"
2
+
3
+ describe Serket::EncryptedFields do
4
+ describe 'field encryption' do
5
+ before :each do
6
+ @encrypted_model = Serket::EncryptedModel.new
7
+ end
8
+
9
+ it "should encrypt a plaintext field" do
10
+ field_decrypter = Serket::FieldDecrypter.new
11
+ @encrypted_model.email = 'kemba.walker@aol.com'
12
+ decrypted = field_decrypter.decrypt(@encrypted_model.email)
13
+ decrypted.should == 'kemba.walker@aol.com'
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,55 @@
1
+ require 'spec_helper'
2
+
3
+ describe Serket::FieldDecrypter do
4
+ before :all do
5
+ @encrypted_message = 'RC78JOorP4EKyuh3Bh9atg==::iiaRmPaoFbke5d81FkL5SgQYudtnV2rl370QVlJel0f2+IniSugQ359gvsTqT2YPg1JcZ+9eky9GLGoDdAX90jsqYBG3CnawS6FgHNfO2HxBxTcgTWgfRmZv1lLo4JJ423lVXMdQv2mxgBTTMMO1ZNJsHtDjOoKlTGPh1lL3DARRrkz67J8LKN/zGDMIQXr9kfag/qkCJgJB36Owsj+9qIKKrzxtznsp/t8/Q2JGaKJmvh0srKr35hXJ6+cucI/+2uhEE78oKadUTyxxzgKrrmhKkKW2TvO4BPRsA7kpU6/X44UYYxQ2eqiMrhvklbzZKgtOuk84LZg4gXi9zCc80g==::gPYO7iDShmrk+RzO5ydjMfSUizmVEA6dAdrpsLeZvqI='
6
+
7
+ end
8
+
9
+ before :each do
10
+ @field_decrypter = Serket::FieldDecrypter.new
11
+ end
12
+
13
+ it "should correctly decrypt a message" do
14
+ @field_decrypter.decrypt(@encrypted_message).should == "Hello out there!"
15
+ end
16
+
17
+ it "should return nil for a blank string" do
18
+ @field_decrypter.decrypt(' ').should be_nil
19
+ end
20
+
21
+ it "should allow a change in field delimiter" do
22
+ @field_decrypter.field_delimiter = '**'
23
+ @field_decrypter.field_delimiter.should == '**'
24
+ asterik_delimited = @encrypted_message.gsub(/:/, '*')
25
+ @field_decrypter.decrypt(asterik_delimited).should == "Hello out there!"
26
+ end
27
+
28
+ it "should not allow a base64 character as a delimiter" do
29
+ [*('a'..'z'), *('A'..'Z'), *('0'..'9'), '+', '/'].each do |char|
30
+ expect { @field_decrypter.field_delimiter = char }.to raise_error
31
+ end
32
+ end
33
+
34
+ describe "json parsing" do
35
+ before :each do
36
+ @field_decrypter_json = Serket::FieldDecrypter.new(format: :json)
37
+ iv, key, message = @encrypted_message.split(@field_decrypter.field_delimiter)
38
+ @message = {}
39
+ @message['iv'] = iv
40
+ @message['key'] = key
41
+ @message['message'] = message
42
+ end
43
+
44
+ it "should parse a json field" do
45
+ iv, key, m = @field_decrypter_json.send(:parse, @message.to_json)
46
+ iv.should == @message['iv']
47
+ key.should == @message['key']
48
+ m.should == @message['message']
49
+ end
50
+
51
+ it "should decrypt a json value" do
52
+ @field_decrypter_json.decrypt(@message.to_json).should == "Hello out there!"
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ describe Serket::FieldEncrypter do
4
+ before :each do
5
+ @field_encrypter = Serket::FieldEncrypter.new
6
+ @field_decrypter = Serket::FieldDecrypter.new
7
+ end
8
+
9
+ it "should correctly encrypt/decrypt a message" do
10
+ encrypted_text = @field_encrypter.encrypt("Hello out there!")
11
+ @field_decrypter.decrypt(encrypted_text).should == "Hello out there!"
12
+ end
13
+
14
+ it "should not allow a base64 character as a delimiter" do
15
+ [*('a'..'z'), *('A'..'Z'), *('0'..'9'), '+', '/'].each do |char|
16
+ expect { @field_encrypter.field_delimiter = char }.to raise_error
17
+ end
18
+ end
19
+
20
+ describe "json parsing" do
21
+ before :each do
22
+ @field_encrypter_json = Serket::FieldEncrypter.new(format: :json)
23
+ @encrypted_json = @field_encrypter_json.encrypt("Hello out there!")
24
+ @parsed_json = JSON.parse(@encrypted_json)
25
+ end
26
+
27
+ it "should have the correct json fields" do
28
+ @parsed_json.has_key?('iv').should be_true
29
+ @parsed_json.has_key?('key').should be_true
30
+ @parsed_json.has_key?('message').should be_true
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ class Serket::EncryptedModel
4
+ extend Serket::DecryptedFields
5
+ extend Serket::EncryptedFields
6
+
7
+ attr_accessor :name, :email
8
+
9
+ decrypted_fields :name
10
+ encrypted_fields :email
11
+
12
+ def write_attribute(field, value)
13
+ self.instance_variable_set("@#{field}", value)
14
+ end
15
+ end
@@ -0,0 +1,13 @@
1
+ require "rspec"
2
+ require "json"
3
+
4
+ $:.unshift((Pathname(__FILE__).dirname.parent + "lib").to_s)
5
+
6
+ require "serket"
7
+
8
+ Serket.configure do |config|
9
+ config.private_key_path = "spec/resources/test_private_key.pem"
10
+ config.public_key_path = "spec/resources/test_public_key.pem"
11
+ end
12
+
13
+ require "serket/models/encrypted_model.rb"
metadata ADDED
@@ -0,0 +1,116 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: serket
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Michael Nipper
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-12-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: Automatically encrypt and decrypt fields.
56
+ email:
57
+ - mjn4406@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - .gitignore
63
+ - EncryptUtil.java
64
+ - Gemfile
65
+ - LICENSE.txt
66
+ - README.md
67
+ - Rakefile
68
+ - lib/serket.rb
69
+ - lib/serket/configuration.rb
70
+ - lib/serket/decrypted_fields.rb
71
+ - lib/serket/encrypted_fields.rb
72
+ - lib/serket/field_decrypter.rb
73
+ - lib/serket/field_encrypter.rb
74
+ - lib/serket/version.rb
75
+ - serket.gemspec
76
+ - spec/resources/test_private_key.pem
77
+ - spec/resources/test_public_key.pem
78
+ - spec/serket/decrypted_fields_spec.rb
79
+ - spec/serket/encrypted_fields_spec.rb
80
+ - spec/serket/field_decrypter_spec.rb
81
+ - spec/serket/field_encrypter_spec.rb
82
+ - spec/serket/models/encrypted_model.rb
83
+ - spec/spec_helper.rb
84
+ homepage: https://github.com/mnipper/serket
85
+ licenses:
86
+ - MIT
87
+ metadata: {}
88
+ post_install_message:
89
+ rdoc_options: []
90
+ require_paths:
91
+ - lib
92
+ required_ruby_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ required_rubygems_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ requirements: []
103
+ rubyforge_project:
104
+ rubygems_version: 2.4.1
105
+ signing_key:
106
+ specification_version: 4
107
+ summary: Automatically encrypt and decrypt fields using RSA and AES-256-CBC.
108
+ test_files:
109
+ - spec/resources/test_private_key.pem
110
+ - spec/resources/test_public_key.pem
111
+ - spec/serket/decrypted_fields_spec.rb
112
+ - spec/serket/encrypted_fields_spec.rb
113
+ - spec/serket/field_decrypter_spec.rb
114
+ - spec/serket/field_encrypter_spec.rb
115
+ - spec/serket/models/encrypted_model.rb
116
+ - spec/spec_helper.rb