certificate.rb 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. # Copyright (C) 2012-2023 Zammad Foundation, https://zammad-foundation.org/
  2. class SecureMailing::SMIME::Certificate < OpenSSL::X509::Certificate
  3. include SecureMailing::SMIME::Certificate::Attributes
  4. attr_reader :email_addresses, :fingerprint, :issuer_hash, :uid, :subject_hash
  5. def self.parse(pem)
  6. begin
  7. new(pem)
  8. rescue OpenSSL::X509::CertificateError
  9. raise Exceptions::UnprocessableEntity, __('The certificate is not valid for S/MIME usage. Please check the certificate format.')
  10. end
  11. end
  12. def initialize(pem)
  13. super(pem.gsub(%r{(?:TRUSTED\s)?(CERTIFICATE---)}, '\1'))
  14. @email_addresses = fetch_email_addresses
  15. @fingerprint = OpenSSL::Digest.new('SHA1', to_der).to_s
  16. @subject_hash = subject.hash.to_s(16)
  17. @issuer_hash = issuer.hash.to_s(16)
  18. @uid = determine_uid
  19. end
  20. def ca?
  21. return false if !extensions_as_hash.key?('basicConstraints')
  22. basic_constraints = extensions_as_hash['basicConstraints']
  23. return false if basic_constraints.exclude?('CA:TRUE')
  24. true
  25. end
  26. def rsa?
  27. public_key.class.name.end_with?('RSA')
  28. end
  29. def ec?
  30. public_key.class.name.end_with?('EC')
  31. end
  32. def effective?
  33. Time.zone.now >= not_before
  34. end
  35. def expired?
  36. Time.zone.now > not_after
  37. end
  38. def applicable?
  39. return false if ca?
  40. extended_key_usage = extensions_as_hash['extendedKeyUsage']
  41. # This is necessary because some legacy certificates may not have an extended key usage.
  42. return true if extended_key_usage.nil?
  43. extended_key_usage.include?('E-mail Protection')
  44. end
  45. def signature?
  46. return false if ca?
  47. key_usage = extensions_as_hash['keyUsage']
  48. # This is necessary because some legacy certificates may not have a key usage.
  49. return true if key_usage.nil?
  50. return false if !applicable?
  51. key_usage.include?('Digital Signature')
  52. end
  53. def encryption?
  54. return false if ca?
  55. key_usage = extensions_as_hash['keyUsage']
  56. # This is necessary because some legacy certificates may not have a key usage.
  57. return true if key_usage.nil?
  58. return false if !applicable?
  59. key_usage.include?('Key Encipherment')
  60. end
  61. def usable?
  62. effective? && !expired?
  63. end
  64. def valid_smime_certificate? # rubocop:disable Metrics/CyclomaticComplexity
  65. return true if ca?
  66. return false if !applicable?
  67. return false if !signature? && !encryption?
  68. return false if @email_addresses.blank?
  69. return false if !rsa? && !ec?
  70. true
  71. end
  72. def valid_smime_certificate!
  73. return if valid_smime_certificate?
  74. message = __('The certificate is not valid for S/MIME usage. Please check the key usage, subject alternative name and public key cryptographic algorithm.')
  75. Rails.logger.error { "SMIME::Certificate: #{message}" }
  76. Rails.logger.error { "SMIME::Certificate:\n #{to_text}" }
  77. raise Exceptions::UnprocessableEntity, message
  78. end
  79. end