gorush/vendor/github.com/sideshow/apns2/certificate/certificate.go

134 lines
4.0 KiB
Go

// 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
}