Skip to content

Commit

Permalink
Allow to sign past/future certificates
Browse files Browse the repository at this point in the history
within validity bounds of the issuing certificate
  • Loading branch information
miroshko authored and blenk92 committed Jul 26, 2021
1 parent bc725dc commit 7c1f38b
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 41 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ All notable changes to this project will be documented in this file.
different orders of items in the underlying distinguished name. This fixes
issue #95. The DistinguishedName object is still not order-aware when loading
a DN from OpenSSL. This is to be fixed in a later step.
* X509Certificate::signCSR doesn't validate the certificate at the current
system time anymore but at certificate's notBefore and notAfter dates.
This fixes issue #96 by allowing to sign past and future certificates but
also ensures that the certificate's validity period does not exceed the
validity bounds of the issuing certificate.

## Added

Expand Down
10 changes: 9 additions & 1 deletion src/ca.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,15 @@ X509Certificate CertificateAuthority::_signCSR(const CertificateSigningRequest &
//Sanity check: certificate must be verifiable now
try {
auto cert = X509Certificate{std::move(newCertificate)};
cert.verify({_rootCert}, {});
X509Certificate::VerificationContext ctx;
ctx.addTrustedCertificates({_rootCert})
.addIntermediateCertificates({})
.setVerificationCheckTime(cert.getNotBeforeAsn1());
cert.verify(ctx);
// OpenSSL wants context verification time strictly earlier than notAfter. See check in
// x509_check_cert_time that fails if X509_cmp_time returns -1 (notAfter <= ctx verification time)
ctx.setVerificationCheckTime(cert.getNotAfterAsn1() - Asn1Time::Seconds(1));
cert.verify(ctx);
_nextSerialNumber++;
return cert;
} catch (const MoCOCrWException &e) {
Expand Down
116 changes: 76 additions & 40 deletions tests/unit/test_ca.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,16 +77,22 @@ class CATest : public ::testing::Test,

std::unique_ptr<X509Certificate> _rootRsaCert;

std::unique_ptr<X509Certificate> _rootRsaCertOneYearOldOneYearToGo;

std::unique_ptr<X509Certificate> _rootEccCert;

std::unique_ptr<KeyUsageExtension> _exampleUsage;

std::unique_ptr<BasicConstraintsExtension> _exampleConstraints;

CertificateSigningParameters::Builder _signParamsBuilder;

CertificateSigningParameters _signParams;

CertificateSigningParameters _caSignParams;

CertificateSigningParameters _caSignParamsOneYearOldOneYearToGo;

std::unique_ptr<CertificateAuthority> _rsaCa;

std::unique_ptr<CertificateAuthority> _eccCa;
Expand Down Expand Up @@ -143,16 +149,25 @@ void CATest::SetUp()

BasicConstraintsExtension caConstraint{true, 1};

_signParams = CertificateSigningParameters::Builder{}
_signParamsBuilder = CertificateSigningParameters::Builder{}
.digestType(openssl::DigestTypes::SHA256)
.addExtension(*_exampleConstraints)
.addExtension(*_exampleUsage)
.certificateValidity(Asn1Time::Seconds(120))
.notBeforeAsn1(Asn1Time::now() - std::chrono::seconds{1});

_signParams = _signParamsBuilder.build();

_caSignParams = CertificateSigningParameters::Builder{}
.certificateValidity(Asn1Time::Seconds(120))
.notBeforeAsn1(Asn1Time::now() - std::chrono::seconds{1})
.digestType(openssl::DigestTypes::SHA256)
.addExtension(*_exampleConstraints)
.addExtension(caConstraint)
.addExtension(*_exampleUsage)
.build();

_caSignParams = CertificateSigningParameters::Builder{}
.certificateValidity(Asn1Time::Seconds(120))
_caSignParamsOneYearOldOneYearToGo = CertificateSigningParameters::Builder{}
.certificateValidity(Asn1Time::Seconds(60 * 60 * 24 * 365 * 2))
.notBeforeAsn1(Asn1Time::now() - Asn1Time::Seconds(60 * 60 * 24 * 365))
.digestType(openssl::DigestTypes::SHA256)
.addExtension(caConstraint)
.addExtension(*_exampleUsage)
Expand All @@ -164,6 +179,12 @@ void CATest::SetUp()
0,
_caSignParams));

_rootRsaCertOneYearOldOneYearToGo = std::make_unique<X509Certificate>(CertificateAuthority::createRootCertificate(
*_rootRSAKey,
*_rootCertDetails,
0,
_caSignParamsOneYearOldOneYearToGo));

_rootEccCert = std::make_unique<X509Certificate>(CertificateAuthority::createRootCertificate(
*_rootEccKey,
*_rootCertDetails,
Expand Down Expand Up @@ -412,27 +433,6 @@ TEST_F(CATest, testVerifyCAAgainstPureOpenSslOutputECC)
remove("cert.pem");
}

TEST_F(CATest, testIssueLongLivedCertificate)
{
// Certificate shall be valid for 1000 years
Asn1Time::Seconds validityTime(60l * 60 * 24 * 365 * 1000);
_signParams = CertificateSigningParameters::Builder{}
.certificateValidity(validityTime)
.notBeforeAsn1(Asn1Time::now() - std::chrono::seconds{1})
.digestType(openssl::DigestTypes::SHA256)
.addExtension(*_exampleConstraints)
.addExtension(*_exampleUsage)
.build();

auto rsaCa = std::make_unique<CertificateAuthority>(_signParams, 0, *_rootRsaCert, *_rootRSAKey);

X509Certificate cert = rsaCa->signCSR(CertificateSigningRequest{*_certDetails,
AsymmetricKeypair::generateRSA()});

testValiditySpan(cert, validityTime, _signParams.notBeforeAsn1());

}

TEST_F(CATest, testGetNextSerialNumber)
{
auto eccCa = std::make_unique<CertificateAuthority>(_caSignParams, 0, *_rootRsaCert, *_rootRSAKey);
Expand Down Expand Up @@ -472,26 +472,62 @@ TEST_F(CATest, testSigningWithCustomOrderCA)
});
}

// This test requires the ability to set the time for which a certificate is verified.
TEST_F(CATest, DISABLED_testIssueCertificateInFarFuture)
TEST_F(CATest, testIssueCertificateInFuture)
{
// Certificate shall be valid in 1000 years
Asn1Time validFrom = Asn1Time::now() + Asn1Time::Seconds(60l * 60 * 24 * 365 * 1000);
Asn1Time::Seconds validityTime(120);
auto signParams = _signParamsBuilder
.notBeforeAsn1(Asn1Time::now() + Asn1Time::Seconds(60 * 60 * 24 * 364))
.certificateValidity(Asn1Time::Seconds(120))
.build();

auto signParams = CertificateSigningParameters::Builder{}
.certificateValidity(validityTime)
.notBeforeAsn1(validFrom)
.digestType(openssl::DigestTypes::SHA256)
.addExtension(*_exampleConstraints)
.addExtension(*_exampleUsage)
auto rsaCa = std::make_unique<CertificateAuthority>(signParams, 0, *_rootRsaCertOneYearOldOneYearToGo, *_rootRSAKey);

X509Certificate cert = rsaCa->signCSR(CertificateSigningRequest{*_certDetails,
AsymmetricKeypair::generateRSA()});

testValiditySpan(cert, signParams.certificateValidity(), signParams.notBeforeAsn1());
}

TEST_F(CATest, testIssueCertificateInPast)
{
auto signParams = _signParamsBuilder
.notBeforeAsn1(Asn1Time::now() - Asn1Time::Seconds(60 * 60 * 24 * 364))
.certificateValidity(Asn1Time::Seconds(120))
.build();

auto rsaCa = std::make_unique<CertificateAuthority>(signParams, 0, *_rootRsaCert, *_rootRSAKey);
auto rsaCa = std::make_unique<CertificateAuthority>(signParams, 0, *_rootRsaCertOneYearOldOneYearToGo, *_rootRSAKey);

X509Certificate cert = rsaCa->signCSR(CertificateSigningRequest{*_certDetails,
AsymmetricKeypair::generateRSA()});
AsymmetricKeypair::generateRSA()});

testValiditySpan(cert, signParams.certificateValidity(), signParams.notBeforeAsn1());
}

TEST_F(CATest, testCantIssueCertificateInFutureThatExpiresAfterRoot)
{
auto signParams = _signParamsBuilder
.notBeforeAsn1(Asn1Time::now() + Asn1Time::Seconds(60 * 60 * 24 * 364))
.certificateValidity(Asn1Time::Seconds(60 * 60 * 24 * 2))
.build();

auto rsaCa = std::make_unique<CertificateAuthority>(signParams, 0, *_rootRsaCertOneYearOldOneYearToGo, *_rootRSAKey);

EXPECT_THROW(
rsaCa->signCSR(CertificateSigningRequest{*_certDetails, AsymmetricKeypair::generateRSA()}),
MoCOCrWException
);
}

TEST_F(CATest, testCantIssueCertificateInPastThatIsValidBeforeRoot)
{
auto signParams = _signParamsBuilder
.notBeforeAsn1(Asn1Time::now() - Asn1Time::Seconds(60 * 60 * 24 * 366))
.certificateValidity(Asn1Time::Seconds(60 * 60 * 24 * 2))
.build();

testValiditySpan(cert, validityTime, validFrom);
auto rsaCa = std::make_unique<CertificateAuthority>(signParams, 0, *_rootRsaCertOneYearOldOneYearToGo, *_rootRSAKey);

EXPECT_THROW(
rsaCa->signCSR(CertificateSigningRequest{*_certDetails, AsymmetricKeypair::generateRSA()}),
MoCOCrWException
);
}

0 comments on commit 7c1f38b

Please sign in to comment.