Skip to content

Commit 6dbd740

Browse files
authored
Parse X509 certificates in SSL/TLS messages (#1915)
1 parent f32527d commit 6dbd740

File tree

5 files changed

+73
-31
lines changed

5 files changed

+73
-31
lines changed

Common++/src/Logger.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ namespace pcpp
6464
return "OFF";
6565
case LogLevel::Error:
6666
return "ERROR";
67+
case LogLevel::Warn:
68+
return "WARN";
6769
case LogLevel::Info:
6870
return "INFO";
6971
case LogLevel::Debug:

Packet++/header/SSLHandshake.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "SSLCommon.h"
55
#include "PointerVector.h"
66
#include "Asn1Codec.h"
7+
#include "X509Decoder.h"
78

89
/// @file
910
/// See detailed explanation of the TLS/SSL protocol support in PcapPlusPlus in SSLLayer.h
@@ -228,10 +229,16 @@ namespace pcpp
228229
return m_DataLen;
229230
}
230231

231-
/// @return The root ASN.1 record of the certificate data. All of the certificate data will be under this
232-
/// record. If the Root ASN.1 record is malformed, an exception is thrown
232+
/// @return The root ASN.1 record of the certificate data. All the certificate data will be under this
233+
/// record. If the certificate data isn't complete, this method will return nullptr. If the Root ASN.1 record
234+
/// is malformed, an exception is thrown
233235
Asn1SequenceRecord* getRootAsn1Record();
234236

237+
/// Parse the certificate data as X509 certificate. If the certificate data isn't complete, this method will
238+
/// return nullptr
239+
/// @return A unique pointer to the parsed X509 certificate
240+
std::unique_ptr<X509Certificate> getX509Certificate();
241+
235242
/// Certificate messages usually spread on more than 1 packet. So a certificate is likely to split between 2
236243
/// packets or more. This method provides an indication whether all certificate data exists or only part of it
237244
/// @return True if this data contains all certificate data, false otherwise

Packet++/src/SSLHandshake.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1222,6 +1222,12 @@ namespace pcpp
12221222

12231223
Asn1SequenceRecord* SSLx509Certificate::getRootAsn1Record()
12241224
{
1225+
if (!m_AllDataExists)
1226+
{
1227+
PCPP_LOG_ERROR("Certificate data is not complete, cannot parse ASN.1 record");
1228+
return nullptr;
1229+
}
1230+
12251231
if (m_Asn1Record == nullptr)
12261232
{
12271233
m_Asn1Record = Asn1Record::decode(m_Data, m_DataLen);
@@ -1230,6 +1236,17 @@ namespace pcpp
12301236
return m_Asn1Record->castAs<Asn1SequenceRecord>();
12311237
}
12321238

1239+
std::unique_ptr<X509Certificate> SSLx509Certificate::getX509Certificate()
1240+
{
1241+
if (!m_AllDataExists)
1242+
{
1243+
PCPP_LOG_ERROR("Certificate data is not complete, cannot parse X509 certificate");
1244+
return nullptr;
1245+
}
1246+
1247+
return X509Certificate::fromDER(m_Data, m_DataLen);
1248+
}
1249+
12331250
// ---------------------------
12341251
// SSLHandshakeMessage methods
12351252
// ---------------------------

Packet++/src/X509Decoder.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -975,7 +975,7 @@ namespace pcpp
975975
std::unique_ptr<X509Certificate> X509Certificate::fromPEM(const std::string& pemData)
976976
{
977977
auto derData = PemCodec::decode(pemData, certificatePemLabel);
978-
std::unique_ptr<uint8_t[]> derDataBuffer(new uint8_t[derData.size()]);
978+
auto derDataBuffer = std::make_unique<uint8_t[]>(derData.size());
979979
std::copy(derData.begin(), derData.end(), derDataBuffer.get());
980980
return std::unique_ptr<X509Certificate>(new X509Certificate(std::move(derDataBuffer), derData.size()));
981981
}

Tests/Packet++Test/Tests/SSLTests.cpp

Lines changed: 44 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "Packet.h"
55
#include "SSLLayer.h"
66
#include "SystemUtils.h"
7+
#include "Logger.h"
78
#include <fstream>
89
#include <sstream>
910

@@ -344,34 +345,41 @@ PTF_TEST_CASE(SSLMultipleRecordParsing3Test)
344345
PTF_ASSERT_EQUAL(certMsg->getNumOfCertificates(), 3);
345346
PTF_ASSERT_NULL(certMsg->getCertificate(1000));
346347

347-
pcpp::SSLx509Certificate* cert = certMsg->getCertificate(0);
348-
PTF_ASSERT_NOT_NULL(cert);
349-
PTF_ASSERT_TRUE(cert->allDataExists());
350-
PTF_ASSERT_EQUAL(cert->getDataLength(), 1509);
351-
std::string certBuffer(cert->getData(), cert->getData() + cert->getDataLength());
352-
std::size_t pos = certBuffer.find("LDAP Intermediate CA");
353-
PTF_ASSERT_TRUE(pos != std::string::npos);
354-
pos = certBuffer.find("Internal Development CA");
355-
PTF_ASSERT_EQUAL(pos, std::string::npos, ptr);
356-
auto asn1Record = cert->getRootAsn1Record();
357-
PTF_ASSERT_NOT_NULL(asn1Record);
358-
PTF_ASSERT_EQUAL(asn1Record->getSubRecords().size(), 3);
359-
360-
cert = certMsg->getCertificate(1);
361-
PTF_ASSERT_NOT_NULL(cert);
362-
PTF_ASSERT_TRUE(cert->allDataExists());
363-
PTF_ASSERT_EQUAL(cert->getDataLength(), 1728);
364-
certBuffer = std::string(cert->getData(), cert->getData() + cert->getDataLength());
365-
pos = certBuffer.find("Internal Development CA");
366-
PTF_ASSERT_TRUE(pos != std::string::npos);
367-
368-
cert = certMsg->getCertificate(2);
369-
PTF_ASSERT_NOT_NULL(cert);
370-
PTF_ASSERT_TRUE(cert->allDataExists());
371-
PTF_ASSERT_EQUAL(cert->getDataLength(), 1713);
372-
certBuffer = std::string(cert->getData(), cert->getData() + cert->getDataLength());
373-
pos = certBuffer.find("Internal Development CA");
374-
PTF_ASSERT_TRUE(pos != std::string::npos);
348+
{
349+
auto cert = certMsg->getCertificate(0);
350+
PTF_ASSERT_NOT_NULL(cert);
351+
PTF_ASSERT_TRUE(cert->allDataExists());
352+
PTF_ASSERT_EQUAL(cert->getDataLength(), 1509);
353+
auto asn1Record = cert->getRootAsn1Record();
354+
PTF_ASSERT_NOT_NULL(asn1Record);
355+
PTF_ASSERT_EQUAL(asn1Record->getSubRecords().size(), 3);
356+
auto x509Cert = cert->getX509Certificate();
357+
PTF_ASSERT_EQUAL(
358+
x509Cert->getIssuer().toString(),
359+
"C=US, ST=Washington, L=Seattle, O=Hubspan, Inc., OU=Development, CN=LDAP Intermediate CA, [email protected]");
360+
}
361+
362+
{
363+
auto cert = certMsg->getCertificate(1);
364+
PTF_ASSERT_NOT_NULL(cert);
365+
PTF_ASSERT_TRUE(cert->allDataExists());
366+
PTF_ASSERT_EQUAL(cert->getDataLength(), 1728);
367+
auto x509Cert = cert->getX509Certificate();
368+
PTF_ASSERT_EQUAL(
369+
x509Cert->getSubject().toString(),
370+
"C=US, ST=Washington, L=Seattle, O=Hubspan, Inc., OU=Development, CN=LDAP Intermediate CA, [email protected]");
371+
}
372+
373+
{
374+
auto cert = certMsg->getCertificate(2);
375+
PTF_ASSERT_NOT_NULL(cert);
376+
PTF_ASSERT_TRUE(cert->allDataExists());
377+
PTF_ASSERT_EQUAL(cert->getDataLength(), 1713);
378+
auto x509Cert = cert->getX509Certificate();
379+
PTF_ASSERT_EQUAL(
380+
x509Cert->getSubject().toString(),
381+
"C=US, ST=Washington, O=Hubspan, Inc., OU=Development, CN=Internal Development CA, [email protected]");
382+
}
375383

376384
pcpp::SSLCertificateRequestMessage* certReqMsg =
377385
handshakeLayer->getHandshakeMessageOfType<pcpp::SSLCertificateRequestMessage>();
@@ -482,6 +490,10 @@ PTF_TEST_CASE(SSLPartialCertificateParseTest)
482490
pcpp::SSLx509Certificate* cert = certMsg->getCertificate(0);
483491
PTF_ASSERT_FALSE(cert->allDataExists());
484492
PTF_ASSERT_EQUAL(cert->getDataLength(), 1266);
493+
pcpp::Logger::getInstance().suppressLogs();
494+
PTF_ASSERT_NULL(cert->getX509Certificate());
495+
PTF_ASSERT_NULL(cert->getRootAsn1Record());
496+
pcpp::Logger::getInstance().enableLogs();
485497

486498
READ_FILE_AND_CREATE_PACKET(2, "PacketExamples/SSL-PartialCertificate2.dat");
487499

@@ -499,6 +511,10 @@ PTF_TEST_CASE(SSLPartialCertificateParseTest)
499511
cert = certMsg->getCertificate(0);
500512
PTF_ASSERT_FALSE(cert->allDataExists());
501513
PTF_ASSERT_EQUAL(cert->getDataLength(), 1268);
514+
pcpp::Logger::getInstance().suppressLogs();
515+
PTF_ASSERT_NULL(cert->getX509Certificate());
516+
PTF_ASSERT_NULL(cert->getRootAsn1Record());
517+
pcpp::Logger::getInstance().enableLogs();
502518
} // SSLPartialCertificateParseTest
503519

504520
PTF_TEST_CASE(SSLNewSessionTicketParseTest)

0 commit comments

Comments
 (0)