Class: Savvy::Cert::Signer

Inherits:
Object
  • Object
show all
Defined in:
lib/savvy/cert/signer.rb

Overview

Savvy Signer

Author:

Since:

  • 0.2.0

Version:

  • 0.1

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(opts = {}) ⇒ Savvy::Cert::Signer

A new instance of Signer

Options Hash (opts):

  • :id (String | Fixnum)

    the signer identifier

  • :key_size (Fixnum | nil) — default: 8192

    the byte size to generate new keys with

  • :private_key (String | nil)

    the Signer's RSA private key

Raises:

  • (OpenSSL::PKey::RSAError)

    if private_key is invalid

Since:

  • 0.2.0


30
31
32
33
34
35
# File 'lib/savvy/cert/signer.rb', line 30

def initialize(opts = {})
  @id = opts[:id]
  rsa_opts = [opts[:private_key], opts[:key_size]].compact
  @private_key = rsa(*rsa_opts).to_s
  @public_key = rsa.public_key.to_s
end

Instance Attribute Details

#idString | Fixnum

Returns the Signer's unique identifier

Since:

  • 0.2.0


19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/savvy/cert/signer.rb', line 19

class Signer
  attr_accessor :id
  attr_reader :public_key, :private_key

  # A new instance of Signer
  # @param opts [Hash] the options to initialize the Signer instance with
  # @option opts [String | Fixnum] :id the signer identifier
  # @option opts [Fixnum | nil] :key_size (8192) the byte size to generate new keys with
  # @option opts [String | nil] :private_key the Signer's RSA private key
  # @raise [OpenSSL::PKey::RSAError] if private_key is invalid
  # @return [Savvy::Cert::Signer] an instance of Signer
  def initialize(opts = {})
    @id = opts[:id]
    rsa_opts = [opts[:private_key], opts[:key_size]].compact
    @private_key = rsa(*rsa_opts).to_s
    @public_key = rsa.public_key.to_s
  end

  # Load a local RSA signer from file
  # @param id [String | Fixnum] the signer identifier
  # @param dir [String | nil] the directory to load from
  # @example Load a local signer
  #   Savvy::Cert::Signer.load_local('my_signer')
  #   #=> <#Savvy::Cert::Signer>
  # @raise [OpenSSL::PKey::RSAError] if key is invalid
  # @return [Savvy::Cert::Signer | nil] an instance of Signer
  #   will return nil if key file is not found
  def self.load_local(id, dir = Savvy::Cert.config.ssh_dir)
    pkey = File.read(File.join(dir, id)) if File.exist?(File.join(dir, id))
    return unless pkey
    new(id: id, private_key: pkey)
  end

  # Persist RSA private_key to file
  # @param dir [String | nil] ('$HOME/.ssh/savvy/') the directory to save the key to
  # @example Save a Signer's pkey to file
  #   my_signer = Signer.new(id: 'fe34a634-64c9-4333-93a5-f1728563603c')
  #   my_signer.save_pkey_to_file
  #   #=> true
  # @return [Boolean] whether or not the file was created
  def save_pkey_to_file(dir = Savvy::Cert.config.ssh_dir)
    File.open(File.join(dir, id), 'w') do |file|
      file.write(private_key)
      file.close
    end
    File.exist?(File.join(dir, id))
  end

  # Sign data
  # @param data [String] the data to sign
  # @example Sign a string
  #   json_data = {
  #     name: 'My Awesome Application',
  #     id: 'd58539ce114dfedf324fb1508e4db660',
  #     secret: '64a6c90b9de9a85863eabaf828d123cc',
  #     redirect_uri: 'https://my-awesome-app.com/oauth/callback',
  #     agent: 'My Awesome Application/1.0.0',
  #     version: '1.0.0'
  #   }.to_json
  #   my_signer.sign(json_data)
  #   #=> "BXRV0qEYB...dYx2SYyA=="
  # @return [String] a Base64 encoded RSA signature
  def sign(data)
    signature = rsa.sign_pss(Savvy::Cert.config.signature_hash_algorithm,
                             data,
                             salt_length: :max,
                             mgf1_hash: Savvy::Cert.config.signature_hash_algorithm)
    Base64.strict_encode64 signature
  end

  # Validate a signed data
  # @param signature [String] the encoded signature
  # @param data [String] the data that was signed
  # @example Validate a signed string
  #   json_data = {
  #     name: 'My Awesome Application',
  #     id: 'd58539ce114dfedf324fb1508e4db660',
  #     secret: '64a6c90b9de9a85863eabaf828d123cc',
  #     redirect_uri: 'https://my-awesome-app.com/oauth/callback',
  #     agent: 'My Awesome Application/1.0.0',
  #     version: '1.0.0'
  #   }.to_json
  #   signature = my_signer.sign(json_data)
  #   my_signer.validate_signed_data(signature, json_data)
  #   #=> true
  # @raise [ArgumentError] if signature is not Base64 encoded
  # @return [Boolean] whether or not the data was signed by Signer
  def validate_signature(signature, data)
    decoded_signature = Base64.strict_decode64(signature)
    rsa.public_key.verify_pss(Savvy::Cert.config.signature_hash_algorithm,
                              decoded_signature,
                              data,
                              salt_length: :auto,
                              mgf1_hash: Savvy::Cert.config.signature_hash_algorithm)
  end

  # Encrypt data with the public key
  # @param clear_data [String] the data to encrypt
  # @example Encrypt a string
  #   my_signer.encrypt(json_data)
  #   #=> "TG9yZW0g...uZHVzdHJ5=="
  # @return [String] the encoded encrypted data
  def encrypt(clear_data)
    Base64.strict_encode64 rsa.public_encrypt(clear_data)
  end

  # Encrypt and sign data
  # @param clear_data [String] the data to encrypt and sign
  # @example Encrypt and sign a string
  #   my_signer.encrypt_and_sign(json_data)
  #   #=> { :signature => "BXRV0qEYB...dYx2SYyA==", :data => "TG9yZW0g...uZHVzdHJ5==" }
  # @return [Hash{Symbol : String}] the encrypted data and signature
  #   * :signature (String) the Base64 encoded signature
  #   * :data (String) the Base64 encoded encrypted string
  def encrypt_and_sign(clear_data)
    encrypted = encrypt(clear_data)
    { signature: sign(encrypted), data: encrypted }
  end

  # Read encrypted data
  # @param encoded_data [String] the encoded encrpyted data to read
  # @example Read an encrypted string
  #   json_data = {
  #     name: 'My Awesome Application',
  #     id: 'd58539ce114dfedf324fb1508e4db660',
  #     secret: '64a6c90b9de9a85863eabaf828d123cc',
  #     redirect_uri: 'https://my-awesome-app.com/oauth/callback',
  #     agent: 'My Awesome Application/1.0.0',
  #     version: '1.0.0'
  #   }.to_json
  #   encrypted = my_signer.encrypt(json_data)
  #   decrypted = my_signer.read(encrypted)
  #   JSON.parse(decrypted)
  #   #=> {"name" => "My Awesome Application","id" => "d58539ce114dfedf324fb1508e4db660", ... }
  # @raise [ArgumentError] if encoded_data is not Base64 encoded
  # @raise [OpenSSL::PKey::RSAError] if encrypted data is invalid
  # @return [String] the decrypted data
  def read(encoded_data)
    decoded = Base64.strict_decode64(encoded_data)
    rsa.private_decrypt(decoded)
  end

  private

  def rsa(pkey = nil, key_size = Savvy::Cert.config.ssh_key_size)
    @rsa ||= pkey ? OpenSSL::PKey::RSA.new(pkey) : OpenSSL::PKey::RSA.new(key_size)
  end
end

#private_keyString | nil (readonly)

Returns the Signer's private rsa key

Since:

  • 0.2.0


19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/savvy/cert/signer.rb', line 19

class Signer
  attr_accessor :id
  attr_reader :public_key, :private_key

  # A new instance of Signer
  # @param opts [Hash] the options to initialize the Signer instance with
  # @option opts [String | Fixnum] :id the signer identifier
  # @option opts [Fixnum | nil] :key_size (8192) the byte size to generate new keys with
  # @option opts [String | nil] :private_key the Signer's RSA private key
  # @raise [OpenSSL::PKey::RSAError] if private_key is invalid
  # @return [Savvy::Cert::Signer] an instance of Signer
  def initialize(opts = {})
    @id = opts[:id]
    rsa_opts = [opts[:private_key], opts[:key_size]].compact
    @private_key = rsa(*rsa_opts).to_s
    @public_key = rsa.public_key.to_s
  end

  # Load a local RSA signer from file
  # @param id [String | Fixnum] the signer identifier
  # @param dir [String | nil] the directory to load from
  # @example Load a local signer
  #   Savvy::Cert::Signer.load_local('my_signer')
  #   #=> <#Savvy::Cert::Signer>
  # @raise [OpenSSL::PKey::RSAError] if key is invalid
  # @return [Savvy::Cert::Signer | nil] an instance of Signer
  #   will return nil if key file is not found
  def self.load_local(id, dir = Savvy::Cert.config.ssh_dir)
    pkey = File.read(File.join(dir, id)) if File.exist?(File.join(dir, id))
    return unless pkey
    new(id: id, private_key: pkey)
  end

  # Persist RSA private_key to file
  # @param dir [String | nil] ('$HOME/.ssh/savvy/') the directory to save the key to
  # @example Save a Signer's pkey to file
  #   my_signer = Signer.new(id: 'fe34a634-64c9-4333-93a5-f1728563603c')
  #   my_signer.save_pkey_to_file
  #   #=> true
  # @return [Boolean] whether or not the file was created
  def save_pkey_to_file(dir = Savvy::Cert.config.ssh_dir)
    File.open(File.join(dir, id), 'w') do |file|
      file.write(private_key)
      file.close
    end
    File.exist?(File.join(dir, id))
  end

  # Sign data
  # @param data [String] the data to sign
  # @example Sign a string
  #   json_data = {
  #     name: 'My Awesome Application',
  #     id: 'd58539ce114dfedf324fb1508e4db660',
  #     secret: '64a6c90b9de9a85863eabaf828d123cc',
  #     redirect_uri: 'https://my-awesome-app.com/oauth/callback',
  #     agent: 'My Awesome Application/1.0.0',
  #     version: '1.0.0'
  #   }.to_json
  #   my_signer.sign(json_data)
  #   #=> "BXRV0qEYB...dYx2SYyA=="
  # @return [String] a Base64 encoded RSA signature
  def sign(data)
    signature = rsa.sign_pss(Savvy::Cert.config.signature_hash_algorithm,
                             data,
                             salt_length: :max,
                             mgf1_hash: Savvy::Cert.config.signature_hash_algorithm)
    Base64.strict_encode64 signature
  end

  # Validate a signed data
  # @param signature [String] the encoded signature
  # @param data [String] the data that was signed
  # @example Validate a signed string
  #   json_data = {
  #     name: 'My Awesome Application',
  #     id: 'd58539ce114dfedf324fb1508e4db660',
  #     secret: '64a6c90b9de9a85863eabaf828d123cc',
  #     redirect_uri: 'https://my-awesome-app.com/oauth/callback',
  #     agent: 'My Awesome Application/1.0.0',
  #     version: '1.0.0'
  #   }.to_json
  #   signature = my_signer.sign(json_data)
  #   my_signer.validate_signed_data(signature, json_data)
  #   #=> true
  # @raise [ArgumentError] if signature is not Base64 encoded
  # @return [Boolean] whether or not the data was signed by Signer
  def validate_signature(signature, data)
    decoded_signature = Base64.strict_decode64(signature)
    rsa.public_key.verify_pss(Savvy::Cert.config.signature_hash_algorithm,
                              decoded_signature,
                              data,
                              salt_length: :auto,
                              mgf1_hash: Savvy::Cert.config.signature_hash_algorithm)
  end

  # Encrypt data with the public key
  # @param clear_data [String] the data to encrypt
  # @example Encrypt a string
  #   my_signer.encrypt(json_data)
  #   #=> "TG9yZW0g...uZHVzdHJ5=="
  # @return [String] the encoded encrypted data
  def encrypt(clear_data)
    Base64.strict_encode64 rsa.public_encrypt(clear_data)
  end

  # Encrypt and sign data
  # @param clear_data [String] the data to encrypt and sign
  # @example Encrypt and sign a string
  #   my_signer.encrypt_and_sign(json_data)
  #   #=> { :signature => "BXRV0qEYB...dYx2SYyA==", :data => "TG9yZW0g...uZHVzdHJ5==" }
  # @return [Hash{Symbol : String}] the encrypted data and signature
  #   * :signature (String) the Base64 encoded signature
  #   * :data (String) the Base64 encoded encrypted string
  def encrypt_and_sign(clear_data)
    encrypted = encrypt(clear_data)
    { signature: sign(encrypted), data: encrypted }
  end

  # Read encrypted data
  # @param encoded_data [String] the encoded encrpyted data to read
  # @example Read an encrypted string
  #   json_data = {
  #     name: 'My Awesome Application',
  #     id: 'd58539ce114dfedf324fb1508e4db660',
  #     secret: '64a6c90b9de9a85863eabaf828d123cc',
  #     redirect_uri: 'https://my-awesome-app.com/oauth/callback',
  #     agent: 'My Awesome Application/1.0.0',
  #     version: '1.0.0'
  #   }.to_json
  #   encrypted = my_signer.encrypt(json_data)
  #   decrypted = my_signer.read(encrypted)
  #   JSON.parse(decrypted)
  #   #=> {"name" => "My Awesome Application","id" => "d58539ce114dfedf324fb1508e4db660", ... }
  # @raise [ArgumentError] if encoded_data is not Base64 encoded
  # @raise [OpenSSL::PKey::RSAError] if encrypted data is invalid
  # @return [String] the decrypted data
  def read(encoded_data)
    decoded = Base64.strict_decode64(encoded_data)
    rsa.private_decrypt(decoded)
  end

  private

  def rsa(pkey = nil, key_size = Savvy::Cert.config.ssh_key_size)
    @rsa ||= pkey ? OpenSSL::PKey::RSA.new(pkey) : OpenSSL::PKey::RSA.new(key_size)
  end
end

#public_keyString | nil (readonly)

Returns the Signer's public rsa key

Since:

  • 0.2.0


19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/savvy/cert/signer.rb', line 19

class Signer
  attr_accessor :id
  attr_reader :public_key, :private_key

  # A new instance of Signer
  # @param opts [Hash] the options to initialize the Signer instance with
  # @option opts [String | Fixnum] :id the signer identifier
  # @option opts [Fixnum | nil] :key_size (8192) the byte size to generate new keys with
  # @option opts [String | nil] :private_key the Signer's RSA private key
  # @raise [OpenSSL::PKey::RSAError] if private_key is invalid
  # @return [Savvy::Cert::Signer] an instance of Signer
  def initialize(opts = {})
    @id = opts[:id]
    rsa_opts = [opts[:private_key], opts[:key_size]].compact
    @private_key = rsa(*rsa_opts).to_s
    @public_key = rsa.public_key.to_s
  end

  # Load a local RSA signer from file
  # @param id [String | Fixnum] the signer identifier
  # @param dir [String | nil] the directory to load from
  # @example Load a local signer
  #   Savvy::Cert::Signer.load_local('my_signer')
  #   #=> <#Savvy::Cert::Signer>
  # @raise [OpenSSL::PKey::RSAError] if key is invalid
  # @return [Savvy::Cert::Signer | nil] an instance of Signer
  #   will return nil if key file is not found
  def self.load_local(id, dir = Savvy::Cert.config.ssh_dir)
    pkey = File.read(File.join(dir, id)) if File.exist?(File.join(dir, id))
    return unless pkey
    new(id: id, private_key: pkey)
  end

  # Persist RSA private_key to file
  # @param dir [String | nil] ('$HOME/.ssh/savvy/') the directory to save the key to
  # @example Save a Signer's pkey to file
  #   my_signer = Signer.new(id: 'fe34a634-64c9-4333-93a5-f1728563603c')
  #   my_signer.save_pkey_to_file
  #   #=> true
  # @return [Boolean] whether or not the file was created
  def save_pkey_to_file(dir = Savvy::Cert.config.ssh_dir)
    File.open(File.join(dir, id), 'w') do |file|
      file.write(private_key)
      file.close
    end
    File.exist?(File.join(dir, id))
  end

  # Sign data
  # @param data [String] the data to sign
  # @example Sign a string
  #   json_data = {
  #     name: 'My Awesome Application',
  #     id: 'd58539ce114dfedf324fb1508e4db660',
  #     secret: '64a6c90b9de9a85863eabaf828d123cc',
  #     redirect_uri: 'https://my-awesome-app.com/oauth/callback',
  #     agent: 'My Awesome Application/1.0.0',
  #     version: '1.0.0'
  #   }.to_json
  #   my_signer.sign(json_data)
  #   #=> "BXRV0qEYB...dYx2SYyA=="
  # @return [String] a Base64 encoded RSA signature
  def sign(data)
    signature = rsa.sign_pss(Savvy::Cert.config.signature_hash_algorithm,
                             data,
                             salt_length: :max,
                             mgf1_hash: Savvy::Cert.config.signature_hash_algorithm)
    Base64.strict_encode64 signature
  end

  # Validate a signed data
  # @param signature [String] the encoded signature
  # @param data [String] the data that was signed
  # @example Validate a signed string
  #   json_data = {
  #     name: 'My Awesome Application',
  #     id: 'd58539ce114dfedf324fb1508e4db660',
  #     secret: '64a6c90b9de9a85863eabaf828d123cc',
  #     redirect_uri: 'https://my-awesome-app.com/oauth/callback',
  #     agent: 'My Awesome Application/1.0.0',
  #     version: '1.0.0'
  #   }.to_json
  #   signature = my_signer.sign(json_data)
  #   my_signer.validate_signed_data(signature, json_data)
  #   #=> true
  # @raise [ArgumentError] if signature is not Base64 encoded
  # @return [Boolean] whether or not the data was signed by Signer
  def validate_signature(signature, data)
    decoded_signature = Base64.strict_decode64(signature)
    rsa.public_key.verify_pss(Savvy::Cert.config.signature_hash_algorithm,
                              decoded_signature,
                              data,
                              salt_length: :auto,
                              mgf1_hash: Savvy::Cert.config.signature_hash_algorithm)
  end

  # Encrypt data with the public key
  # @param clear_data [String] the data to encrypt
  # @example Encrypt a string
  #   my_signer.encrypt(json_data)
  #   #=> "TG9yZW0g...uZHVzdHJ5=="
  # @return [String] the encoded encrypted data
  def encrypt(clear_data)
    Base64.strict_encode64 rsa.public_encrypt(clear_data)
  end

  # Encrypt and sign data
  # @param clear_data [String] the data to encrypt and sign
  # @example Encrypt and sign a string
  #   my_signer.encrypt_and_sign(json_data)
  #   #=> { :signature => "BXRV0qEYB...dYx2SYyA==", :data => "TG9yZW0g...uZHVzdHJ5==" }
  # @return [Hash{Symbol : String}] the encrypted data and signature
  #   * :signature (String) the Base64 encoded signature
  #   * :data (String) the Base64 encoded encrypted string
  def encrypt_and_sign(clear_data)
    encrypted = encrypt(clear_data)
    { signature: sign(encrypted), data: encrypted }
  end

  # Read encrypted data
  # @param encoded_data [String] the encoded encrpyted data to read
  # @example Read an encrypted string
  #   json_data = {
  #     name: 'My Awesome Application',
  #     id: 'd58539ce114dfedf324fb1508e4db660',
  #     secret: '64a6c90b9de9a85863eabaf828d123cc',
  #     redirect_uri: 'https://my-awesome-app.com/oauth/callback',
  #     agent: 'My Awesome Application/1.0.0',
  #     version: '1.0.0'
  #   }.to_json
  #   encrypted = my_signer.encrypt(json_data)
  #   decrypted = my_signer.read(encrypted)
  #   JSON.parse(decrypted)
  #   #=> {"name" => "My Awesome Application","id" => "d58539ce114dfedf324fb1508e4db660", ... }
  # @raise [ArgumentError] if encoded_data is not Base64 encoded
  # @raise [OpenSSL::PKey::RSAError] if encrypted data is invalid
  # @return [String] the decrypted data
  def read(encoded_data)
    decoded = Base64.strict_decode64(encoded_data)
    rsa.private_decrypt(decoded)
  end

  private

  def rsa(pkey = nil, key_size = Savvy::Cert.config.ssh_key_size)
    @rsa ||= pkey ? OpenSSL::PKey::RSA.new(pkey) : OpenSSL::PKey::RSA.new(key_size)
  end
end

Class Method Details

.load_local(id, dir = Savvy::Cert.config.ssh_dir) ⇒ Savvy::Cert::Signer | nil

Load a local RSA signer from file

Examples:

Load a local signer

Savvy::Cert::Signer.load_local('my_signer')
#=> <#Savvy::Cert::Signer>

Raises:

  • (OpenSSL::PKey::RSAError)

    if key is invalid

Since:

  • 0.2.0


46
47
48
49
50
# File 'lib/savvy/cert/signer.rb', line 46

def self.load_local(id, dir = Savvy::Cert.config.ssh_dir)
  pkey = File.read(File.join(dir, id)) if File.exist?(File.join(dir, id))
  return unless pkey
  new(id: id, private_key: pkey)
end

Instance Method Details

#encrypt(clear_data) ⇒ String

Encrypt data with the public key

Examples:

Encrypt a string

my_signer.encrypt(json_data)
#=> "TG9yZW0g...uZHVzdHJ5=="

Since:

  • 0.2.0


121
122
123
# File 'lib/savvy/cert/signer.rb', line 121

def encrypt(clear_data)
  Base64.strict_encode64 rsa.public_encrypt(clear_data)
end

#encrypt_and_sign(clear_data) ⇒ Hash{Symbol : String}

Encrypt and sign data

Examples:

Encrypt and sign a string

my_signer.encrypt_and_sign(json_data)
#=> { :signature => "BXRV0qEYB...dYx2SYyA==", :data => "TG9yZW0g...uZHVzdHJ5==" }

Since:

  • 0.2.0


133
134
135
136
# File 'lib/savvy/cert/signer.rb', line 133

def encrypt_and_sign(clear_data)
  encrypted = encrypt(clear_data)
  { signature: sign(encrypted), data: encrypted }
end

#read(encoded_data) ⇒ String

Read encrypted data

Examples:

Read an encrypted string

json_data = {
  name: 'My Awesome Application',
  id: 'd58539ce114dfedf324fb1508e4db660',
  secret: '64a6c90b9de9a85863eabaf828d123cc',
  redirect_uri: 'https://my-awesome-app.com/oauth/callback',
  agent: 'My Awesome Application/1.0.0',
  version: '1.0.0'
}.to_json
encrypted = my_signer.encrypt(json_data)
decrypted = my_signer.read(encrypted)
JSON.parse(decrypted)
#=> {"name" => "My Awesome Application","id" => "d58539ce114dfedf324fb1508e4db660", ... }

Raises:

  • (ArgumentError)

    if encoded_data is not Base64 encoded

  • (OpenSSL::PKey::RSAError)

    if encrypted data is invalid

Since:

  • 0.2.0


156
157
158
159
# File 'lib/savvy/cert/signer.rb', line 156

def read(encoded_data)
  decoded = Base64.strict_decode64(encoded_data)
  rsa.private_decrypt(decoded)
end

#save_pkey_to_file(dir = Savvy::Cert.config.ssh_dir) ⇒ Boolean

Persist RSA private_key to file

Examples:

Save a Signer's pkey to file

my_signer = Signer.new(id: 'fe34a634-64c9-4333-93a5-f1728563603c')
my_signer.save_pkey_to_file
#=> true

Since:

  • 0.2.0


59
60
61
62
63
64
65
# File 'lib/savvy/cert/signer.rb', line 59

def save_pkey_to_file(dir = Savvy::Cert.config.ssh_dir)
  File.open(File.join(dir, id), 'w') do |file|
    file.write(private_key)
    file.close
  end
  File.exist?(File.join(dir, id))
end

#sign(data) ⇒ String

Sign data

Examples:

Sign a string

json_data = {
  name: 'My Awesome Application',
  id: 'd58539ce114dfedf324fb1508e4db660',
  secret: '64a6c90b9de9a85863eabaf828d123cc',
  redirect_uri: 'https://my-awesome-app.com/oauth/callback',
  agent: 'My Awesome Application/1.0.0',
  version: '1.0.0'
}.to_json
my_signer.sign(json_data)
#=> "BXRV0qEYB...dYx2SYyA=="

Since:

  • 0.2.0


81
82
83
84
85
86
87
# File 'lib/savvy/cert/signer.rb', line 81

def sign(data)
  signature = rsa.sign_pss(Savvy::Cert.config.signature_hash_algorithm,
                           data,
                           salt_length: :max,
                           mgf1_hash: Savvy::Cert.config.signature_hash_algorithm)
  Base64.strict_encode64 signature
end

#validate_signature(signature, data) ⇒ Boolean

Validate a signed data

Examples:

Validate a signed string

json_data = {
  name: 'My Awesome Application',
  id: 'd58539ce114dfedf324fb1508e4db660',
  secret: '64a6c90b9de9a85863eabaf828d123cc',
  redirect_uri: 'https://my-awesome-app.com/oauth/callback',
  agent: 'My Awesome Application/1.0.0',
  version: '1.0.0'
}.to_json
signature = my_signer.sign(json_data)
my_signer.validate_signed_data(signature, json_data)
#=> true

Raises:

  • (ArgumentError)

    if signature is not Base64 encoded

Since:

  • 0.2.0


106
107
108
109
110
111
112
113
# File 'lib/savvy/cert/signer.rb', line 106

def validate_signature(signature, data)
  decoded_signature = Base64.strict_decode64(signature)
  rsa.public_key.verify_pss(Savvy::Cert.config.signature_hash_algorithm,
                            decoded_signature,
                            data,
                            salt_length: :auto,
                            mgf1_hash: Savvy::Cert.config.signature_hash_algorithm)
end