Getting Started with PKI with Golang ―― 4

1. People covered by this article

--People who want to create a certificate revocation list (CRL) in Golang

2. Overview

In this article ** 1. Generate private key and certificate with Go ** ** 2. Generate a list of revoked certificates with Go ** ** 3. Create an Extension for Issuing Distribution Point in Go ** ** 4. Create Certificate Revocation List (CRL) in Go ** ** 5. Check the contents of the certificate revocation list (CRL) with OpenSSL ** To do.

3. Create a self-signed CA certificate and private key in Golang

Create a "certificate" and "private key" for the self-signed CA that issues the certificate revocation list. For a detailed explanation, refer to Introduction to PKI with Golang-2. Keep the private key in DER format as it will be required as an argument when creating the certificate revocation list.


	//PrivateKey of Self Sign CA Certificate
	privateCaKey, err := rsa.GenerateKey(rand.Reader, 2048)
	publicCaKey := privateCaKey.Public()

	//[RFC5280]
	subjectCa := pkix.Name{
		CommonName:         "ca01",
		OrganizationalUnit: []string{"Example Org Unit"},
		Organization:       []string{"Example Org"},
		Country:            []string{"JP"},
	}

	caTpl := &x509.Certificate{
		SerialNumber:          big.NewInt(1),
		Subject:               subjectCa,
		NotAfter:              time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC),
		NotBefore:             time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC),
		IsCA:                  true,
		KeyUsage:              x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
		BasicConstraintsValid: true,
	}

	//Self Sign CA Certificate
	caCertificate, err := x509.CreateCertificate(rand.Reader, caTpl, caTpl, publicCaKey, privateCaKey)


	//Convert to ASN.1 DER encoded form
	derCaCert, err = x509.ParseCertificate(caCertificate)
	if err != nil {
		log.Fatalf("ERROR:%v\n", err)
	}

4. Create a certificate revocation list in Golang

Create a list of revoked certificates

	var rcs []pkix.RevokedCertificate
	rc := pkix.RevokedCertificate{
		SerialNumber:   big.NewInt(100),
		RevocationTime: time.Now(),
	}

	rcs = append(rcs, rc)

	rc = pkix.RevokedCertificate{
		SerialNumber:   big.NewInt(108),
		RevocationTime: time.Now(),
	}

	rcs = append(rcs, rc)

Here, the serial revokes 100 and 108 certificates.

Added Issuing Distribution Point to crlExtensions in Certificate Revocation List

The x509.RevocationList used when creating a certificate revocation list in Go does not have a Field to add Issuing Distribution Points directly. You need to create a separate structure for Issuing Distribution Point and add it to the Extension. In RFC5280, Issuing Distribution Point is defined as follows.

   id-ce-issuingDistributionPoint OBJECT IDENTIFIER ::= { id-ce 28 }

   IssuingDistributionPoint ::= SEQUENCE {
        distributionPoint          [0] DistributionPointName OPTIONAL,
        onlyContainsUserCerts      [1] BOOLEAN DEFAULT FALSE,
        onlyContainsCACerts        [2] BOOLEAN DEFAULT FALSE,
        onlySomeReasons            [3] ReasonFlags OPTIONAL,
        indirectCRL                [4] BOOLEAN DEFAULT FALSE,
        onlyContainsAttributeCerts [5] BOOLEAN DEFAULT FALSE }

   DistributionPointName ::= CHOICE {
        fullName                [0]     GeneralNames,
        nameRelativeToCRLIssuer [1]     RelativeDistinguishedName }

   GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName

   GeneralName ::= CHOICE {
        otherName                       [0]     OtherName,
        rfc822Name                      [1]     IA5String,
        dNSName                         [2]     IA5String,
        x400Address                     [3]     ORAddress,
        directoryName                   [4]     Name,
        ediPartyName                    [5]     EDIPartyName,
        uniformResourceIdentifier       [6]     IA5String,
        iPAddress                       [7]     OCTET STRING,
        registeredID                    [8]     OBJECT IDENTIFIER }

Following the above, we have defined issuingDistributionPoint and distributionPointName as the following Go structs.

// RFC5280, 5.2.5
type issuingDistributionPoint struct {
	DistributionPoint          distributionPointName `asn1:"optional,tag:0"`
	OnlyContainsUserCerts      bool                  `asn1:"optional,tag:1"`
	OnlyContainsCACerts        bool                  `asn1:"optional,tag:2"`
	OnlySomeReasons            asn1.BitString        `asn1:"optional,tag:3"`
	IndirectCRL                bool                  `asn1:"optional,tag:4"`
	OnlyContainsAttributeCerts bool                  `asn1:"optional,tag:5"`
}

type distributionPointName struct {
	FullName     []asn1.RawValue  `asn1:"optional,tag:0"`
	RelativeName pkix.RDNSequence `asn1:"optional,tag:1"`
}

The type of the FullName field for distributionPointName is GeneralNames. I want to specify where to get the certificate revocation list with the uniformResourceIdentifier of GeneralName. Set as follows in asn1.RawValue type Class: 2 Context-specific (as defined by asn1.RawValue) Tag: 6 6th of GeneralName, that is, uniformResourceIdentifier Bytes: []byte("http://www.example.com/example.crl") The encoding of uniformResourceIdentifier is IA5String. However, since the character string in the range used in the URI to crl is the same byte in IA5String and UTF8, it is passed directly as a byte array.

	dp := distributionPointName{
		FullName: []asn1.RawValue{
			{Tag: 6, Class: 2, Bytes: []byte("http://www.example.com/example.crl")},
		},
	}

Set the IssuingDistributionPoint created in the Extension.

        
var oidExtensionIssuingDistributionPoint = []int{2, 5, 29, 28}

	idp := issuingDistributionPoint{
		DistributionPoint: dp,
	}

	v, err := asn1.Marshal(idp)

	cdpExt := pkix.Extension{
		Id:       oidExtensionIssuingDistributionPoint,
		Critical: true,
		Value:    v,
	}

x509.RevocationList structure settings

Enter the values you want to set in the x509.RevocationList structure.

	crlTpl := &x509.RevocationList{
		SignatureAlgorithm:  x509.SHA256WithRSA,
		RevokedCertificates: rcs,
		Number:              big.NewInt(2),
		ThisUpdate:          time.Now(),
		NextUpdate:          time.Now().Add(24 * time.Hour),
		ExtraExtensions:     []pkix.Extension{cdpExt},
	}

Create a certificate revocation list

Issue a certificate revocation list

	var derCrl []byte
	derCrl, err = x509.CreateRevocationList(rand.Reader, crlTpl, derCaCert, privateCaKey)
	if err != nil {
		log.Fatalf("ERROR:%v\n", err)
	}

	f, err = os.Create("ca01.crl")
	if err != nil {
		log.Fatalf("ERROR:%v\n", err)
	}

	err = pem.Encode(f, &pem.Block{Type: "X509 CRL", Bytes: derCrl})
	if err != nil {
		log.Fatalf("ERROR:%v\n", err)
	}
	err = f.Close()

5. Check the certificate revocation list

Check the issued certificate revocation list with Openssl. It contains all the set elements.

$ openssl crl -inform pem -in example.crl -text
Certificate Revocation List (CRL):
        Version 2 (0x1)
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = JP, O = Example Org, OU = Example Org Unit, CN = ca01
        Last Update: Oct 24 04:16:04 2020 GMT
        Next Update: Oct 25 04:16:04 2020 GMT
        CRL extensions:
            X509v3 Authority Key Identifier:
                keyid:0A:42:8D:9B:23:A9:77:11:FF:FD:0F:CC:58:F4:36:F4:98:06:7F:28

            X509v3 CRL Number:
                2
            X509v3 Issuing Distribution Point: critical
                Full Name:
                  URI:http://www.example.com/example.crl

Revoked Certificates:
    Serial Number: 64
        Revocation Date: Oct 24 04:16:04 2020 GMT
    Serial Number: 6C
        Revocation Date: Oct 24 04:16:04 2020 GMT
    Signature Algorithm: sha256WithRSAEncryption
         6c:0d:23:e8:50:bf:84:ae:10:85:3e:43:28:0f:43:fd:58:cb:
         83:8c:7c:a8:5c:7d:78:71:f1:0c:03:97:43:88:8c:32:02:5c:
         a6:6c:e2:a4:7d:94:56:08:a8:9c:17:95:b4:be:11:bb:65:52:
         43:25:de:c0:d5:d0:df:ac:0f:ca:8c:a7:23:82:19:12:e2:9d:
         49:83:9e:ca:bc:2e:f3:60:79:39:47:cb:ed:17:52:25:9f:42:
         26:9e:1b:67:5f:af:e1:3a:14:67:5f:4f:de:10:c5:32:03:7f:
         40:a0:b6:bc:3f:05:33:73:91:0b:73:4e:f2:3c:be:b0:e4:63:
         e0:d0:81:6e:91:14:d9:04:35:21:3e:22:1e:31:bd:47:40:c9:
         69:f0:e5:57:bc:c3:2c:ae:b8:06:38:35:f1:59:6f:45:2c:45:
         08:2e:63:49:ab:f5:54:0b:54:d2:a8:fc:62:ea:a5:46:62:28:
         a9:89:76:96:cf:47:28:3d:81:c3:e9:fb:ce:54:a8:07:71:6d:
         c6:d8:b7:e7:33:b0:05:df:c4:79:56:e1:99:ed:9f:33:f8:15:
         b9:32:4e:82:4c:0c:a7:a5:23:d4:f7:e1:94:26:2b:e0:55:1a:
         38:f6:72:21:a9:e0:29:06:80:9a:05:e3:43:c2:4a:dd:74:c6:
         d6:79:ec:9d
-----BEGIN X509 CRL-----
MIICKDCCARACAQEwDQYJKoZIhvcNAQELBQAwTTELMAkGA1UEBhMCSlAxFDASBgNV
BAoTC0V4YW1wbGUgT3JnMRkwFwYDVQQLExBFeGFtcGxlIE9yZyBVbml0MQ0wCwYD
VQQDEwRjYTAxFw0yMDEwMjQwNDE2MDRaFw0yMDEwMjUwNDE2MDRaMCgwEgIBZBcN
MjAxMDI0MDQxNjA0WjASAgFsFw0yMDEwMjQwNDE2MDRaoGUwYzAfBgNVHSMEGDAW
gBQKQo2bI6l3Ef/9D8xY9Db0mAZ/KDAKBgNVHRQEAwIBAjA0BgNVHRwBAf8EKjAo
oCagJIYiaHR0cDovL3d3dy5leGFtcGxlLmNvbS9leGFtcGxlLmNybDANBgkqhkiG
9w0BAQsFAAOCAQEAbA0j6FC/hK4QhT5DKA9D/VjLg4x8qFx9eHHxDAOXQ4iMMgJc
pmzipH2UVgionBeVtL4Ru2VSQyXewNXQ36wPyoynI4IZEuKdSYOeyrwu82B5OUfL
7RdSJZ9CJp4bZ1+v4ToUZ19P3hDFMgN/QKC2vD8FM3ORC3NO8jy+sORj4NCBbpEU
2QQ1IT4iHjG9R0DJafDlV7zDLK64Bjg18VlvRSxFCC5jSav1VAtU0qj8YuqlRmIo
qYl2ls9HKD2Bw+n7zlSoB3Ftxti35zOwBd/EeVbhme2fM/gVuTJOgkwMp6Uj1Pfh
lCYr4FUaOPZyIangKQaAmgXjQ8JK3XTG1nnsnQ==
-----END X509 CRL-----

6. Code

Click here for the code https://github.com/tardevnull/gopkicookbook4

Recommended Posts

Getting Started with PKI with Golang ―― 4
Getting Started with Golang 2
Getting Started with Golang 1
Getting Started with Golang 3
Getting Started with Golang 4
1.1 Getting Started with Python
Getting started with apache2
Getting Started with Python
Getting Started with Django 1
Getting Started with Optimization
Getting Started with Numpy
Getting started with Spark
Getting Started with Python
Getting Started with Pydantic
Getting Started with Jython
Getting Started with Django 2
Translate Getting Started With TensorFlow
Getting Started with Python Functions
Getting Started with Tkinter 2: Buttons
Getting Started with Go Assembly
Getting Started with Python Django (1)
Getting Started with Python Django (4)
Getting Started with Python Django (3)
Getting Started with Python Django (6)
Getting Started with Django with PyCharm
Python3 | Getting Started with numpy
Getting Started with Python Django (5)
Getting Started with Python responder v2
Getting Started with Git (1) History Storage
Getting started with Sphinx. Generate docstring with Sphinx
Getting Started with Python Web Applications
Getting Started with Python for PHPer-Classes
Getting Started with Sparse Matrix with scipy.sparse
Getting Started with Julia for Pythonista
Getting Started with Python Basics of Python
Getting Started with Cisco Spark REST-API
Getting started with USD on Windows
Getting Started with Python Genetic Algorithms
Getting started with Python 3.8 on Windows
Getting Started with Python for PHPer-Functions
Getting Started with CPU Steal Time
Getting Started with python3 # 1 Learn Basic Knowledge
Getting Started with Flask with Azure Web Apps
Getting Started with Python Web Scraping Practice
Getting Started with Python for PHPer-Super Basics
Getting Started with Python Web Scraping Practice
Getting started with Dynamo from Python boto
Getting Started with Lisp for Pythonista: Supplement
Getting Started with Heroku, Deploying Flask App
Getting Started with TDD with Cyber-dojo at MobPro
Grails getting started
Getting started with Python with 100 knocks on language processing
MongoDB Basics: Getting Started with CRUD in JAVA
Getting Started with Drawing with matplotlib: Writing Simple Functions
Getting started with Keras Sequential model Japanese translation
[Translation] Getting Started with Rust for Python Programmers
Django Getting Started Part 2 with eclipse Plugin (PyDev)
Getting started with AWS IoT easily in Python
Getting Started with Python's ast Module (Using NodeVisitor)
Materials to read when getting started with Python
Settings for getting started with MongoDB in python