Signing new "root" with old root to roll root certificates

1,161 views
Skip to first unread message

Chris McEniry

unread,
Aug 26, 2017, 3:20:31 PM8/26/17
to Vault
I'm trying to roll a root certificate for a private internal CA. To avoid a three stage distribution (new root propagate everywhere, then reissue everyone, then remove old root), I'm looking at signing the new "root" with the old root (two step -- new root + reissue at the same time, later remove old root). I don't believe this is a grossly unusual pattern (iirc, Window's CA infrastructure does this to renew; looks like some CAs have issued certs with latter expirations).

The kicker is that it's unclear whether you should be able to issue a new cert with a NotAfter after the original CA in general - it's very clear that Vault doesn't allow it. I'll admit that I'm mixed on this one. It is a reasonable practice most of the time, but there are counter examples and I can't find any authoritative source that says what is or isn't allowed. The best I've found is that OpenSSL explicitly does not enforce this check for this specific reason: https://github.com/openssl/openssl/issues/2622. Yes, it does touch the issue of "root" vs "intermediates-acting-like-roots" but again this is an existing pattern.

Is this a use case of "shouldn't really store roots in vault" (in which case - why have the root paths anyways), or is this a bug, or am I trying to be too clever and and really should just use the three stage process?

Side note: I tried looking at dual signing the intermediate with both roots, but since I'm not supposed to send both as part of the TLS ServerCertificate message (RFC5246,S7.4.2), it falls back into the 3 step distribution again.

Thanks,
--mac

Jeff Mitchell

unread,
Aug 29, 2017, 9:34:05 AM8/29/17
to Vault
Hi Chris,

The reason Vault doesn't allow it is actually probably not what you might expect. When the backend was first built (around Vault 0.2) the thinking was that it would be governed by leases in a hierarchy just like tokens are, so that revoking a root CA also revokes its issued certs. But that didn't happen for various reasons, and since then we actually turned off leases by default for issued certs. So at this point it's this way simply because nobody has asked us to change it.

I'm totally amenable to changing this behavior. I have a couple of concerns which are technical in nature:

* Path length restrictions -- right now attempting to sign another CA (via the pki/root/sign-intermediate path) will end up, if the root has a path length restriction, on setting a lower length on the signed cert. So this might require the ability to change this

* Cross-signing support -- there are other pki handling libraries out there, but right now the PKI backend uses Go's x509 package which I don't believe has support for adding signatures onto existing certificates.

Of course, if the only behavioral change needed on your end is simply the removal of the time restriction, that's much easier to do.

Best,
Jeff


--
This mailing list is governed under the HashiCorp Community Guidelines - https://www.hashicorp.com/community-guidelines.html. Behavior in violation of those guidelines may result in your removal from this mailing list.
 
GitHub Issues: https://github.com/hashicorp/vault/issues
IRC: #vault-tool on Freenode
---
You received this message because you are subscribed to the Google Groups "Vault" group.
To unsubscribe from this group and stop receiving emails from it, send an email to vault-tool+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/vault-tool/54773c5d-789d-488b-8209-8d223593cc36%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Jeff Mitchell

unread,
Aug 29, 2017, 6:27:09 PM8/29/17
to Vault
Hi Chris,

We're going to add in a new endpoint (likely pki/root/sign-root) that will allow signing a CSR that indicates a CA; the difference between that and sign-intermediate is that it will not adjust decrement the path length (if set) and will allow CAs with lifetimes that go beyond the root. We didn't see a reason to change the lifetime behavior for intermediates or leaf certs at the moment; if we're missing something, let me know!

Best,
Jeff

Chris McEniry

unread,
Sep 6, 2017, 8:26:57 PM9/6/17
to Vault
(Sorry - had to do my homework on the path length).

I think the new endpoint makes sense. I agree that the usual case is to not sign past the expiration and this is a special case.

Related - does it make sense that this would appear in sign verbatim as well?

Since it does sound acceptable as a feature, should I submit this as an issue in Github?

Thanks,
--mac
To unsubscribe from this group and stop receiving emails from it, send an email to vault-tool+...@googlegroups.com.

Jeff Mitchell

unread,
Sep 6, 2017, 8:29:24 PM9/6/17
to Vault
Hi Chris,

It's already in 0.8.2 :-)

Best,
Jeff

To unsubscribe from this group and stop receiving emails from it, send an email to vault-tool+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/vault-tool/61bdca98-1435-4ac7-bbfa-9eaadc946a49%40googlegroups.com.

Chris McEniry

unread,
Sep 8, 2017, 8:09:21 PM9/8/17
to Vault
I saw that release when I commented and thought "hrm... That's interesting. I wonder if someone else has a similar use case."

Unfortunately, I'm not sure this is working. Or... specifically, I'm not sure the output of this is working. I created two roots in vault (A and B), used sign-self-issued to sign B by A, and then tried to validate B using a bundle of just A. The go standard library gives you NameMistmatch error "issuer name does not match subject from issuing certificate". OpenSSL gives me a "unable to get local issuer certificate" error. Just to be even more fun, golang is happy with the self-issued-but-signed-by-other when added as a root, but openssl is not. I think in the openssl case, it's not able to find the certificate since it's looking it up by issuer string (version 0.9.8hz on OSX). I think golang finds parents by keys but then validates by issuer and subject names.

I'll admit that I'm used to the issuer being the definitive key to look up for name, but the go library is obviously a little different even if it uses the issuer as the final determination of validity of the chain.

Unfortunately, I can't say which way is right... I can't find a useful standard for this behavior at all. Found the ITU standard on the certificate, but I'm not seeing anything about how to handle use cases like this....

--mac

Jeff Mitchell

unread,
Sep 8, 2017, 11:35:14 PM9/8/17
to Vault
Hi Chris,

On Fri, Sep 8, 2017 at 8:09 PM, Chris McEniry <mce...@gmail.com> wrote:
Unfortunately, I'm not sure this is working. Or... specifically, I'm not sure the output of this is working. I created two roots in vault (A and B), used sign-self-issued to sign B by A, and then tried to validate B using a bundle of just A.

So what this endpoint does is exactly what it says -- it signs a self-issued certificate. The RFC is quite clear what a self-issued certificate is; it's a certificate which has the same Issuer and Subject DNs:
   Self-issued certificates are CA certificates in which
   the issuer and subject are the same entity.  Self-issued certificates
   are generated to support changes in policy or operations.  Self-
   signed certificates are self-issued certificates where the digital
   signature may be verified by the public key bound into the
   certificate.
The endpoint takes a self-issued certificate and signs it; the output therefore is a certificate that is self-issued but where the digital signature is verified not by the public key bound into the certificate, but by a different key, encoded in the authority key identifier. So it's self-issued but not self-signed.
 
Now, it's possible that it turns out that this is not actually what you *want*.  :-)  But as far as I can tell, the endpoint does what it claims to do, and as far as I can tell it's a *valid* thing to do. You're using the digital signature of something you trust to assert that the self-issued certificate is valid, which helps bootstrap a new root. The rest is just verification logic. But speaking of that...

The go standard library gives you NameMistmatch error "issuer name does not match subject from issuing certificate". OpenSSL gives me a "unable to get local issuer certificate" error. Just to be even more fun, golang is happy with the self-issued-but-signed-by-other when added as a root, but openssl is not. I think in the openssl case, it's not able to find the certificate since it's looking it up by issuer string (version 0.9.8hz on OSX). I think golang finds parents by keys but then validates by issuer and subject names.

One thing I've found over the last couple of years -- and you can see this in the history of the PKI backend between the 0.3 and 0.6 releases especially as we got feedback from users on various other crypto stacks -- is that it's one thing for the RFC to say something is valid and quite another for the various crypto stacks to *like* it. For instance, we used to by default sign certificates with the Any usage so that, in absence of specific usage information, they could be used for any purpose, only to find that some crypto stacks (IIRC Mozilla's) just say "nope" to Any. There's actually a comment in the Golang code around how some of its behavior was modeled after the Windows Crypto API -- for better and for worse.
 
I'll admit that I'm used to the issuer being the definitive key to look up for name, but the go library is obviously a little different even if it uses the issuer as the final determination of validity of the chain.

Unfortunately, I can't say which way is right... I can't find a useful standard for this behavior at all. Found the ITU standard on the certificate, but I'm not seeing anything about how to handle use cases like this....

I can't either. I'm happy to change the endpoint logic to do the right thing if the right thing is something other than what it's doing. (I'm also happy to leave it alone for the few but valid use-cases it provides, and add another endpoint to do something that fits your use-case.)

What I don't really know however is what will work for your use-case if this doesn't. I fully admit this isn't common. Even the Let's Encrypt certs that were signed by a third party CA to get them bootstrapped weren't actually self-issued certs. Mimicking this kind of signing can already be done via the intermediate (sign-intermediate/set-signed) endpoints, because there the subject can be what you provide but the issuer will be the root CA; the point of the new endpoint was to go a step further and also deal with real, honest-to-goodness self-issued certs. And if you want to bootstrap a new root CA cert, if anything else is the issuer it's not really a root, is it. 

I'm all ears in terms of what you think will actually solve your use-case, but my current understanding is that in theory this should have done it, and it's RFC-valid, except in practice in the real world you're finding that the crypto stacks are like "meh, nah" in their validation code.

Let me know if you have any advice. I'm versed in X509 but not an expert, and perhaps someone will know what, if anything, I'm doing wrong here.

Best,
Jeff


Chris McEniry

unread,
Sep 9, 2017, 3:55:43 AM9/9/17
to Vault
So what this endpoint does is exactly what it says -- it signs a self-issued certificate. The RFC is quite clear what a self-issued certificate is; it's a certificate which has the same Issuer and Subject DNs:
   Self-issued certificates are CA certificates in which
   the issuer and subject are the same entity.  Self-issued certificates
   are generated to support changes in policy or operations.  Self-
   signed certificates are self-issued certificates where the digital
   signature may be verified by the public key bound into the
   certificate.
The endpoint takes a self-issued certificate and signs it; the output therefore is a certificate that is self-issued but where the digital signature is verified not by the public key bound into the certificate, but by a different key, encoded in the authority key identifier. So it's self-issued but not self-signed.
 
Now, it's possible that it turns out that this is not actually what you *want*.  :-)  But as far as I can tell, the endpoint does what it claims to do, and as far as I can tell it's a *valid* thing to do. You're using the digital signature of something you trust to assert that the self-issued certificate is valid, which helps bootstrap a new root. The rest is just verification logic. But speaking of that...

I think the crux is producing certificates which can be used in as part of a valid chain, and this hinges on the issuer/subject match. In RFC5280,Section 6:

   To meet this goal, the path validation process verifies, among other
   things, that a prospective certification path (a sequence of n
   certificates) satisfies the following conditions:

      (a)  for all x in {1, ..., n-1}, the subject of certificate x is
           the issuer of certificate x+1;

I've been issuing generations of the "root" certificates with the generations having different generation names (ala G1, G2, G3...), and the Gn generation being issued by the G(n-1) generation. This is valid - outside of the questionable constraint of not issuing a certificate which expires after the issuing certificate (see prior openssl reference).

This makes the only valid(?) use of a self-issued certificate is when it is also signed by a certificate which has the same subject. But the sign-self-issued endpoint does still allow for the new cert's issuer/subject to differ from the current subject. Would it make sense to have another check or a warn response when there's a mismatch between the new cert and the old one?

The go standard library gives you NameMistmatch error "issuer name does not match subject from issuing certificate". OpenSSL gives me a "unable to get local issuer certificate" error. Just to be even more fun, golang is happy with the self-issued-but-signed-by-other when added as a root, but openssl is not. I think in the openssl case, it's not able to find the certificate since it's looking it up by issuer string (version 0.9.8hz on OSX). I think golang finds parents by keys but then validates by issuer and subject names.

One thing I've found over the last couple of years -- and you can see this in the history of the PKI backend between the 0.3 and 0.6 releases especially as we got feedback from users on various other crypto stacks -- is that it's one thing for the RFC to say something is valid and quite another for the various crypto stacks to *like* it. For instance, we used to by default sign certificates with the Any usage so that, in absence of specific usage information, they could be used for any purpose, only to find that some crypto stacks (IIRC Mozilla's) just say "nope" to Any. There's actually a comment in the Golang code around how some of its behavior was modeled after the Windows Crypto API -- for better and for worse.

Off the cuff, do you know of a library that doesn't enforce the issuer<->parent subject naming check?
 
 
I'll admit that I'm used to the issuer being the definitive key to look up for name, but the go library is obviously a little different even if it uses the issuer as the final determination of validity of the chain.

Unfortunately, I can't say which way is right... I can't find a useful standard for this behavior at all. Found the ITU standard on the certificate, but I'm not seeing anything about how to handle use cases like this....

I can't either. I'm happy to change the endpoint logic to do the right thing if the right thing is something other than what it's doing. (I'm also happy to leave it alone for the few but valid use-cases it provides, and add another endpoint to do something that fits your use-case.)

What I don't really know however is what will work for your use-case if this doesn't. I fully admit this isn't common. Even the Let's Encrypt certs that were signed by a third party CA to get them bootstrapped weren't actually self-issued certs. Mimicking this kind of signing can already be done via the intermediate (sign-intermediate/set-signed) endpoints, because there the subject can be what you provide but the issuer will be the root CA; the point of the new endpoint was to go a step further and also deal with real, honest-to-goodness self-issued certs. And if you want to bootstrap a new root CA cert, if anything else is the issuer it's not really a root, is it. 

I'm all ears in terms of what you think will actually solve your use-case, but my current understanding is that in theory this should have done it, and it's RFC-valid, except in practice in the real world you're finding that the crypto stacks are like "meh, nah" in their validation code.

For my use case, I've been using the generational "root" certificates. I'll admit that this a constraint that I'm going on based on other descriptions (e.g. Docker's cert rotation: https://docs.docker.com/engine/swarm/how-swarm-mode-works/pki/) but the description is never precise and I can't say if that implementation is exactly that way or not (I'll take a look at the code after some sleep). To that end,  for my use case it might match more to creating a new endpoint - issue-ignoretime or something probably better named - which will sign certs without looking at the time window on them..

The crypto libraries are unhappy because, in the generational model, there's a difference between the issuer on the second cert and the subject of the issuing cert (based on KeyID). If the issuer/subject of the "new" root matches the subject of the old "root", both openssl and golang verify it successfully.

Let's Encrypt is dual signing their intermediate so it's a matter of attaching the appropriately signed intermediate in TLS. Even in this case, it's a matter of switching out the attached intermediate when you decide that the ISRG roots hit ubiquity. I'm avoiding that because with an internal distribution model, it matters being able to only do that rotation once which means having the valid chain go up to the old "root" - i.e. not having to have new CA ubiquity before using the intermediate issued by the new CA. (It's not an issue of distributing the CA -- that part is easy. The processes (variety of implementations) are not all set up to reload the certificate bundle from disk -- they expect an interrupted restart of the process to read in the new cert bundle --- that interruption is what I'm trying to avoid).
 

Let me know if you have any advice. I'm versed in X509 but not an expert, and perhaps someone will know what, if anything, I'm doing wrong here.

Similar boat, so can't say too well. My biggest cringe factor is the reissuing of the same named CA. Keeping it straight as for which one is valid just seems like some major operational overhead. I know some CAs have reissued their roots - but I can't say if that was with or with AuthID keys.... so don't know what's right :(. If it stays as sign-self-issued, I think there definitely needs to be an error or warning in the even that subject/issue don't match for the corresponding certs.

Thanks,
--mac

Jeff Mitchell

unread,
Sep 9, 2017, 10:17:11 AM9/9/17
to Vault
Hi Chris,

I'll put some proposals for the way forward at the end, but if you don't mind going deeper down the rabbit hole with me it's instructive for figuring out the right path.

I think the crux is producing certificates which can be used in as part of a valid chain, and this hinges on the issuer/subject match. In RFC5280,Section 6:

   To meet this goal, the path validation process verifies, among other
   things, that a prospective certification path (a sequence of n
   certificates) satisfies the following conditions:

      (a)  for all x in {1, ..., n-1}, the subject of certificate x is
           the issuer of certificate x+1;
The context before and after this snippet is relevant. But first, we have to note what defines a trust anchor:

           trust anchor information, describing a CA that serves as a
           trust anchor for the certification path.  The trust anchor
           information includes:

         (1)  the trusted issuer name,

         (2)  the trusted public key algorithm,

         (3)  the trusted public key, and

         (4)  optionally, the trusted public key parameters associated
              with the public key.

      The trust anchor information may be provided to the path
      processing procedure in the form of a self-signed certificate.
      When the trust anchor information is provided in the form of a
      certificate, the name in the subject field is used as the trusted
      issuer name and the contents of the subjectPublicKeyInfo field is
      used as the source of the trusted public key algorithm and the
      trusted public key.
You'll probably note something important here, which is that a trust anchor *may* be provided as a self-signed certificate. But it's not a requirement; essentially the trust anchor is defining a trusted public key.

Moving down, we see:

   The primary goal of path validation is to verify the binding between
   a subject distinguished name or a subject alternative name and
   subject public key, as represented in the target certificate, based
   on the public key of the trust anchor.
Here's that annoying thing again -- the goal is to verify the binding _based on the public key of the trust anchor_, which as we found before isn't required to be in the form of a self-signed certificate. But you can imagine that you could have a trusted root defining a trusted public key, and the public key itself satisfying the requirements of the trust anchor.

OK, let's keep going:

To meet this goal, the path validation process verifies, among other
   things, that a prospective certification path (a sequence of n
   certificates) satisfies the following conditions:

      (a)  for all x in {1, ..., n-1}, the subject of certificate x is
           the issuer of certificate x+1;

      (b)  certificate 1 is issued by the trust anchor;

      (c)  certificate n is the certificate to be validated (i.e., the
           target certificate); and

      (d)  for all x in {1, ..., n}, the certificate was valid at the
           time in question.
You quoted (a) before, but (b) and (d) are both super important. (b) says certificate 1 is issued by the trust anchor, but it doesn't specify whether issued here means that the Issuer DN encoded in the certificate must match that of a self-signed certificate signing the public key of the trust anchor (which as we found earlier is not a requirement for trusting a public key). So in theory, a certificate with a signature from the right public key and authority key id encoded in the cert ought to be satisfied by the "issued by the trust anchor" line. (d) is obviously relevant here because it means that if you're walking up a certification path, you can't have a certificate issued that is not a root in a certification path be valid while the issuer isn't. We'll get back to that, but we have to look at one more thing first, which is just below the path validation algorithm:

   A certificate is self-issued if the same DN appears in the subject
   and issuer fields (the two DNs are the same if they match according
   to the rules specified in Section 7.1).  In general, the issuer and
   subject of the certificates that make up a path are different for
   each certificate.  However, a CA may issue a certificate to itself to
   support key rollover or changes in certificate policies.  These
   self-issued certificates are not counted when evaluating path length
   or name constraints.
Here's where we get into trouble. 

This explicitly says that a CA may issue a certificate to itself to support key rollover, and these self-issued certificates are not counted when evaluating name constraints. The "name constraints" here likely means not the DNs, but rather the permitted DNS domains on a cert. Likely. Maybe. I think. But regardless, this paragraph was the kicker for me thinking that the right thing to do was to code it as I did.

The reason is that if you need to support key rollover, it can't be a self-signed cert. If it was, you wouldn't be rolling over to a new key, you'd be using the same one -- issuing a self-issued cert with the same key (e.g. self-signed) would roll over nothing.

It's actually not super clear what the right thing is, and that's the problem, and it's why different crypto stacks behave in different ways based on interpretation. This goes back to my statement of thinking that what the endpoint does is literally a valid thing to do, but it also means other people have to be willing to validate it that way.  :-(

So here we're at a point where based on the definition of a trust anchor and based on the explicit text of self-issued certs being used for key rollover...well...

OK, so let's get to what you've been doing:

I've been issuing generations of the "root" certificates with the generations having different generation names (ala G1, G2, G3...), and the Gn generation being issued by the G(n-1) generation. This is valid - outside of the questionable constraint of not issuing a certificate which expires after the issuing certificate (see prior openssl reference).

Right, so going back to (d) in the path verification algorithm, this should be invalid. I realize the openssl person said there's no requirement for the CA higher up the chain to be valid, but that probably depends on whether you are following the given path validation logic in the RFC to the letter, because (d) seems to indicate that this should in fact be invalid once the root CA expires, since if the trust anchor is a self-signed certificate, that puts it in the validation chain (the text only says that path length and name constraints checks are skipped for self-issued certs).
 
This makes the only valid(?) use of a self-issued certificate is when it is also signed by a certificate which has the same subject. But the sign-self-issued endpoint does still allow for the new cert's issuer/subject to differ from the current subject.

It does? I have checks in there that should ensure it doesn't, and a unit test besides. Or did you assume this?
 
Off the cuff, do you know of a library that doesn't enforce the issuer<->parent subject naming check?

No, but off the cuff I don't know the details of how various crypto stacks have coded their validation algorithms :-)
 
For my use case, I've been using the generational "root" certificates. I'll admit that this a constraint that I'm going on based on other descriptions (e.g. Docker's cert rotation: https://docs.docker.com/engine/swarm/how-swarm-mode-works/pki/) but the description is never precise and I can't say if that implementation is exactly that way or not (I'll take a look at the code after some sleep). To that end,  for my use case it might match more to creating a new endpoint - issue-ignoretime or something probably better named - which will sign certs without looking at the time window on them..

Their cert rotation indicates that they just issue a new root and use it as an intermediate until such a time as all of the nodes start using certs issued by that CA. As you said, the details are scarce. If you dig into the code I'd be interested in knowing what they're doing (I may give it a try later but can't at the moment), because it seems like the same problem -- if your new "root" cert is issued by your old root cert, and isn't self-signed, then if clients don't have the old root cert for path validation you should run into a problem. Unless all of the clients carry around all of the previous roots for path validation, and it only "forgets" the root CA to not ever issue more certs from it. And in fact as you note:

The crypto libraries are unhappy because, in the generational model, there's a difference between the issuer on the second cert and the subject of the issuing cert (based on KeyID). If the issuer/subject of the "new" root matches the subject of the old "root", both openssl and golang verify it successfully.

Which makes me wonder if there's some custom validation logic they're doing.

OK, so I can think of a couple of possibilities. They're not necessarily mutually exclusive. Note that we're going to spin a new release soon and this has only been out in 0.8.2 so I'm happy to switch things up and get a fix for this in as well depending on timing.

One possibility would be to put a flag into sign-self-issued that controls whether or not the Issuer uses the CA's issuer DN or the original cert's. If the flag is on this makes it a kind of a "super sign-verbatim" but with a really key difference: because it takes in a cert, not a CSR signed with the private key of the requestor, it means for generating new root certs with the same key you don't need to have access to the CA's private key -- obviously a good thing. *But* if the whole point here is to get a new self-signed root CA with the same key, might as well just not require the certificate to be provided anyways, since the CA knows it, and just provide a desired validity length and a generation number. That wouldn't have the flexibility of using a CSR to specify a custom DN for the new cert, but again, no private key for the CA needed to be exported to generate one. But, a key point here is it doesn't allow for key rotation.

So option 1: rather than sign a self-issued cert, just generate a new cert (but not self-issued/self-signed!) with the same key, different DN, and new validity period. (I would think this would fall afoul of (d) above if the original cert expires, and create an ever-longer validation path, but openssl clearly doesn't care about validity period.)  Also, allow such a cert with the CA's key and the existing CA as the issuer to be uploaded and replace the cert used for that mount.

The option above, if various crypto libraries are happy validating it, is a decent option if you don't want key rotation but just don't want validity to be forever, since it doesn't require knowledge of the private key, and your crypto libraries don't actually care about validity periods when verifying CAs in a validation path.

But, what if you do want key rotation?

Option 2: allow the sign-intermediate endpoint to use a validity period longer than the original CA. Same concerns with respect to (d) but again, if crypto stacks are happy with this, who am I to judge. (I will also note that in orgs where I've worked and in most public CAs the root CAs have lifetimes counted in decades so nobody on the public internet really has to deal with this much.) But, then I have concerns around path length. From the RFC:

      (k)  max_path_length:  this integer is initialized to n, is
           decremented for each non-self-issued certificate in the path,
           and may be reduced to the value in the path length constraint
           field within the basic constraints extension of a CA
           certificate.
"For each non-self-issued certificate". Which suggests that you can have multiple self-issued certificates in a chain, which again, means the Issuer/Subject DNs match, and if the key is the same as the subject it's self-signed, which denotes a root, so you wouldn't have more than one of those in the path. So the RFC here is suggesting that sure, you can totally have self-issued certs in a path (which would be anchored by a trust anchor, which is maybe a self-signed cert but not required and really is a trusted public key). You can see why I thought the way it was implemented was the right thing to do...
So while option 2 is also possible, it probably means that you would want to ensure that your root has no path length constraint, and if your want to go gen1 -> gen2 -> gen3 -> etc. you'd never want to introduce a path length constraint because eventually you'll run out. And, oh, that pesky thing about statement (d) in the algorithm, except openssl ignores and other crypto stacks may or may not ignore.
And of course, option 1, if the Subject/Issuer DNs aren't the same, also potentially has path length concerns.
Oh, X509.
Let me know your thoughts...
Best,
Jeff

Jeff Mitchell

unread,
Sep 9, 2017, 10:40:09 AM9/9/17
to Vault
Hi Chris,

I noticed something I originally missed about the openssl statement when rereading it again.

My concerns about step (d) in the path validation is literally, when a certificate in the chain is expired, which is how I originally read the openssl issue, that it would ignore an *expired* certificate.

However, the openssl issue is actually saying that it doesn't validate that the *range* of validity. If you have a CA cert that expires after its root, openssl is fine with that -- and that's as it should be. What the issue doesn't say is that openssl doesn't check validity of each cert in its path against the *current* time.

So please read the above post with that in mind -- I had thought from my initial reading that it was saying openssl is fine with expired certs, but it's actually just saying that openssl doesn't pre-judge certificates based on validity period, so long as they are valid now. Which makes sense.

Assuming openssl in fact does do part (d) of that algorithm properly, it does make me think that it's still a bad idea to issue a certificate valid past the parent's expiration, because unless I'm misreading the RFC it seems like that should, in fact, cause validation to fail. (See my note in the previous email about my experience with true self-signed root CAs on the public internet being that their validity is in decades, which gives plenty of time to create a new root, get it into browsers, and switch over issuance of certs way in advance of expiration.)

That makes me think option 2 is maybe not a bad idea, but a useless one and leads me back to option 1.

If you really truly want a new root, I think you need to create a new root. You could cross-sign it to make it palatable to applications while you seed the new root around, so I think in that case the sign-self-issued still makes sense, but with the ability to set the Issuer as expected (maybe toggleable with the current behavior for those using custom validation logic).

I could also maybe add the ability to issue a new self-signed cert with the same key you're already using -- again, depends on how you want to define trust anchor and your validation logic -- but I think in most cases you'd still have to seed this cert around because if you're not going solely on public key you still need a mapping to a real cert (and if this wasn't the case the endpoint would be acceptable as-is). So I think this puts you in the same boat as getting a new real root, except that you aren't also rotating your key...so generally a loss, not a win.

In your generation-based example, you didn't go into any details about validity, but you did say you've been doing that up to this point. I'm curious about whether you have hit a point where any of the certificates were actually past their validity date and the behavior of crypto stacks if so.

Best,
Jeff

Chris McEniry

unread,
Sep 9, 2017, 12:40:09 PM9/9/17
to Vault


On Saturday, September 9, 2017 at 7:40:09 AM UTC-7, Jeff Mitchell wrote:
My concerns about step (d) in the path validation is literally, when a certificate in the chain is expired, which is how I originally read the openssl issue, that it would ignore an *expired* certificate.

However, the openssl issue is actually saying that it doesn't validate that the *range* of validity. If you have a CA cert that expires after its root, openssl is fine with that -- and that's as it should be. What the issue doesn't say is that openssl doesn't check validity of each cert in its path against the *current* time.

Odd - when I read that issue, I read it as for signing new certificates, not for validating them (mainly because that was my mental focus at the time... yeah, confirmation bias). I'll admit that I'm not following the use case now. If it's for validating, I feel like I gotta disagree with the issue now ironically, because yeah, that does explicitly go against (d) in the path validation requirement.

I would expect the proper path to be that (using the openssl example) both Org 1 Root CA and Org 2 Root CA are in the CA bundle and when Org 1 Root CA expired (or at least it's validation of Org 2 Root CA which is separately attached to any TLS negotiations), it would automatically flip over to using the Org 2 Root CA (assuming no conflicts with the attached chain, yaddayadda).

Comparing to golang, it does do a time validation check against each of the certificates:

* called even on certificates in the RootPool in https://golang.org/src/crypto/x509/verify.go#L365
 
So please read the above post with that in mind -- I had thought from my initial reading that it was saying openssl is fine with expired certs, but it's actually just saying that openssl doesn't pre-judge certificates based on validity period, so long as they are valid now. Which makes sense.

Assuming openssl in fact does do part (d) of that algorithm properly, it does make me think that it's still a bad idea to issue a certificate valid past the parent's expiration, because unless I'm misreading the RFC it seems like that should, in fact, cause validation to fail. (See my note in the previous email about my experience with true self-signed root CAs on the public internet being that their validity is in decades, which gives plenty of time to create a new root, get it into browsers, and switch over issuance of certs way in advance of expiration.)

Gimme a few hours - I'll put together a sample chain and see if openssl does actual validate it.
 
That makes me think option 2 is maybe not a bad idea, but a useless one and leads me back to option 1.

If you really truly want a new root, I think you need to create a new root. You could cross-sign it to make it palatable to applications while you seed the new root around, so I think in that case the sign-self-issued still makes sense, but with the ability to set the Issuer as expected (maybe toggleable with the current behavior for those using custom validation logic).

(barring this concern) I think there's reasons to support both use cases --- see way below.
 
I could also maybe add the ability to issue a new self-signed cert with the same key you're already using -- again, depends on how you want to define trust anchor and your validation logic -- but I think in most cases you'd still have to seed this cert around because if you're not going solely on public key you still need a mapping to a real cert (and if this wasn't the case the endpoint would be acceptable as-is). So I think this puts you in the same boat as getting a new real root, except that you aren't also rotating your key...so generally a loss, not a win.

In your generation-based example, you didn't go into any details about validity, but you did say you've been doing that up to this point. I'm curious about whether you have hit a point where any of the certificates were actually past their validity date and the behavior of crypto stacks if so.

This is working in the Golang library - while the original trust anchor certificate is valid. I'll get back two chains, [leaf Intermediate TA2 TA1] and [leaf intermediate TA2], with TA1 and TA2 in the RootPool. I'll get back one chain, [leaf intermediate TA2 TA1] with just TA1 in the RootPool. After TA1 expires, I get [leaf intermediate TA2] even with TA1 in the RootPool.

I think the crux is producing certificates which can be used in as part of a valid chain, and this hinges on the issuer/subject match. In RFC5280,Section 6:

   To meet this goal, the path validation process verifies, among other
   things, that a prospective certification path (a sequence of n
   certificates) satisfies the following conditions:

      (a)  for all x in {1, ..., n-1}, the subject of certificate x is
           the issuer of certificate x+1;
The context before and after this snippet is relevant. But first, we have to note what defines a trust anchor:

The trust anchor is basically what goes into your CA bundle. You're right - it doesn't have to be self issued or self signed. I was hoping to find one in the default for OSX or a linux (I could have sworn I saw it before when troubleshooting an intermediate mismatch issue), but I'm not finding any right now. However, in our private CA bundles, the only issue I've seen with non-si and non-ss is that openssl needs to the -partial_chain option for the verify subcommand (can't say if it's needed outside of the verify command...) to not require that the trust anchor of the candidate chain to be si/ss - and prior to 1.0.2g that option is not there. Golang doesn't have that requirement.

 
         (1)  the trusted issuer name,

(I don't like the wording of point #1 --- "the trusted issuer name".  Does that mean the issuer of the trust anchor or does that mean the subject of the trust anchor which becomes the issuer on any n=2 certificates. I think this is clarified obtusely the second paragraph, but still... meh).

      The trust anchor information may be provided to the path
      processing procedure in the form of a self-signed certificate.
      When the trust anchor information is provided in the form of a
      certificate, the name in the subject field is used as the trusted
      issuer name and the contents of the subjectPublicKeyInfo field is
      used as the source of the trusted public key algorithm and the
      trusted public key.
You'll probably note something important here, which is that a trust anchor *may* be provided as a self-signed certificate. But it's not a requirement; essentially the trust anchor is defining a trusted public key.

Yup.

 
Moving down, we see:

   The primary goal of path validation is to verify the binding between
   a subject distinguished name or a subject alternative name and
   subject public key, as represented in the target certificate, based
   on the public key of the trust anchor.
Here's that annoying thing again -- the goal is to verify the binding _based on the public key of the trust anchor_, which as we found before isn't required to be in the form of a self-signed certificate. But you can imagine that you could have a trusted root defining a trusted public key, and the public key itself satisfying the requirements of the trust anchor.

And I think one of the keys here is that the binding is 1.) of that public key being a TA, and 2.) of the public key to the subject (DN or SAN) of that cert. To reiterate - #2 is big because of step a.
 
(a) for all x in {1, ..., n-1}, the subject of certificate x is
           the issuer of certificate x+1;

      (b)  certificate 1 is issued by the trust anchor;

      (c)  certificate n is the certificate to be validated (i.e., the
           target certificate); and

      (d)  for all x in {1, ..., n}, the certificate was valid at the
           time in question.
You quoted (a) before, but (b) and (d) are both super important. (b) says certificate 1 is issued by the trust anchor, but it doesn't specify whether issued here means that the Issuer DN encoded in the certificate must match that of a self-signed certificate signing the public key of the trust anchor (which as we found earlier is not a requirement for trusting a public key). So in theory, a certificate with a signature from the right public key and authority key id encoded in the cert ought to be satisfied by the "issued by the trust anchor" line.

Ok - so, yeah, the big item is "what does issued by mean?" Reading RFC3820 again, I think 4.1.2.4 locks that one in:

   The issuer field identifies the entity who has signed and issued the
   certificate.  The issuer field MUST contain a non-empty distinguished
   name (DN).

So, even in the case of having a Authority Key ID, the issuer still has to match a subject field. This kinda makes the situation of self-signed and self-issued to be one and the same at least with respect to the Issuer DN. Clarifying the Authority Key ID (4.2.1.1):

   This extension is used where an issuer has
   multiple signing keys (either due to multiple concurrent key pairs or
   due to changeover).

So, it's not so much to allow for endorsing a certificate without "issuing" it, but for allowing a CA to distinguish between multiple signing keys/certs all of which have the same Subject DN.

 
(d) is obviously relevant here because it means that if you're walking up a certification path, you can't have a certificate issued that is not a root in a certification path be valid while the issuer isn't. We'll get back to that, but we have to look at one more thing first, which is just below the path validation algorithm:

   A certificate is self-issued if the same DN appears in the subject
   and issuer fields (the two DNs are the same if they match according
   to the rules specified in Section 7.1).  In general, the issuer and
   subject of the certificates that make up a path are different for
   each certificate.  However, a CA may issue a certificate to itself to
   support key rollover or changes in certificate policies.  These
   self-issued certificates are not counted when evaluating path length
   or name constraints.
Here's where we get into trouble. 

This explicitly says that a CA may issue a certificate to itself to support key rollover, and these self-issued certificates are not counted when evaluating name constraints. The "name constraints" here likely means not the DNs, but rather the permitted DNS domains on a cert. Likely. Maybe. I think. But regardless, this paragraph was the kicker for me thinking that the right thing to do was to code it as I did.

The reason is that if you need to support key rollover, it can't be a self-signed cert. If it was, you wouldn't be rolling over to a new key, you'd be using the same one -- issuing a self-issued cert with the same key (e.g. self-signed) would roll over nothing.

In the event of just a key rollover, yes, it would not be a self-signed cert, but it would be self-issued and signed with the original which has a matching subject DN. The original cert (assuming it was set as a root) would have just the Subject Key Identifier. The second cert would have the Authority Key Identifier pointing to the SKI of the original cert, it's own SKI pointing to itself, and the Subject DN and Issuer DN would be the same and matching the Subject DN of the original cert.

I think it's coming down to --- issuers are strictly based on the DN.
 
It's actually not super clear what the right thing is, and that's the problem, and it's why different crypto stacks behave in different ways based on interpretation. This goes back to my statement of thinking that what the endpoint does is literally a valid thing to do, but it also means other people have to be willing to validate it that way.  :-(

So here we're at a point where based on the definition of a trust anchor and based on the explicit text of self-issued certs being used for key rollover...well...

Yeah... I'm worried about the implementations being different.
 
OK, so let's get to what you've been doing:

I've been issuing generations of the "root" certificates with the generations having different generation names (ala G1, G2, G3...), and the Gn generation being issued by the G(n-1) generation. This is valid - outside of the questionable constraint of not issuing a certificate which expires after the issuing certificate (see prior openssl reference).

Right, so going back to (d) in the path verification algorithm, this should be invalid. I realize the openssl person said there's no requirement for the CA higher up the chain to be valid, but that probably depends on whether you are following the given path validation logic in the RFC to the letter, because (d) seems to indicate that this should in fact be invalid once the root CA expires, since if the trust anchor is a self-signed certificate, that puts it in the validation chain (the text only says that path length and name constraints checks are skipped for self-issued certs).

See use case with the TA1 and TA2 above. The driver behind what I'm doing is trying to keep everything working while doing a "root" (I'm just going to keep saying TA at this point) roll. So, before TA1 expires, I issue TA2 and start issuing certs that chain up to that. Anyone with only TA1 in their CA bundle will trust the new chains because they can draw from TA2->TA1; anyone with both TA1 and TA2 in their bundle are just find an dandy either way because it has two valid cert chains it can try. I have until TA1 expires to make sure that everyone is rolled (at least, every leaf node has the updated TA1+TA2 bundle --- but since the leafs shouldn't outlive their CA, they get rolled as well). I.e. at any given point, no one is expected to trust a cert that's outside the validity window.
 
 This makes the only valid(?) use of a self-issued certificate is when it is also signed by a certificate which has the same subject. But the sign-self-issued endpoint does still allow for the new cert's issuer/subject to differ from the current subject.

It does? I have checks in there that should ensure it doesn't, and a unit test besides. Or did you assume this?

Attached is the sample that I used:

* original-root : in vault 0.8.2 just generated with original/root/generate/internal
* new-root-submitted-to-sign-self-signed: in vault 0.8.2 just generated with new/root/generate/internal
* new-root-signed-with-original-via-sign-self-signed : in vault 0.8.2 generated via submitting "new-root-submitted-to-sign-self-signed" to original/root/sign-self-issued  (sorry - term mismatch when I originally ran it)

In this case, it was just fine signing "CN=new one" with "CN=sample" (original).

Off the cuff, do you know of a library that doesn't enforce the issuer<->parent subject naming check?

No, but off the cuff I don't know the details of how various crypto stacks have coded their validation algorithms :-)

I'm only really familiar with openssl and Golang so figured I'd see what diversity we had together...
 
 
For my use case, I've been using the generational "root" certificates. I'll admit that this a constraint that I'm going on based on other descriptions (e.g. Docker's cert rotation: https://docs.docker.com/engine/swarm/how-swarm-mode-works/pki/) but the description is never precise and I can't say if that implementation is exactly that way or not (I'll take a look at the code after some sleep). To that end,  for my use case it might match more to creating a new endpoint - issue-ignoretime or something probably better named - which will sign certs without looking at the time window on them..

Their cert rotation indicates that they just issue a new root and use it as an intermediate until such a time as all of the nodes start using certs issued by that CA. As you said, the details are scarce. If you dig into the code I'd be interested in knowing what they're doing (I may give it a try later but can't at the moment), because it seems like the same problem -- if your new "root" cert is issued by your old root cert, and isn't self-signed, then if clients don't have the old root cert for path validation you should run into a problem. Unless all of the clients carry around all of the previous roots for path validation, and it only "forgets" the root CA to not ever issue more certs from it. And in fact as you note:

The crypto libraries are unhappy because, in the generational model, there's a difference between the issuer on the second cert and the subject of the issuing cert (based on KeyID). If the issuer/subject of the "new" root matches the subject of the old "root", both openssl and golang verify it successfully.

Which makes me wonder if there's some custom validation logic they're doing.

OK, so I can think of a couple of possibilities. They're not necessarily mutually exclusive. Note that we're going to spin a new release soon and this has only been out in 0.8.2 so I'm happy to switch things up and get a fix for this in as well depending on timing.

One possibility would be to put a flag into sign-self-issued that controls whether or not the Issuer uses the CA's issuer DN or the original cert's. If the flag is on this makes it a kind of a "super sign-verbatim" but with a really key difference: because it takes in a cert, not a CSR signed with the private key of the requestor, it means for generating new root certs with the same key you don't need to have access to the CA's private key -- obviously a good thing. *But* if the whole point here is to get a new self-signed root CA with the same key, might as well just not require the certificate to be provided anyways, since the CA knows it, and just provide a desired validity length and a generation number. That wouldn't have the flexibility of using a CSR to specify a custom DN for the new cert, but again, no private key for the CA needed to be exported to generate one. But, a key point here is it doesn't allow for key rotation.

So option 1: rather than sign a self-issued cert, just generate a new cert (but not self-issued/self-signed!) with the same key, different DN, and new validity period. (I would think this would fall afoul of (d) above if the original cert expires, and create an ever-longer validation path, but openssl clearly doesn't care about validity period.)  Also, allow such a cert with the CA's key and the existing CA as the issuer to be uploaded and replace the cert used for that mount.

The option above, if various crypto libraries are happy validating it, is a decent option if you don't want key rotation but just don't want validity to be forever, since it doesn't require knowledge of the private key, and your crypto libraries don't actually care about validity periods when verifying CAs in a validation path.

But, what if you do want key rotation?

Option 2: allow the sign-intermediate endpoint to use a validity period longer than the original CA. Same concerns with respect to (d) but again, if crypto stacks are happy with this, who am I to judge. (I will also note that in orgs where I've worked and in most public CAs the root CAs have lifetimes counted in decades so nobody on the public internet really has to deal with this much.) But, then I have concerns around path length. From the RFC:

      (k)  max_path_length:  this integer is initialized to n, is
           decremented for each non-self-issued certificate in the path,
           and may be reduced to the value in the path length constraint
           field within the basic constraints extension of a CA
           certificate.
"For each non-self-issued certificate". Which suggests that you can have multiple self-issued certificates in a chain, which again, means the Issuer/Subject DNs match, and if the key is the same as the subject it's self-signed, which denotes a root, so you wouldn't have more than one of those in the path. So the RFC here is suggesting that sure, you can totally have self-issued certs in a path (which would be anchored by a trust anchor, which is maybe a self-signed cert but not required and really is a trusted public key). You can see why I thought the way it was implemented was the right thing to do...
So while option 2 is also possible, it probably means that you would want to ensure that your root has no path length constraint, and if your want to go gen1 -> gen2 -> gen3 -> etc. you'd never want to introduce a path length constraint because eventually you'll run out. And, oh, that pesky thing about statement (d) in the algorithm, except openssl ignores and other crypto stacks may or may not ignore.
And of course, option 1, if the Subject/Issuer DNs aren't the same, also potentially has path length concerns.

I think we're trying to account for a variety of use cases which we haven't codified yet. I'm currently seeing 4 practices:

1.) Reissuing an existing CA for key rotation or for policy change: I believe this should behave (minus any potential issue with not matching DNs) as the sign-self-issued is currently. I'm mixed on to if this should allow for certs past their original expiration.

2.) Issuing a rolling chain (my use case). Issuing multiple generations of certificates each of which is built off of the previous generation, but allowed to live longer with the expectation that they will eventually be a ubiquitously shared trust authority and this one will expire out. This is along the lines of the super sign-verbatim.

3.) Cross issuing --- honestly, I'm not sure on this case. I know it's done, but it really seems like an interim step because at some point you have to tell everyone to switch over their intermediate chain. Either way, not sure if there's anything for vault to do for this case -- outside of lifetime.

4.) Just generating new roots with no connection to the old roots other than some partial matching (nothing checked) of the DNs -- and the expectation of having enough time to get root ubiquity for the new roots. Nothing for vault to do special in this case.

I believe #1 and #4 are primary for what public CA do. #3 has definitely been used by public CAs (Let's Encrypt being the poster child). #2 is what I did and that was based off of what is in the docker swarm setup and I *believe* what the Windows Internal CA does (I'm going based off colleague's explanation of behavior --- haven't looked at it myself). So, of these, #2 is really used just for private CAs... I think?

Does that seem like coverage? 

--mac 
original-root
new-root-signed-with-original-via-sign-self-signed
new-root-submitted-to-sign-self-signed

Jeff Mitchell

unread,
Sep 9, 2017, 6:13:01 PM9/9/17
to Vault
Hi Chris,

On Saturday, September 9, 2017 at 7:40:09 AM UTC-7, Jeff Mitchell wrote:
My concerns about step (d) in the path validation is literally, when a certificate in the chain is expired, which is how I originally read the openssl issue, that it would ignore an *expired* certificate.

However, the openssl issue is actually saying that it doesn't validate that the *range* of validity. If you have a CA cert that expires after its root, openssl is fine with that -- and that's as it should be. What the issue doesn't say is that openssl doesn't check validity of each cert in its path against the *current* time.

Odd - when I read that issue, I read it as for signing new certificates, not for validating them (mainly because that was my mental focus at the time... yeah, confirmation bias). I'll admit that I'm not following the use case now. If it's for validating, I feel like I gotta disagree with the issue now ironically, because yeah, that does explicitly go against (d) in the path validation requirement.

I think you read it right the first time. What they're saying is, they're not checking validity *bounds* against CA certs a priori. But I have every reason to believe they are in fact checking the current time against all CAs up the path as they should, I just misread.

This is working in the Golang library - while the original trust anchor certificate is valid. I'll get back two chains, [leaf Intermediate TA2 TA1] and [leaf intermediate TA2], with TA1 and TA2 in the RootPool. I'll get back one chain, [leaf intermediate TA2 TA1] with just TA1 in the RootPool. After TA1 expires, I get [leaf intermediate TA2] even with TA1 in the RootPool.

That sounds correct!
 
The trust anchor is basically what goes into your CA bundle. You're right - it doesn't have to be self issued or self signed. I was hoping to find one in the default for OSX or a linux (I could have sworn I saw it before when troubleshooting an intermediate mismatch issue), but I'm not finding any right now. However, in our private CA bundles, the only issue I've seen with non-si and non-ss is that openssl needs to the -partial_chain option for the verify subcommand (can't say if it's needed outside of the verify command...) to not require that the trust anchor of the candidate chain to be si/ss - and prior to 1.0.2g that option is not there. Golang doesn't have that requirement.

So if I understand what you're saying here, Go doesn't care if the issuer of your CA cert at the root of your available chain is self-signed/issued so long as Go is told that the CA cert is specifically seeded into your root pool (e.g. RootCAs in a tls.Config).

You quoted (a) before, but (b) and (d) are both super important. (b) says certificate 1 is issued by the trust anchor, but it doesn't specify whether issued here means that the Issuer DN encoded in the certificate must match that of a self-signed certificate signing the public key of the trust anchor (which as we found earlier is not a requirement for trusting a public key). So in theory, a certificate with a signature from the right public key and authority key id encoded in the cert ought to be satisfied by the "issued by the trust anchor" line.

Ok - so, yeah, the big item is "what does issued by mean?"

Yep, yep, yep.
 
Reading RFC3820 again, I think 4.1.2.4 locks that one in:

   The issuer field identifies the entity who has signed and issued the
   certificate.  The issuer field MUST contain a non-empty distinguished
   name (DN).

So, even in the case of having a Authority Key ID, the issuer still has to match a subject field. This kinda makes the situation of self-signed and self-issued to be one and the same at least with respect to the Issuer DN.

Great, now we have to parse "entity", because: 
 
 
Clarifying the Authority Key ID (4.2.1.1):

   This extension is used where an issuer has
   multiple signing keys (either due to multiple concurrent key pairs or
   due to changeover).

So, it's not so much to allow for endorsing a certificate without "issuing" it, but for allowing a CA to distinguish between multiple signing keys/certs all of which have the same Subject DN.

But wait! If they all have the same Subject DN, and if they have the same issuer but have "multiple concurrent key pairs", don't you have the exact scenario that sign-self-issued creates? Where it's the same exact entity and the same CA but they may be signed by different keys? :-D

X509...
 
In the event of just a key rollover, yes, it would not be a self-signed cert, but it would be self-issued and signed with the original which has a matching subject DN. The original cert (assuming it was set as a root) would have just the Subject Key Identifier. The second cert would have the Authority Key Identifier pointing to the SKI of the original cert, it's own SKI pointing to itself, and the Subject DN and Issuer DN would be the same and matching the Subject DN of the original cert.

I think it's coming down to --- issuers are strictly based on the DN.

Agreed...I still think this isn't strictly mandated by the RFC, but the fact that I'm not sure after all this suggests that the spec is not clearly written (what?!?) and anyways, what really matters in the end is what stacks are willing to validate.
 
See use case with the TA1 and TA2 above. The driver behind what I'm doing is trying to keep everything working while doing a "root" (I'm just going to keep saying TA at this point) roll. So, before TA1 expires, I issue TA2 and start issuing certs that chain up to that. Anyone with only TA1 in their CA bundle will trust the new chains because they can draw from TA2->TA1; anyone with both TA1 and TA2 in their bundle are just find an dandy either way because it has two valid cert chains it can try. I have until TA1 expires to make sure that everyone is rolled (at least, every leaf node has the updated TA1+TA2 bundle --- but since the leafs shouldn't outlive their CA, they get rolled as well). I.e. at any given point, no one is expected to trust a cert that's outside the validity window.

Great. Totally with you :-)
 
Attached is the sample that I used:

* original-root : in vault 0.8.2 just generated with original/root/generate/internal
* new-root-submitted-to-sign-self-signed: in vault 0.8.2 just generated with new/root/generate/internal
* new-root-signed-with-original-via-sign-self-signed : in vault 0.8.2 generated via submitting "new-root-submitted-to-sign-self-signed" to original/root/sign-self-issued  (sorry - term mismatch when I originally ran it)

In this case, it was just fine signing "CN=new one" with "CN=sample" (original).

Ah, no -- remember, what that endpoint does is sign a *self-issued* certificate. What it checks is that the DN and Subject of the certificate *to be signed* are the same. It doesn't matter if there's a match with the signing cert. In your case, since you submitted a self-signed certificate for signing, they matched.
 
I think we're trying to account for a variety of use cases which we haven't codified yet. I'm currently seeing 4 practices:

1.) Reissuing an existing CA for key rotation or for policy change: I believe this should behave (minus any potential issue with not matching DNs) as the sign-self-issued is currently. I'm mixed on to if this should allow for certs past their original expiration.

Hang a moment -- if you're reissuing an existing CA for key rotation or policy change this actually doesn't help as-is. Because unless you're reissuing for the *same key* then there will be a difference in the signer vs the encoded subject key id which is what was causing issues for you.

Now you *could* use this endpoint to issue new certificates for the same CA *with the same key* by presenting your existing self-signed certificate with that key. But that won't account for policy change (currently). So that puts the usefulness of the whole thing into question.
 
2.) Issuing a rolling chain (my use case). Issuing multiple generations of certificates each of which is built off of the previous generation, but allowed to live longer with the expectation that they will eventually be a ubiquitously shared trust authority and this one will expire out. This is along the lines of the super sign-verbatim.

This makes sense, and allowing it to be issued for longer also makes sense if you are doing partial validation. Two thoughts:

* In this case it probably makes sense to generate the new one as an intermediate and then get it signed. What I'm not sure about is whether it should be its own endpoint or not. The existing endpoint is essentially designed for the admin to have a lot of flexibility over the csr they're signing, but you can of course use use_csr_values. So for your use case it seems like you really do just need to use that endpoint but have the restriction on NotAfter ignored if the requested TTL is higher.

* On the other hand, if all you really want is to just sign exactly what you're given, including the lifetime, you could issue the cert you want and then ask it to be signed -- exactly what the sign-self-issued endpoint does, but with adjusting the Issuer DN.
 
3.) Cross issuing --- honestly, I'm not sure on this case. I know it's done, but it really seems like an interim step because at some point you have to tell everyone to switch over their intermediate chain. Either way, not sure if there's anything for vault to do for this case -- outside of lifetime.

I think you're right.

4.) Just generating new roots with no connection to the old roots other than some partial matching (nothing checked) of the DNs -- and the expectation of having enough time to get root ubiquity for the new roots. Nothing for vault to do special in this case.

I believe #1 and #4 are primary for what public CA do. #3 has definitely been used by public CAs (Let's Encrypt being the poster child). #2 is what I did and that was based off of what is in the docker swarm setup and I *believe* what the Windows Internal CA does (I'm going based off colleague's explanation of behavior --- haven't looked at it myself). So, of these, #2 is really used just for private CAs... I think?

Does that seem like coverage? 

Sure.

So, I'm a bit torn as to the right thing to do, in large part because I think signing an existing cert can be useful and don't want to mandate a CSR, but for people getting a CSR from a new root and wanting that signed I want them to be able to as well.

That said, possibilities:

1) Change sign-self-issued so that it puts the root CA's subject DN in the Issuer DN of the new cert. IOW it will keep the certificate as-is, just as it does now, but use the root CA as the parent cert (to update the Issuer info) rather than using the same cert for both parent/child and just changing the signing key. An open question with this approach is whether it then makes sense to require self-issued certs as a check, or whether it should just generally sign any cert that you're asking it to. Certainly that behavior could be relaxed later.

2) Add the ability to just give a certificate to sign-intermediate in addition to a CSR, so you can do whichever you want, but I'm not sure whether the use_csr_values approach is the right thing there because it does slightly modify things (ensures a basic set of key usages, for instance...sign-self-issued only updates configured URL values for issuing/ocsp/crls, which I thought made sense). And I'm a bit leery of adding both the ability to pop in a cert *and* a use_cert_values with a different meaning.

3) Allow sign-intermediate to issue child CAs with validity dates past the root CA if requested in the TTL parameter, defaulting (if no TTL is given) to the lifetime of the root CA cert.

What I'm leaning towards for now is to make the changes in #1 and #3. For #1, you submit your desired new self-issued CA and what you get out is a cert signed (in the normal sense, with the changed Issuer) by the root that otherwise keeps all other values intact (except, also, for the URLs, as mentioned before, and which I think make sense). You generate it with your desired subject, validity dates, etc.

I believe between these two it would would fix your use case and indeed most use-cases we've discussed. If there's demonstrated need to allow signing other certs (as opposed to CSRs) that aren't self-issued that could be added later to either sign-self-issued (maybe renaming in the process) or sign-intermediate.

Thoughts?

Best,
Jeff

Chris McEniry

unread,
Sep 10, 2017, 1:09:44 AM9/10/17
to Vault
The trust anchor is basically what goes into your CA bundle. You're right - it doesn't have to be self issued or self signed. I was hoping to find one in the default for OSX or a linux (I could have sworn I saw it before when troubleshooting an intermediate mismatch issue), but I'm not finding any right now. However, in our private CA bundles, the only issue I've seen with non-si and non-ss is that openssl needs to the -partial_chain option for the verify subcommand (can't say if it's needed outside of the verify command...) to not require that the trust anchor of the candidate chain to be si/ss - and prior to 1.0.2g that option is not there. Golang doesn't have that requirement.

So if I understand what you're saying here, Go doesn't care if the issuer of your CA cert at the root of your available chain is self-signed/issued so long as Go is told that the CA cert is specifically seeded into your root pool (e.g. RootCAs in a tls.Config).

Yup. Golang makes no additional checks about it being specifically self signed, just as long as the issuer is in the RootCAs cert pool. OpenSSL also seems good with this *if* the partial_chains option is present.
 
Clarifying the Authority Key ID (4.2.1.1):

   This extension is used where an issuer has
   multiple signing keys (either due to multiple concurrent key pairs or
   due to changeover).

So, it's not so much to allow for endorsing a certificate without "issuing" it, but for allowing a CA to distinguish between multiple signing keys/certs all of which have the same Subject DN.

But wait! If they all have the same Subject DN, and if they have the same issuer but have "multiple concurrent key pairs", don't you have the exact scenario that sign-self-issued creates? Where it's the same exact entity and the same CA but they may be signed by different keys? :-D

Multiple signing keys is fine. One might have a case where there's keys geographically distributed or multiple keys for other reasons.

Going back to Go for a reference implementation, both CertPool.byName and CertPool.SubjectKeyId return a slice, so it expects to be able to return multiple ones. This might crop up as a problem if you have to attach a TA to a chain (e.g. TA2 in my example case), but from what I'm seeing of the behavior and reading, Golang doesn't care about it --- as long as it can construct even one valid chain, it's happy. Can't speak to other implementations. 

In the event of just a key rollover, yes, it would not be a self-signed cert, but it would be self-issued and signed with the original which has a matching subject DN. The original cert (assuming it was set as a root) would have just the Subject Key Identifier. The second cert would have the Authority Key Identifier pointing to the SKI of the original cert, it's own SKI pointing to itself, and the Subject DN and Issuer DN would be the same and matching the Subject DN of the original cert.

I think it's coming down to --- issuers are strictly based on the DN.

Agreed...I still think this isn't strictly mandated by the RFC, but the fact that I'm not sure after all this suggests that the spec is not clearly written (what?!?) and anyways, what really matters in the end is what stacks are willing to validate.

I'm of the mind that it can be pieced together from the RFCs. The Issuer field and the references to (Issuer of one is Subject of other) seem pretty close. Could be little-I issuer versus big-I Issuer, but.... <shrug>

Attached is the sample that I used:

* original-root : in vault 0.8.2 just generated with original/root/generate/internal
* new-root-submitted-to-sign-self-signed: in vault 0.8.2 just generated with new/root/generate/internal
* new-root-signed-with-original-via-sign-self-signed : in vault 0.8.2 generated via submitting "new-root-submitted-to-sign-self-signed" to original/root/sign-self-issued  (sorry - term mismatch when I originally ran it)

In this case, it was just fine signing "CN=new one" with "CN=sample" (original).

Ah, no -- remember, what that endpoint does is sign a *self-issued* certificate. What it checks is that the DN and Subject of the certificate *to be signed* are the same. It doesn't matter if there's a match with the signing cert. In your case, since you submitted a self-signed certificate for signing, they matched.

Ah - sorry. My original comment for a warning or error was if the Issuer/Subject of the cert to be signed didn't match the Subject of the CA/signing cert in the pki mount. Sorry if I wasn't clear about that. 

I think we're trying to account for a variety of use cases which we haven't codified yet. I'm currently seeing 4 practices:

1.) Reissuing an existing CA for key rotation or for policy change: I believe this should behave (minus any potential issue with not matching DNs) as the sign-self-issued is currently. I'm mixed on to if this should allow for certs past their original expiration.

Hang a moment -- if you're reissuing an existing CA for key rotation or policy change this actually doesn't help as-is. Because unless you're reissuing for the *same key* then there will be a difference in the signer vs the encoded subject key id which is what was causing issues for you.

Now you *could* use this endpoint to issue new certificates for the same CA *with the same key* by presenting your existing self-signed certificate with that key. But that won't account for policy change (currently). So that puts the usefulness of the whole thing into question.

A Subject can have multiple Subject Key IDs for it (multiple keys for the same entity), but a Subject Key ID should (probably?) only have one certificate for it and by that only point to one Subject.

In the case where the signing key is rotated, it does still work because the Subject doesn't change - just that there's another Subject Key ID that points to the same Subject.

I ran into the issue with "CN=new one" and "CN=sample" because the Issuer field of the "CN=new one" as issue/signed by "CN=sample" wasn't set to "CN=sample" - it was still the self-issued "CN=new one". In this case, the Golang library was able to find the signer cert by Authority Key ID but the Subject of that cert didn't match this cert's issuer and so that broke rule (a).

2.) Issuing a rolling chain (my use case). Issuing multiple generations of certificates each of which is built off of the previous generation, but allowed to live longer with the expectation that they will eventually be a ubiquitously shared trust authority and this one will expire out. This is along the lines of the super sign-verbatim.

This makes sense, and allowing it to be issued for longer also makes sense if you are doing partial validation. Two thoughts:

* In this case it probably makes sense to generate the new one as an intermediate and then get it signed. What I'm not sure about is whether it should be its own endpoint or not. The existing endpoint is essentially designed for the admin to have a lot of flexibility over the csr they're signing, but you can of course use use_csr_values. So for your use case it seems like you really do just need to use that endpoint but have the restriction on NotAfter ignored if the requested TTL is higher.

* On the other hand, if all you really want is to just sign exactly what you're given, including the lifetime, you could issue the cert you want and then ask it to be signed -- exactly what the sign-self-issued endpoint does, but with adjusting the Issuer DN.

Yeah, either of these would handle the use case I'm using.

I'm not sure on the terminology. Root and Intermediate seem to be incompletely defined with respect to how they're general used. Based on the "it's not si/ss" part, all certs after the first one will be Intermediate, but they're also going to be TAs so that kinda makes them Roots. Bleh... language is hard. That being said, I think calling them intermediates does fit the principle of least surprise (not *no* surprise, not *intuitive*, just *least*). If I think "what do I need to do?" I'll eventually get to "I should sign it like an intermediate since it's not si/ss."
 
I think the "sign-self-issued" name maybe should be something a bit more based on purpose (though that does feel like a trap). "reissue-self" or something like that seems to be a bit more on for its use cases.

4.) Just generating new roots with no connection to the old roots other than some partial matching (nothing checked) of the DNs -- and the expectation of having enough time to get root ubiquity for the new roots. Nothing for vault to do special in this case.

I believe #1 and #4 are primary for what public CA do. #3 has definitely been used by public CAs (Let's Encrypt being the poster child). #2 is what I did and that was based off of what is in the docker swarm setup and I *believe* what the Windows Internal CA does (I'm going based off colleague's explanation of behavior --- haven't looked at it myself). So, of these, #2 is really used just for private CAs... I think?

Does that seem like coverage? 

Sure.

So, I'm a bit torn as to the right thing to do, in large part because I think signing an existing cert can be useful and don't want to mandate a CSR, but for people getting a CSR from a new root and wanting that signed I want them to be able to as well.

This is enough of a edge case that I think it might be ok to force the CSR. There's generally two categories here:

1.) If you've gotten to this point, you probably have done some general cert management before and know how to get a CSR - in fact, that's probably the way you're used to getting a Cert. "Well, I've got a si/ss root cert but I want to get it signed with this other root cert too. Oh, the docs say I need a CSR to be signed... oh, I've done that before so... ok." Depending on how you did it (single liner openssl req, or multiple step), you might even have a CSR you can use.

2.) If you've been through having to handle root rolling, you probably have an idea of the complexities and getting a CSR is the least of your concerns ;).

The downside of the CSR is that it does rely on other signalling for "how long should I issue this?" and "should vault force expiry before the CA?" since the CSR doesn't have the NotBefore nor the NotAfter fields.
 
That said, possibilities:

1) Change sign-self-issued so that it puts the root CA's subject DN in the Issuer DN of the new cert. IOW it will keep the certificate as-is, just as it does now, but use the root CA as the parent cert (to update the Issuer info) rather than using the same cert for both parent/child and just changing the signing key. An open question with this approach is whether it then makes sense to require self-issued certs as a check, or whether it should just generally sign any cert that you're asking it to. Certainly that behavior could be relaxed later.

In a way, I feel like this is only for use case #3 (cross signing). When dealing with a rolling TA, I never actually issue a ss/si cert because it starts life expecting to be Issued by its predecessor.
 
2) Add the ability to just give a certificate to sign-intermediate in addition to a CSR, so you can do whichever you want, but I'm not sure whether the use_csr_values approach is the right thing there because it does slightly modify things (ensures a basic set of key usages, for instance...sign-self-issued only updates configured URL values for issuing/ocsp/crls, which I thought made sense). And I'm a bit leery of adding both the ability to pop in a cert *and* a use_cert_values with a different meaning.

Yeah.... this one doesn't feel quite right.
 
3) Allow sign-intermediate to issue child CAs with validity dates past the root CA if requested in the TTL parameter, defaulting (if no TTL is given) to the lifetime of the root CA cert.

I think this is good, but I do think it should give a warning when the TTL is past the root expiry. This seems like one of those "Are you sure you really mean for it to do this?" ;)
 
What I'm leaning towards for now is to make the changes in #1 and #3. For #1, you submit your desired new self-issued CA and what you get out is a cert signed (in the normal sense, with the changed Issuer) by the root that otherwise keeps all other values intact (except, also, for the URLs, as mentioned before, and which I think make sense). You generate it with your desired subject, validity dates, etc.

I believe between these two it would would fix your use case and indeed most use-cases we've discussed. If there's demonstrated need to allow signing other certs (as opposed to CSRs) that aren't self-issued that could be added later to either sign-self-issued (maybe renaming in the process) or sign-intermediate.

Thoughts?

Given my preference, I think #3 makes the most sense, but certainly can work within #1.

Maybe it shouldn't be "sign-intermediate" but it should be "sign-ca" since that can mean "intermediate" or "something that will become a TA" or "reissue me" in all these cases, the CA is the crux. Taking an adoption/transition part out of the picture, that's really what this is for, right? If I submit to sign-ca with a CSR, then I'm expecting it to have the Issuer set to this CA. If I submit with a ss/si cert, then I expect it to be cross signed or (if DN matches this CA) a re-issuance with policy/key changes. I think it's a bit of do you want multiple explicit endpoints (and let's just then call them out what they're for - "reissue", "sign-TA", "sign-intermediate", "cross-sign") or a single endpoint that has different behavior based on what's fed into it?

Thanks,
--mac

Jeff Mitchell

unread,
Sep 10, 2017, 11:22:55 AM9/10/17
to Vault
Hi Chris,

On Sun, Sep 10, 2017 at 1:09 AM, Chris McEniry <mce...@gmail.com> wrote:
I'm of the mind that it can be pieced together from the RFCs. The Issuer field and the references to (Issuer of one is Subject of other) seem pretty close. Could be little-I issuer versus big-I Issuer, but.... <shrug>

Little-i issuer vs bit-I issuer is a constant source of frustration for me on that RFC, because big I can generally be taken to mean Issuer DN, but little i means...issuer DN? The entity performing the issuing? (And if so, define entity?)

I'm not sure on the terminology. Root and Intermediate seem to be incompletely defined with respect to how they're general used. Based on the "it's not si/ss" part, all certs after the first one will be Intermediate, but they're also going to be TAs so that kinda makes them Roots. Bleh... language is hard. That being said, I think calling them intermediates does fit the principle of least surprise (not *no* surprise, not *intuitive*, just *least*). If I think "what do I need to do?" I'll eventually get to "I should sign it like an intermediate since it's not si/ss."

Right -- technically in the RFC, any CA certificate that is neither self-issued or self-signed (again, note the terminology, and so if you have a self-issued cert that isn't self-signed then the Issuer/Subject DNs need to match but the authority key ID would be different.......but in practice nobody will validate these as we've found, sigh) is a cross certificate. Which means technically any intermediate is a cross certificate, but in practice people usually mean intermediate to be "not currently the root of the validation chain" and cross to mean <insert X, Y, or Z here> so I went with sign-intermediate just to be a bit less confusing and indicate that it's signing a certificate that is further down the path.

I think the "sign-self-issued" name maybe should be something a bit more based on purpose (though that does feel like a trap). "reissue-self" or something like that seems to be a bit more on for its use cases.

Maybe. I'm not opposed to renaming it. Will hold off on further comments about this until later since this is easy to do at any time and just alias for backwards compatibility as needed:
 
This is enough of a edge case that I think it might be ok to force the CSR. There's generally two categories here:

1.) If you've gotten to this point, you probably have done some general cert management before and know how to get a CSR - in fact, that's probably the way you're used to getting a Cert. "Well, I've got a si/ss root cert but I want to get it signed with this other root cert too. Oh, the docs say I need a CSR to be signed... oh, I've done that before so... ok." Depending on how you did it (single liner openssl req, or multiple step), you might even have a CSR you can use.

2.) If you've been through having to handle root rolling, you probably have an idea of the complexities and getting a CSR is the least of your concerns ;).

My concern here is a bit more of the Vault-specific use-case where you may well have generated the original CA internally and never exposed the key and want to continue not exposing the key. So if you want to then sign a CSR you need a way to tell the CA to spit out a CSR to then sign. If you're using a different key, it'd be a different Vault mount, and you can just use the intermediate generation step. The reason to then spit out a CSR only comes down to when you want to use the same key, and in that case, I don't see what spitting out a CSR buys you over simply using the existing CA cert itself and its encoded subject public key.

Only in the case where you want to modify parameters on the CSR does this become useful, and then that requires a *lot* more API (although some could be shared with the intermediate case).

That all said, I question whether this is all that useful because even then such a cert would have to be added to your roots. And in that case, you may as well just use another key and do some key rolling while you're at it. I wonder if this currently a solution looking for a practical use-case, basically.

So, what utility does sign-self-issued have now that the validation issues are in the way? I do actually see a potentially useful workflow, given the following:

* You want to switch to a new root with a new key so want to (let's call it) cross-sign
* You want to configure the root exactly as you want it, without having to worry about CSR values being ignored/modified (such as key usage, lifetime, etc.)

The workflow would be:

1) Create a new Vault mount
2) Generate a new root cert/key pair with the customizations you want
3) Feed the new CA certificate to the old CA certificate's sign-self-issued, which will touch nothing but the Issuer DN, authority key ID, and URLs, but leave all other values (including path-length) alone
4) Feed the signed certificate back to the new mount; set-signed should work for this even though it's in the intermediate path at /pki/intermediate/set-signed, could just also put that in the root path at /pki/root/set-signed

Given my preference, I think #3 makes the most sense, but certainly can work within #1.

Maybe it shouldn't be "sign-intermediate" but it should be "sign-ca" since that can mean "intermediate" or "something that will become a TA" or "reissue me" in all these cases, the CA is the crux. Taking an adoption/transition part out of the picture, that's really what this is for, right? If I submit to sign-ca with a CSR, then I'm expecting it to have the Issuer set to this CA. If I submit with a ss/si cert, then I expect it to be cross signed or (if DN matches this CA) a re-issuance with policy/key changes. I think it's a bit of do you want multiple explicit endpoints (and let's just then call them out what they're for - "reissue", "sign-TA", "sign-intermediate", "cross-sign") or a single endpoint that has different behavior based on what's fed into it?

I think there's a chance for some consolidation or revamping/renaming later, but for now the terminology works for most people, and I have found from discussions in the past that people like having the sort of understood separation of workflows for root/intermediate. You're right that it's really sign-ca, but looking at it another way, whether you end up popping the resulting cert into your roots or not, it's still a child of your existing/signing root so it's not super wrong to call it an intermediate at the time of issuance.

OK, that all said, I have a favor to ask. Any chance I can get you to re-run the validation checks you ran before using the self-issued-wrong-pubkey branch? I realized as I was writing this that there's actually a bug in the code in that endpoint -- it's passing in the signing bundle's public key instead of the subject's public key. I can totally believe this would affect validation -- it should :-)  So then the question becomes is validation actually working (which according to the class of self-issued CA certs in the RFC it feels like it *ought* to) with the right public key being signed :-/ In which case it's a simple bug fix for the endpoint to already do what you need!

I've put an amd64 build of Vault with this fix at https://drive.google.com/open?id=0B9k5inNzLXz2N3BhU3VXQVNMVW8 (filename: vault-272cffe5fc2f51affb152fb41a2547ec33d8a805.bz2 sha256: b75af83d297930d27cc3936c7475bc0d99a360de3fdbe3d17f002c3282bfb5ce)

Best,
Jeff

 

Thanks,
--mac

--
This mailing list is governed under the HashiCorp Community Guidelines - https://www.hashicorp.com/community-guidelines.html. Behavior in violation of those guidelines may result in your removal from this mailing list.
 
GitHub Issues: https://github.com/hashicorp/vault/issues
IRC: #vault-tool on Freenode
---
You received this message because you are subscribed to the Google Groups "Vault" group.
To unsubscribe from this group and stop receiving emails from it, send an email to vault-tool+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/vault-tool/887eeb91-803d-4539-b735-655ce3c8f00d%40googlegroups.com.

Chris McEniry

unread,
Sep 12, 2017, 1:41:23 AM9/12/17
to Vault
OK, that all said, I have a favor to ask. Any chance I can get you to re-run the validation checks you ran before using the self-issued-wrong-pubkey branch? I realized as I was writing this that there's actually a bug in the code in that endpoint -- it's passing in the signing bundle's public key instead of the subject's public key. I can totally believe this would affect validation -- it should :-)  So then the question becomes is validation actually working (which according to the class of self-issued CA certs in the RFC it feels like it *ought* to) with the right public key being signed :-/ In which case it's a simple bug fix for the endpoint to already do what you need!

I've put an amd64 build of Vault with this fix at https://drive.google.com/open?id=0B9k5inNzLXz2N3BhU3VXQVNMVW8 (filename: vault-272cffe5fc2f51affb152fb41a2547ec33d8a805.bz2 sha256: b75af83d297930d27cc3936c7475bc0d99a360de3fdbe3d17f002c3282bfb5ce)

(Processing the rest of this, but figured I could run this test...)

I'm getting a different signature on that binary:
$ shasum -a 256 ../Desktop/vault/vault-272cffe5fc2f51affb152fb41a2547ec33d8a805
9bd2d00a913bbe7c90210d4221b8629ba37b4c275280ee4da69af34f7cf94e0c  ../Desktop/vault/vault-272cffe5fc2f51affb152fb41a2547ec33d8a805
I went ahead and ran the tests, so... For the record, this was client on darwin, server in a docker container on the stable docker4mac.

I'm still getting a verification error (name mismatch) for both Golang and OpenSSL:

$ go run vault-test-20170911-test.go
panic: x509: issuer name does not match subject from issuing certificate

goroutine 1 [running]:
main.main()
/Users/cmceniry/Downloads/vault-test-20170911-test.go:42 +0x40c
exit status 2
$ openssl verify -CAfile ./vault-test-20170911-pkia vault-test-20170911-pkib-signed-by-pkia
vault-test-20170911-pkib-signed-by-pkia: /CN=pki b
error 20 at 0 depth lookup:unable to get local issuer certificate

Attached are the test files I used, but here's the short of how I got them.

vault mount -path pkia pki
vault mount -path pkib pki
vault write pkia/root/generate/internal common_name="pki a"
vault read pkia/cert/ca ====> vault-test-20170911-pkia
vault write pkib/root/generate/internal common_name="pki b"
vault read pkib/cert/ca ====> vault-test-20170911-pkib-original
write pkia/root/sign-self-issued certificate=@vault-test-20170911-pkib-original =====> write pkia/root/sign-self-issued certificate=@vault-test-20170911-pkib-original

--mac
vault-test-20170911.tgz

Chris McEniry

unread,
Sep 12, 2017, 1:46:26 AM9/12/17
to Vault
$ openssl verify -CAfile ./vault-test-20170911-pkia vault-test-20170911-pkib-signed-by-pkia
 
write pkia/root/sign-self-issued certificate=@vault-test-20170911-pkib-original =====> write pkia/root/sign-self-issued certificate=@vault-test-20170911-pkib-original

Sorry, that should be

 
write pkia/root/sign-self-issued certificate=@vault-test-20170911-pkib-original =====> vault-test-20170911-pkib-signed-by-pkia

to match the file and what's on the OpenSSL line... 

--mac

Jeff Mitchell

unread,
Sep 12, 2017, 10:10:05 AM9/12/17
to Vault
Hi mac,

Odd about the shasum. I used sha256sum and maybe it required -b?

Thanks for testing again. (Normally I'd not be shoving such basic testing off to you, please believe me when I say "HashiConf" as an excuse for being lazy about this at the current time.)

It's too bad the bug fix doesn't solve the issuer problem, so I guess we'll have to proceed with the previously discussed changes. What I was hoping was that by using the right public key to be signed it might fix any validation errors caused by the subject key id being incorrect.

We'll get the changes in for 0.8.3.

Best,
Jeff

--
This mailing list is governed under the HashiCorp Community Guidelines - https://www.hashicorp.com/community-guidelines.html. Behavior in violation of those guidelines may result in your removal from this mailing list.
 
GitHub Issues: https://github.com/hashicorp/vault/issues
IRC: #vault-tool on Freenode
---
You received this message because you are subscribed to the Google Groups "Vault" group.
To unsubscribe from this group and stop receiving emails from it, send an email to vault-tool+unsubscribe@googlegroups.com.

Jeff Mitchell

unread,
Sep 12, 2017, 12:18:28 PM9/12/17
to Vault
PR is up here, for anyone interested:

Chris McEniry

unread,
Sep 12, 2017, 12:51:16 PM9/12/17
to Vault

PR is up here, for anyone interested:

 
Odd about the shasum. I used sha256sum and maybe it required -b?

Sigh... I was checksumming the expanded binary, not the bz2.  The checksum matches on the compressed file.

Thanks for testing again. (Normally I'd not be shoving such basic testing off to you, please believe me when I say "HashiConf" as an excuse for being lazy about this at the current time.)

No worries. I can't ask for something and expect to not have to work for it.
 
It's too bad the bug fix doesn't solve the issuer problem, so I guess we'll have to proceed with the previously discussed changes. What I was hoping was that by using the right public key to be signed it might fix any validation errors caused by the subject key id being incorrect.

Is the PR above the same code that's in the engineering build (272cffe5)? Just looking over the code and my test, pkib is self-issued but not self-signed. The PR's pathCASignSelfIssued does change the Issuer DN on it. Just trying to follow if I missed an update or am misreading something. 

We'll get the changes in for 0.8.3.

Awesome. The past-expiry-intermediate will certainly work for me, so thank you!

--mac

Jeff Mitchell

unread,
Sep 12, 2017, 1:12:26 PM9/12/17
to Vault
Hi Mac,

The PR is *not* the same code that is in the engineering build -- the build I sent before was only the first commit in the PR, and not the latter.

As you can see there are unit tests added, but if you test yourself, please let us know how it goes :-)

Best,
Jeff


--mac

--
This mailing list is governed under the HashiCorp Community Guidelines - https://www.hashicorp.com/community-guidelines.html. Behavior in violation of those guidelines may result in your removal from this mailing list.
 
GitHub Issues: https://github.com/hashicorp/vault/issues
IRC: #vault-tool on Freenode
---
You received this message because you are subscribed to the Google Groups "Vault" group.
To unsubscribe from this group and stop receiving emails from it, send an email to vault-tool+unsubscribe@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages