Skip to content

Commit f2add92

Browse files
committed
Land rapid7#4239 - Support SSL intermediate certs
2 parents 85de75c + d530046 commit f2add92

File tree

1 file changed

+92
-40
lines changed

1 file changed

+92
-40
lines changed

lib/rex/socket/ssl_tcp_server.rb

Lines changed: 92 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,93 @@ def accept(opts = {})
9999
end
100100
end
101101

102+
#
103+
# Parse a certificate in unified PEM format that contains a private key and
104+
# one or more certificates. The first certificate is the primary, while any
105+
# additional certificates are treated as intermediary certificates. This emulates
106+
# the behavior of web servers like nginx.
107+
#
108+
# @param [String] ssl_cert
109+
# @return [String, String, Array]
110+
def self.ssl_parse_pem(ssl_cert)
111+
cert = nil
112+
key = nil
113+
chain = nil
114+
115+
certs = []
116+
ssl_cert.scan(/-----BEGIN\s*[^\-]+-----+\r?\n[^\-]*-----END\s*[^\-]+-----\r?\n?/nm).each do |pem|
117+
if pem =~ /PRIVATE KEY/
118+
key = OpenSSL::PKey::RSA.new(pem)
119+
elsif pem =~ /CERTIFICATE/
120+
certs << OpenSSL::X509::Certificate.new(pem)
121+
end
122+
end
123+
124+
cert = certs.shift
125+
if certs.length > 0
126+
chain = certs
127+
end
128+
129+
[key, cert, chain]
130+
end
131+
132+
#
133+
# Shim for the ssl_parse_pem module method
134+
#
135+
def ssl_parse_pem(ssl_cert)
136+
Rex::Socket::SslTcpServer.ssl_parse_pem(ssl_cert)
137+
end
138+
139+
#
140+
# Generate a realistic-looking but obstensibly fake SSL
141+
# certificate.
142+
#
143+
# @return [String, String, Array]
144+
def self.ssl_generate_certificate
145+
key = OpenSSL::PKey::RSA.new(1024){ }
146+
cert = OpenSSL::X509::Certificate.new
147+
cert.version = 2
148+
cert.serial = rand(0xFFFFFFFF)
149+
subject = OpenSSL::X509::Name.new([
150+
["C","US"],
151+
['ST', Rex::Text.rand_state()],
152+
["L", Rex::Text.rand_text_alpha(rand(20) + 10)],
153+
["O", Rex::Text.rand_text_alpha(rand(20) + 10)],
154+
["CN", Rex::Text.rand_hostname],
155+
])
156+
issuer = OpenSSL::X509::Name.new([
157+
["C","US"],
158+
['ST', Rex::Text.rand_state()],
159+
["L", Rex::Text.rand_text_alpha(rand(20) + 10)],
160+
["O", Rex::Text.rand_text_alpha(rand(20) + 10)],
161+
["CN", Rex::Text.rand_hostname],
162+
])
163+
164+
cert.subject = subject
165+
cert.issuer = issuer
166+
cert.not_before = Time.now - (3600 * 365)
167+
cert.not_after = Time.now + (3600 * 365)
168+
cert.public_key = key.public_key
169+
ef = OpenSSL::X509::ExtensionFactory.new(nil,cert)
170+
cert.extensions = [
171+
ef.create_extension("basicConstraints","CA:FALSE"),
172+
ef.create_extension("subjectKeyIdentifier","hash"),
173+
ef.create_extension("extendedKeyUsage","serverAuth"),
174+
ef.create_extension("keyUsage","keyEncipherment,dataEncipherment,digitalSignature")
175+
]
176+
ef.issuer_certificate = cert
177+
cert.add_extension ef.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always")
178+
cert.sign(key, OpenSSL::Digest::SHA1.new)
179+
180+
[key, cert, nil]
181+
end
182+
183+
#
184+
# Shim for the ssl_generate_certificate module method
185+
#
186+
def ssl_generate_certificate
187+
Rex::Socket::SslTcpServer.ssl_generate_certificate
188+
end
102189

103190
#
104191
# Create a new ssl context. If +ssl_cert+ is not given, generates a new
@@ -107,54 +194,19 @@ def accept(opts = {})
107194
# @param [Rex::Socket::Parameters] params
108195
# @return [::OpenSSL::SSL::SSLContext]
109196
def makessl(params)
110-
ssl_cert = params.ssl_cert
111-
if ssl_cert
112-
cert = OpenSSL::X509::Certificate.new(ssl_cert)
113-
key = OpenSSL::PKey::RSA.new(ssl_cert)
197+
198+
if params.ssl_cert
199+
key, cert, chain = ssl_parse_pem(params.ssl_cert)
114200
else
115-
key = OpenSSL::PKey::RSA.new(1024){ }
116-
cert = OpenSSL::X509::Certificate.new
117-
cert.version = 2
118-
cert.serial = rand(0xFFFFFFFF)
119-
# name = OpenSSL::X509::Name.new([["C","JP"],["O","TEST"],["CN","localhost"]])
120-
subject = OpenSSL::X509::Name.new([
121-
["C","US"],
122-
['ST', Rex::Text.rand_state()],
123-
["L", Rex::Text.rand_text_alpha(rand(20) + 10)],
124-
["O", Rex::Text.rand_text_alpha(rand(20) + 10)],
125-
["CN", Rex::Text.rand_hostname],
126-
])
127-
issuer = OpenSSL::X509::Name.new([
128-
["C","US"],
129-
['ST', Rex::Text.rand_state()],
130-
["L", Rex::Text.rand_text_alpha(rand(20) + 10)],
131-
["O", Rex::Text.rand_text_alpha(rand(20) + 10)],
132-
["CN", Rex::Text.rand_hostname],
133-
])
134-
135-
cert.subject = subject
136-
cert.issuer = issuer
137-
cert.not_before = Time.now - (3600 * 365)
138-
cert.not_after = Time.now + (3600 * 365)
139-
cert.public_key = key.public_key
140-
ef = OpenSSL::X509::ExtensionFactory.new(nil,cert)
141-
cert.extensions = [
142-
ef.create_extension("basicConstraints","CA:FALSE"),
143-
ef.create_extension("subjectKeyIdentifier","hash"),
144-
ef.create_extension("extendedKeyUsage","serverAuth"),
145-
ef.create_extension("keyUsage","keyEncipherment,dataEncipherment,digitalSignature")
146-
]
147-
ef.issuer_certificate = cert
148-
cert.add_extension ef.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always")
149-
cert.sign(key, OpenSSL::Digest::SHA1.new)
201+
key, cert, chain = ssl_generate_certificate
150202
end
151203

152204
ctx = OpenSSL::SSL::SSLContext.new()
153205
ctx.key = key
154206
ctx.cert = cert
207+
ctx.extra_chain_cert = chain
155208
ctx.options = 0
156209

157-
158210
# Older versions of OpenSSL do not export the OP_NO_COMPRESSION symbol
159211
if defined?(OpenSSL::SSL::OP_NO_COMPRESSION)
160212
# enable/disable the SSL/TLS-level compression

0 commit comments

Comments
 (0)