Skip to content

Commit 77292e2

Browse files
authored
feat: allow user to specify CRL date validity (#19)
allow user to specify CRL date validity when revoking the certificate and during CA creation
1 parent c701791 commit 77292e2

File tree

7 files changed

+67
-29
lines changed

7 files changed

+67
-29
lines changed

README.md

+18-10
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Certify is an easy-to-use certificate manager and can be used as an alternative
99
+ Show certificate information from file or remote host
1010
+ Export certificate to PKCS12 format
1111
+ Verify private key matches with certificate
12+
+ Revoke certificate
1213

1314

1415
## Installation
@@ -20,36 +21,43 @@ Download in the [release page](https://github.com/nothinux/certify/releases)
2021
___ ___ ___| |_|_| _|_ _
2122
| _| -_| _| _| | _| | |
2223
|___|___|_| |_| |_|_| |_ |
23-
|___| Certify v1.5
24+
|___| Certify v1.x
2425
2526
Usage of certify:
2627
certify [flag] [ip-or-dns-san] [cn:default certify] [eku:default serverAuth,clientAuth] [expiry:default 8766h s,m,h,d]
2728
2829
$ certify server.local 172.17.0.1 cn:web-server eku:serverAuth expiry:1d
30+
$ certify -init cn:web-server o:nothinux crl-nextupdate:100d
2931
3032
Flags:
3133
-init
32-
Initialize new root CA Certificate and Key
34+
Initialize new root CA Certificate and Key
3335
-intermediate
34-
Generate intermediate certificate
36+
Generate intermediate certificate
3537
-read <filename>
36-
Read certificate information from file server.local.pem
38+
Read certificate information from file or stdin
39+
-read-crl <filename>
40+
Read certificate revocation list from file or stdin
3741
-connect <host:443> <tlsver:1.2> <insecure> <with-ca:ca-path>
38-
Show certificate information from remote host, use tlsver to set spesific tls version
42+
Show certificate information from remote host, use tlsver to set spesific tls version
3943
-export-p12 <cert> <private-key> <ca-cert>
40-
Generate client.p12 pem file containing certificate, private key and ca certificate
44+
Generate client.p12 pem file containing certificate, private key and ca certificate
4145
-match <private-key> <cert>
42-
Verify cert-key.pem and cert.pem has same public key
46+
Verify cert-key.pem and cert.pem has same public key
4347
-interactive
44-
Run certify interactively
48+
Run certify interactively
49+
-revoke <certificate> <crl-file> <crl-nextupdate:10d(optional)>
50+
Revoke certificate, the certificate will be added to CRL
51+
-verify-crl <certificate> <crl-file>
52+
Check if the certificate was revoked
4553
-version
46-
print certify version
54+
print certify version
4755
```
4856

4957
Create Certificate with CN nothinux and expiry 30 days
5058
```
5159
# create CA
52-
$ certify init
60+
$ certify -init cn:nothinux o:nothinux
5361
5462
# create Certificate
5563
$ certify cn:nothinux expiry:30d

cmd/certify/command.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ func initCA(args []string) error {
2626

2727
fmt.Println("CA certificate file generated", caPath)
2828

29-
if err := generateCRL(pkey.PrivateKey, caCert.Cert); err != nil {
29+
if err := generateCRL(args, pkey.PrivateKey, caCert.Cert); err != nil {
3030
return err
3131
}
3232
fmt.Println("CRL file generated", caCRLPath)
@@ -236,8 +236,10 @@ func revokeCertificate(args []string) (string, error) {
236236
return "", err
237237
}
238238

239+
_, _, _, _, _, _, nextUpdate := parseArgs(args)
240+
239241
fmt.Printf("revoking certificate cn=%s o=%s with serial number %s\n", cert.Subject.CommonName, cert.Subject.Organization, cert.SerialNumber)
240-
crl, crlNum, err := certify.RevokeCertificate(crlBytes, cert, caCert, pkey)
242+
crl, crlNum, err := certify.RevokeCertificate(crlBytes, cert, caCert, pkey, nextUpdate)
241243
if err != nil {
242244
return "", err
243245
}

cmd/certify/helper.go

+18-9
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ func getKeyIdentifier(publicKey *ecdsa.PublicKey) ([]byte, error) {
4040
}
4141

4242
func generateCA(pkey *ecdsa.PrivateKey, args []string, path string) (*certify.Result, error) {
43-
_, _, cn, org, expiry, _ := parseArgs(args)
43+
_, _, cn, org, expiry, _, _ := parseArgs(args)
4444

4545
ski, err := getKeyIdentifier(&pkey.PublicKey)
4646
if err != nil {
@@ -67,8 +67,10 @@ func generateCA(pkey *ecdsa.PrivateKey, args []string, path string) (*certify.Re
6767
return caCert, store(caCert.String(), path)
6868
}
6969

70-
func generateCRL(pkey *ecdsa.PrivateKey, caCert *x509.Certificate) error {
71-
crl, _, err := certify.CreateCRL(pkey, caCert, nil)
70+
func generateCRL(args []string, pkey *ecdsa.PrivateKey, caCert *x509.Certificate) error {
71+
_, _, _, _, _, _, nextUpdate := parseArgs(args)
72+
73+
crl, _, err := certify.CreateCRL(pkey, caCert, nil, nextUpdate)
7274
if err != nil {
7375
return err
7476
}
@@ -77,7 +79,7 @@ func generateCRL(pkey *ecdsa.PrivateKey, caCert *x509.Certificate) error {
7779
}
7880

7981
func generateCert(pkey *ecdsa.PrivateKey, args []string) (err error) {
80-
iplist, dnsnames, cn, org, expiry, ekus := parseArgs(args)
82+
iplist, dnsnames, cn, org, expiry, ekus, _ := parseArgs(args)
8183

8284
var parent *x509.Certificate
8385
var parentKey *ecdsa.PrivateKey
@@ -154,7 +156,7 @@ func generateCert(pkey *ecdsa.PrivateKey, args []string) (err error) {
154156
}
155157

156158
func generateIntermediateCert(pkey *ecdsa.PrivateKey, args []string) error {
157-
_, _, cn, org, expiry, _ := parseArgs(args)
159+
_, _, cn, org, expiry, _, _ := parseArgs(args)
158160

159161
parentKey, err := getCAPrivateKey(caKeyPath)
160162
if err != nil {
@@ -201,7 +203,7 @@ func generateIntermediateCert(pkey *ecdsa.PrivateKey, args []string) error {
201203
// first it will check dnsnames, if nil, then check iplist, if iplist nil too
202204
// it will check common name
203205
func getFilename(args []string, key bool) string {
204-
iplist, dnsnames, cn, _, _, _ := parseArgs(args)
206+
iplist, dnsnames, cn, _, _, _, _ := parseArgs(args)
205207

206208
var ext string
207209
var path string
@@ -334,12 +336,13 @@ func matcher(key, cert string) (string, string, error) {
334336
return comparePublicKey(k, c)
335337
}
336338

337-
// parseAltNames returns parsed net.IP, DNS, Common Name, Organization and expiry date in slice format
338-
func parseArgs(args []string) ([]net.IP, []string, string, string, time.Time, []x509.ExtKeyUsage) {
339+
// parseAltNames returns parsed net.IP, DNS, Common Name, Organization, expiry date, EKU and crl.Nextupdate in slice format
340+
func parseArgs(args []string) ([]net.IP, []string, string, string, time.Time, []x509.ExtKeyUsage, time.Time) {
339341
var iplist []net.IP
340342
var dnsnames []string
341343
var cn, organization string
342344
var expiry time.Time
345+
var crlNextUpdate time.Time
343346
var ekus []x509.ExtKeyUsage
344347

345348
for _, arg := range args[1:] {
@@ -357,6 +360,8 @@ func parseArgs(args []string) ([]net.IP, []string, string, string, time.Time, []
357360
expiry = parseExpiry(arg)
358361
} else if strings.Contains(arg, "eku:") {
359362
ekus = parseEKU(arg)
363+
} else if strings.Contains(arg, "crl-nextupdate:") {
364+
crlNextUpdate = parseExpiry(arg)
360365
} else {
361366
dnsnames = append(dnsnames, arg)
362367
}
@@ -366,6 +371,10 @@ func parseArgs(args []string) ([]net.IP, []string, string, string, time.Time, []
366371
expiry = parseExpiry("expiry:")
367372
}
368373

374+
if crlNextUpdate.IsZero() {
375+
crlNextUpdate = parseExpiry("crl-nextupdate:10d")
376+
}
377+
369378
if cn == "" {
370379
cn = "certify"
371380
}
@@ -381,7 +390,7 @@ func parseArgs(args []string) ([]net.IP, []string, string, string, time.Time, []
381390
}
382391
}
383392

384-
return iplist, dnsnames, cn, organization, expiry, ekus
393+
return iplist, dnsnames, cn, organization, expiry, ekus, crlNextUpdate
385394
}
386395

387396
func parseString(ss string) string {

cmd/certify/helper_test.go

+19-1
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ func TestParseArgs(t *testing.T) {
115115
expectedOrganization string
116116
expectedExpiry time.Time
117117
expectedEku []x509.ExtKeyUsage
118+
expectedNextUpdate time.Time
118119
}{
119120
{
120121
Name: "Test with ip and dns names",
@@ -201,11 +202,18 @@ func TestParseArgs(t *testing.T) {
201202
expectedCN: "client",
202203
expectedOrganization: "certify",
203204
},
205+
{
206+
Name: "Test with crl-nextupdate 100d",
207+
Args: []string{"certify", "crl-nextupdate:100d"},
208+
expectedNextUpdate: time.Now().Add(2400 * time.Hour),
209+
expectedCN: "certify",
210+
expectedOrganization: "certify",
211+
},
204212
}
205213

206214
for _, tt := range tests {
207215
t.Run(tt.Name, func(t *testing.T) {
208-
ips, dns, cn, o, expiry, ekus := parseArgs(tt.Args)
216+
ips, dns, cn, o, expiry, ekus, nextUpdate := parseArgs(tt.Args)
209217

210218
if len(tt.expectedIP) != 0 {
211219
for i, ip := range ips {
@@ -255,6 +263,16 @@ func TestParseArgs(t *testing.T) {
255263
t.Fatalf("got %v, want %v", ekus, defaultEku)
256264
}
257265
}
266+
267+
if !tt.expectedNextUpdate.IsZero() {
268+
if nextUpdate.Unix() != tt.expectedNextUpdate.Unix() {
269+
t.Fatalf("got %v, want %v", nextUpdate.Unix(), tt.expectedNextUpdate.Unix())
270+
}
271+
} else {
272+
if nextUpdate.Unix() != time.Now().Add(240*time.Hour).Unix() {
273+
t.Fatalf("got %v, want %v", nextUpdate.Unix(), time.Now().Add(240*time.Hour).Unix())
274+
}
275+
}
258276
})
259277
}
260278
}

cmd/certify/main.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Usage of certify:
2020
certify [flag] [ip-or-dns-san] [cn:default certify] [o: default certify] [eku:default serverAuth,clientAuth] [expiry:default 8766h s,m,h,d]
2121
2222
$ certify server.local 172.17.0.1 cn:web-server eku:serverAuth expiry:1d
23+
$ certify -init cn:web-server o:nothinux crl-nextupdate:100d
2324
2425
Flags:
2526
-init
@@ -38,7 +39,7 @@ Flags:
3839
Verify cert-key.pem and cert.pem has same public key
3940
-interactive
4041
Run certify interactively
41-
-revoke <certificate> <crl-file>
42+
-revoke <certificate> <crl-file> <crl-nextupdate:10d(optional)>
4243
Revoke certificate, the certificate will be added to CRL
4344
-verify-crl <certificate> <crl-file>
4445
Check if the certificate was revoked

crl.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ type CertRevocationList struct {
1919
}
2020

2121
// CreateCRL Create certificate revocation list
22-
func CreateCRL(pkey *ecdsa.PrivateKey, caCert *x509.Certificate, crl *x509.RevocationList) (*CertRevocationList, *big.Int, error) {
22+
func CreateCRL(pkey *ecdsa.PrivateKey, caCert *x509.Certificate, crl *x509.RevocationList, nextUpdate time.Time) (*CertRevocationList, *big.Int, error) {
2323
crlNumber := time.Now().UTC().Format("20060102150405")
2424
num, _ := big.NewInt(0).SetString(crlNumber, 10)
2525

@@ -31,7 +31,7 @@ func CreateCRL(pkey *ecdsa.PrivateKey, caCert *x509.Certificate, crl *x509.Revoc
3131

3232
crl.Number = num
3333
crl.ThisUpdate = time.Now()
34-
crl.NextUpdate = time.Now().Add(96 * time.Hour)
34+
crl.NextUpdate = nextUpdate
3535

3636
crlByte, err := x509.CreateRevocationList(rand.Reader, crl, caCert, pkey)
3737
if err != nil {
@@ -63,7 +63,7 @@ func ParseCRL(crl []byte) (*x509.RevocationList, error) {
6363
return x509.ParseRevocationList(c.Bytes)
6464
}
6565

66-
func RevokeCertificate(crl []byte, cert *x509.Certificate, caCert *x509.Certificate, pkey *ecdsa.PrivateKey) (*CertRevocationList, *big.Int, error) {
66+
func RevokeCertificate(crl []byte, cert *x509.Certificate, caCert *x509.Certificate, pkey *ecdsa.PrivateKey, nextUpdate time.Time) (*CertRevocationList, *big.Int, error) {
6767
crlF, err := ParseCRL(crl)
6868
if err != nil {
6969
return nil, nil, err
@@ -74,7 +74,7 @@ func RevokeCertificate(crl []byte, cert *x509.Certificate, caCert *x509.Certific
7474
RevocationTime: time.Now(),
7575
})
7676

77-
return CreateCRL(pkey, caCert, crlF)
77+
return CreateCRL(pkey, caCert, crlF, nextUpdate)
7878

7979
}
8080

crl_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ func TestCreateCRL(t *testing.T) {
7373
t.Fatal(err)
7474
}
7575

76-
_, _, err = CreateCRL(pkey.PrivateKey, caCert.Cert, nil)
76+
_, _, err = CreateCRL(pkey.PrivateKey, caCert.Cert, nil, time.Now().Add(48*time.Hour))
7777
if err == nil {
7878
t.Fatalf("this should be error, because the cert doesn't have keyUsage")
7979
}
@@ -88,7 +88,7 @@ func TestCreateCRL(t *testing.T) {
8888
t.Fatal(err)
8989
}
9090

91-
_, _, err = CreateCRL(pkey.PrivateKey, caCert.Cert, nil)
91+
_, _, err = CreateCRL(pkey.PrivateKey, caCert.Cert, nil, time.Now().Add(48*time.Hour))
9292
if err != nil {
9393
t.Fatal(err)
9494
}

0 commit comments

Comments
 (0)