smime_certificate.rb 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. class SMIMECertificate < ApplicationModel
  3. default_scope { order(created_at: :desc, id: :desc) }
  4. validates :fingerprint, uniqueness: { case_sensitive: true }
  5. # public class methods
  6. def self.find_for_multiple_email_addresses!(addresses, filter: nil, blame: false)
  7. certificates = []
  8. missing_addresses = []
  9. addresses.each do |address|
  10. certs = find_by_email_address(address, filter: filter)
  11. if certs.blank? && blame
  12. missing_addresses << address
  13. next
  14. end
  15. certificates.push(*certs)
  16. end
  17. raise ActiveRecord::RecordNotFound, "Can't find S/MIME encryption certificates for: #{missing_addresses.join(', ')}" if missing_addresses.present? && blame
  18. certificates
  19. end
  20. def self.find_by_email_address(address, filter: nil)
  21. cert_selector = SMIMECertificate.where(SqlHelper.new(object: SMIMECertificate).array_contains_one('email_addresses', address.downcase))
  22. return cert_selector.all if filter.nil?
  23. filter.each do |filter_key, filter_value|
  24. cert_selector = send(:"filter_#{filter_key}", cert_selector, filter_value)
  25. return [] if cert_selector.blank?
  26. end
  27. cert_selector
  28. end
  29. def self.create_certificates(pem)
  30. parts(pem).select { |part| part.include?('CERTIFICATE') }.each_with_object([]) do |part, result|
  31. result << create!(public_key: part)
  32. end
  33. end
  34. def self.create_private_keys(pem, secret)
  35. parts(pem).select { |part| part.include?('PRIVATE KEY') }.each do |part|
  36. private_key = SecureMailing::SMIME::PrivateKey.new(part, secret)
  37. private_key.valid_smime_private_key!
  38. certificate = find_by(uid: private_key.uid)
  39. raise Exceptions::UnprocessableEntity, __('The certificate for this private key could not be found.') if !certificate
  40. certificate.update!(private_key: private_key.pem, private_key_secret: secret)
  41. end
  42. end
  43. # private class methods
  44. def self.filter_ignore_usable(cert_selector, filter_value)
  45. return cert_selector if cert_selector.blank?
  46. return cert_selector if filter_value
  47. cert_selector.select { |cert| cert.parsed.usable? }
  48. end
  49. def self.filter_key(cert_selector, filter_value)
  50. raise ArgumentError, 'filter_value must be either "public" or "private"' if %w[public private].exclude?(filter_value.to_s)
  51. return cert_selector if cert_selector.blank?
  52. return cert_selector if filter_value.eql?('public')
  53. cert_selector.where.not(private_key: nil)
  54. end
  55. def self.filter_usage(cert_selector, filter_value)
  56. raise ArgumentError, 'filter_value must be either "signature" or "encryption"' if %w[signature encryption].exclude?(filter_value.to_s)
  57. return cert_selector if cert_selector.blank?
  58. cert_selector.select { |cert| cert.parsed.send(:"#{filter_value}?") }
  59. end
  60. def self.parts(pem)
  61. pem.scan(%r{-----BEGIN[^-]+-----.+?-----END[^-]+-----}m)
  62. end
  63. private_class_method %i[
  64. filter_ignore_usable
  65. filter_key
  66. filter_usage
  67. parts
  68. ]
  69. # public instance methods
  70. def parsed
  71. @parsed ||= Certificate::X509::SMIME.new(pem)
  72. end
  73. def public_key=(string)
  74. cert = Certificate::X509::SMIME.new(string)
  75. self.email_addresses = cert.email_addresses
  76. self.pem = cert.to_pem
  77. # The fingerprint is a hash of the certificate in DER format.
  78. self.fingerprint = cert.fingerprint
  79. # The following both attributes are hashes of the certificate issuer and
  80. # subject strings.
  81. # They are used for certificate chain checks.
  82. #
  83. # Because of legacy certificates the usage of the x509 extension
  84. # "subjectKeyIdentifier" and "authorityKeyIdentifier" is not possible.
  85. self.issuer_hash = cert.issuer_hash
  86. self.subject_hash = cert.subject_hash
  87. # This is a unique information of the public key.
  88. # It is used to find the corresponding private key.
  89. self.uid = cert.uid
  90. end
  91. end