PageRenderTime 50ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/plugins/adva_contact_mails/vendor/gems/ezcrypto-0.7.2/lib/ezsig.rb

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