ommit issuer by default if cert has AIA

this adds a new --include-certs=-3 option (default) that includes the
chain except for the root (as per gpgsm). The difference is that each
issuer in the chain will be omitted if the cert has the AIA extension
specifying where to download the issuer.
This commit is contained in:
Ben Toews 2018-07-26 11:15:20 -06:00
Родитель 59baa0932a
Коммит b7cd4ea23c
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: E9C423BE17EFEE70
7 изменённых файлов: 120 добавлений и 15 удалений

2
Gopkg.lock сгенерированный
Просмотреть файл

@ -34,7 +34,7 @@
branch = "master"
name = "github.com/mastahyeti/fakeca"
packages = ["."]
revision = "cb55136fa97a78a6e6cf8bdc483c30fe2eb562e6"
revision = "5f91b32d1226951f9631342eee20f5142387a216"
[[projects]]
branch = "master"

Просмотреть файл

@ -122,11 +122,21 @@ func findUserIdentity() (certstore.Identity, error) {
// certsForSignature determines which certificates to include in the signature
// based on the --include-certs option specified by the user.
func certsForSignature(chain []*x509.Certificate) []*x509.Certificate {
if *includeCertsOpt <= -2 {
if hasRoot := bytes.Equal(chain[len(chain)-1].RawIssuer, chain[len(chain)-1].RawSubject); hasRoot {
return chain[0 : len(chain)-1]
if *includeCertsOpt <= -3 {
for i := len(chain) - 1; i > 0; i-- {
issuer, cert := chain[i], chain[i-1]
// remove issuer when cert has AIA extension
if bytes.Equal(issuer.RawSubject, cert.RawIssuer) && len(cert.IssuingCertificateURL) > 0 {
chain = chain[0:i]
}
}
return chain
return chainWithoutRoot(chain)
}
if *includeCertsOpt == -2 {
return chainWithoutRoot(chain)
}
if *includeCertsOpt == -1 {
@ -140,3 +150,21 @@ func certsForSignature(chain []*x509.Certificate) []*x509.Certificate {
return chain[0:include]
}
// Returns the provided chain, having removed the root certificate, if present.
// This includes removing the cert itself if the chain is a single self-signed
// cert.
func chainWithoutRoot(chain []*x509.Certificate) []*x509.Certificate {
if len(chain) == 0 {
return chain
}
lastIdx := len(chain) - 1
last := chain[lastIdx]
if bytes.Equal(last.RawIssuer, last.RawSubject) {
return chain[0:lastIdx]
}
return chain
}

Просмотреть файл

@ -21,6 +21,25 @@ func TestSign(t *testing.T) {
require.NoError(t, err)
}
func TestSignIncludeCertsAIA(t *testing.T) {
defer testSetup(t, "--sign", "-u", certHexFingerprint(aiaLeaf.Certificate))()
stdinBuf.WriteString("hello, world!")
commandSign()
ci, err := protocol.ParseContentInfo(stdoutBuf.Bytes())
require.NoError(t, err)
sd, err := ci.SignedDataContent()
require.NoError(t, err)
certs, err := sd.X509Certificates()
require.NoError(t, err)
require.Equal(t, 1, len(certs))
require.True(t, certs[0].Equal(aiaLeaf.Certificate))
}
func TestSignIncludeCertsDefault(t *testing.T) {
defer testSetup(t, "--sign", "-u", certHexFingerprint(leaf.Certificate))()
@ -41,6 +60,26 @@ func TestSignIncludeCertsDefault(t *testing.T) {
require.True(t, certs[1].Equal(intermediate.Certificate))
}
func TestSignIncludeCertsMinus3(t *testing.T) {
defer testSetup(t, "--sign", "--include-certs=-3", "-u", certHexFingerprint(leaf.Certificate))()
stdinBuf.WriteString("hello, world!")
commandSign()
ci, err := protocol.ParseContentInfo(stdoutBuf.Bytes())
require.NoError(t, err)
sd, err := ci.SignedDataContent()
require.NoError(t, err)
certs, err := sd.X509Certificates()
require.NoError(t, err)
require.Equal(t, 2, len(certs))
require.True(t, certs[0].Equal(leaf.Certificate))
require.True(t, certs[1].Equal(intermediate.Certificate))
}
func TestSignIncludeCertsMinus2(t *testing.T) {
defer testSetup(t, "--sign", "--include-certs=-2", "-u", certHexFingerprint(leaf.Certificate))()

Просмотреть файл

@ -28,7 +28,7 @@ var (
statusFdOpt = getopt.IntLong("status-fd", 0, -1, "write special status strings to the file descriptor n.", "n")
keyFormatOpt = getopt.EnumLong("keyid-format", 0, []string{"long"}, "long", "select how to display key IDs.", "{long}")
tsaOpt = getopt.StringLong("timestamp-authority", 't', defaultTSA, "URL of RFC3161 timestamp authority to use for timestamping")
includeCertsOpt = getopt.IntLong("include-certs", 0, -2, "-2 includes all certs except root. -1 includes all certs. 0 includes no certs. 1 includes leaf cert. >1 includes n from the leaf. Default -2.")
includeCertsOpt = getopt.IntLong("include-certs", 0, -3, "-3 is the same as -2, but ommits issuer when cert has Authority Information Access extension. -2 includes all certs except root. -1 includes all certs. 0 includes no certs. 1 includes leaf cert. >1 includes n from the leaf. Default -3.")
// Remaining arguments
fileArgs []string

Просмотреть файл

@ -17,7 +17,10 @@ var (
ca = fakeca.New(fakeca.IsCA)
intermediate = ca.Issue(fakeca.IsCA)
leaf = intermediate.Issue()
wrappedLeaf = identity{leaf}
aiaLeaf = intermediate.Issue(fakeca.IssuingCertificateURL("http://foo"))
wrappedLeaf = identity{leaf}
wrappedAIALeaf = identity{aiaLeaf}
)
// make *fakeca.Identity implement certstore.Identity
@ -100,7 +103,10 @@ func testSetup(t *testing.T, args ...string) func() {
getopt.CommandLine.Parse(append([]string{"smimesign"}, args...))
idents = []certstore.Identity{wrappedLeaf}
idents = []certstore.Identity{
wrappedLeaf,
wrappedAIALeaf,
}
return resetFunc
}

33
vendor/github.com/mastahyeti/fakeca/configuration.go сгенерированный поставляемый
Просмотреть файл

@ -13,13 +13,15 @@ import (
)
type configuration struct {
subject *pkix.Name
issuer *Identity
nextSN *int64
priv *crypto.Signer
isCA bool
notBefore *time.Time
notAfter *time.Time
subject *pkix.Name
issuer *Identity
nextSN *int64
priv *crypto.Signer
isCA bool
notBefore *time.Time
notAfter *time.Time
issuingCertificateURL []string
ocspServer []string
}
func (c *configuration) generate() *Identity {
@ -29,6 +31,8 @@ func (c *configuration) generate() *Identity {
BasicConstraintsValid: true,
NotAfter: c.getNotAfter(),
NotBefore: c.getNotBefore(),
IssuingCertificateURL: c.issuingCertificateURL,
OCSPServer: c.ocspServer,
}
var (
@ -206,6 +210,21 @@ func NotAfter(value time.Time) Option {
}
}
// IssuingCertificateURL is an Option for setting the identity's certificate's
// IssuingCertificateURL.
func IssuingCertificateURL(value ...string) Option {
return func(c *configuration) {
c.issuingCertificateURL = append(c.issuingCertificateURL, value...)
}
}
// OCSPServer is an Option for setting the identity's certificate's OCSPServer.
func OCSPServer(value ...string) Option {
return func(c *configuration) {
c.ocspServer = append(c.ocspServer, value...)
}
}
// IsCA is an Option for making an identity a certificate authority.
var IsCA Option = func(c *configuration) {
c.isCA = true

13
vendor/github.com/mastahyeti/fakeca/fakeca_test.go сгенерированный поставляемый
Просмотреть файл

@ -7,6 +7,7 @@ import (
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"reflect"
"testing"
)
@ -154,6 +155,18 @@ func TestPFX(t *testing.T) {
})
}
func TestAIA(t *testing.T) {
i := New(IssuingCertificateURL("a", "b"), OCSPServer("c", "d"))
if !reflect.DeepEqual(i.Certificate.IssuingCertificateURL, []string{"a", "b"}) {
t.Error("bad IssuingCertificateURL: ", i.Certificate.IssuingCertificateURL)
}
if !reflect.DeepEqual(i.Certificate.OCSPServer, []string{"c", "d"}) {
t.Error("bad OCSPServer: ", i.Certificate.OCSPServer)
}
}
func assertNoPanic(t *testing.T, cb func()) {
// Check that t.Helper() is defined for Go<1.9
if h, ok := interface{}(t).(interface{ Helper() }); ok {