forked from braze-inc/apns2
-
Notifications
You must be signed in to change notification settings - Fork 0
/
certificate.go
133 lines (124 loc) · 3.97 KB
/
certificate.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// Package certificate contains functions to load an Apple APNs PKCS#12
// or PEM certificate from either an in memory byte array or a local file.
package certificate
import (
"crypto"
"crypto/tls"
"crypto/x509"
"encoding/pem"
"errors"
"io/ioutil"
"strings"
"golang.org/x/crypto/pkcs12"
)
// Possible errors when parsing a certificate.
var (
ErrFailedToDecryptKey = errors.New("failed to decrypt private key")
ErrFailedToParsePrivateKey = errors.New("failed to parse private key")
ErrFailedToParseCertificate = errors.New("failed to parse certificate PEM data")
ErrNoPrivateKey = errors.New("no private key")
ErrNoCertificate = errors.New("no certificate")
)
// FromP12File loads a PKCS#12 certificate from a local file and returns a
// tls.Certificate.
//
// Use "" as the password argument if the PKCS#12 certificate is not password
// protected.
func FromP12File(filename string, password string) (tls.Certificate, error) {
p12bytes, err := ioutil.ReadFile(filename)
if err != nil {
return tls.Certificate{}, err
}
return FromP12Bytes(p12bytes, password)
}
// FromP12Bytes loads a PKCS#12 certificate from an in memory byte array and
// returns a tls.Certificate.
//
// Use "" as the password argument if the PKCS#12 certificate is not password
// protected.
func FromP12Bytes(bytes []byte, password string) (tls.Certificate, error) {
key, cert, err := pkcs12.Decode(bytes, password)
if err != nil {
return tls.Certificate{}, err
}
return tls.Certificate{
Certificate: [][]byte{cert.Raw},
PrivateKey: key,
Leaf: cert,
}, nil
}
// FromPemFile loads a PEM certificate from a local file and returns a
// tls.Certificate. This function is similar to the crypto/tls LoadX509KeyPair
// function, however it supports PEM files with the cert and key combined
// in the same file, as well as password protected key files which are both
// common with APNs certificates.
//
// Use "" as the password argument if the PEM certificate is not password
// protected.
func FromPemFile(filename string, password string) (tls.Certificate, error) {
bytes, err := ioutil.ReadFile(filename)
if err != nil {
return tls.Certificate{}, err
}
return FromPemBytes(bytes, password)
}
// FromPemBytes loads a PEM certificate from an in memory byte array and
// returns a tls.Certificate. This function is similar to the crypto/tls
// X509KeyPair function, however it supports PEM files with the cert and
// key combined, as well as password protected keys which are both common with
// APNs certificates.
//
// Use "" as the password argument if the PEM certificate is not password
// protected.
func FromPemBytes(bytes []byte, password string) (tls.Certificate, error) {
var cert tls.Certificate
var block *pem.Block
for {
block, bytes = pem.Decode(bytes)
if block == nil {
break
}
if block.Type == "CERTIFICATE" {
cert.Certificate = append(cert.Certificate, block.Bytes)
}
if block.Type == "PRIVATE KEY" || strings.HasSuffix(block.Type, "PRIVATE KEY") {
key, err := unencryptPrivateKey(block, password)
if err != nil {
return tls.Certificate{}, err
}
cert.PrivateKey = key
}
}
if len(cert.Certificate) == 0 {
return tls.Certificate{}, ErrNoCertificate
}
if cert.PrivateKey == nil {
return tls.Certificate{}, ErrNoPrivateKey
}
if c, e := x509.ParseCertificate(cert.Certificate[0]); e == nil {
cert.Leaf = c
}
return cert, nil
}
func unencryptPrivateKey(block *pem.Block, password string) (crypto.PrivateKey, error) {
if x509.IsEncryptedPEMBlock(block) {
bytes, err := x509.DecryptPEMBlock(block, []byte(password))
if err != nil {
return nil, ErrFailedToDecryptKey
}
return parsePrivateKey(bytes)
}
return parsePrivateKey(block.Bytes)
}
func parsePrivateKey(bytes []byte) (crypto.PrivateKey, error) {
var key crypto.PrivateKey
key, err := x509.ParsePKCS1PrivateKey(bytes)
if err == nil {
return key, nil
}
key, err = x509.ParsePKCS8PrivateKey(bytes)
if err == nil {
return key, nil
}
return nil, ErrFailedToParsePrivateKey
}