Class: Rex::Proto::X509::Request

Inherits:
Object
  • Object
show all
Defined in:
lib/rex/proto/x509/request.rb

Class Method Summary collapse

Class Method Details

.build_csr(cn:, private_key:, dns: nil, msext_sid: nil, msext_upn: nil, algorithm: 'SHA256', application_policies: []) ⇒ OpenSSL::X509::Request

Make a certificate signing request.

Parameters:

  • cn (String)

    The common name for the certificate.

  • private_key (OpenSSL::PKey)

    The private key for the certificate.

  • dns (String) (defaults to: nil)

    An alternative DNS name to use.

  • msext_sid (String) (defaults to: nil)

    An explicit SID to specify for strong identity mapping.

  • msext_upn (String) (defaults to: nil)

    An alternative User Principal Name (this is a Microsoft-specific feature).

  • algorithm (String) (defaults to: 'SHA256')

    The algorithm to use when signing the CSR.

  • application_policies (Array<String>) (defaults to: [])

    OIDs to add as application policies.

Returns:

  • (OpenSSL::X509::Request)

    The request object.



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/rex/proto/x509/request.rb', line 38

def self.build_csr(cn:, private_key:, dns: nil, msext_sid: nil, msext_upn: nil, algorithm: 'SHA256', application_policies: [])
  Rex::Proto::X509::Request.create_csr(private_key, cn, algorithm) do |request|
    extensions = []

    subject_alt_names = []
    subject_alt_names << "otherName = #{OID_NT_PRINCIPAL_NAME};UTF8:#{msext_upn}" if msext_upn

    if msext_sid
      subject_alt_names << "URI = #{SAN_URL_PREFIX}#{msext_sid}"
      subject_alt_names << "URI = #{msext_sid}"
    end

    subject_alt_names << "DNS = #{dns}" if dns

    unless subject_alt_names.empty?
      # factory.create_extension accepts a comma separated list of SANs or a config file of SANs.
      # SAN_URL_PREFIX in the URI SAN contains a comma so we create a config file and add it to the factory
      # The config file requires an identifier we define at the top of the file [alt_names]
      subject_alt_names.prepend("[alt_names]")
      subject_alt_names_conf = subject_alt_names.join("\n")
      config = OpenSSL::Config.parse(subject_alt_names_conf)
      factory = OpenSSL::X509::ExtensionFactory.new
      factory.config = config
      extensions << factory.create_extension('subjectAltName', '@alt_names', false)
    end

    if msext_sid
      ntds_ca_security_ext = Rex::Proto::CryptoAsn1::NtdsCaSecurityExt.new(OtherName: {
        type_id: OID_NTDS_OBJECTSID,
        value: msext_sid
      })
      extensions << OpenSSL::X509::Extension.new(OID_NTDS_CA_SECURITY_EXT, ntds_ca_security_ext.to_der, false)
    end

    unless application_policies.blank?
      application_cert_policies = Rex::Proto::CryptoAsn1::X509::CertificatePolicies.new(
        certificatePolicies: application_policies.map { |policy_oid| Rex::Proto::CryptoAsn1::X509::PolicyInformation.new(policyIdentifier: policy_oid) }
      )
      extensions << OpenSSL::X509::Extension.new(OID_APPLICATION_CERT_POLICIES, application_cert_policies.to_der, false)
    end

    unless extensions.empty?
      request.add_attribute(OpenSSL::X509::Attribute.new(
        'extReq',
        OpenSSL::ASN1::Set.new(
          [OpenSSL::ASN1::Sequence.new(extensions)]
        )
      ))
    end
  end
end

.build_on_behalf_of(csr:, on_behalf_of:, cert:, key:, algorithm: 'SHA256') ⇒ Rex::Proto::CryptoAsn1::Cms::ContentInfo

Make a certificate request on behalf of another user.

Parameters:

  • csr (OpenSSL::X509::Request)

    The certificate request to make on behalf of the user.

  • on_behalf_of (String)

    The user to make the request on behalf of.

  • cert (OpenSSL::X509::Certificate)

    The public key to use for signing the request.

  • key (OpenSSL::PKey::RSA)

    The private key to use for signing the request.

  • algorithm (String) (defaults to: 'SHA256')

    The digest algorithm to use.

Returns:



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/rex/proto/x509/request.rb', line 98

def self.build_on_behalf_of(csr:, on_behalf_of:, cert:, key:, algorithm: 'SHA256')
  # algorithm needs to be one that OpenSSL supports, but we also need the OID constants defined
  digest = OpenSSL::Digest.new(algorithm)
  unless [ digest.name, "RSAWith#{digest.name}" ].all? { |s| Rex::Proto::Kerberos::Model::OID.constants.include?(s.to_sym) }
    raise ArgumentError, "Can not map digest algorithm #{digest.name} to the necessary OIDs."
  end

  digest_oid = Rex::Proto::Kerberos::Model::OID.const_get(digest.name)

  signer_info = Rex::Proto::CryptoAsn1::Cms::SignerInfo.new(
    version: 1,
    sid: {
      issuer: cert.issuer,
      serial_number: cert.serial.to_i
    },
    digest_algorithm: {
      algorithm: digest_oid
    },
    signed_attrs: [
      {
        attribute_type: OID_ENROLLMENT_NAME_VALUE_PAIR,
        attribute_values: [
          RASN1::Types::Any.new(value: Rex::Proto::CryptoAsn1::EnrollmentNameValuePair.new(
            name: 'requestername',
            value: on_behalf_of
          ))
        ]
      },
      {
        attribute_type: Rex::Proto::Kerberos::Model::OID::MessageDigest,
        attribute_values: [RASN1::Types::Any.new(value: RASN1::Types::OctetString.new(value: digest.digest(csr.to_der)))]
      }
    ],
    signature_algorithm: {
      algorithm: Rex::Proto::Kerberos::Model::OID.const_get("RSAWith#{digest.name}")
    }
  )
  data = RASN1::Types::Set.new(value: signer_info[:signed_attrs].value).to_der
  signature = key.sign(digest, data)

  signer_info[:signature] = signature

  signed_data = Rex::Proto::CryptoAsn1::Cms::SignedData.new(
    version: 3,
    digest_algorithms: [
      {
        algorithm: digest_oid
      }
    ],
    encap_content_info: {
      econtent_type: Rex::Proto::Kerberos::Model::OID::PkinitAuthData,
      econtent: csr.to_der
    },
    certificates: [{ openssl_certificate: cert }],
    signer_infos: [signer_info]
  )

  Rex::Proto::CryptoAsn1::Cms::ContentInfo.new(
    content_type: Rex::Proto::Kerberos::Model::OID::SignedData,
    data: signed_data
  )
end

.create_csr(private_key, cn, algorithm = 'SHA256') {|request| ... } ⇒ Object

Yields:

  • (request)


16
17
18
19
20
21
22
23
24
25
26
27
# File 'lib/rex/proto/x509/request.rb', line 16

def self.create_csr(private_key, cn, algorithm = 'SHA256')
  request = OpenSSL::X509::Request.new
  request.subject = OpenSSL::X509::Name.new([
    ['CN', cn, OpenSSL::ASN1::UTF8STRING]
  ])
  request.public_key = private_key.public_key

  yield request if block_given?

  request.sign(private_key, OpenSSL::Digest.new(algorithm))
  request
end