class ICFS::Email::Smime
Receive S/MIME email
This processes the most common S/MIME implementation:
* Signed: multipart/signed; protocol="application/pkcs7-signature" with application/pkcs7-signature as the second part * Encrypted: application/pkcs7-mime containing eenvelope data * Both: application/pkcs7-mime containing encrypted data, which contains multipart/signed message In all cases, it will generate an email containing just the content. If the message is signed and verifies, it will try a lookup of the DN to determine the user.
Public Class Methods
new(key, cert, ca, map)
click to toggle source
New instance
@param key [::OpenSSL::PKey] The key for the ICFS
gateway @param cert [::OpenSSL::X509::Certificate] the ICFS
gateway certificate @param ca [::OpenSSL::X509::Store] Trusted CA certs @param map [Object] Maps DN to user name
# File lib/icfs/email/smime.rb, line 46 def initialize(key, cert, ca, map) @key = key @cert = cert @ca = ca @map = map end
Public Instance Methods
receive(env)
click to toggle source
Process for S/MIME encryption or signatures
# File lib/icfs/email/smime.rb, line 57 def receive(env) # start with the main message part = env[:msg] replace = false # process all PKCS7 while true case part.header[:content_type].string # encrypted when 'application/pkcs7-mime', 'application/x-pkcs7-mime' # decrypt enc_raw = part.body.decoded enc_p7 = ::OpenSSL::PKCS7.new(enc_raw) dec_raw = enc_p7.decrypt(@key, @cert) # new part is whatever we decrypted part = ::Mail::Part.new(dec_raw) replace = true # signed when 'multipart/signed' # check sig multi = part.body.parts msg = multi[0].raw_source.dup.force_encoding('ASCII-8BIT') msg = msg[2..-1] if msg[0,2] == "\r\n" # extra cr/lf sig_raw = multi[1].body.decoded sig_p7 = ::OpenSSL::PKCS7.new(sig_raw) val = sig_p7.verify([], @ca, msg) break unless val # get cert that signed first si = sig_p7.signers.first cert = sig_p7.certificates.select do |c| (c.issuer == si.issuer) && (c.serial == si.serial) end break if cert.empty? # get user unam = @map[cert.first.subject.to_s(::OpenSSL::X509::Name::RFC2253)] break unless unam # values env[:user] = unam env[:time] = si.signed_time.to_i # new part is body part = multi[0] replace = true # not PKCS7 else break end end return :continue unless replace # create new message msg = ::Mail::Message.new msg.body = part.body.encoded oh = env[:msg].header hd = msg.header Core::CopyFields.each do |fn| fi = oh[fn] hd[fn] = fi.value if fi end part.header.fields.each{|fd| hd[fd.name] = fd.value } # Mail acts wierd about parts unless we run it thru text... env[:msg] = ::Mail::Message.new(msg.encoded) return :continue end