Skip to content

Commit 3e6c956

Browse files
committed
feature: support load x-ca/ca
Signed-off-by: xiexianbin <me@xiexianbin.cn>
1 parent 5e77e24 commit 3e6c956

File tree

5 files changed

+119
-41
lines changed

5 files changed

+119
-41
lines changed

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,4 @@
1717
bin/
1818
.history/
1919
.idea/
20-
certs/
2120
x-ca

README.md

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,35 @@
11
# go-ca
22

3-
golang x-ca client which can simple Sign Self Root/Second-Level CA, and sign for Domains and IPs.
3+
golang x-ca client, which can simple Sign Self Root/Second-Level CA, and sign for Domains and IPs.
44

55
shell implement at [x-ca/x-ca](https://github.com/x-ca/x-ca)
66

77
## install
88

99
```
1010
curl -Lfs -o xca https://github.com/x-ca/go-ca/releases/latest/download/xca-{linux|darwin|windows}
11-
chmox +x xca
11+
chmod +x xca
12+
mv xca /usr/local/bin/
1213
```
1314

1415
## Help
1516

1617
```
17-
$ bin/xca --help
18+
$ xca --help
1819
Create Root CA and TLS CA:
19-
goca -create-ca true \
20+
xca -create-ca true \
2021
-root-cert x-ca/ca/root-ca.crt \
2122
-root-key x-ca/ca/root-ca/private/root-ca.key \
2223
-tls-cert x-ca/ca/tls-ca.crt \
23-
-tls-key x-ca/ca/tls-ca/private/tls-ca.key
24+
-tls-key x-ca/ca/tls-ca/private/tls-ca.key \
25+
-tls-chain x-ca/ca/tls-ca-chain.pem
2426
2527
Sign Domains or Ips:
2628
xca -cn xxxx \
2729
--domains "xxx,xxx" --ips "xxx,xxx" \
2830
-tls-cert x-ca/ca/tls-ca.crt \
29-
-tls-key x-ca/ca/tls-ca/private/tls-ca.key
31+
-tls-key x-ca/ca/tls-ca/private/tls-ca.key \
32+
-tls-chain x-ca/ca/tls-ca-chain.pem
3033
3134
Usage:
3235
-cn string
@@ -42,19 +45,26 @@ Usage:
4245
-root-cert string
4346
Root certificate file path, PEM format. (default "x-ca/ca/root-ca.crt")
4447
-root-key string
45-
Root private key file path, PEM/? format. (default "x-ca/ca/root-ca/private/root-ca.key")
48+
Root private key file path, PEM format. (default "x-ca/ca/root-ca/private/root-ca.key")
4649
-tls-cert string
4750
Second-Level certificate file path, PEM format. (default "x-ca/ca/tls-ca.crt")
51+
-tls-chain string
52+
Root/Second-Level CA Chain file path, PEM format. (default "x-ca/ca/tls-ca-chain.pem")
4853
-tls-key string
49-
Second-Level private key file path, PEM/? format. (default "x-ca/ca/tls-ca/private/tls-ca.key")
54+
Second-Level private key file path, PEM format. (default "x-ca/ca/tls-ca/private/tls-ca.key")
55+
-tls-key-password string
56+
tls key password, only work for load github.com/x-ca/x-ca.
57+
58+
Source Code:
59+
https://github.com/x-ca/go-ca
5060
```
5161

5262
## Usage Demo
5363

5464
- create ca
5565

5666
```
57-
bin/xca -create-ca true \
67+
xca -create-ca true \
5868
-root-cert x-ca/ca/root-ca.crt \
5969
-root-key x-ca/ca/root-ca/private/root-ca.key \
6070
-tls-cert x-ca/ca/tls-ca.crt \
@@ -73,11 +83,11 @@ git clone git@github.com:x-ca/ca.git x-ca
7383
- sign domain
7484

7585
```
76-
bin/xca -cn xiexianbin.cn \
86+
xca -cn xiexianbin.cn \
7787
--domains "*.xiexianbin.cn,*.80.xyz" \
7888
--ips 100.80.0.128 \
7989
-tls-cert x-ca/ca/tls-ca.crt \
80-
-tls-key x-ca/ca/tls-ca/private/tls-ca.key
90+
-tls-key x-ca/ca/tls-ca/private/[tls-ca.key | tls-ca-des3.key]
8191
```
8292

8393
- test cert
@@ -86,13 +96,18 @@ bin/xca -cn xiexianbin.cn \
8696
docker run -it -d \
8797
-p 8443:443 \
8898
-v $(pwd)/examples/default.conf:/etc/nginx/conf.d/default.conf \
89-
-v $(pwd)/certs/xiexianbin.cn/xiexianbin.cn.bundle.crt:/etc/pki/nginx/server.crt \
90-
-v $(pwd)/certs/xiexianbin.cn/xiexianbin.cn.key:/etc/pki/nginx/private/server.key \
99+
-v $(pwd)/x-ca/certs/xiexianbin.cn/xiexianbin.cn.bundle.crt:/etc/pki/nginx/server.crt \
100+
-v $(pwd)/x-ca/certs/xiexianbin.cn/xiexianbin.cn.key:/etc/pki/nginx/private/server.key \
91101
nginx
92102
```
93103

94104
visit https://dev.xiexianbin.cn:8443/
95105

106+
## FaQ
107+
108+
if CA Cert begin with `BEGIN ENCRYPTED PRIVATE KEY`(raise `Error: fromPEMBytes: x509: no DEK-Info header in block`),
109+
Use `openssl rsa -in root-ca.key -des3` change cipher
110+
96111
## Ref
97112

98113
- [基于OpenSSL签署根CA、二级CA](https://www.xiexianbin.cn/s/ca/)

ca/root.go

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ const (
3737
)
3838

3939
var (
40+
// sort.StringsAreSorted(supportPemType) == true
4041
supportPemType = []string{"ECDSA PRIVATE KEY", "RSA PRIVATE KEY"}
4142
)
4243

@@ -64,7 +65,7 @@ func NewRootCA(keyBits int) (*RootCA, error) {
6465
}
6566

6667
// LoadRootCA create new tls CA
67-
func LoadRootCA(keyPath, certPath string) (*RootCA, error) {
68+
func LoadRootCA(keyPath, certPath, password string) (*RootCA, error) {
6869
keyBytes, kErr := ioutil.ReadFile(keyPath)
6970
certBytes, cErr := ioutil.ReadFile(certPath)
7071
if kErr != nil {
@@ -77,10 +78,39 @@ func LoadRootCA(keyPath, certPath string) (*RootCA, error) {
7778
keyBlock, _ := pem.Decode(keyBytes)
7879
if keyBlock == nil {
7980
return nil, fmt.Errorf("decode key is nil")
80-
} else if sort.SearchStrings(supportPemType, keyBlock.Type) < 0 {
81+
} else if supportPemType[sort.SearchStrings(supportPemType, keyBlock.Type)] != keyBlock.Type {
8182
return nil, fmt.Errorf("unsupport PEM type %s", keyBlock.Type)
8283
}
83-
key, err := x509.ParsePKCS1PrivateKey(keyBlock.Bytes)
84+
85+
/* Fix x-ca/ca root/tls key Problem
86+
* https://github.com/x-ca/ca/blob/f82f6cc529662d5a751b79d87698a13c65f342ec/etc/root-ca.conf#L15
87+
* https://security.stackexchange.com/questions/93417/what-encryption-is-applied-on-a-key-generated-by-openssl-req
88+
* https://rfc-editor.org/rfc/rfc1423.html
89+
* openssl asn1parse -in root-ca.key -i | cut -c-90
90+
* - golang code
91+
*
92+
* if x509.IsEncryptedPEMBlock(keyBlock) == true {
93+
* der, err := x509.DecryptPEMBlock(keyBlock, []byte("pwd"))
94+
* key, _ = x509.ParsePKCS1PrivateKey(der)
95+
* } else {
96+
* key, err = x509.ParsePKCS1PrivateKey(keyBlock.Bytes)
97+
* }
98+
*
99+
* Raise error: `Error: fromPEMBytes: x509: no DEK-Info header in block`
100+
*
101+
* - fix run: `openssl rsa -in root-ca.key -des3`
102+
*/
103+
var key *rsa.PrivateKey
104+
var err error
105+
if x509.IsEncryptedPEMBlock(keyBlock) == true {
106+
der, err := x509.DecryptPEMBlock(keyBlock, []byte(password))
107+
if err != nil {
108+
return nil, err
109+
}
110+
key, _ = x509.ParsePKCS1PrivateKey(der)
111+
} else {
112+
key, err = x509.ParsePKCS1PrivateKey(keyBlock.Bytes)
113+
}
84114
if err != nil {
85115
return nil, fmt.Errorf("load private key %s, error %s", keyPath, err)
86116
}

ca/tls.go

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ func NewTLSCA(keyBits int, rootCert *x509.Certificate, rootKey *rsa.PrivateKey)
7272
}
7373

7474
// LoadTLSCA create new tls CA
75-
func LoadTLSCA(keyPath, certPath string) (*TLSCA, error) {
75+
func LoadTLSCA(keyPath, certPath, password string) (*TLSCA, error) {
7676
keyBytes, kErr := ioutil.ReadFile(keyPath)
7777
certBytes, cErr := ioutil.ReadFile(certPath)
7878
if kErr != nil {
@@ -85,10 +85,39 @@ func LoadTLSCA(keyPath, certPath string) (*TLSCA, error) {
8585
keyBlock, _ := pem.Decode(keyBytes)
8686
if keyBlock == nil {
8787
return nil, fmt.Errorf("decode key is nil")
88-
} else if sort.SearchStrings(supportPemType, keyBlock.Type) < 0 {
88+
} else if supportPemType[sort.SearchStrings(supportPemType, keyBlock.Type)] != keyBlock.Type {
8989
return nil, fmt.Errorf("unsupport PEM type %s", keyBlock.Type)
9090
}
91-
key, err := x509.ParsePKCS1PrivateKey(keyBlock.Bytes)
91+
92+
/* Fix x-ca/ca root/tls key Problem
93+
* https://github.com/x-ca/ca/blob/f82f6cc529662d5a751b79d87698a13c65f342ec/etc/root-ca.conf#L15
94+
* https://security.stackexchange.com/questions/93417/what-encryption-is-applied-on-a-key-generated-by-openssl-req
95+
* https://rfc-editor.org/rfc/rfc1423.html
96+
* openssl asn1parse -in root-ca.key -i | cut -c-90
97+
* - golang code
98+
*
99+
* if x509.IsEncryptedPEMBlock(keyBlock) == true {
100+
* der, err := x509.DecryptPEMBlock(keyBlock, []byte("pwd"))
101+
* key, _ = x509.ParsePKCS1PrivateKey(der)
102+
* } else {
103+
* key, err = x509.ParsePKCS1PrivateKey(keyBlock.Bytes)
104+
* }
105+
*
106+
* Raise error: `Error: fromPEMBytes: x509: no DEK-Info header in block`
107+
*
108+
* - fix run: `openssl rsa -in root-ca.key -des3`
109+
*/
110+
var key *rsa.PrivateKey
111+
var err error
112+
if x509.IsEncryptedPEMBlock(keyBlock) == true {
113+
der, err := x509.DecryptPEMBlock(keyBlock, []byte(password))
114+
if err != nil {
115+
return nil, err
116+
}
117+
key, _ = x509.ParsePKCS1PrivateKey(der)
118+
} else {
119+
key, err = x509.ParsePKCS1PrivateKey(keyBlock.Bytes)
120+
}
92121
if err != nil {
93122
return nil, fmt.Errorf("load private key %s, error %s", keyPath, err)
94123
}
@@ -294,13 +323,13 @@ func (c *TLSCA) Sign(commonName string, domains []string, ips []net.IP, days, ke
294323
func (c *TLSCA) WriteCert(commonName string, key *rsa.PrivateKey, cert *x509.Certificate, tlsChainPath string) error {
295324
// mkdir
296325
var dir = strings.Replace(commonName, "*.", "", -1)
297-
err := os.MkdirAll(fmt.Sprintf("certs/%s", dir), 0700)
326+
err := os.MkdirAll(fmt.Sprintf("x-ca/certs/%s", dir), 0700)
298327
if err != nil && !os.IsExist(err) {
299328
return err
300329
}
301330

302331
// write key
303-
keyPath := fmt.Sprintf("certs/%s/%s.key", dir, commonName)
332+
keyPath := fmt.Sprintf("x-ca/certs/%s/%s.key", dir, commonName)
304333
keyFile, err := os.OpenFile(keyPath, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0600)
305334
if err != nil {
306335
return err
@@ -316,7 +345,7 @@ func (c *TLSCA) WriteCert(commonName string, key *rsa.PrivateKey, cert *x509.Cer
316345
}
317346

318347
// write cert
319-
certPath := fmt.Sprintf("certs/%s/%s.crt", dir, commonName)
348+
certPath := fmt.Sprintf("x-ca/certs/%s/%s.crt", dir, commonName)
320349
certFile, err := os.OpenFile(certPath, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0600)
321350
if err != nil {
322351
return err
@@ -332,7 +361,7 @@ func (c *TLSCA) WriteCert(commonName string, key *rsa.PrivateKey, cert *x509.Cer
332361
}
333362

334363
// write cert chain
335-
certChainPath := fmt.Sprintf("certs/%s/%s.bundle.crt", dir, commonName)
364+
certChainPath := fmt.Sprintf("x-ca/certs/%s/%s.bundle.crt", dir, commonName)
336365
certChainFile, err := os.OpenFile(certChainPath, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0600)
337366
if err != nil {
338367
return err
@@ -355,7 +384,7 @@ func (c *TLSCA) WriteCert(commonName string, key *rsa.PrivateKey, cert *x509.Cer
355384
}
356385

357386
// print
358-
fmt.Println("write cert to", fmt.Sprintf("./certs/%s/{%s.key,%s.crt,%s.bundle.crt}", commonName, commonName, commonName, commonName))
387+
fmt.Println("write cert to", fmt.Sprintf("./x-ca/certs/%s/{%s.key,%s.crt,%s.bundle.crt}", commonName, commonName, commonName, commonName))
359388

360389
return nil
361390
}

main.go

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -25,29 +25,31 @@ import (
2525
)
2626

2727
var (
28-
createCa bool
29-
rootKeyPath string
30-
rootCertPath string
31-
tlsKeyPath string
32-
tlsCertPath string
33-
tlsChainPath string
34-
domainStr string
35-
commonName string
36-
domains []string
37-
ipStr string
38-
ips []net.IP
39-
help bool
28+
createCa bool
29+
rootKeyPath string
30+
rootCertPath string
31+
tlsKeyPath string
32+
tlsCertPath string
33+
tlsChainPath string
34+
tlsKeyPassword string
35+
domainStr string
36+
commonName string
37+
domains []string
38+
ipStr string
39+
ips []net.IP
40+
help bool
4041
)
4142

4243
func init() {
4344
flag.BoolVar(&createCa, "create-ca", false, "Create Root CA.")
44-
flag.StringVar(&rootKeyPath, "root-key", "x-ca/ca/root-ca/private/root-ca.key", "Root private key file path, PEM/? format.")
45+
flag.StringVar(&rootKeyPath, "root-key", "x-ca/ca/root-ca/private/root-ca.key", "Root private key file path, PEM format.")
4546
flag.StringVar(&rootCertPath, "root-cert", "x-ca/ca/root-ca.crt", "Root certificate file path, PEM format.")
46-
flag.StringVar(&tlsKeyPath, "tls-key", "x-ca/ca/tls-ca/private/tls-ca.key", "Second-Level private key file path, PEM/? format.")
47+
flag.StringVar(&tlsKeyPath, "tls-key", "x-ca/ca/tls-ca/private/tls-ca.key", "Second-Level private key file path, PEM format.")
4748
flag.StringVar(&tlsCertPath, "tls-cert", "x-ca/ca/tls-ca.crt", "Second-Level certificate file path, PEM format.")
4849
flag.StringVar(&tlsChainPath, "tls-chain", "x-ca/ca/tls-ca-chain.pem", "Root/Second-Level CA Chain file path, PEM format.")
49-
flag.StringVar(&commonName, "cn", "", "sign cert common name.")
50+
flag.StringVar(&tlsKeyPassword, "tls-key-password", "", "tls key password, only work for load github.com/x-ca/x-ca.")
5051
flag.StringVar(&domainStr, "domains", "", "Comma-Separated domain names.")
52+
flag.StringVar(&commonName, "cn", "", "sign cert common name.")
5153
flag.StringVar(&ipStr, "ips", "", "Comma-Separated IP addresses.")
5254
flag.BoolVar(&help, "help", false, "show help message")
5355

@@ -72,6 +74,9 @@ xca -cn xxxx \
7274
fmt.Println()
7375
fmt.Println("Usage:")
7476
flag.PrintDefaults()
77+
fmt.Println()
78+
fmt.Println(`Source Code:
79+
https://github.com/x-ca/go-ca`)
7580
}
7681
}
7782

@@ -143,7 +148,7 @@ func doCreateCa() error {
143148
func doSign() error {
144149
var err error
145150

146-
tlsCA, err := ca.LoadTLSCA(tlsKeyPath, tlsCertPath)
151+
tlsCA, err := ca.LoadTLSCA(tlsKeyPath, tlsCertPath, tlsKeyPassword)
147152
if err != nil {
148153
return err
149154
}

0 commit comments

Comments
 (0)