Skip to content

crypto/x509: cannot generate version 1 or 2 certificates #21593

@CRThaze

Description

@CRThaze

Questionarie

What version of Go are you using (go version)?

1.9rc2_cl165246139

Does this issue reproduce with the latest release?

Yes

What operating system and processor architecture are you using (go env)?

GOARCH="amd64"
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"

What did you do?

package main

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"crypto/x509/pkix"
	"fmt"
	"io/ioutil"
	"math/big"
	"time"
)

func main() {
	privatekey, err := rsa.GenerateKey(rand.Reader, 4096)
	if err != nil {
		fmt.Println(err)
	}

	publickey := &privatekey.PublicKey

	caTemplate := &x509.Certificate{
		Version: 1,
		SerialNumber: big.NewInt(1),
		Subject: pkix.Name{
			Country:            []string{"US"},
			Province:           []string{"California"},
			Locality:           []string{"Mountain View"},
			Organization:       []string{"Acme"},
			OrganizationalUnit: []string{"Security"},
			CommonName:         "acme-dev",
		},
		NotBefore: time.Now(),
		NotAfter:  time.Now().AddDate(10, 0, 0),
	}
  
	cert, err := x509.CreateCertificate(rand.Reader, caTemplate, caTemplate, publickey, privatekey)
	if err != nil {
		fmt.Println(err)
	}
	ioutil.WriteFile("cert.pem", cert, 0777)
	fmt.Println("certificate saved to cert.pem")
}  

What did you expect to see?

The preceding Go program (shared here as source instead of a play.golang.org link since the crypto work takes too long and the tool times out) should generate a Version 1 x509 self singed certificate.

What did you see instead?

As can be seen by running openssl x509 -in cert.pem -inform der -text -noout on the resulting certificate file, this will instead generate a version 3 certificate.

Observations

x509 versions 2 & 3 merely support extra fields, on top of those provided by preceding versions. And so a version 3 certificate which does not use any version 2 or 3 specific fields would still be functionally identical to a version 1 certificate. However, I would expect that the crypto/x509 library should support applying the specified version to the certificate, as well as ensure that no fields which are not supported by a given version are being used.

Upon inspection of the x509.CreateCertificate() function's source code it appears that the version is simply hard-coded to v3

Proposed Fix

As near as I can tell, all that needs to be done here is to provide a check that only fields supported by the specified version are being used, and then pass the value of template.Version to the tbsCertificate struct.

Or rather, passing template.Version - 1 to the struct, so that we may use the more readable version values of 1 - 3 instead of the zero-index value used by the tbsCertificate. This also allows us to not break existing functionality and continue to use v3 if no value for Version is specified (and thus set to 0).

Something along the lines of:

if template.Version == 0 {
  template.Version = 3
}

if template.Version < 0 || template.Version > 3 {
	return nil, errors.New(fmt.Sprintf("x509: invalid version '%d'", template.Version))
} else if template.Version == 1 && (len(template.AuthorityKeyId) > 0 ||
	len(template.SubjectKeyId) > 0 ||
	len(template.ExtKeyUsage) > 0 ||
	template.KeyUsage != 0 ||
	template.BasicConstraintsValid == true) {
	return nil, errors.New("x509: version 1 does not support AuthorityKeyId, SubjectKeyId, BasicConstraints, KeyUsage, or ExtKeyUsage")
} else if template.Version == 2 && (len(template.ExtKeyUsage) > 0 ||
	template.KeyUsage != 0 ||
	template.BasicConstraintsValid == true) {
	return nil, errors.New("x509: version 2 does not support BasicConstraints, KeyUsage, or ExtKeyUsage")
}

within the x509.CreateCertificate() function and prior to the creation of the tbsCertificate struct.

I've tested this on my own branch and found that it does allow for non v3 labeled certificates to be created (providing they do not use fields unsupported by the speficied version). But I wanted to file an issue first and see what people thought, and make sure I'm not way off base here, before submitting my changes for review.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions