123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124 |
- # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
- class SMIMECertificate < ApplicationModel
- default_scope { order(created_at: :desc, id: :desc) }
- validates :fingerprint, uniqueness: { case_sensitive: true }
- # public class methods
- def self.find_for_multiple_email_addresses!(addresses, filter: nil, blame: false)
- certificates = []
- missing_addresses = []
- addresses.each do |address|
- certs = find_by_email_address(address, filter: filter)
- if certs.blank? && blame
- missing_addresses << address
- next
- end
- certificates.push(*certs)
- end
- raise ActiveRecord::RecordNotFound, "Can't find S/MIME encryption certificates for: #{missing_addresses.join(', ')}" if missing_addresses.present? && blame
- certificates
- end
- def self.find_by_email_address(address, filter: nil)
- cert_selector = SMIMECertificate.where(SqlHelper.new(object: SMIMECertificate).array_contains_one('email_addresses', address.downcase))
- return cert_selector.all if filter.nil?
- filter.each do |filter_key, filter_value|
- cert_selector = send(:"filter_#{filter_key}", cert_selector, filter_value)
- return [] if cert_selector.blank?
- end
- cert_selector
- end
- def self.create_certificates(pem)
- parts(pem).select { |part| part.include?('CERTIFICATE') }.each_with_object([]) do |part, result|
- result << create!(public_key: part)
- end
- end
- def self.create_private_keys(pem, secret)
- parts(pem).select { |part| part.include?('PRIVATE KEY') }.each do |part|
- private_key = SecureMailing::SMIME::PrivateKey.new(part, secret)
- private_key.valid_smime_private_key!
- certificate = find_by(uid: private_key.uid)
- raise Exceptions::UnprocessableEntity, __('The certificate for this private key could not be found.') if !certificate
- certificate.update!(private_key: private_key.pem, private_key_secret: secret)
- end
- end
- # private class methods
- def self.filter_ignore_usable(cert_selector, filter_value)
- return cert_selector if cert_selector.blank?
- return cert_selector if filter_value
- cert_selector.select { |cert| cert.parsed.usable? }
- end
- def self.filter_key(cert_selector, filter_value)
- raise ArgumentError, 'filter_value must be either "public" or "private"' if %w[public private].exclude?(filter_value.to_s)
- return cert_selector if cert_selector.blank?
- return cert_selector if filter_value.eql?('public')
- cert_selector.where.not(private_key: nil)
- end
- def self.filter_usage(cert_selector, filter_value)
- raise ArgumentError, 'filter_value must be either "signature" or "encryption"' if %w[signature encryption].exclude?(filter_value.to_s)
- return cert_selector if cert_selector.blank?
- cert_selector.select { |cert| cert.parsed.send(:"#{filter_value}?") }
- end
- def self.parts(pem)
- pem.scan(%r{-----BEGIN[^-]+-----.+?-----END[^-]+-----}m)
- end
- private_class_method %i[
- filter_ignore_usable
- filter_key
- filter_usage
- parts
- ]
- # public instance methods
- def parsed
- @parsed ||= Certificate::X509::SMIME.new(pem)
- end
- def public_key=(string)
- cert = Certificate::X509::SMIME.new(string)
- self.email_addresses = cert.email_addresses
- self.pem = cert.to_pem
- # The fingerprint is a hash of the certificate in DER format.
- self.fingerprint = cert.fingerprint
- # The following both attributes are hashes of the certificate issuer and
- # subject strings.
- # They are used for certificate chain checks.
- #
- # Because of legacy certificates the usage of the x509 extension
- # "subjectKeyIdentifier" and "authorityKeyIdentifier" is not possible.
- self.issuer_hash = cert.issuer_hash
- self.subject_hash = cert.subject_hash
- # This is a unique information of the public key.
- # It is used to find the corresponding private key.
- self.uid = cert.uid
- end
- end
|