Class: OPA::Signer

Inherits:
Object
  • Object
show all
Defined in:
lib/opa/signer.rb

Overview

Signs OPA archives using JAR-format digital signatures. Produces META-INF/SIGNATURE.SF and a signature block file (.RSA, .DSA, or .EC).

Constant Summary collapse

SUPPORTED_DIGESTS =
{
  "SHA-256" => OpenSSL::Digest::SHA256,
  "SHA-384" => OpenSSL::Digest::SHA384,
  "SHA-512" => OpenSSL::Digest::SHA512
}.freeze
REJECTED_DIGESTS =
%w[MD5 SHA-1].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(private_key, certificate, algorithm: "SHA-256") ⇒ Signer

Returns a new instance of Signer.

Raises:

  • (ArgumentError)


21
22
23
24
25
26
27
28
29
# File 'lib/opa/signer.rb', line 21

def initialize(private_key, certificate, algorithm: "SHA-256")
  raise ArgumentError, "Digest #{algorithm} is not allowed" if REJECTED_DIGESTS.include?(algorithm)
  raise ArgumentError, "Unsupported digest: #{algorithm}" unless SUPPORTED_DIGESTS.key?(algorithm)

  @private_key = private_key
  @certificate = certificate
  @digest_name = algorithm
  @digest_class = SUPPORTED_DIGESTS[algorithm]
end

Instance Attribute Details

#digest_nameObject (readonly)

Returns the value of attribute digest_name.



19
20
21
# File 'lib/opa/signer.rb', line 19

def digest_name
  @digest_name
end

Instance Method Details

#block_extensionObject

File extension for the signature block based on key type.



63
64
65
66
67
68
69
70
# File 'lib/opa/signer.rb', line 63

def block_extension
  case @private_key
  when OpenSSL::PKey::RSA then ".RSA"
  when OpenSSL::PKey::DSA then ".DSA"
  when OpenSSL::PKey::EC then ".EC"
  else ".RSA"
  end
end

#digest(data) ⇒ Object

Compute the Base64-encoded digest of data.



32
33
34
# File 'lib/opa/signer.rb', line 32

def digest(data)
  Base64.strict_encode64(@digest_class.digest(data))
end

#signature_block(sf_content) ⇒ Object

Create the PKCS#7 signature block over the SF content.



56
57
58
59
60
# File 'lib/opa/signer.rb', line 56

def signature_block(sf_content)
  flags = OpenSSL::PKCS7::BINARY | OpenSSL::PKCS7::DETACHED | OpenSSL::PKCS7::NOSMIMECAP
  pkcs7 = OpenSSL::PKCS7.sign(@certificate, @private_key, sf_content, [], flags)
  pkcs7.to_der
end

#signature_file(manifest_content) ⇒ Object

Generate the SIGNATURE.SF content from manifest content. Contains a digest of the entire manifest and per-entry section digests.



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/opa/signer.rb', line 38

def signature_file(manifest_content)
  lines = []
  lines << "Signature-Version: 1.0"
  lines << "#{@digest_name}-Digest-Manifest: #{digest(manifest_content)}"
  lines << ""

  # Compute per-entry section digests
  sections = extract_entry_sections(manifest_content)
  sections.each do |name, section_text|
    lines << "Name: #{name}"
    lines << "#{@digest_name}-Digest: #{digest(section_text)}"
    lines << ""
  end

  lines.join("\n")
end