]> Cypherpunks.ru repositories - gogost.git/blob - cmd/cer-selfsigned-example/main.go
942de48c484bea51c889d069497fb290074bdb8c
[gogost.git] / cmd / cer-selfsigned-example / main.go
1 // GoGOST -- Pure Go GOST cryptographic functions library
2 // Copyright (C) 2015-2023 Sergey Matveev <stargrave@stargrave.org>
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, version 3 of the License.
7 //
8 // This program is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 // GNU General Public License for more details.
12 //
13 // You should have received a copy of the GNU General Public License
14 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
15
16 // Example X.509 certificate issuing utility.
17 package main
18
19 import (
20         "crypto/rand"
21         "crypto/x509"
22         "crypto/x509/pkix"
23         "encoding/pem"
24         "flag"
25         "io"
26         "log"
27         "math/big"
28         "os"
29         "time"
30
31         "crypto/go.cypherpunks.ru/gogost/v5/gost3410"
32         "crypto/go.cypherpunks.ru/gogost/v5/gost34112012256"
33 )
34
35 const (
36         PEMKey = "PRIVATE KEY"
37         PEMCer = "CERTIFICATE"
38 )
39
40 func loadKeypair(filename string) (cer *x509.Certificate, prv any, err error) {
41         var data []byte
42         data, err = os.ReadFile(filename)
43         if err != nil {
44                 return
45         }
46         var block *pem.Block
47         for len(data) > 0 {
48                 block, data = pem.Decode(data)
49                 if block == nil {
50                         continue
51                 }
52                 switch block.Type {
53                 case PEMCer:
54                         cer, err = x509.ParseCertificate(block.Bytes)
55                 case PEMKey:
56                         prv, err = x509.ParsePKCS8PrivateKey(block.Bytes)
57                 }
58                 if err != nil {
59                         return
60                 }
61         }
62         return
63 }
64
65 func main() {
66         ca := flag.Bool("ca", false, "Enable BasicConstraints.cA")
67         cn := flag.String("cn", "", "Subject's CommonName")
68         country := flag.String("country", "", "Subject's Country")
69         serial := flag.Int64("serial", -1, "Serial number")
70         ai := flag.String("ai", "", "Signing algorithm: {256[ABCD],512[ABC]}")
71         issueWith := flag.String("issue-with", "", "Path to PEM with CA to issue the child")
72         reuseKey := flag.String("reuse-key", "", "Path to PEM with the key to reuse")
73         outKey := flag.String("out-key", "", "Path to PEM with the resulting key")
74         onlyKey := flag.Bool("only-key", false, "Only generate the key")
75         outCer := flag.String("out-cert", "", "Path to PEM with the resulting certificate")
76         flag.Parse()
77         log.SetFlags(log.Lshortfile)
78
79         if *cn == "" {
80                 log.Fatalln("no CommonName is set")
81         }
82         var curve *gost3410.Curve
83         var sigAlg x509.SignatureAlgorithm
84         switch *ai {
85         case "256A":
86                 curve = gost3410.CurveIdtc26gost341012256paramSetA()
87                 sigAlg = x509.GOST256
88         case "256B":
89                 curve = gost3410.CurveIdtc26gost341012256paramSetB()
90                 sigAlg = x509.GOST256
91         case "256C":
92                 curve = gost3410.CurveIdtc26gost341012256paramSetC()
93                 sigAlg = x509.GOST256
94         case "256D":
95                 curve = gost3410.CurveIdtc26gost341012256paramSetD()
96                 sigAlg = x509.GOST256
97         case "512A":
98                 curve = gost3410.CurveIdtc26gost341012512paramSetA()
99                 sigAlg = x509.GOST512
100         case "512B":
101                 curve = gost3410.CurveIdtc26gost341012512paramSetB()
102                 sigAlg = x509.GOST512
103         case "512C":
104                 curve = gost3410.CurveIdtc26gost341012512paramSetC()
105                 sigAlg = x509.GOST512
106         default:
107                 log.Fatalln("unknown curve name")
108         }
109
110         var err error
111         var caCer *x509.Certificate
112         var caPrv any
113         if *issueWith != "" {
114                 caCer, caPrv, err = loadKeypair(*issueWith)
115                 if err != nil {
116                         log.Fatalln(err)
117                 }
118                 sigAlg = caCer.SignatureAlgorithm
119         }
120
121         var prv any
122         if *reuseKey == "" {
123                 prvRaw := make([]byte, curve.PointSize())
124                 if _, err := io.ReadFull(rand.Reader, prvRaw); err != nil {
125                         log.Fatalln(err)
126                 }
127                 prv, err = gost3410.NewPrivateKey(curve, prvRaw)
128                 if err != nil {
129                         log.Fatalln(err)
130                 }
131                 data, err := x509.MarshalPKCS8PrivateKey(prv)
132                 if err != nil {
133                         log.Fatalln(err)
134                 }
135                 data = pem.EncodeToMemory(&pem.Block{Type: PEMKey, Bytes: data})
136                 if *outKey == "" {
137                         _, err = os.Stdout.Write(data)
138                 } else {
139                         err = os.WriteFile(*outKey, data, 0o666)
140                 }
141                 if err != nil {
142                         log.Fatalln(err)
143                 }
144                 if *onlyKey {
145                         return
146                 }
147         } else {
148                 _, prv, err = loadKeypair(*reuseKey)
149                 if err != nil {
150                         log.Fatalln(err)
151                 }
152         }
153
154         notBefore := time.Now().UTC()
155         days := 365 * 24 * time.Hour
156         if *ca {
157                 days *= 10
158         }
159         notAfter := notBefore.Add(days)
160
161         sn := big.NewInt(0)
162         if *serial == -1 {
163                 data := make([]byte, 16, gost34112012256.Size)
164                 if _, err = io.ReadFull(rand.Reader, data); err != nil {
165                         log.Fatalln(err)
166                 }
167                 hasher := gost34112012256.New()
168                 if _, err = hasher.Write(data); err != nil {
169                         log.Fatalln(err)
170                 }
171                 data = hasher.Sum(data[:0])
172                 sn = sn.SetBytes(data[:20])
173         } else {
174                 sn = sn.SetInt64(*serial)
175         }
176
177         subj := pkix.Name{CommonName: *cn}
178         if *country != "" {
179                 subj.Country = []string{*country}
180         }
181
182         pub, err := prv.(*gost3410.PrivateKey).PublicKey()
183         if err != nil {
184                 log.Fatalln(err)
185         }
186         hasher := gost34112012256.New()
187         if _, err = hasher.Write(pub.Raw()); err != nil {
188                 log.Fatalln(err)
189         }
190         spki := hasher.Sum(nil)
191         spki = spki[:20]
192
193         cerTmpl := x509.Certificate{
194                 KeyUsage:           x509.KeyUsageDigitalSignature,
195                 NotBefore:          notBefore,
196                 NotAfter:           notAfter,
197                 SerialNumber:       sn,
198                 SignatureAlgorithm: sigAlg,
199                 Subject:            subj,
200                 SubjectKeyId:       spki,
201         }
202         if *ca {
203                 cerTmpl.BasicConstraintsValid = true
204                 cerTmpl.IsCA = true
205                 cerTmpl.KeyUsage |= x509.KeyUsageCertSign
206         } else {
207                 cerTmpl.DNSNames = []string{*cn}
208         }
209
210         if caCer == nil {
211                 caCer = &cerTmpl
212                 caPrv = prv
213         }
214         data, err := x509.CreateCertificate(
215                 rand.Reader,
216                 &cerTmpl, caCer, pub,
217                 &gost3410.PrivateKeyReverseDigest{Prv: caPrv.(*gost3410.PrivateKey)},
218         )
219         data = pem.EncodeToMemory(&pem.Block{Type: PEMCer, Bytes: data})
220         if *outCer == "" {
221                 _, err = os.Stdout.Write(data)
222         } else {
223                 err = os.WriteFile(*outCer, data, 0o666)
224         }
225         if err != nil {
226                 log.Fatalln(err)
227         }
228 }