Consolidate key/cert/signature encoding and decoding (#1243)
This commit is contained in:
parent
0ffdaeebce
commit
ef61c170b1
@ -129,9 +129,9 @@ func saveIdentityTar(path string, key *ecdsa.PrivateKey, id storj.NodeID) error
|
||||
tw := tar.NewWriter(tarData)
|
||||
|
||||
caCertBytes, caCertErr := peertls.ChainBytes(ca.Cert)
|
||||
caKeyBytes, caKeyErr := pkcrypto.KeyBytes(ca.Key)
|
||||
caKeyBytes, caKeyErr := pkcrypto.PrivateKeyToPEM(ca.Key)
|
||||
identCertBytes, identCertErr := peertls.ChainBytes(ident.Leaf, ident.CA)
|
||||
identKeyBytes, identKeyErr := pkcrypto.KeyBytes(ident.Key)
|
||||
identKeyBytes, identKeyErr := pkcrypto.PrivateKeyToPEM(ident.Key)
|
||||
if err := errs.Combine(caCertErr, caKeyErr, identCertErr, identKeyErr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -159,7 +159,7 @@ func cmdAuthorize(cmd *cobra.Command, args []string) error {
|
||||
return errs.New("error occurred while signing certificate: %s\n(identity files were still generated and saved, if you try again existing files will be loaded)", err)
|
||||
}
|
||||
|
||||
signedChain, err := identity.ParseCertChain(signedChainBytes)
|
||||
signedChain, err := pkcrypto.CertsFromDER(signedChainBytes)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
@ -101,14 +100,14 @@ func inmemoryTest(flags *Flags, command string, args []string) error {
|
||||
}
|
||||
|
||||
var chainPEM bytes.Buffer
|
||||
errLeaf := pem.Encode(&chainPEM, pkcrypto.NewCertBlock(identity.Leaf.Raw))
|
||||
errCA := pem.Encode(&chainPEM, pkcrypto.NewCertBlock(identity.CA.Raw))
|
||||
errLeaf := pkcrypto.WriteCertPEM(&chainPEM, identity.Leaf)
|
||||
errCA := pkcrypto.WriteCertPEM(&chainPEM, identity.CA)
|
||||
if errLeaf != nil || errCA != nil {
|
||||
return errs.Combine(errLeaf, errCA, planet.Shutdown())
|
||||
}
|
||||
|
||||
var key bytes.Buffer
|
||||
errKey := pkcrypto.WriteKey(&key, identity.Key)
|
||||
errKey := pkcrypto.WritePrivateKeyPEM(&key, identity.Key)
|
||||
if errKey != nil {
|
||||
return errs.Combine(errKey, planet.Shutdown())
|
||||
}
|
||||
|
@ -9,7 +9,6 @@ package main
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/pem"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/format"
|
||||
@ -58,7 +57,7 @@ func main() {
|
||||
}
|
||||
|
||||
var keys bytes.Buffer
|
||||
err = peertls.WriteKey(&keys, identity.Key)
|
||||
err = peertls.WriteKeyPEM(&keys, identity.Key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -86,13 +85,3 @@ func main() {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func encodeBlocks(blocks ...*pem.Block) ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
for _, block := range blocks {
|
||||
if err := pem.Encode(&buf, block); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ package auth
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/x509"
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/gtank/cryptopasta"
|
||||
@ -13,6 +12,7 @@ import (
|
||||
|
||||
"storj.io/storj/pkg/identity"
|
||||
"storj.io/storj/pkg/peertls"
|
||||
"storj.io/storj/pkg/pkcrypto"
|
||||
"storj.io/storj/pkg/storj"
|
||||
)
|
||||
|
||||
@ -128,7 +128,7 @@ func VerifyMsg(msg SignableMessage, signer storj.NodeID) error {
|
||||
}
|
||||
|
||||
func parseECDSA(rawCert []byte) (*ecdsa.PublicKey, error) {
|
||||
cert, err := x509.ParseCertificate(rawCert)
|
||||
cert, err := pkcrypto.CertFromDER(rawCert)
|
||||
if err != nil {
|
||||
return nil, ErrVerify.Wrap(err)
|
||||
}
|
||||
|
@ -293,7 +293,8 @@ func testDatabase(ctx context.Context, t *testing.T, bwdb bwagreement.DB) {
|
||||
assert.NoError(t, err)
|
||||
rba.Signature = []byte("invalid")
|
||||
reply, err := satellite.BandwidthAgreements(ctxSN1, rba)
|
||||
assert.True(t, auth.ErrSigLen.Has(err) && pb.ErrRenter.Has(err), err.Error())
|
||||
assert.Error(t, err)
|
||||
assert.True(t, pb.ErrRenter.Has(err), err.Error())
|
||||
assert.Equal(t, pb.AgreementsSummary_REJECTED, reply.Status)
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,7 @@ import (
|
||||
"storj.io/storj/internal/testplanet"
|
||||
"storj.io/storj/pkg/identity"
|
||||
"storj.io/storj/pkg/pb"
|
||||
"storj.io/storj/pkg/pkcrypto"
|
||||
"storj.io/storj/pkg/server"
|
||||
"storj.io/storj/pkg/transport"
|
||||
"storj.io/storj/pkg/utils"
|
||||
@ -663,7 +664,7 @@ func TestCertificateSigner_Sign_E2E(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, signedChainBytes)
|
||||
|
||||
signedChain, err := identity.ParseCertChain(signedChainBytes)
|
||||
signedChain, err := pkcrypto.CertsFromDER(signedChainBytes)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, clientIdent.CA.RawTBSCertificate, signedChain[0].RawTBSCertificate)
|
||||
@ -829,7 +830,7 @@ func TestCertificateSigner_Sign(t *testing.T) {
|
||||
require.NotNil(t, res)
|
||||
require.NotEmpty(t, res.Chain)
|
||||
|
||||
signedChain, err := identity.ParseCertChain(res.Chain)
|
||||
signedChain, err := pkcrypto.CertsFromDER(res.Chain)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, clientIdent.CA.RawTBSCertificate, signedChain[0].RawTBSCertificate)
|
||||
|
@ -10,7 +10,6 @@ import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
@ -238,10 +237,9 @@ func (fc FullCAConfig) Load() (*FullCertificateAuthority, error) {
|
||||
if err != nil {
|
||||
return nil, peertls.ErrNotExist.Wrap(err)
|
||||
}
|
||||
kp, _ := pem.Decode(kb)
|
||||
k, err := x509.ParseECPrivateKey(kp.Bytes)
|
||||
k, err := pkcrypto.PrivateKeyFromPEM(kb)
|
||||
if err != nil {
|
||||
return nil, errs.New("unable to parse EC private key: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &FullCertificateAuthority{
|
||||
@ -271,7 +269,7 @@ func (fc FullCAConfig) Save(ca *FullCertificateAuthority) error {
|
||||
}
|
||||
|
||||
if fc.KeyPath != "" {
|
||||
if err := pkcrypto.WriteKey(&keyData, ca.Key); err != nil {
|
||||
if err := pkcrypto.WritePrivateKeyPEM(&keyData, ca.Key); err != nil {
|
||||
writeErrs.Add(err)
|
||||
return writeErrs.Err()
|
||||
}
|
||||
@ -299,7 +297,7 @@ func (pc PeerCAConfig) Load() (*PeerCertificateAuthority, error) {
|
||||
return nil, peertls.ErrNotExist.Wrap(err)
|
||||
}
|
||||
|
||||
chain, err := DecodeAndParseChainPEM(chainPEM)
|
||||
chain, err := pkcrypto.CertsFromPEM(chainPEM)
|
||||
if err != nil {
|
||||
return nil, errs.New("failed to load identity %#v: %v",
|
||||
pc.CertPath, err)
|
||||
@ -408,7 +406,7 @@ func (ca *FullCertificateAuthority) Sign(cert *x509.Certificate) (*x509.Certific
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
|
||||
signedCert, err := x509.ParseCertificate(signedCertBytes)
|
||||
signedCert, err := pkcrypto.CertFromDER(signedCertBytes)
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
|
@ -85,15 +85,11 @@ func FullIdentityFromPEM(chainPEM, keyPEM []byte) (*FullIdentity, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
keysBytes, err := decodePEM(keyPEM)
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
// NB: there shouldn't be multiple keys in the key file but if there
|
||||
// are, this uses the first one
|
||||
key, err := x509.ParseECPrivateKey(keysBytes[0])
|
||||
key, err := pkcrypto.PrivateKeyFromPEM(keyPEM)
|
||||
if err != nil {
|
||||
return nil, errs.New("unable to parse EC private key: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &FullIdentity{
|
||||
@ -108,12 +104,12 @@ func FullIdentityFromPEM(chainPEM, keyPEM []byte) (*FullIdentity, error) {
|
||||
// PeerIdentityFromPEM loads a PeerIdentity from a certificate chain and
|
||||
// private key PEM-encoded bytes
|
||||
func PeerIdentityFromPEM(chainPEM []byte) (*PeerIdentity, error) {
|
||||
chain, err := DecodeAndParseChainPEM(chainPEM)
|
||||
chain, err := pkcrypto.CertsFromPEM(chainPEM)
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
if len(chain) < peertls.CAIndex+1 {
|
||||
return nil, ErrChainLength.New("identity chain does not contain a CA certificate")
|
||||
return nil, pkcrypto.ErrChainLength.New("identity chain does not contain a CA certificate")
|
||||
}
|
||||
nodeID, err := NodeIDFromKey(chain[peertls.CAIndex].PublicKey)
|
||||
if err != nil {
|
||||
@ -128,19 +124,6 @@ func PeerIdentityFromPEM(chainPEM []byte) (*PeerIdentity, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ParseCertChain converts a chain of certificate bytes into x509 certs
|
||||
func ParseCertChain(chain [][]byte) ([]*x509.Certificate, error) {
|
||||
c := make([]*x509.Certificate, len(chain))
|
||||
for i, ct := range chain {
|
||||
cp, err := x509.ParseCertificate(ct)
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
c[i] = cp
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// PeerIdentityFromCerts loads a PeerIdentity from a pair of leaf and ca x509 certificates
|
||||
func PeerIdentityFromCerts(leaf, ca *x509.Certificate, rest []*x509.Certificate) (*PeerIdentity, error) {
|
||||
i, err := NodeIDFromKey(ca.PublicKey)
|
||||
@ -192,7 +175,7 @@ func NodeIDFromCertPath(certPath string) (storj.NodeID, error) {
|
||||
|
||||
// NodeIDFromPEM loads a node ID from certificate bytes
|
||||
func NodeIDFromPEM(pemBytes []byte) (storj.NodeID, error) {
|
||||
chain, err := DecodeAndParseChainPEM(pemBytes)
|
||||
chain, err := pkcrypto.CertsFromPEM(pemBytes)
|
||||
if err != nil {
|
||||
return storj.NodeID{}, Error.New("invalid identity certificate")
|
||||
}
|
||||
@ -299,7 +282,7 @@ func (ic Config) Save(fi *FullIdentity) error {
|
||||
}
|
||||
|
||||
if ic.KeyPath != "" {
|
||||
writeKeyErr = pkcrypto.WriteKey(&keyData, fi.Key)
|
||||
writeKeyErr = pkcrypto.WritePrivateKeyPEM(&keyData, fi.Key)
|
||||
writeKeyDataErr = writeKeyData(ic.KeyPath, keyData.Bytes())
|
||||
}
|
||||
|
||||
|
@ -7,8 +7,6 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"os"
|
||||
"runtime"
|
||||
"testing"
|
||||
@ -70,15 +68,11 @@ func TestFullIdentityFromPEM(t *testing.T) {
|
||||
assert.NotEmpty(t, leafCert)
|
||||
|
||||
chainPEM := bytes.NewBuffer([]byte{})
|
||||
assert.NoError(t, pem.Encode(chainPEM, pkcrypto.NewCertBlock(leafCert.Raw)))
|
||||
assert.NoError(t, pem.Encode(chainPEM, pkcrypto.NewCertBlock(caCert.Raw)))
|
||||
|
||||
leafKeyBytes, err := x509.MarshalECPrivateKey(leafKey)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, leafKeyBytes)
|
||||
assert.NoError(t, pkcrypto.WriteCertPEM(chainPEM, leafCert))
|
||||
assert.NoError(t, pkcrypto.WriteCertPEM(chainPEM, caCert))
|
||||
|
||||
keyPEM := bytes.NewBuffer([]byte{})
|
||||
assert.NoError(t, pem.Encode(keyPEM, pkcrypto.NewKeyBlock(leafKeyBytes)))
|
||||
assert.NoError(t, pkcrypto.WritePrivateKeyPEM(keyPEM, leafKey))
|
||||
|
||||
fullIdent, err := identity.FullIdentityFromPEM(chainPEM.Bytes(), keyPEM.Bytes())
|
||||
assert.NoError(t, err)
|
||||
@ -98,22 +92,18 @@ func TestConfig_SaveIdentity(t *testing.T) {
|
||||
fi := pregeneratedIdentity(t)
|
||||
|
||||
chainPEM := bytes.NewBuffer([]byte{})
|
||||
assert.NoError(t, pem.Encode(chainPEM, pkcrypto.NewCertBlock(fi.Leaf.Raw)))
|
||||
assert.NoError(t, pem.Encode(chainPEM, pkcrypto.NewCertBlock(fi.CA.Raw)))
|
||||
assert.NoError(t, pkcrypto.WriteCertPEM(chainPEM, fi.Leaf))
|
||||
assert.NoError(t, pkcrypto.WriteCertPEM(chainPEM, fi.CA))
|
||||
|
||||
privateKey, ok := fi.Key.(*ecdsa.PrivateKey)
|
||||
assert.True(t, ok)
|
||||
assert.NotEmpty(t, privateKey)
|
||||
|
||||
keyBytes, err := x509.MarshalECPrivateKey(privateKey)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, keyBytes)
|
||||
|
||||
keyPEM := bytes.NewBuffer([]byte{})
|
||||
assert.NoError(t, pem.Encode(keyPEM, pkcrypto.NewKeyBlock(keyBytes)))
|
||||
assert.NoError(t, pkcrypto.WritePrivateKeyPEM(keyPEM, privateKey))
|
||||
|
||||
{ // test saving
|
||||
err = ic.Save(fi)
|
||||
err := ic.Save(fi)
|
||||
assert.NoError(t, err)
|
||||
|
||||
certInfo, err := os.Stat(ic.CertPath)
|
||||
|
@ -4,18 +4,11 @@
|
||||
package identity
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"encoding/pem"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/zeebo/errs"
|
||||
|
||||
"storj.io/storj/pkg/pkcrypto"
|
||||
"storj.io/storj/pkg/utils"
|
||||
)
|
||||
|
||||
// TLSFilesStatus is the status of keys
|
||||
@ -30,66 +23,10 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrChainLength is used when the length of a cert chain isn't what was expected
|
||||
ErrChainLength = errs.Class("cert chain length error")
|
||||
// ErrZeroBytes is returned for zero slice
|
||||
ErrZeroBytes = errs.New("byte slice was unexpectedly empty")
|
||||
)
|
||||
|
||||
type encodedChain struct {
|
||||
chain [][]byte
|
||||
extensions [][][]byte
|
||||
}
|
||||
|
||||
// DecodeAndParseChainPEM parses a PEM chain
|
||||
func DecodeAndParseChainPEM(PEMBytes []byte) ([]*x509.Certificate, error) {
|
||||
var (
|
||||
encChain encodedChain
|
||||
blockErrs utils.ErrorGroup
|
||||
)
|
||||
for {
|
||||
var pemBlock *pem.Block
|
||||
pemBlock, PEMBytes = pem.Decode(PEMBytes)
|
||||
if pemBlock == nil {
|
||||
break
|
||||
}
|
||||
switch pemBlock.Type {
|
||||
case pkcrypto.BlockTypeCertificate:
|
||||
encChain.AddCert(pemBlock.Bytes)
|
||||
case pkcrypto.BlockTypeExtension:
|
||||
if err := encChain.AddExtension(pemBlock.Bytes); err != nil {
|
||||
blockErrs.Add(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := blockErrs.Finish(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return encChain.Parse()
|
||||
}
|
||||
|
||||
func decodePEM(PEMBytes []byte) ([][]byte, error) {
|
||||
var DERBytes [][]byte
|
||||
|
||||
for {
|
||||
var DERBlock *pem.Block
|
||||
|
||||
DERBlock, PEMBytes = pem.Decode(PEMBytes)
|
||||
if DERBlock == nil {
|
||||
break
|
||||
}
|
||||
|
||||
DERBytes = append(DERBytes, DERBlock.Bytes)
|
||||
}
|
||||
|
||||
if len(DERBytes) == 0 || len(DERBytes[0]) == 0 {
|
||||
return nil, ErrZeroBytes
|
||||
}
|
||||
|
||||
return DERBytes, nil
|
||||
}
|
||||
|
||||
// writeChainData writes data to path ensuring permissions are appropriate for a cert
|
||||
func writeChainData(path string, data []byte) error {
|
||||
err := writeFile(path, 0744, 0644, data)
|
||||
@ -150,43 +87,3 @@ func (t TLSFilesStatus) String() string {
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (e *encodedChain) AddCert(b []byte) {
|
||||
e.chain = append(e.chain, b)
|
||||
e.extensions = append(e.extensions, [][]byte{})
|
||||
}
|
||||
|
||||
func (e *encodedChain) AddExtension(b []byte) error {
|
||||
chainLen := len(e.chain)
|
||||
if chainLen < 1 {
|
||||
return ErrChainLength.New("expected: >= 1; actual: %d", chainLen)
|
||||
}
|
||||
|
||||
i := chainLen - 1
|
||||
e.extensions[i] = append(e.extensions[i], b)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *encodedChain) Parse() ([]*x509.Certificate, error) {
|
||||
chain, err := ParseCertChain(e.chain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var extErrs utils.ErrorGroup
|
||||
for i, cert := range chain {
|
||||
for _, ee := range e.extensions[i] {
|
||||
ext := pkix.Extension{}
|
||||
_, err := asn1.Unmarshal(ee, &ext)
|
||||
if err != nil {
|
||||
extErrs.Add(err)
|
||||
}
|
||||
cert.ExtraExtensions = append(cert.ExtraExtensions, ext)
|
||||
}
|
||||
}
|
||||
if err := extErrs.Finish(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return chain, nil
|
||||
}
|
||||
|
@ -10,8 +10,6 @@ import (
|
||||
"crypto/rand"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/asn1"
|
||||
"encoding/pem"
|
||||
"io"
|
||||
|
||||
"github.com/zeebo/errs"
|
||||
@ -23,10 +21,10 @@ import (
|
||||
var (
|
||||
// ErrNotExist is used when a file or directory doesn't exist.
|
||||
ErrNotExist = errs.Class("file or directory not found error")
|
||||
// ErrTLSTemplate is used when an error occurs during tls template generation.
|
||||
ErrTLSTemplate = errs.Class("tls template error")
|
||||
// ErrGenerate is used when an error occurred during cert/key generation.
|
||||
ErrGenerate = errs.Class("tls generation error")
|
||||
// ErrTLSTemplate is used when an error occurs during tls template generation.
|
||||
ErrTLSTemplate = errs.Class("tls template error")
|
||||
// ErrVerifyPeerCert is used when an error occurs during `VerifyPeerCertificate`.
|
||||
ErrVerifyPeerCert = errs.Class("tls peer certificate verification error")
|
||||
// ErrVerifyCertificateChain is used when a certificate chain can't be verified from leaf to root
|
||||
@ -44,7 +42,7 @@ type PeerCertVerificationFunc func([][]byte, [][]*x509.Certificate) error
|
||||
// functions and adds certificate parsing.
|
||||
func VerifyPeerFunc(next ...PeerCertVerificationFunc) PeerCertVerificationFunc {
|
||||
return func(chain [][]byte, _ [][]*x509.Certificate) error {
|
||||
c, err := pkcrypto.ParseCertificates(chain)
|
||||
c, err := pkcrypto.CertsFromDER(chain)
|
||||
if err != nil {
|
||||
return ErrVerifyPeerCert.Wrap(err)
|
||||
}
|
||||
@ -87,7 +85,7 @@ func VerifyCAWhitelist(cas []*x509.Certificate) PeerCertVerificationFunc {
|
||||
func TLSCert(chain [][]byte, leaf *x509.Certificate, key crypto.PrivateKey) (*tls.Certificate, error) {
|
||||
var err error
|
||||
if leaf == nil {
|
||||
leaf, err = x509.ParseCertificate(chain[0])
|
||||
leaf, err = pkcrypto.CertFromDER(chain[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -108,16 +106,12 @@ func WriteChain(w io.Writer, chain ...*x509.Certificate) error {
|
||||
|
||||
var extErrs utils.ErrorGroup
|
||||
for _, c := range chain {
|
||||
if err := pem.Encode(w, pkcrypto.NewCertBlock(c.Raw)); err != nil {
|
||||
if err := pkcrypto.WriteCertPEM(w, c); err != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
|
||||
for _, e := range c.ExtraExtensions {
|
||||
extBytes, err := asn1.Marshal(e)
|
||||
if err != nil {
|
||||
extErrs.Add(errs.Wrap(err))
|
||||
}
|
||||
if err := pem.Encode(w, pkcrypto.NewExtensionBlock(extBytes)); err != nil {
|
||||
if err := pkcrypto.WritePKIXExtensionPEM(w, &e); err != nil {
|
||||
extErrs.Add(errs.Wrap(err))
|
||||
}
|
||||
}
|
||||
@ -162,7 +156,7 @@ func NewCert(key, parentKey crypto.PrivateKey, template, parent *x509.Certificat
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
|
||||
cert, err := x509.ParseCertificate(cb)
|
||||
cert, err := pkcrypto.CertFromDER(cb)
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
|
@ -8,12 +8,20 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
// BlockTypeEcPrivateKey is the value to define a block type of private key
|
||||
BlockTypeEcPrivateKey = "EC PRIVATE KEY"
|
||||
// BlockTypeCertificate is the value to define a block type of certificates
|
||||
BlockTypeCertificate = "CERTIFICATE"
|
||||
// BlockTypeExtension is the value to define a block type of certificate extensions
|
||||
BlockTypeExtension = "EXTENSION"
|
||||
// BlockLabelEcPrivateKey is the value to define a block label of EC private key
|
||||
// (which is used here only for backwards compatibility). Use a general PKCS#8
|
||||
// encoding instead.
|
||||
BlockLabelEcPrivateKey = "EC PRIVATE KEY"
|
||||
// BlockLabelPrivateKey is the value to define a block label of general private key
|
||||
// (used for PKCS#8-encoded private keys of type RSA, ECDSA, and others).
|
||||
BlockLabelPrivateKey = "PRIVATE KEY"
|
||||
// BlockLabelPublicKey is the value to define a block label of general public key
|
||||
// (used for PKIX-encoded public keys of type RSA, ECDSA, and others).
|
||||
BlockLabelPublicKey = "PUBLIC KEY"
|
||||
// BlockLabelCertificate is the value to define a block label of certificates
|
||||
BlockLabelCertificate = "CERTIFICATE"
|
||||
// BlockLabelExtension is the value to define a block label of certificate extensions
|
||||
BlockLabelExtension = "EXTENSION"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -25,4 +33,6 @@ var (
|
||||
ErrSign = errs.Class("unable to generate signature")
|
||||
// ErrVerifySignature is used when a cert-chain signature verificaion error occurs.
|
||||
ErrVerifySignature = errs.Class("tls certificate signature verification error")
|
||||
// ErrChainLength is used when the length of a cert chain isn't what was expected
|
||||
ErrChainLength = errs.Class("cert chain length error")
|
||||
)
|
||||
|
@ -4,75 +4,313 @@
|
||||
package pkcrypto
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"encoding/pem"
|
||||
"io"
|
||||
"math/big"
|
||||
|
||||
"github.com/zeebo/errs"
|
||||
|
||||
"storj.io/storj/pkg/utils"
|
||||
)
|
||||
|
||||
// WriteKey writes the private key, PEM-encoded.
|
||||
func WriteKey(w io.Writer, key crypto.PrivateKey) error {
|
||||
var (
|
||||
kb []byte
|
||||
err error
|
||||
)
|
||||
|
||||
switch k := key.(type) {
|
||||
case *ecdsa.PrivateKey:
|
||||
kb, err = x509.MarshalECPrivateKey(k)
|
||||
if err != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
default:
|
||||
return ErrUnsupportedKey.New("%T", k)
|
||||
// WritePublicKeyPEM writes the public key, in a PEM-enveloped
|
||||
// PKIX form.
|
||||
func WritePublicKeyPEM(w io.Writer, key crypto.PublicKey) error {
|
||||
kb, err := PublicKeyToPKIX(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = pem.Encode(w, &pem.Block{Type: BlockLabelPublicKey, Bytes: kb})
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
|
||||
if err := pem.Encode(w, NewKeyBlock(kb)); err != nil {
|
||||
// PublicKeyToPEM encodes a public key to a PEM-enveloped PKIX form.
|
||||
func PublicKeyToPEM(key crypto.PublicKey) ([]byte, error) {
|
||||
kb, err := PublicKeyToPKIX(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pem.EncodeToMemory(&pem.Block{Type: BlockLabelPublicKey, Bytes: kb}), nil
|
||||
}
|
||||
|
||||
// PublicKeyToPKIX serializes a public key to a PKIX-encoded form.
|
||||
func PublicKeyToPKIX(key crypto.PublicKey) ([]byte, error) {
|
||||
return x509.MarshalPKIXPublicKey(key)
|
||||
}
|
||||
|
||||
// PublicKeyFromPKIX parses a public key from its PKIX encoding.
|
||||
func PublicKeyFromPKIX(pkixData []byte) (crypto.PublicKey, error) {
|
||||
return x509.ParsePKIXPublicKey(pkixData)
|
||||
}
|
||||
|
||||
// PublicKeyFromPEM parses a public key from its PEM-enveloped PKIX
|
||||
// encoding.
|
||||
func PublicKeyFromPEM(pemData []byte) (crypto.PublicKey, error) {
|
||||
pb, _ := pem.Decode(pemData)
|
||||
if pb == nil {
|
||||
return nil, ErrParseCerts.New("could not parse PEM encoding")
|
||||
}
|
||||
if pb.Type != BlockLabelPublicKey {
|
||||
return nil, ErrParseCerts.New("can not parse public key from PEM block labeled %q", pb.Type)
|
||||
}
|
||||
return PublicKeyFromPKIX(pb.Bytes)
|
||||
}
|
||||
|
||||
// WritePrivateKeyPEM writes the private key to the writer, in a PEM-enveloped
|
||||
// PKCS#8 form.
|
||||
func WritePrivateKeyPEM(w io.Writer, key crypto.PrivateKey) error {
|
||||
kb, err := PrivateKeyToPKCS8(key)
|
||||
if err != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
return nil
|
||||
err = pem.Encode(w, &pem.Block{Type: BlockLabelPrivateKey, Bytes: kb})
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
|
||||
// KeyBytes returns bytes of the private key, PEM-encoded.
|
||||
func KeyBytes(key crypto.PrivateKey) ([]byte, error) {
|
||||
var data bytes.Buffer
|
||||
err := WriteKey(&data, key)
|
||||
return data.Bytes(), err
|
||||
// PrivateKeyToPEM serializes a private key to a PEM-enveloped PKCS#8 form.
|
||||
func PrivateKeyToPEM(key crypto.PrivateKey) ([]byte, error) {
|
||||
kb, err := PrivateKeyToPKCS8(key)
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
return pem.EncodeToMemory(&pem.Block{Type: BlockLabelPrivateKey, Bytes: kb}), nil
|
||||
}
|
||||
|
||||
// NewKeyBlock converts an ASN1/DER-encoded byte-slice of a private key into
|
||||
// a `pem.Block` pointer.
|
||||
func NewKeyBlock(b []byte) *pem.Block {
|
||||
return &pem.Block{Type: BlockTypeEcPrivateKey, Bytes: b}
|
||||
// PrivateKeyToPKCS8 serializes a private key to a PKCS#8-encoded form.
|
||||
func PrivateKeyToPKCS8(key crypto.PrivateKey) ([]byte, error) {
|
||||
return x509.MarshalPKCS8PrivateKey(key)
|
||||
}
|
||||
|
||||
// NewCertBlock converts an ASN1/DER-encoded byte-slice of a tls certificate
|
||||
// into a `pem.Block` pointer.
|
||||
func NewCertBlock(b []byte) *pem.Block {
|
||||
return &pem.Block{Type: BlockTypeCertificate, Bytes: b}
|
||||
// PrivateKeyFromPKCS8 parses a private key from its PKCS#8 encoding.
|
||||
func PrivateKeyFromPKCS8(keyBytes []byte) (crypto.PrivateKey, error) {
|
||||
key, err := x509.ParsePKCS8PrivateKey(keyBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return crypto.PrivateKey(key), nil
|
||||
}
|
||||
|
||||
// NewExtensionBlock converts an ASN1/DER-encoded byte-slice of a tls certificate
|
||||
// extension into a `pem.Block` pointer.
|
||||
func NewExtensionBlock(b []byte) *pem.Block {
|
||||
return &pem.Block{Type: BlockTypeExtension, Bytes: b}
|
||||
// PrivateKeyFromPEM parses a private key from its PEM-enveloped PKCS#8
|
||||
// encoding.
|
||||
func PrivateKeyFromPEM(keyBytes []byte) (crypto.PrivateKey, error) {
|
||||
pb, _ := pem.Decode(keyBytes)
|
||||
if pb == nil {
|
||||
return nil, ErrParseCerts.New("could not parse PEM encoding")
|
||||
}
|
||||
switch pb.Type {
|
||||
case BlockLabelEcPrivateKey:
|
||||
return ecPrivateKeyFromASN1(pb.Bytes)
|
||||
case BlockLabelPrivateKey:
|
||||
return PrivateKeyFromPKCS8(pb.Bytes)
|
||||
}
|
||||
return nil, ErrParseCerts.New("can not parse private key from PEM block labeled %q", pb.Type)
|
||||
}
|
||||
|
||||
// ParseCertificates parses an x509 certificate from each of the given byte
|
||||
// slices, which should be encoded in DER. (Unwrap PEM encoding first if
|
||||
// necessary.)
|
||||
func ParseCertificates(rawCerts [][]byte) ([]*x509.Certificate, error) {
|
||||
// WriteCertPEM writes the certificate to the writer, in a PEM-enveloped DER
|
||||
// encoding.
|
||||
func WriteCertPEM(w io.Writer, cert *x509.Certificate) error {
|
||||
err := pem.Encode(w, &pem.Block{Type: BlockLabelCertificate, Bytes: cert.Raw})
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
|
||||
// CertToPEM returns the bytes of the certificate, in a PEM-enveloped DER
|
||||
// encoding.
|
||||
func CertToPEM(cert *x509.Certificate) []byte {
|
||||
return pem.EncodeToMemory(&pem.Block{Type: BlockLabelCertificate, Bytes: cert.Raw})
|
||||
}
|
||||
|
||||
// CertToDER returns the bytes of the certificate, in a DER encoding.
|
||||
//
|
||||
// Note that this is fairly useless, as x509.Certificate objects are always
|
||||
// supposed to have a member containing the raw DER encoding. But this is
|
||||
// included for completeness with the rest of this module's API.
|
||||
func CertToDER(cert *x509.Certificate) ([]byte, error) {
|
||||
return cert.Raw, nil
|
||||
}
|
||||
|
||||
// CertFromDER parses an X.509 certificate from its DER encoding.
|
||||
func CertFromDER(certDER []byte) (*x509.Certificate, error) {
|
||||
return x509.ParseCertificate(certDER)
|
||||
}
|
||||
|
||||
// CertFromPEM parses an X.509 certificate from its PEM-enveloped DER encoding.
|
||||
func CertFromPEM(certPEM []byte) (*x509.Certificate, error) {
|
||||
kb, _ := pem.Decode(certPEM)
|
||||
if kb == nil {
|
||||
return nil, ErrParseCerts.New("could not decode certificate as PEM")
|
||||
}
|
||||
if kb.Type != BlockLabelCertificate {
|
||||
return nil, ErrParseCerts.New("can not parse certificate from PEM block labeled %q", kb.Type)
|
||||
}
|
||||
return CertFromDER(kb.Bytes)
|
||||
}
|
||||
|
||||
// CertsFromDER parses an x509 certificate from each of the given byte
|
||||
// slices, which should be encoded in DER.
|
||||
func CertsFromDER(rawCerts [][]byte) ([]*x509.Certificate, error) {
|
||||
certs := make([]*x509.Certificate, len(rawCerts))
|
||||
for i, c := range rawCerts {
|
||||
var err error
|
||||
certs[i], err = x509.ParseCertificate(c)
|
||||
certs[i], err = CertFromDER(c)
|
||||
if err != nil {
|
||||
return nil, ErrParseCerts.New("unable to parse certificate at index %d", i)
|
||||
}
|
||||
}
|
||||
return certs, nil
|
||||
}
|
||||
|
||||
// CertsFromPEM parses a PEM chain from a single byte string (the PEM-enveloped
|
||||
// certificates should be concatenated). The PEM blocks may include PKIX
|
||||
// extensions.
|
||||
func CertsFromPEM(pemBytes []byte) ([]*x509.Certificate, error) {
|
||||
var (
|
||||
encChain encodedChain
|
||||
blockErrs utils.ErrorGroup
|
||||
)
|
||||
for {
|
||||
var pemBlock *pem.Block
|
||||
pemBlock, pemBytes = pem.Decode(pemBytes)
|
||||
if pemBlock == nil {
|
||||
break
|
||||
}
|
||||
switch pemBlock.Type {
|
||||
case BlockLabelCertificate:
|
||||
encChain.AddCert(pemBlock.Bytes)
|
||||
case BlockLabelExtension:
|
||||
if err := encChain.AddExtension(pemBlock.Bytes); err != nil {
|
||||
blockErrs.Add(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := blockErrs.Finish(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return encChain.Parse()
|
||||
}
|
||||
|
||||
type encodedChain struct {
|
||||
chain [][]byte
|
||||
extensions [][][]byte
|
||||
}
|
||||
|
||||
func (e *encodedChain) AddCert(b []byte) {
|
||||
e.chain = append(e.chain, b)
|
||||
e.extensions = append(e.extensions, [][]byte{})
|
||||
}
|
||||
|
||||
func (e *encodedChain) AddExtension(b []byte) error {
|
||||
chainLen := len(e.chain)
|
||||
if chainLen < 1 {
|
||||
return ErrChainLength.New("expected: >= 1; actual: %d", chainLen)
|
||||
}
|
||||
|
||||
i := chainLen - 1
|
||||
e.extensions[i] = append(e.extensions[i], b)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *encodedChain) Parse() ([]*x509.Certificate, error) {
|
||||
chain, err := CertsFromDER(e.chain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var extErrs utils.ErrorGroup
|
||||
for i, cert := range chain {
|
||||
for _, ee := range e.extensions[i] {
|
||||
ext, err := PKIXExtensionFromASN1(ee)
|
||||
if err != nil {
|
||||
extErrs.Add(err)
|
||||
}
|
||||
cert.ExtraExtensions = append(cert.ExtraExtensions, *ext)
|
||||
}
|
||||
}
|
||||
if err := extErrs.Finish(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return chain, nil
|
||||
}
|
||||
|
||||
// WritePKIXExtensionPEM writes the certificate extension to the writer, in a PEM-
|
||||
// enveloped PKIX form.
|
||||
func WritePKIXExtensionPEM(w io.Writer, extension *pkix.Extension) error {
|
||||
extBytes, err := PKIXExtensionToASN1(extension)
|
||||
if err != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
err = pem.Encode(w, &pem.Block{Type: BlockLabelExtension, Bytes: extBytes})
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
|
||||
// PKIXExtensionToPEM serializes a PKIX certificate extension to PEM-
|
||||
// enveloped ASN.1 bytes.
|
||||
func PKIXExtensionToPEM(extension *pkix.Extension) ([]byte, error) {
|
||||
asn, err := PKIXExtensionToASN1(extension)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pem.EncodeToMemory(&pem.Block{Type: BlockLabelExtension, Bytes: asn}), nil
|
||||
}
|
||||
|
||||
// PKIXExtensionToASN1 serializes a PKIX certificate extension to the
|
||||
// appropriate ASN.1 structure for such things. See RFC 5280, section 4.1.1.2.
|
||||
func PKIXExtensionToASN1(extension *pkix.Extension) ([]byte, error) {
|
||||
extBytes, err := asn1.Marshal(extension)
|
||||
return extBytes, errs.Wrap(err)
|
||||
}
|
||||
|
||||
// PKIXExtensionFromASN1 deserializes a PKIX certificate extension from
|
||||
// the appropriate ASN.1 structure for such things.
|
||||
func PKIXExtensionFromASN1(extData []byte) (*pkix.Extension, error) {
|
||||
var extension pkix.Extension
|
||||
if _, err := asn1.Unmarshal(extData, &extension); err != nil {
|
||||
return nil, ErrParseCerts.New("unable to unmarshal PKIX extension: %v", err)
|
||||
}
|
||||
return &extension, nil
|
||||
}
|
||||
|
||||
// PKIXExtensionFromPEM parses a PKIX certificate extension from
|
||||
// PEM-enveloped ASN.1 bytes.
|
||||
func PKIXExtensionFromPEM(pemBytes []byte) (*pkix.Extension, error) {
|
||||
pb, _ := pem.Decode(pemBytes)
|
||||
if pb == nil {
|
||||
return nil, ErrParseCerts.New("unable to parse PEM block")
|
||||
}
|
||||
if pb.Type != BlockLabelExtension {
|
||||
return nil, ErrParseCerts.New("can not parse PKIX cert extension from PEM block labeled %q", pb.Type)
|
||||
}
|
||||
return PKIXExtensionFromASN1(pb.Bytes)
|
||||
}
|
||||
|
||||
type ecdsaSignature struct {
|
||||
R, S *big.Int
|
||||
}
|
||||
|
||||
func marshalECDSASignature(r, s *big.Int) ([]byte, error) {
|
||||
return asn1.Marshal(ecdsaSignature{R: r, S: s})
|
||||
}
|
||||
|
||||
func unmarshalECDSASignature(signatureBytes []byte) (r, s *big.Int, err error) {
|
||||
var signature ecdsaSignature
|
||||
if _, err = asn1.Unmarshal(signatureBytes, &signature); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return signature.R, signature.S, nil
|
||||
}
|
||||
|
||||
// ecPrivateKeyFromASN1 parses a private key from the special Elliptic Curve
|
||||
// Private Key ASN.1 structure. This is here only for backward compatibility.
|
||||
// Use PKCS#8 instead.
|
||||
func ecPrivateKeyFromASN1(privKeyData []byte) (crypto.PrivateKey, error) {
|
||||
key, err := x509.ParseECPrivateKey(privKeyData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return crypto.PrivateKey(key), nil
|
||||
}
|
||||
|
@ -8,16 +8,8 @@ import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"encoding/asn1"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// ECDSASignature holds the `r` and `s` values in an ecdsa signature
|
||||
// (see https://golang.org/pkg/crypto/ecdsa)
|
||||
type ECDSASignature struct {
|
||||
R, S *big.Int
|
||||
}
|
||||
|
||||
var authECCurve = elliptic.P256()
|
||||
|
||||
// GeneratePrivateKey returns a new PrivateKey for signing messages
|
||||
@ -32,12 +24,12 @@ func VerifySignature(signedData, data []byte, pubKey crypto.PublicKey) error {
|
||||
return ErrUnsupportedKey.New("%T", key)
|
||||
}
|
||||
|
||||
signature := new(ECDSASignature)
|
||||
if _, err := asn1.Unmarshal(signedData, signature); err != nil {
|
||||
r, s, err := unmarshalECDSASignature(signedData)
|
||||
if err != nil {
|
||||
return ErrVerifySignature.New("unable to unmarshal ecdsa signature: %v", err)
|
||||
}
|
||||
digest := SHA256Hash(data)
|
||||
if !ecdsa.Verify(key, digest, signature.R, signature.S) {
|
||||
if !ecdsa.Verify(key, digest, r, s) {
|
||||
return ErrVerifySignature.New("signature is not valid")
|
||||
}
|
||||
return nil
|
||||
@ -56,7 +48,7 @@ func SignBytes(key crypto.PrivateKey, data []byte) ([]byte, error) {
|
||||
return nil, ErrSign.Wrap(err)
|
||||
}
|
||||
|
||||
return asn1.Marshal(ECDSASignature{R: r, S: s})
|
||||
return marshalECDSASignature(r, s)
|
||||
}
|
||||
|
||||
// SignHashOf signs a SHA-256 digest of the given data and returns the new
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
|
||||
"storj.io/storj/pkg/identity"
|
||||
"storj.io/storj/pkg/peertls"
|
||||
"storj.io/storj/pkg/pkcrypto"
|
||||
)
|
||||
|
||||
// Options holds config, identity, and peer verification function data for use with a grpc server.
|
||||
@ -53,7 +54,7 @@ func (opts *Options) configure(c Config) (err error) {
|
||||
return Error.New("unable to find whitelist file %v: %v", c.PeerCAWhitelistPath, err)
|
||||
}
|
||||
}
|
||||
parsed, err := identity.DecodeAndParseChainPEM(whitelist)
|
||||
parsed, err := pkcrypto.CertsFromPEM(whitelist)
|
||||
if err != nil {
|
||||
return Error.Wrap(err)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user