ezcrypto 0.5 → 0.6

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,3 +1,9 @@
1
+ 0.6 August 10th, 2006 Certified PKI release
2
+
3
+ Finally I have had a good reason http://www.tractis.com to add Digital Signature support to EzCrypto. We have support for RSA and DSA private and public keys as well as basic X509 certificate support. All in typical EzCrypto simple Ruby like methods.
4
+
5
+ KNOWN PROBLEM. The DSA Signer.public_key method has some sort of problem but will be fixed for 0.6.1.
6
+
1
7
  0.5 July 19th, 2006 Good citizen release
2
8
 
3
9
  I have cleaned up the ActiveCrypto namespaces. It now does not use ActiveRecord::Crypto, but ActiveCrypto::*, if you have called stuff directly please update your code.
data/README CHANGED
@@ -13,7 +13,7 @@ EzCrypto is an easy to use wrapper around the poorly documented OpenSSL ruby lib
13
13
 
14
14
  Download it from here:
15
15
 
16
- http://rubyforge.org/frs/?group_id=755&release_id=3321
16
+ http://rubyforge.org/frs/?group_id=755
17
17
 
18
18
  or install it via Ruby Gems:
19
19
 
@@ -0,0 +1,446 @@
1
+ require 'ezcrypto'
2
+ =begin rdoc
3
+
4
+ These modules provides a simple ruby like way to create and verify digital signatures.
5
+
6
+ == License
7
+
8
+ ActiveCrypto and EzCrypto are released under the MIT license.
9
+
10
+
11
+ == Support
12
+
13
+ To contact the author, send mail to pelleb@gmail.com
14
+
15
+ Also see my blogs at:
16
+ http://stakeventures.com and
17
+ http://neubia.com
18
+
19
+ This project was based on code used in my project StakeItOut, where you can securely share web services with your partners.
20
+ https://stakeitout.com
21
+
22
+ (C) 2005 Pelle Braendgaard
23
+
24
+ =end
25
+ module EzCrypto
26
+
27
+ =begin rdoc
28
+ The signer is used for signing stuff. It encapsulates the functionality of a private key.
29
+ =end
30
+ class Signer
31
+ =begin rdoc
32
+ Initialize a Signer with a OpenSSL Private Key. You generally should not call new directly.
33
+ Unless you are interfacing with your own underlying OpenSSL code.
34
+ =end
35
+ def initialize(priv,options = {})
36
+ @priv=priv
37
+ end
38
+
39
+ =begin rdoc
40
+ Generate a new keypair. Defaults to 2048 bit RSA.
41
+ =end
42
+ def self.generate(strength=2048,type=:rsa)
43
+ key_class=case type
44
+ when :dsa
45
+ OpenSSL::PKey::DSA
46
+ else
47
+ OpenSSL::PKey::RSA
48
+ end
49
+ EzCrypto::Signer.new(key_class.generate(strength))
50
+ end
51
+
52
+ =begin rdoc
53
+ Decode a PEM encoded Private Key and return a signer. Takes an optional password
54
+ =end
55
+ def self.decode(encoded,password=nil)
56
+ begin
57
+ EzCrypto::Signer.new(OpenSSL::PKey::RSA.new( encoded,password))
58
+ rescue
59
+ EzCrypto::Signer.new(OpenSSL::PKey::DSA.new( encoded,password))
60
+ end
61
+ end
62
+
63
+ =begin rdoc
64
+ Decode a PEM encoded Private Key file and return a signer. Takes an optional password
65
+ =end
66
+ def self.from_file(filename,password=nil)
67
+ file = File.read( filename )
68
+ decode(file,password)
69
+ end
70
+
71
+ =begin rdoc
72
+ Returns the OpenSSL Public Key object. You normally do not need to use this.
73
+ =end
74
+ def public_key
75
+ @priv.public_key
76
+ end
77
+
78
+ =begin rdoc
79
+ Returns the corresponding Verifier object.
80
+ =end
81
+ def verifier
82
+ Verifier.new(public_key)
83
+ end
84
+
85
+ =begin rdoc
86
+ Returns the OpenSSL Private Key object. You normally do not need to use this.
87
+ =end
88
+ def private_key
89
+ @priv
90
+ end
91
+
92
+ =begin rdoc
93
+ signs data using the private key and the corresponding digest function. SHA1 for RSA and DSS1 for DSA.
94
+ 99% of signing use these parameters.
95
+ Email a request or send me a patch if you have other requirements.
96
+ =end
97
+ def sign(data)
98
+ if rsa?
99
+ @priv.sign(OpenSSL::Digest::SHA1.new,data)
100
+ elsif dsa?
101
+ @priv.sign(OpenSSL::Digest::DSS1.new,data)
102
+ end
103
+ end
104
+
105
+ =begin rdoc
106
+ Returns true if it is a RSA private key
107
+ =end
108
+ def rsa?
109
+ @priv.is_a? OpenSSL::PKey::RSA
110
+ end
111
+
112
+ =begin rdoc
113
+ Returns true if it is a DSA private key
114
+ =end
115
+ def dsa?
116
+ @priv.is_a? OpenSSL::PKey::DSA
117
+ end
118
+
119
+ end
120
+
121
+ =begin rdoc
122
+ The Verifier is used for verifying signatures. If you use the decode or
123
+ from_file methods you can use either raw PEM encoded public keys or certificate.
124
+ =end
125
+ class Verifier
126
+ =begin rdoc
127
+ Initializes a Verifier using a OpenSSL public key object.
128
+ =end
129
+ def initialize(pub)
130
+ @pub=pub
131
+ end
132
+
133
+ =begin rdoc
134
+ Decodes a PEM encoded Certificate or Public Key and returns a Verifier object.
135
+ =end
136
+ def self.decode(encoded)
137
+ case encoded
138
+ when /-----BEGIN CERTIFICATE-----/
139
+ EzCrypto::Certificate.new(OpenSSL::X509::Certificate.new( encoded))
140
+ else
141
+ begin
142
+ EzCrypto::Verifier.new(OpenSSL::PKey::RSA.new( encoded))
143
+ rescue
144
+ EzCrypto::Verifier.new(OpenSSL::PKey::DSA.new( encoded))
145
+ end
146
+ end
147
+ end
148
+
149
+ =begin rdoc
150
+ Decodes a PEM encoded Certificate or Public Key from a file and returns a Verifier object.
151
+ =end
152
+ def self.from_file(filename)
153
+ file = File.read( filename )
154
+ decode(file)
155
+ end
156
+
157
+ =begin rdoc
158
+ Is the Verifier a Certificate or not.
159
+ =end
160
+ def cert?
161
+ false
162
+ end
163
+
164
+ =begin rdoc
165
+ Returns the OpenSSL public key object. You would normally not need to use this.
166
+ =end
167
+ def public_key
168
+ @pub
169
+ end
170
+
171
+ =begin rdoc
172
+ Returns the SHA1 hexdigest of the DER encoded public key. This can be used as a unique key identifier.
173
+ =end
174
+ def digest
175
+ Digest::SHA1.hexdigest(@pub.to_der)
176
+ end
177
+ =begin rdoc
178
+ Is this a RSA key?
179
+ =end
180
+ def rsa?
181
+ @pub.is_a? OpenSSL::PKey::RSA
182
+ end
183
+ =begin rdoc
184
+ Is this a DSA key?
185
+ =end
186
+ def dsa?
187
+ @pub.is_a? OpenSSL::PKey::DSA
188
+ end
189
+
190
+
191
+ =begin rdoc
192
+ Returns true if the public key signed the given data.
193
+ =end
194
+ def verify(sig,data)
195
+ if rsa?
196
+ @pub.verify( OpenSSL::Digest::SHA1.new, sig, data )
197
+ elsif dsa?
198
+ @pub.verify( OpenSSL::Digest::DSS1.new, sig, data )
199
+ else
200
+ false
201
+ end
202
+ end
203
+ end
204
+
205
+ =begin rdoc
206
+ Certificate provides functionality to make it easy to extract information from a Certificate.
207
+ This also provides all the same functionality as a Verifier.
208
+ =end
209
+ class Certificate < Verifier
210
+
211
+ =begin rdoc
212
+ Intialize with a OpenSSL cert object.
213
+ =end
214
+ def initialize(cert)
215
+ super(cert.public_key)
216
+ @cert=cert
217
+ end
218
+
219
+ =begin rdoc
220
+ Returns true
221
+ =end
222
+ def cert?
223
+ true
224
+ end
225
+
226
+ =begin rdoc
227
+ Returns the SHA1 hex digest of a the DER encoded certificate. This is useful as a unique identifier.
228
+ =end
229
+ def cert_digest
230
+ Digest::SHA1.hexdigest(@cert.to_der)
231
+ end
232
+
233
+ =begin rdoc
234
+ Returns a Name object containt the subject of the certificate. The subject in X509 speak is the details of the certificate owner.
235
+ =end
236
+ def subject
237
+ @subject=EzCrypto::Name.new(@cert.subject) unless @subject
238
+ @subject
239
+ end
240
+
241
+ =begin rdoc
242
+ Returns a Name object containt the issuer of the certificate.
243
+ =end
244
+ def issuer
245
+ @issuer=EzCrypto::Name.new(@cert.subject) unless @issuer
246
+ @issuer
247
+ end
248
+
249
+ =begin rdoc
250
+ Returns the issuers serial number for this certificate
251
+ =end
252
+ def serial
253
+ @cert.serial
254
+ end
255
+
256
+ =begin rdoc
257
+ Returns the OpenSSL Certificate object
258
+ =end
259
+ def cert
260
+ @cert
261
+ end
262
+
263
+ =begin rdoc
264
+ Returns the certificates valid not before date.
265
+ =end
266
+ def not_before
267
+ @cert.not_before
268
+ end
269
+
270
+ =begin rdoc
271
+ Returns the certificates valid not after date.
272
+ =end
273
+ def not_after
274
+ @cert.not_after
275
+ end
276
+
277
+ =begin rdoc
278
+ Is this certificate valid at this point in time. Note this only checks if it is valid with respect to time.
279
+ It is important to realize that it does not check with any CRL or OCSP services to see if the certificate was
280
+ revoked.
281
+ =end
282
+ def valid?(time=Time.now.utc)
283
+ time.to_i>self.not_before.to_i && time.to_i<self.not_after.to_i
284
+ end
285
+
286
+ =begin rdoc
287
+ Returns the hash of extensions available in the certificate. These are not always present.
288
+ =end
289
+ def extensions
290
+ unless @extensions
291
+ @extensions={}
292
+ cert.extensions.each {|e| @extensions[e.oid]=e.value} if cert.extensions
293
+ end
294
+ @extensions
295
+ end
296
+
297
+ =begin rdoc
298
+ Any methods defined in Name can be used here. This means you can do cert.email rather than cert.subject.email.
299
+ =end
300
+ def method_missing(method)
301
+ subject.send method
302
+ end
303
+
304
+ end
305
+
306
+ =begin rdoc
307
+ A handy ruby wrapper around OpenSSL's Name object. This was created to make it really easy to extract information out of the certificate.
308
+ =end
309
+ class Name
310
+ =begin rdoc
311
+ Initializes the Name object with the underlying OpenSSL Name object. You generally do not need to use this.
312
+ Rather use the Certificates subject or issuer methods.
313
+ =end
314
+ def initialize(name)
315
+ @name=name
316
+ @attributes={}
317
+ name.to_s.split(/\//).each do |field|
318
+ key, val = field.split(/=/,2)
319
+ if key
320
+ @attributes[key.to_sym]=val
321
+ end
322
+ end
323
+ end
324
+
325
+ =begin rdoc
326
+ Returns the full name object in classic horrible X500 format.
327
+ =end
328
+ def to_s
329
+ @name.to_s
330
+ end
331
+
332
+ =begin rdoc
333
+ Returns the email if present in the name
334
+ =end
335
+ def email
336
+ self[:emailAddress]
337
+ end
338
+ =begin rdoc
339
+ The 2 letter country code of the name
340
+ =end
341
+ def country
342
+ self[:C]
343
+ end
344
+ alias_method :c,:country
345
+ =begin rdoc
346
+ The state or province code
347
+ =end
348
+ def state
349
+ self[:ST]
350
+ end
351
+ alias_method :st,:state
352
+ alias_method :province,:state
353
+
354
+ =begin rdoc
355
+ The locality
356
+ =end
357
+ def locality
358
+ self[:L]
359
+ end
360
+ alias_method :l,:locality
361
+
362
+ =begin rdoc
363
+ The Organizational Unit
364
+ =end
365
+ def organizational_unit
366
+ self[:OU]
367
+ end
368
+ alias_method :ou,:organizational_unit
369
+ alias_method :organisational_unit,:organizational_unit
370
+
371
+ =begin rdoc
372
+ The Organization
373
+ =end
374
+ def organization
375
+ self[:O]
376
+ end
377
+ alias_method :o,:organization
378
+ alias_method :organisation,:organization
379
+
380
+ =begin rdoc
381
+ The common name. For SSL this means the domain name. For personal certificates it is the name.
382
+ =end
383
+ def common_name
384
+ self[:CN]
385
+ end
386
+ alias_method :name,:common_name
387
+ alias_method :cn,:common_name
388
+
389
+ =begin rdoc
390
+ Lookup fields in the certificate.
391
+ =end
392
+ def [](attr_key)
393
+ @attributes[attr_key.to_sym]
394
+ end
395
+
396
+ def method_missing(method)
397
+ self[method]
398
+ end
399
+
400
+ end
401
+
402
+ =begin rdoc
403
+ Wraps around the OpenSSL trust store. This allows you to decide which certificates you trust.
404
+
405
+ You can either point it at a path which contains a OpenSSL trust store (see OpenSSL for more) or build it up manually.
406
+
407
+ For a certificate to verify you need the issuer and the issuers issuers certs added to the Trust store.
408
+
409
+ NOTE: Currently this does not support CRL's or OCSP. We may add support for this later.
410
+ =end
411
+ class TrustStore
412
+ =begin rdoc
413
+ Create trust store with an optional list of paths of openssl trust stores.
414
+ =end
415
+ def initialize(*paths)
416
+ @store=OpenSSL::X509::Store.new
417
+ # @store.set_default_path paths.shift if paths.length>0
418
+ paths.each {|path| @store.add_path path}
419
+ end
420
+
421
+ =begin rdoc
422
+ Add either a EzCrypto::Certificate or a OpenSSL::X509::Cert object to the TrustStore. This should be a trusted certificate such as a CA's issuer certificate.
423
+ =end
424
+ def add(obj)
425
+ if obj.kind_of?(EzCrypto::Certificate)
426
+ @store.add_cert obj.cert
427
+ elsif obj.kind_of?(OpenSSL::X509::Cert)
428
+ @store.add_cert obj
429
+ else
430
+ raise "unsupported object type"
431
+ end
432
+ end
433
+ =begin rdoc
434
+ Returns true if either the EzCrypto::Certificate or OpenSSL::X509::Cert object is verified using issuer certificates in the trust store.
435
+ =end
436
+ def verify(cert)
437
+ if cert.kind_of?(EzCrypto::Certificate)
438
+ @store.verify cert.cert
439
+ elsif cert.kind_of?(OpenSSL::X509::Cert)
440
+ @store.verify cert
441
+ else
442
+ false
443
+ end
444
+ end
445
+ end
446
+ end