Replace pkcs12 code with upstream

This commit is contained in:
Matthew Hooker 2017-09-02 16:09:29 -07:00
parent 2a0a8cb8fa
commit 07bb47f793
No known key found for this signature in database
GPG Key ID: 7B5F933D9CE8C6A1
19 changed files with 916 additions and 359 deletions

View File

@ -1,9 +0,0 @@
This is a fork of the from the original PKCS#12 parsing code
published in the Azure repository [go-pkcs12](https://github.com/Azure/go-pkcs12).
This fork adds serializing a x509 certificate and private key as PKCS#12 binary blob
(aka .PFX file). Due to the specific nature of this code it was not accepted for
inclusion in the official Go crypto repository.
The methods used for decoding PKCS#12 have been moved to the test files to further
discourage the use of this library for decoding. Please use the official
[pkcs12](https://godoc.org/golang.org/x/crypto/pkcs12) library for decode support.

View File

@ -1,3 +1,7 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package pkcs12 package pkcs12
import ( import (
@ -5,6 +9,7 @@ import (
"unicode/utf16" "unicode/utf16"
) )
// bmpString returns s encoded in UCS-2 with a zero terminator.
func bmpString(s string) ([]byte, error) { func bmpString(s string) ([]byte, error) {
// References: // References:
// https://tools.ietf.org/html/rfc7292#appendix-B.1 // https://tools.ietf.org/html/rfc7292#appendix-B.1
@ -13,14 +18,33 @@ func bmpString(s string) ([]byte, error) {
// EncodeRune returns 0xfffd if the rune does not need special encoding // EncodeRune returns 0xfffd if the rune does not need special encoding
// - the above RFC provides the info that BMPStrings are NULL terminated. // - the above RFC provides the info that BMPStrings are NULL terminated.
rv := make([]byte, 0, 2*len(s)+2) ret := make([]byte, 0, 2*len(s)+2)
for _, r := range s { for _, r := range s {
if t, _ := utf16.EncodeRune(r); t != 0xfffd { if t, _ := utf16.EncodeRune(r); t != 0xfffd {
return nil, errors.New("string contains characters that cannot be encoded in UCS-2") return nil, errors.New("pkcs12: string contains characters that cannot be encoded in UCS-2")
} }
rv = append(rv, byte(r/256), byte(r%256)) ret = append(ret, byte(r/256), byte(r%256))
} }
rv = append(rv, 0, 0)
return rv, nil return append(ret, 0, 0), nil
}
func decodeBMPString(bmpString []byte) (string, error) {
if len(bmpString)%2 != 0 {
return "", errors.New("pkcs12: odd-length BMP string")
}
// strip terminator if present
if l := len(bmpString); l >= 2 && bmpString[l-1] == 0 && bmpString[l-2] == 0 {
bmpString = bmpString[:l-2]
}
s := make([]uint16, 0, len(bmpString)/2)
for len(bmpString) > 0 {
s = append(s, uint16(bmpString[0])<<8+uint16(bmpString[1]))
bmpString = bmpString[2:]
}
return string(utf16.Decode(s)), nil
} }

View File

@ -1,70 +1,69 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package pkcs12 package pkcs12
import ( import (
"bytes" "bytes"
"errors" "encoding/hex"
"testing" "testing"
"unicode/utf16"
) )
func decodeBMPString(bmpString []byte) (string, error) { var bmpStringTests = []struct {
if len(bmpString)%2 != 0 { in string
return "", errors.New("expected BMP byte string to be an even length") expectedHex string
} shouldFail bool
}{
// strip terminator if present {"", "0000", false},
if terminator := bmpString[len(bmpString)-2:]; terminator[0] == terminator[1] && terminator[1] == 0 { // Example from https://tools.ietf.org/html/rfc7292#appendix-B.
bmpString = bmpString[:len(bmpString)-2] {"Beavis", "0042006500610076006900730000", false},
} // Some characters from the "Letterlike Symbols Unicode block".
{"\u2115 - Double-struck N", "21150020002d00200044006f00750062006c0065002d00730074007200750063006b0020004e0000", false},
s := make([]uint16, 0, len(bmpString)/2) // any character outside the BMP should trigger an error.
for len(bmpString) > 0 { {"\U0001f000 East wind (Mahjong)", "", true},
s = append(s, uint16(bmpString[0])*265+uint16(bmpString[1]))
bmpString = bmpString[2:]
}
return string(utf16.Decode(s)), nil
} }
func TestBMPStringDecode(t *testing.T) { func TestBMPStringDecode(t *testing.T) {
_, err := decodeBMPString([]byte("a")) if _, err := decodeBMPString([]byte("a")); err == nil {
if err == nil { t.Fatalf("expected decode to fail, but it succeeded")
t.Fatal("expected decode to fail, but it succeeded")
} }
} }
func TestBMPString(t *testing.T) { func TestBMPString(t *testing.T) {
str, err := bmpString("") for i, test := range bmpStringTests {
if !bytes.Equal(str, []byte{0, 0}) { expected, err := hex.DecodeString(test.expectedHex)
t.Errorf("expected empty string to return double 0, but found: % x", str)
}
if err != nil { if err != nil {
t.Errorf("err: %v", err) t.Fatalf("#%d: failed to decode expectation", i)
} }
// Example from https://tools.ietf.org/html/rfc7292#appendix-B out, err := bmpString(test.in)
str, err = bmpString("Beavis") if err == nil && test.shouldFail {
if !bytes.Equal(str, []byte{0x00, 0x42, 0x00, 0x65, 0x00, 0x61, 0x00, 0x0076, 0x00, 0x69, 0x00, 0x73, 0x00, 0x00}) { t.Errorf("#%d: expected to fail, but produced %x", i, out)
t.Errorf("expected 'Beavis' to return 0x00 0x42 0x00 0x65 0x00 0x61 0x00 0x76 0x00 0x69 0x00 0x73 0x00 0x00, but found: % x", str) continue
}
if err != nil {
t.Errorf("err: %v", err)
} }
// some characters from the "Letterlike Symbols Unicode block" if err != nil && !test.shouldFail {
tst := "\u2115 - Double-struck N" t.Errorf("#%d: failed unexpectedly: %s", i, err)
str, err = bmpString(tst) continue
if !bytes.Equal(str, []byte{0x21, 0x15, 0x00, 0x20, 0x00, 0x2d, 0x00, 0x20, 0x00, 0x44, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x62, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x2d, 0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, 0x75, 0x00, 0x63, 0x00, 0x6b, 0x00, 0x20, 0x00, 0x4e, 0x00, 0x00}) {
t.Errorf("expected '%s' to return 0x21 0x15 0x00 0x20 0x00 0x2d 0x00 0x20 0x00 0x44 0x00 0x6f 0x00 0x75 0x00 0x62 0x00 0x6c 0x00 0x65 0x00 0x2d 0x00 0x73 0x00 0x74 0x00 0x72 0x00 0x75 0x00 0x63 0x00 0x6b 0x00 0x20 0x00 0x4e 0x00 0x00, but found: % x", tst, str)
}
if err != nil {
t.Errorf("err: %v", err)
} }
// some character outside the BMP should error if !test.shouldFail {
tst = "\U0001f000 East wind (Mahjong)" if !bytes.Equal(out, expected) {
_, err = bmpString(tst) t.Errorf("#%d: expected %s, got %x", i, test.expectedHex, out)
if err == nil { continue
t.Errorf("expected '%s' to throw error because the first character is not in the BMP", tst) }
roundTrip, err := decodeBMPString(out)
if err != nil {
t.Errorf("#%d: decoding output gave an error: %s", i, err)
continue
}
if roundTrip != test.in {
t.Errorf("#%d: decoding output resulted in %q, but it should have been %q", i, roundTrip, test.in)
continue
}
}
} }
} }

View File

@ -1,4 +1,6 @@
// implementation of https://tools.ietf.org/html/rfc2898#section-6.1.2 // Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package pkcs12 package pkcs12
@ -15,31 +17,52 @@ import (
"github.com/hashicorp/packer/builder/azure/pkcs12/rc2" "github.com/hashicorp/packer/builder/azure/pkcs12/rc2"
) )
const (
pbeWithSHAAnd3KeyTripleDESCBC = "pbeWithSHAAnd3-KeyTripleDES-CBC"
pbewithSHAAnd40BitRC2CBC = "pbewithSHAAnd40BitRC2-CBC"
)
const ( const (
pbeIterationCount = 2048 pbeIterationCount = 2048
pbeSaltSizeBytes = 8 pbeSaltSizeBytes = 8
) )
var ( var (
oidPbeWithSHAAnd3KeyTripleDESCBC = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 12, 1, 3} oidPBEWithSHAAnd3KeyTripleDESCBC = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 1, 3})
oidPbewithSHAAnd40BitRC2CBC = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 12, 1, 6} oidPBEWithSHAAnd40BitRC2CBC = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 1, 6})
) )
var algByOID = map[string]string{ // pbeCipher is an abstraction of a PKCS#12 cipher.
oidPbeWithSHAAnd3KeyTripleDESCBC.String(): pbeWithSHAAnd3KeyTripleDESCBC, type pbeCipher interface {
oidPbewithSHAAnd40BitRC2CBC.String(): pbewithSHAAnd40BitRC2CBC, // create returns a cipher.Block given a key.
create(key []byte) (cipher.Block, error)
// deriveKey returns a key derived from the given password and salt.
deriveKey(salt, password []byte, iterations int) []byte
// deriveKey returns an IV derived from the given password and salt.
deriveIV(salt, password []byte, iterations int) []byte
} }
var blockcodeByAlg = map[string]func(key []byte) (cipher.Block, error){ type shaWithTripleDESCBC struct{}
pbeWithSHAAnd3KeyTripleDESCBC: des.NewTripleDESCipher,
pbewithSHAAnd40BitRC2CBC: func(key []byte) (cipher.Block, error) { func (shaWithTripleDESCBC) create(key []byte) (cipher.Block, error) {
return des.NewTripleDESCipher(key)
}
func (shaWithTripleDESCBC) deriveKey(salt, password []byte, iterations int) []byte {
return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 1, 24)
}
func (shaWithTripleDESCBC) deriveIV(salt, password []byte, iterations int) []byte {
return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 2, 8)
}
type shaWith40BitRC2CBC struct{}
func (shaWith40BitRC2CBC) create(key []byte) (cipher.Block, error) {
return rc2.New(key, len(key)*8) return rc2.New(key, len(key)*8)
}, }
func (shaWith40BitRC2CBC) deriveKey(salt, password []byte, iterations int) []byte {
return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 1, 5)
}
func (shaWith40BitRC2CBC) deriveIV(salt, password []byte, iterations int) []byte {
return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 2, 8)
} }
type pbeParams struct { type pbeParams struct {
@ -47,6 +70,67 @@ type pbeParams struct {
Iterations int Iterations int
} }
func pbDecrypterFor(algorithm pkix.AlgorithmIdentifier, password []byte) (cipher.BlockMode, int, error) {
var cipherType pbeCipher
switch {
case algorithm.Algorithm.Equal(oidPBEWithSHAAnd3KeyTripleDESCBC):
cipherType = shaWithTripleDESCBC{}
case algorithm.Algorithm.Equal(oidPBEWithSHAAnd40BitRC2CBC):
cipherType = shaWith40BitRC2CBC{}
default:
return nil, 0, NotImplementedError("algorithm " + algorithm.Algorithm.String() + " is not supported")
}
var params pbeParams
if err := unmarshal(algorithm.Parameters.FullBytes, &params); err != nil {
return nil, 0, err
}
key := cipherType.deriveKey(params.Salt, password, params.Iterations)
iv := cipherType.deriveIV(params.Salt, password, params.Iterations)
block, err := cipherType.create(key)
if err != nil {
return nil, 0, err
}
return cipher.NewCBCDecrypter(block, iv), block.BlockSize(), nil
}
func pbDecrypt(info decryptable, password []byte) (decrypted []byte, err error) {
cbc, blockSize, err := pbDecrypterFor(info.Algorithm(), password)
if err != nil {
return nil, err
}
encrypted := info.Data()
if len(encrypted) == 0 {
return nil, errors.New("pkcs12: empty encrypted data")
}
if len(encrypted)%blockSize != 0 {
return nil, errors.New("pkcs12: input is not a multiple of the block size")
}
decrypted = make([]byte, len(encrypted))
cbc.CryptBlocks(decrypted, encrypted)
psLen := int(decrypted[len(decrypted)-1])
if psLen == 0 || psLen > blockSize {
return nil, ErrDecryption
}
if len(decrypted) < psLen {
return nil, ErrDecryption
}
ps := decrypted[len(decrypted)-psLen:]
decrypted = decrypted[:len(decrypted)-psLen]
if bytes.Compare(ps, bytes.Repeat([]byte{byte(psLen)}, psLen)) != 0 {
return nil, ErrDecryption
}
return
}
func pad(src []byte, blockSize int) []byte { func pad(src []byte, blockSize int) []byte {
paddingLength := blockSize - len(src)%blockSize paddingLength := blockSize - len(src)%blockSize
paddingText := bytes.Repeat([]byte{byte(paddingLength)}, paddingLength) paddingText := bytes.Repeat([]byte{byte(paddingLength)}, paddingLength)
@ -54,15 +138,15 @@ func pad(src []byte, blockSize int) []byte {
} }
func pbEncrypt(plainText, salt, password []byte, iterations int) (cipherText []byte, err error) { func pbEncrypt(plainText, salt, password []byte, iterations int) (cipherText []byte, err error) {
_, err = io.ReadFull(rand.Reader, salt) if _, err := io.ReadFull(rand.Reader, salt); err != nil {
if err != nil {
return nil, errors.New("pkcs12: failed to create a random salt value: " + err.Error()) return nil, errors.New("pkcs12: failed to create a random salt value: " + err.Error())
} }
key := deriveKeyByAlg[pbeWithSHAAnd3KeyTripleDESCBC](salt, password, iterations) cipherType := shaWithTripleDESCBC{}
iv := deriveIVByAlg[pbeWithSHAAnd3KeyTripleDESCBC](salt, password, iterations) key := cipherType.deriveKey(salt, password, iterations)
iv := cipherType.deriveIV(salt, password, iterations)
block, err := des.NewTripleDESCipher(key) block, err := cipherType.create(key)
if err != nil { if err != nil {
return nil, errors.New("pkcs12: failed to create a block cipher: " + err.Error()) return nil, errors.New("pkcs12: failed to create a block cipher: " + err.Error())
} }
@ -76,7 +160,8 @@ func pbEncrypt(plainText, salt, password []byte, iterations int) (cipherText []b
return cipherText, nil return cipherText, nil
} }
// decryptable abstracts a object that contains ciphertext.
type decryptable interface { type decryptable interface {
GetAlgorithm() pkix.AlgorithmIdentifier Algorithm() pkix.AlgorithmIdentifier
GetData() []byte Data() []byte
} }

View File

@ -1,60 +1,19 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package pkcs12 package pkcs12
import ( import (
"bytes" "bytes"
"crypto/cipher" "crypto/rand"
"crypto/x509/pkix" "crypto/x509/pkix"
"encoding/asn1" "encoding/asn1"
"io"
"testing" "testing"
) )
func pbDecrypterFor(algorithm pkix.AlgorithmIdentifier, password []byte) (cipher.BlockMode, error) { var sha1WithTripleDES = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 1, 3})
algorithmName, supported := algByOID[algorithm.Algorithm.String()]
if !supported {
return nil, NotImplementedError("algorithm " + algorithm.Algorithm.String() + " is not supported")
}
var params pbeParams
if _, err := asn1.Unmarshal(algorithm.Parameters.FullBytes, &params); err != nil {
return nil, err
}
k := deriveKeyByAlg[algorithmName](params.Salt, password, params.Iterations)
iv := deriveIVByAlg[algorithmName](params.Salt, password, params.Iterations)
code, err := blockcodeByAlg[algorithmName](k)
if err != nil {
return nil, err
}
cbc := cipher.NewCBCDecrypter(code, iv)
return cbc, nil
}
func pbDecrypt(info decryptable, password []byte) (decrypted []byte, err error) {
cbc, err := pbDecrypterFor(info.GetAlgorithm(), password)
if err != nil {
return nil, err
}
encrypted := info.GetData()
decrypted = make([]byte, len(encrypted))
cbc.CryptBlocks(decrypted, encrypted)
if psLen := int(decrypted[len(decrypted)-1]); psLen > 0 && psLen <= cbc.BlockSize() {
m := decrypted[:len(decrypted)-psLen]
ps := decrypted[len(decrypted)-psLen:]
if !bytes.Equal(ps, bytes.Repeat([]byte{byte(psLen)}, psLen)) {
return nil, ErrDecryption
}
decrypted = m
} else {
return nil, ErrDecryption
}
return
}
func TestPbDecrypterFor(t *testing.T) { func TestPbDecrypterFor(t *testing.T) {
params, _ := asn1.Marshal(pbeParams{ params, _ := asn1.Marshal(pbeParams{
@ -70,70 +29,122 @@ func TestPbDecrypterFor(t *testing.T) {
pass, _ := bmpString("Sesame open") pass, _ := bmpString("Sesame open")
_, err := pbDecrypterFor(alg, pass) _, _, err := pbDecrypterFor(alg, pass)
if _, ok := err.(NotImplementedError); !ok { if _, ok := err.(NotImplementedError); !ok {
t.Errorf("expected not implemented error, got: %T %s", err, err) t.Errorf("expected not implemented error, got: %T %s", err, err)
} }
alg.Algorithm = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 1, 3}) alg.Algorithm = sha1WithTripleDES
cbc, err := pbDecrypterFor(alg, pass) cbc, blockSize, err := pbDecrypterFor(alg, pass)
if err != nil { if err != nil {
t.Errorf("err: %v", err) t.Errorf("unexpected error from pbDecrypterFor %v", err)
}
if blockSize != 8 {
t.Errorf("unexpected block size %d, wanted 8", blockSize)
} }
M := []byte{1, 2, 3, 4, 5, 6, 7, 8} plaintext := []byte{1, 2, 3, 4, 5, 6, 7, 8}
expectedM := []byte{185, 73, 135, 249, 137, 1, 122, 247} expectedCiphertext := []byte{185, 73, 135, 249, 137, 1, 122, 247}
cbc.CryptBlocks(M, M) ciphertext := make([]byte, len(plaintext))
cbc.CryptBlocks(ciphertext, plaintext)
if !bytes.Equal(M, expectedM) { if bytes.Compare(ciphertext, expectedCiphertext) != 0 {
t.Errorf("expected M to be '%d', but found '%d", expectedM, M) t.Errorf("bad ciphertext, got %x but wanted %x", ciphertext, expectedCiphertext)
} }
} }
var pbDecryptTests = []struct {
in []byte
expected []byte
expectedError error
}{
{
[]byte("\x33\x73\xf3\x9f\xda\x49\xae\xfc\xa0\x9a\xdf\x5a\x58\xa0\xea\x46"), // 7 padding bytes
[]byte("A secret!"),
nil,
},
{
[]byte("\x33\x73\xf3\x9f\xda\x49\xae\xfc\x96\x24\x2f\x71\x7e\x32\x3f\xe7"), // 8 padding bytes
[]byte("A secret"),
nil,
},
{
[]byte("\x35\x0c\xc0\x8d\xab\xa9\x5d\x30\x7f\x9a\xec\x6a\xd8\x9b\x9c\xd9"), // 9 padding bytes, incorrect
nil,
ErrDecryption,
},
{
[]byte("\xb2\xf9\x6e\x06\x60\xae\x20\xcf\x08\xa0\x7b\xd9\x6b\x20\xef\x41"), // incorrect padding bytes: [ ... 0x04 0x02 ]
nil,
ErrDecryption,
},
}
func TestPbDecrypt(t *testing.T) { func TestPbDecrypt(t *testing.T) {
salt := []byte("\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8")
tests := [][]byte{ for i, test := range pbDecryptTests {
[]byte("\x33\x73\xf3\x9f\xda\x49\xae\xfc\xa0\x9a\xdf\x5a\x58\xa0\xea\x46"), // 7 padding bytes decryptable := makeTestDecryptable(test.in, salt)
[]byte("\x33\x73\xf3\x9f\xda\x49\xae\xfc\x96\x24\x2f\x71\x7e\x32\x3f\xe7"), // 8 padding bytes password, _ := bmpString("sesame")
[]byte("\x35\x0c\xc0\x8d\xab\xa9\x5d\x30\x7f\x9a\xec\x6a\xd8\x9b\x9c\xd9"), // 9 padding bytes, incorrect
[]byte("\xb2\xf9\x6e\x06\x60\xae\x20\xcf\x08\xa0\x7b\xd9\x6b\x20\xef\x41"), // incorrect padding bytes: [ ... 0x04 0x02 ] plaintext, err := pbDecrypt(decryptable, password)
} if err != test.expectedError {
expected := []interface{}{ t.Errorf("#%d: got error %q, but wanted %q", i, err, test.expectedError)
[]byte("A secret!"), continue
[]byte("A secret"),
ErrDecryption,
ErrDecryption,
} }
for i, c := range tests { if !bytes.Equal(plaintext, test.expected) {
td := testDecryptable{ t.Errorf("#%d: got %x, but wanted %x", i, plaintext, test.expected)
data: c, }
}
}
func TestRoundTripPkc12EncryptDecrypt(t *testing.T) {
salt := []byte{0xfe, 0xee, 0xfa, 0xce}
password := salt
// Sweep the possible padding lengths
for i := 0; i < 9; i++ {
bs := make([]byte, i)
_, err := io.ReadFull(rand.Reader, bs)
if err != nil {
t.Fatalf("failed to read: %s", err)
}
cipherText, err := pbEncrypt(bs, salt, password, 4096)
if err != nil {
t.Fatalf("failed to encrypt: %s\n", err)
}
if len(cipherText)%8 != 0 {
t.Fatalf("plain text was not padded as expected")
}
decryptable := makeTestDecryptable(cipherText, salt)
plainText, err := pbDecrypt(decryptable, password)
if err != nil {
t.Fatalf("failed to decrypt: %s\n", err)
}
if !bytes.Equal(bs, plainText) {
t.Fatalf("got %x, but wanted %x", bs, plainText)
}
}
}
func makeTestDecryptable(bytes, salt []byte) testDecryptable {
decryptable := testDecryptable{
data: bytes,
algorithm: pkix.AlgorithmIdentifier{ algorithm: pkix.AlgorithmIdentifier{
Algorithm: asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 1, 3}), // SHA1/3TDES Algorithm: sha1WithTripleDES,
Parameters: pbeParams{ Parameters: pbeParams{
Salt: []byte("\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8"), Salt: salt,
Iterations: 4096, Iterations: 4096,
}.RawASN1(), }.RawASN1(),
}, },
} }
p, _ := bmpString("sesame")
m, err := pbDecrypt(td, p) return decryptable
switch e := expected[i].(type) {
case []byte:
if err != nil {
t.Errorf("error decrypting C=%x: %v", c, err)
}
if !bytes.Equal(m, e) {
t.Errorf("expected C=%x to be decoded to M=%x, but found %x", c, e, m)
}
case error:
if err == nil || err.Error() != e.Error() {
t.Errorf("expecting error '%v' during decryption of c=%x, but found err='%v'", e, c, err)
}
}
}
} }
type testDecryptable struct { type testDecryptable struct {
@ -141,8 +152,8 @@ type testDecryptable struct {
algorithm pkix.AlgorithmIdentifier algorithm pkix.AlgorithmIdentifier
} }
func (d testDecryptable) GetAlgorithm() pkix.AlgorithmIdentifier { return d.algorithm } func (d testDecryptable) Algorithm() pkix.AlgorithmIdentifier { return d.algorithm }
func (d testDecryptable) GetData() []byte { return d.data } func (d testDecryptable) Data() []byte { return d.data }
func (params pbeParams) RawASN1() (raw asn1.RawValue) { func (params pbeParams) RawASN1() (raw asn1.RawValue) {
asn1Bytes, err := asn1.Marshal(params) asn1Bytes, err := asn1.Marshal(params)

View File

@ -1,3 +1,7 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package pkcs12 package pkcs12
import "errors" import "errors"
@ -16,7 +20,7 @@ type NotImplementedError string
type EncodeError string type EncodeError string
func (e NotImplementedError) Error() string { func (e NotImplementedError) Error() string {
return string(e) return "pkcs12: " + string(e)
} }
func (e EncodeError) Error() string { func (e EncodeError) Error() string {

View File

@ -1,3 +1,7 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package pkcs12 package pkcs12
import ( import (
@ -7,10 +11,6 @@ import (
"encoding/asn1" "encoding/asn1"
) )
var (
oidSha1Algorithm = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 26}
)
type macData struct { type macData struct {
Mac digestInfo Mac digestInfo
MacSalt []byte MacSalt []byte
@ -23,6 +23,23 @@ type digestInfo struct {
Digest []byte Digest []byte
} }
var (
oidSHA1 = asn1.ObjectIdentifier([]int{1, 3, 14, 3, 2, 26})
)
func verifyMac(macData *macData, message, password []byte) error {
if !macData.Mac.Algorithm.Algorithm.Equal(oidSHA1) {
return NotImplementedError("unknown digest algorithm: " + macData.Mac.Algorithm.Algorithm.String())
}
expectedMAC := computeMac(message, macData.Iterations, macData.MacSalt, password)
if !hmac.Equal(macData.Mac.Digest, expectedMAC) {
return ErrIncorrectPassword
}
return nil
}
func computeMac(message []byte, iterations int, salt, password []byte) []byte { func computeMac(message []byte, iterations int, salt, password []byte) []byte {
key := pbkdf(sha1Sum, 20, 64, salt, password, iterations, 3, 20) key := pbkdf(sha1Sum, 20, 64, salt, password, iterations, 3, 20)

View File

@ -1,24 +1,14 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package pkcs12 package pkcs12
import ( import (
"crypto/hmac"
"encoding/asn1" "encoding/asn1"
"testing" "testing"
) )
func verifyMac(macData *macData, message, password []byte) error {
if !macData.Mac.Algorithm.Algorithm.Equal(oidSha1Algorithm) {
return NotImplementedError("unknown digest algorithm: " + macData.Mac.Algorithm.Algorithm.String())
}
expectedMAC := computeMac(message, macData.Iterations, macData.MacSalt, password)
if !hmac.Equal(macData.Mac.Digest, expectedMAC) {
return ErrIncorrectPassword
}
return nil
}
func TestVerifyMac(t *testing.T) { func TestVerifyMac(t *testing.T) {
td := macData{ td := macData{
Mac: digestInfo{ Mac: digestInfo{

View File

@ -1,34 +1,35 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package pkcs12 package pkcs12
import ( import (
"bytes"
"crypto/sha1" "crypto/sha1"
"math/big" "math/big"
) )
var ( var (
deriveKeyByAlg = map[string]func(salt, password []byte, iterations int) []byte{ one = big.NewInt(1)
pbeWithSHAAnd3KeyTripleDESCBC: func(salt, password []byte, iterations int) []byte {
return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 1, 24)
},
pbewithSHAAnd40BitRC2CBC: func(salt, password []byte, iterations int) []byte {
return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 1, 5)
},
}
deriveIVByAlg = map[string]func(salt, password []byte, iterations int) []byte{
pbeWithSHAAnd3KeyTripleDESCBC: func(salt, password []byte, iterations int) []byte {
return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 2, 8)
},
pbewithSHAAnd40BitRC2CBC: func(salt, password []byte, iterations int) []byte {
return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 2, 8)
},
}
) )
// sha1Sum returns the SHA-1 hash of in.
func sha1Sum(in []byte) []byte { func sha1Sum(in []byte) []byte {
sum := sha1.Sum(in) sum := sha1.Sum(in)
return sum[:] return sum[:]
} }
// fillWithRepeats returns v*ceiling(len(pattern) / v) bytes consisting of
// repeats of pattern.
func fillWithRepeats(pattern []byte, v int) []byte {
if len(pattern) == 0 {
return nil
}
outputLen := v * ((len(pattern) + v - 1) / v)
return bytes.Repeat(pattern, (outputLen+len(pattern)-1)/len(pattern))[:outputLen]
}
func pbkdf(hash func([]byte) []byte, u, v int, salt, password []byte, r int, ID byte, size int) (key []byte) { func pbkdf(hash func([]byte) []byte, u, v int, salt, password []byte, r int, ID byte, size int) (key []byte) {
// implementation of https://tools.ietf.org/html/rfc7292#appendix-B.2 , RFC text verbatim in comments // implementation of https://tools.ietf.org/html/rfc7292#appendix-B.2 , RFC text verbatim in comments
@ -75,7 +76,7 @@ func pbkdf(hash func([]byte) []byte, u, v int, salt, password []byte, r int, ID
// 1. Construct a string, D (the "diversifier"), by concatenating v/8 // 1. Construct a string, D (the "diversifier"), by concatenating v/8
// copies of ID. // copies of ID.
D := []byte{} var D []byte
for i := 0; i < v; i++ { for i := 0; i < v; i++ {
D = append(D, ID) D = append(D, ID)
} }
@ -85,63 +86,37 @@ func pbkdf(hash func([]byte) []byte, u, v int, salt, password []byte, r int, ID
// truncated to create S). Note that if the salt is the empty // truncated to create S). Note that if the salt is the empty
// string, then so is S. // string, then so is S.
S := []byte{} S := fillWithRepeats(salt, v)
{
s := len(salt)
times := s / v
if s%v > 0 {
times++
}
for len(S) < times*v {
S = append(S, salt...)
}
S = S[:times*v]
}
// 3. Concatenate copies of the password together to create a string P // 3. Concatenate copies of the password together to create a string P
// of length v(ceiling(p/v)) bits (the final copy of the password // of length v(ceiling(p/v)) bits (the final copy of the password
// may be truncated to create P). Note that if the password is the // may be truncated to create P). Note that if the password is the
// empty string, then so is P. // empty string, then so is P.
P := []byte{} P := fillWithRepeats(password, v)
{
s := len(password)
times := s / v
if s%v > 0 {
times++
}
for len(P) < times*v {
P = append(P, password...)
}
P = P[:times*v]
}
// 4. Set I=S||P to be the concatenation of S and P. // 4. Set I=S||P to be the concatenation of S and P.
I := append(S, P...) I := append(S, P...)
// 5. Set c=ceiling(n/u). // 5. Set c=ceiling(n/u).
c := size / u c := (size + u - 1) / u
if size%u > 0 {
c++
}
// 6. For i=1, 2, ..., c, do the following: // 6. For i=1, 2, ..., c, do the following:
A := make([]byte, c*20) A := make([]byte, c*20)
var IjBuf []byte
for i := 0; i < c; i++ { for i := 0; i < c; i++ {
// A. Set A2=H^r(D||I). (i.e., the r-th hash of D||1, // A. Set A2=H^r(D||I). (i.e., the r-th hash of D||1,
// H(H(H(... H(D||I)))) // H(H(H(... H(D||I))))
Ai := hash(append(D, I...)) Ai := hash(append(D, I...))
for j := 1; j < r; j++ { for j := 1; j < r; j++ {
Ai = hash(Ai[:]) Ai = hash(Ai)
} }
copy(A[i*20:], Ai[:]) copy(A[i*20:], Ai[:])
if i < c-1 { // skip on last iteration if i < c-1 { // skip on last iteration
// B. Concatenate copies of Ai to create a string B of length v // B. Concatenate copies of Ai to create a string B of length v
// bits (the final copy of Ai may be truncated to create B). // bits (the final copy of Ai may be truncated to create B).
B := []byte{} var B []byte
for len(B) < v { for len(B) < v {
B = append(B, Ai[:]...) B = append(B, Ai[:]...)
} }
@ -151,20 +126,31 @@ func pbkdf(hash func([]byte) []byte, u, v int, salt, password []byte, r int, ID
// blocks, where k=ceiling(s/v)+ceiling(p/v), modify I by // blocks, where k=ceiling(s/v)+ceiling(p/v), modify I by
// setting I_j=(I_j+B+1) mod 2^v for each j. // setting I_j=(I_j+B+1) mod 2^v for each j.
{ {
Bbi := new(big.Int) Bbi := new(big.Int).SetBytes(B)
Bbi.SetBytes(B) Ij := new(big.Int)
one := big.NewInt(1)
for j := 0; j < len(I)/v; j++ { for j := 0; j < len(I)/v; j++ {
Ij := new(big.Int)
Ij.SetBytes(I[j*v : (j+1)*v]) Ij.SetBytes(I[j*v : (j+1)*v])
Ij.Add(Ij, Bbi) Ij.Add(Ij, Bbi)
Ij.Add(Ij, one) Ij.Add(Ij, one)
Ijb := Ij.Bytes() Ijb := Ij.Bytes()
// We expect Ijb to be exactly v bytes,
// if it is longer or shorter we must
// adjust it accordingly.
if len(Ijb) > v { if len(Ijb) > v {
Ijb = Ijb[len(Ijb)-v:] Ijb = Ijb[len(Ijb)-v:]
} }
if len(Ijb) < v {
if IjBuf == nil {
IjBuf = make([]byte, v)
}
bytesShort := v - len(Ijb)
for i := 0; i < bytesShort; i++ {
IjBuf[i] = 0
}
copy(IjBuf[bytesShort:], Ijb)
Ijb = IjBuf
}
copy(I[j*v:(j+1)*v], Ijb) copy(I[j*v:(j+1)*v], Ijb)
} }
} }
@ -174,9 +160,7 @@ func pbkdf(hash func([]byte) []byte, u, v int, salt, password []byte, r int, ID
// bit string, A. // bit string, A.
// 8. Use the first n bits of A as the output of this entire process. // 8. Use the first n bits of A as the output of this entire process.
A = A[:size] return A[:size]
return A
// If the above process is being used to generate a DES key, the process // If the above process is being used to generate a DES key, the process
// should be used to create 64 random bits, and the key's parity bits // should be used to create 64 random bits, and the key's parity bits

View File

@ -1,3 +1,7 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package pkcs12 package pkcs12
import ( import (
@ -6,13 +10,25 @@ import (
) )
func TestThatPBKDFWorksCorrectlyForLongKeys(t *testing.T) { func TestThatPBKDFWorksCorrectlyForLongKeys(t *testing.T) {
pbkdf := deriveKeyByAlg[pbeWithSHAAnd3KeyTripleDESCBC] cipherInfo := shaWithTripleDESCBC{}
salt := []byte("\xff\xff\xff\xff\xff\xff\xff\xff") salt := []byte("\xff\xff\xff\xff\xff\xff\xff\xff")
password, _ := bmpString("sesame") password, _ := bmpString("sesame")
key := pbkdf(salt, password, 2048) key := cipherInfo.deriveKey(salt, password, 2048)
if expected := []byte("\x7c\xd9\xfd\x3e\x2b\x3b\xe7\x69\x1a\x44\xe3\xbe\xf0\xf9\xea\x0f\xb9\xb8\x97\xd4\xe3\x25\xd9\xd1"); !bytes.Equal(key, expected) { if expected := []byte("\x7c\xd9\xfd\x3e\x2b\x3b\xe7\x69\x1a\x44\xe3\xbe\xf0\xf9\xea\x0f\xb9\xb8\x97\xd4\xe3\x25\xd9\xd1"); bytes.Compare(key, expected) != 0 {
t.Fatalf("expected key '% x', but found '% x'", key, expected) t.Fatalf("expected key '%x', but found '%x'", expected, key)
}
}
func TestThatPBKDFHandlesLeadingZeros(t *testing.T) {
// This test triggers a case where I_j (in step 6C) ends up with leading zero
// byte, meaning that len(Ijb) < v (leading zeros get stripped by big.Int).
// This was previously causing bug whereby certain inputs would break the
// derivation and produce the wrong output.
key := pbkdf(sha1Sum, 20, 64, []byte("\xf3\x7e\x05\xb5\x18\x32\x4b\x4b"), []byte("\x00\x00"), 2048, 1, 24)
expected := []byte("\x00\xf7\x59\xff\x47\xd1\x4d\xd0\x36\x65\xd5\x94\x3c\xb3\xc4\xa3\x9a\x25\x55\xc0\x2a\xed\x66\xe1")
if bytes.Compare(key, expected) != 0 {
t.Fatalf("expected key '%x', but found '%x'", expected, key)
} }
} }

View File

@ -1,20 +1,34 @@
// Package pkcs12 provides some implementations of PKCS#12. // Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package pkcs12 implements some of PKCS#12.
// //
// This implementation is distilled from https://tools.ietf.org/html/rfc7292 and referenced documents. // This implementation is distilled from https://tools.ietf.org/html/rfc7292
// It is intended for decoding P12/PFX-stored certificate+key for use with the crypto/tls package. // and referenced documents. It is intended for decoding P12/PFX-stored
// certificates and keys for use with the crypto/tls package.
package pkcs12 package pkcs12
import ( import (
"crypto/ecdsa"
"crypto/rand" "crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix" "crypto/x509/pkix"
"encoding/asn1" "encoding/asn1"
"encoding/hex"
"encoding/pem"
"errors" "errors"
"io" "io"
) )
var ( var (
oidLocalKeyID = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 21} oidDataContentType = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 7, 1})
oidDataContentType = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 1} oidEncryptedDataContentType = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 7, 6})
oidFriendlyName = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 9, 20})
oidLocalKeyID = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 9, 21})
oidMicrosoftCSPName = asn1.ObjectIdentifier([]int{1, 3, 6, 1, 4, 1, 311, 17, 1})
localKeyId = []byte{0x01, 0x00, 0x00, 0x00} localKeyId = []byte{0x01, 0x00, 0x00, 0x00}
) )
@ -30,6 +44,23 @@ type contentInfo struct {
Content asn1.RawValue `asn1:"tag:0,explicit,optional"` Content asn1.RawValue `asn1:"tag:0,explicit,optional"`
} }
type encryptedData struct {
Version int
EncryptedContentInfo encryptedContentInfo
}
type encryptedContentInfo struct {
ContentType asn1.ObjectIdentifier
ContentEncryptionAlgorithm pkix.AlgorithmIdentifier
EncryptedContent []byte `asn1:"tag:0,optional"`
}
func (i encryptedContentInfo) Algorithm() pkix.AlgorithmIdentifier {
return i.ContentEncryptionAlgorithm
}
func (i encryptedContentInfo) Data() []byte { return i.EncryptedContent }
type safeBag struct { type safeBag struct {
Id asn1.ObjectIdentifier Id asn1.ObjectIdentifier
Value asn1.RawValue `asn1:"tag:0,explicit"` Value asn1.RawValue `asn1:"tag:0,explicit"`
@ -38,7 +69,7 @@ type safeBag struct {
type pkcs12Attribute struct { type pkcs12Attribute struct {
Id asn1.ObjectIdentifier Id asn1.ObjectIdentifier
Value asn1.RawValue `ans1:"set"` Value asn1.RawValue `asn1:"set"`
} }
type encryptedPrivateKeyInfo struct { type encryptedPrivateKeyInfo struct {
@ -46,8 +77,19 @@ type encryptedPrivateKeyInfo struct {
EncryptedData []byte EncryptedData []byte
} }
func (i encryptedPrivateKeyInfo) GetAlgorithm() pkix.AlgorithmIdentifier { return i.AlgorithmIdentifier } func (i encryptedPrivateKeyInfo) Algorithm() pkix.AlgorithmIdentifier {
func (i encryptedPrivateKeyInfo) GetData() []byte { return i.EncryptedData } return i.AlgorithmIdentifier
}
func (i encryptedPrivateKeyInfo) Data() []byte {
return i.EncryptedData
}
// PEM block types
const (
certificateType = "CERTIFICATE"
privateKeyType = "PRIVATE KEY"
)
// unmarshal calls asn1.Unmarshal, but also returns an error if there is any // unmarshal calls asn1.Unmarshal, but also returns an error if there is any
// trailing data after unmarshaling. // trailing data after unmarshaling.
@ -62,6 +104,168 @@ func unmarshal(in []byte, out interface{}) error {
return nil return nil
} }
// ConvertToPEM converts all "safe bags" contained in pfxData to PEM blocks.
func ToPEM(pfxData []byte, password string) ([]*pem.Block, error) {
encodedPassword, err := bmpString(password)
if err != nil {
return nil, ErrIncorrectPassword
}
bags, encodedPassword, err := getSafeContents(pfxData, encodedPassword)
blocks := make([]*pem.Block, 0, len(bags))
for _, bag := range bags {
block, err := convertBag(&bag, encodedPassword)
if err != nil {
return nil, err
}
blocks = append(blocks, block)
}
return blocks, nil
}
func convertBag(bag *safeBag, password []byte) (*pem.Block, error) {
block := &pem.Block{
Headers: make(map[string]string),
}
for _, attribute := range bag.Attributes {
k, v, err := convertAttribute(&attribute)
if err != nil {
return nil, err
}
block.Headers[k] = v
}
switch {
case bag.Id.Equal(oidCertBag):
block.Type = certificateType
certsData, err := decodeCertBag(bag.Value.Bytes)
if err != nil {
return nil, err
}
block.Bytes = certsData
case bag.Id.Equal(oidPKCS8ShroudedKeyBag):
block.Type = privateKeyType
key, err := decodePkcs8ShroudedKeyBag(bag.Value.Bytes, password)
if err != nil {
return nil, err
}
switch key := key.(type) {
case *rsa.PrivateKey:
block.Bytes = x509.MarshalPKCS1PrivateKey(key)
case *ecdsa.PrivateKey:
block.Bytes, err = x509.MarshalECPrivateKey(key)
if err != nil {
return nil, err
}
default:
return nil, errors.New("found unknown private key type in PKCS#8 wrapping")
}
default:
return nil, errors.New("don't know how to convert a safe bag of type " + bag.Id.String())
}
return block, nil
}
func convertAttribute(attribute *pkcs12Attribute) (key, value string, err error) {
isString := false
switch {
case attribute.Id.Equal(oidFriendlyName):
key = "friendlyName"
isString = true
case attribute.Id.Equal(oidLocalKeyID):
key = "localKeyId"
case attribute.Id.Equal(oidMicrosoftCSPName):
// This key is chosen to match OpenSSL.
key = "Microsoft CSP Name"
isString = true
default:
return "", "", errors.New("pkcs12: unknown attribute with OID " + attribute.Id.String())
}
if isString {
if err := unmarshal(attribute.Value.Bytes, &attribute.Value); err != nil {
return "", "", err
}
if value, err = decodeBMPString(attribute.Value.Bytes); err != nil {
return "", "", err
}
} else {
var id []byte
if err := unmarshal(attribute.Value.Bytes, &id); err != nil {
return "", "", err
}
value = hex.EncodeToString(id)
}
return key, value, nil
}
// Decode extracts a certificate and private key from pfxData. This function
// assumes that there is only one certificate and only one private key in the
// pfxData.
func Decode(pfxData []byte, password string) (privateKey interface{}, certificate *x509.Certificate, err error) {
encodedPassword, err := bmpString(password)
if err != nil {
return nil, nil, err
}
bags, encodedPassword, err := getSafeContents(pfxData, encodedPassword)
if err != nil {
return nil, nil, err
}
if len(bags) != 2 {
err = errors.New("pkcs12: expected exactly two safe bags in the PFX PDU")
return
}
for _, bag := range bags {
switch {
case bag.Id.Equal(oidCertBag):
if certificate != nil {
err = errors.New("pkcs12: expected exactly one certificate bag")
}
certsData, err := decodeCertBag(bag.Value.Bytes)
if err != nil {
return nil, nil, err
}
certs, err := x509.ParseCertificates(certsData)
if err != nil {
return nil, nil, err
}
if len(certs) != 1 {
err = errors.New("pkcs12: expected exactly one certificate in the certBag")
return nil, nil, err
}
certificate = certs[0]
case bag.Id.Equal(oidPKCS8ShroudedKeyBag):
if privateKey != nil {
err = errors.New("pkcs12: expected exactly one key bag")
}
if privateKey, err = decodePkcs8ShroudedKeyBag(bag.Value.Bytes, encodedPassword); err != nil {
return nil, nil, err
}
}
}
if certificate == nil {
return nil, nil, errors.New("pkcs12: certificate missing")
}
if privateKey == nil {
return nil, nil, errors.New("pkcs12: private key missing")
}
return
}
func getLocalKeyId(id []byte) (attribute pkcs12Attribute, err error) { func getLocalKeyId(id []byte) (attribute pkcs12Attribute, err error) {
octetString := asn1.RawValue{Tag: 4, Class: 0, IsCompound: false, Bytes: id} octetString := asn1.RawValue{Tag: 4, Class: 0, IsCompound: false, Bytes: id}
bytes, err := asn1.Marshal(octetString) bytes, err := asn1.Marshal(octetString)
@ -115,7 +319,7 @@ func makeCertBagContentInfo(derBytes []byte) (*contentInfo, error) {
return nil, EncodeError("encoding cert bag: " + err.Error()) return nil, EncodeError("encoding cert bag: " + err.Error())
} }
certSafeBags, err := makeSafeBags(oidCertBagType, bytes) certSafeBags, err := makeSafeBags(oidCertBag, bytes)
if err != nil { if err != nil {
return nil, EncodeError("safe bags: " + err.Error()) return nil, EncodeError("safe bags: " + err.Error())
} }
@ -129,7 +333,7 @@ func makeShroudedKeyBagContentInfo(privateKey interface{}, password []byte) (*co
return nil, EncodeError("encode PKCS#8 shrouded key bag: " + err.Error()) return nil, EncodeError("encode PKCS#8 shrouded key bag: " + err.Error())
} }
safeBags, err := makeSafeBags(oidPkcs8ShroudedKeyBagType, shroudedKeyBagBytes) safeBags, err := makeSafeBags(oidPKCS8ShroudedKeyBag, shroudedKeyBagBytes)
if err != nil { if err != nil {
return nil, EncodeError("safe bags: " + err.Error()) return nil, EncodeError("safe bags: " + err.Error())
} }
@ -183,7 +387,7 @@ func makeSalt(saltByteCount int) ([]byte, error) {
// //
// derBytes is a DER encoded certificate. // derBytes is a DER encoded certificate.
// privateKey is an RSA // privateKey is an RSA
func Encode(derBytes []byte, privateKey interface{}, password string) ([]byte, error) { func Encode(derBytes []byte, privateKey interface{}, password string) (pfxBytes []byte, err error) {
secret, err := bmpString(password) secret, err := bmpString(password)
if err != nil { if err != nil {
return nil, ErrIncorrectPassword return nil, ErrIncorrectPassword
@ -230,7 +434,7 @@ func Encode(derBytes []byte, privateKey interface{}, password string) ([]byte, e
MacSalt: salt, MacSalt: salt,
Mac: digestInfo{ Mac: digestInfo{
Algorithm: pkix.AlgorithmIdentifier{ Algorithm: pkix.AlgorithmIdentifier{
Algorithm: oidSha1Algorithm, Algorithm: oidSHA1,
}, },
Digest: digest, Digest: digest,
}, },
@ -244,3 +448,83 @@ func Encode(derBytes []byte, privateKey interface{}, password string) ([]byte, e
return bytes, err return bytes, err
} }
func getSafeContents(p12Data, password []byte) (bags []safeBag, updatedPassword []byte, err error) {
pfx := new(pfxPdu)
if err := unmarshal(p12Data, pfx); err != nil {
return nil, nil, errors.New("pkcs12: error reading P12 data: " + err.Error())
}
if pfx.Version != 3 {
return nil, nil, NotImplementedError("can only decode v3 PFX PDU's")
}
if !pfx.AuthSafe.ContentType.Equal(oidDataContentType) {
return nil, nil, NotImplementedError("only password-protected PFX is implemented")
}
// unmarshal the explicit bytes in the content for type 'data'
if err := unmarshal(pfx.AuthSafe.Content.Bytes, &pfx.AuthSafe.Content); err != nil {
return nil, nil, err
}
if len(pfx.MacData.Mac.Algorithm.Algorithm) == 0 {
return nil, nil, errors.New("pkcs12: no MAC in data")
}
if err := verifyMac(&pfx.MacData, pfx.AuthSafe.Content.Bytes, password); err != nil {
if err == ErrIncorrectPassword && len(password) == 2 && password[0] == 0 && password[1] == 0 {
// some implementations use an empty byte array
// for the empty string password try one more
// time with empty-empty password
password = nil
err = verifyMac(&pfx.MacData, pfx.AuthSafe.Content.Bytes, password)
}
if err != nil {
return nil, nil, err
}
}
var authenticatedSafe []contentInfo
if err := unmarshal(pfx.AuthSafe.Content.Bytes, &authenticatedSafe); err != nil {
return nil, nil, err
}
if len(authenticatedSafe) != 2 {
return nil, nil, NotImplementedError("expected exactly two items in the authenticated safe")
}
for _, ci := range authenticatedSafe {
var data []byte
switch {
case ci.ContentType.Equal(oidDataContentType):
if err := unmarshal(ci.Content.Bytes, &data); err != nil {
return nil, nil, err
}
case ci.ContentType.Equal(oidEncryptedDataContentType):
var encryptedData encryptedData
if err := unmarshal(ci.Content.Bytes, &encryptedData); err != nil {
return nil, nil, err
}
if encryptedData.Version != 0 {
return nil, nil, NotImplementedError("only version 0 of EncryptedData is supported")
}
if data, err = pbDecrypt(encryptedData.EncryptedContentInfo, password); err != nil {
return nil, nil, err
}
default:
return nil, nil, NotImplementedError("only data and encryptedData content types are supported in authenticated safe")
}
var safeContents []safeBag
if err := unmarshal(data, &safeContents); err != nil {
return nil, nil, err
}
bags = append(bags, safeContents...)
}
return bags, password, nil
}

View File

@ -1,3 +1,7 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package pkcs12 package pkcs12
import ( import (
@ -5,16 +9,36 @@ import (
"crypto/elliptic" "crypto/elliptic"
"crypto/rand" "crypto/rand"
"crypto/rsa" "crypto/rsa"
"crypto/tls"
"crypto/x509" "crypto/x509"
"crypto/x509/pkix" "crypto/x509/pkix"
"encoding/base64"
"encoding/pem"
"fmt" "fmt"
"math/big" "math/big"
"testing" "testing"
"time" "time"
gopkcs12 "golang.org/x/crypto/pkcs12"
) )
func TestPfx(t *testing.T) {
for commonName, base64P12 := range testdata {
p12, _ := base64.StdEncoding.DecodeString(base64P12)
priv, cert, err := Decode(p12, "")
if err != nil {
t.Fatal(err)
}
if err := priv.(*rsa.PrivateKey).Validate(); err != nil {
t.Errorf("error while validating private key: %v", err)
}
if cert.Subject.CommonName != commonName {
t.Errorf("expected common name to be %q, but found %q", commonName, cert.Subject.CommonName)
}
}
}
func TestPfxRoundTriRsa(t *testing.T) { func TestPfxRoundTriRsa(t *testing.T) {
privateKey, err := rsa.GenerateKey(rand.Reader, 512) privateKey, err := rsa.GenerateKey(rand.Reader, 512)
if err != nil { if err != nil {
@ -25,7 +49,7 @@ func TestPfxRoundTriRsa(t *testing.T) {
actualPrivateKey, ok := key.(*rsa.PrivateKey) actualPrivateKey, ok := key.(*rsa.PrivateKey)
if !ok { if !ok {
t.Fatal("failed to decode private key") t.Fatalf("failed to decode private key")
} }
if privateKey.D.Cmp(actualPrivateKey.D) != 0 { if privateKey.D.Cmp(actualPrivateKey.D) != 0 {
@ -62,7 +86,7 @@ func testPfxRoundTrip(t *testing.T, privateKey interface{}) interface{} {
t.Fatal(err.Error()) t.Fatal(err.Error())
} }
key, _, err := gopkcs12.Decode(bytes, "sesame") key, _, err := Decode(bytes, "sesame")
if err != nil { if err != nil {
t.Fatalf(err.Error()) t.Fatalf(err.Error())
} }
@ -70,6 +94,61 @@ func testPfxRoundTrip(t *testing.T, privateKey interface{}) interface{} {
return key return key
} }
func TestPEM(t *testing.T) {
for commonName, base64P12 := range testdata {
p12, _ := base64.StdEncoding.DecodeString(base64P12)
blocks, err := ToPEM(p12, "")
if err != nil {
t.Fatalf("error while converting to PEM: %s", err)
}
var pemData []byte
for _, b := range blocks {
pemData = append(pemData, pem.EncodeToMemory(b)...)
}
cert, err := tls.X509KeyPair(pemData, pemData)
if err != nil {
t.Errorf("err while converting to key pair: %v", err)
}
config := tls.Config{
Certificates: []tls.Certificate{cert},
}
config.BuildNameToCertificate()
if _, exists := config.NameToCertificate[commonName]; !exists {
t.Errorf("did not find our cert in PEM?: %v", config.NameToCertificate)
}
}
}
func ExampleToPEM() {
p12, _ := base64.StdEncoding.DecodeString(`MIIJzgIBAzCCCZQGCS ... CA+gwggPk==`)
blocks, err := ToPEM(p12, "password")
if err != nil {
panic(err)
}
var pemData []byte
for _, b := range blocks {
pemData = append(pemData, pem.EncodeToMemory(b)...)
}
// then use PEM data for tls to construct tls certificate:
cert, err := tls.X509KeyPair(pemData, pemData)
if err != nil {
panic(err)
}
config := &tls.Config{
Certificates: []tls.Certificate{cert},
}
_ = config
}
func newCertificate(hostname string, privateKey interface{}) ([]byte, error) { func newCertificate(hostname string, privateKey interface{}) ([]byte, error) {
t, _ := time.Parse("2006-01-02", "2016-01-01") t, _ := time.Parse("2006-01-02", "2016-01-01")
notBefore := t notBefore := t
@ -115,3 +194,54 @@ func newCertificate(hostname string, privateKey interface{}) ([]byte, error) {
return derBytes, nil return derBytes, nil
} }
var testdata = map[string]string{
// 'null' password test case
"Windows Azure Tools": `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`,
// empty string password test case
"testing@example.com": `MIIJzgIBAzCCCZQGCSqGSIb3DQEHAaCCCYUEggmBMIIJfTCCA/cGCSqGSIb3DQEHBqCCA+gwggPk
AgEAMIID3QYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQIIszfRGqcmPcCAggAgIIDsOZ9Eg1L
s5Wx8JhYoV3HAL4aRnkAWvTYB5NISZOgSgIQTssmt/3A7134dibTmaT/93LikkL3cTKLnQzJ4wDf
YZ1bprpVJvUqz+HFT79m27bP9zYXFrvxWBJbxjYKTSjQMgz+h8LAEpXXGajCmxMJ1oCOtdXkhhzc
LdZN6SAYgtmtyFnCdMEDskSggGuLb3fw84QEJ/Sj6FAULXunW/CPaS7Ce0TMsKmNU/jfFWj3yXXw
ro0kwjKiVLpVFlnBlHo2OoVU7hmkm59YpGhLgS7nxLD3n7nBroQ0ID1+8R01NnV9XLGoGzxMm1te
6UyTCkr5mj+kEQ8EP1Ys7g/TC411uhVWySMt/rcpkx7Vz1r9kYEAzJpONAfr6cuEVkPKrxpq4Fh0
2fzlKBky0i/hrfIEUmngh+ERHUb/Mtv/fkv1j5w9suESbhsMLLiCXAlsP1UWMX+3bNizi3WVMEts
FM2k9byn+p8IUD/A8ULlE4kEaWeoc+2idkCNQkLGuIdGUXUFVm58se0auUkVRoRJx8x4CkMesT8j
b1H831W66YRWoEwwDQp2kK1lA2vQXxdVHWlFevMNxJeromLzj3ayiaFrfByeUXhR2S+Hpm+c0yNR
4UVU9WED2kacsZcpRm9nlEa5sr28mri5JdBrNa/K02OOhvKCxr5ZGmbOVzUQKla2z4w+Ku9k8POm
dfDNU/fGx1b5hcFWtghXe3msWVsSJrQihnN6q1ughzNiYZlJUGcHdZDRtiWwCFI0bR8h/Dmg9uO9
4rawQQrjIRT7B8yF3UbkZyAqs8Ppb1TsMeNPHh1rxEfGVQknh/48ouJYsmtbnzugTUt3mJCXXiL+
XcPMV6bBVAUu4aaVKSmg9+yJtY4/VKv10iw88ktv29fViIdBe3t6l/oPuvQgbQ8dqf4T8w0l/uKZ
9lS1Na9jfT1vCoS7F5TRi+tmyj1vL5kr/amEIW6xKEP6oeAMvCMtbPAzVEj38zdJ1R22FfuIBxkh
f0Zl7pdVbmzRxl/SBx9iIBJSqAvcXItiT0FIj8HxQ+0iZKqMQMiBuNWJf5pYOLWGrIyntCWwHuaQ
wrx0sTGuEL9YXLEAsBDrsvzLkx/56E4INGZFrH8G7HBdW6iGqb22IMI4GHltYSyBRKbB0gadYTyv
abPEoqww8o7/85aPSzOTJ/53ozD438Q+d0u9SyDuOb60SzCD/zPuCEd78YgtXJwBYTuUNRT27FaM
3LGMX8Hz+6yPNRnmnA2XKPn7dx/IlaqAjIs8MIIFfgYJKoZIhvcNAQcBoIIFbwSCBWswggVnMIIF
YwYLKoZIhvcNAQwKAQKgggTuMIIE6jAcBgoqhkiG9w0BDAEDMA4ECJr0cClYqOlcAgIIAASCBMhe
OQSiP2s0/46ONXcNeVAkz2ksW3u/+qorhSiskGZ0b3dFa1hhgBU2Q7JVIkc4Hf7OXaT1eVQ8oqND
uhqsNz83/kqYo70+LS8Hocj49jFgWAKrf/yQkdyP1daHa2yzlEw4mkpqOfnIORQHvYCa8nEApspZ
wVu8y6WVuLHKU67mel7db2xwstQp7PRuSAYqGjTfAylElog8ASdaqqYbYIrCXucF8iF9oVgmb/Qo
xrXshJ9aSLO4MuXlTPELmWgj07AXKSb90FKNihE+y0bWb9LPVFY1Sly3AX9PfrtkSXIZwqW3phpv
MxGxQl/R6mr1z+hlTfY9Wdpb5vlKXPKA0L0Rt8d2pOesylFi6esJoS01QgP1kJILjbrV731kvDc0
Jsd+Oxv4BMwA7ClG8w1EAOInc/GrV1MWFGw/HeEqj3CZ/l/0jv9bwkbVeVCiIhoL6P6lVx9pXq4t
KZ0uKg/tk5TVJmG2vLcMLvezD0Yk3G2ZOMrywtmskrwoF7oAUpO9e87szoH6fEvUZlkDkPVW1NV4
cZk3DBSQiuA3VOOg8qbo/tx/EE3H59P0axZWno2GSB0wFPWd1aj+b//tJEJHaaNR6qPRj4IWj9ru
Qbc8eRAcVWleHg8uAehSvUXlFpyMQREyrnpvMGddpiTC8N4UMrrBRhV7+UbCOWhxPCbItnInBqgl
1JpSZIP7iUtsIMdu3fEC2cdbXMTRul+4rdzUR7F9OaezV3jjvcAbDvgbK1CpyC+MJ1Mxm/iTgk9V
iUArydhlR8OniN84GyGYoYCW9O/KUwb6ASmeFOu/msx8x6kAsSQHIkKqMKv0TUR3kZnkxUvdpBGP
KTl4YCTvNGX4dYALBqrAETRDhua2KVBD/kEttDHwBNVbN2xi81+Mc7ml461aADfk0c66R/m2sjHB
2tN9+wG12OIWFQjL6wF/UfJMYamxx2zOOExiId29Opt57uYiNVLOO4ourPewHPeH0u8Gz35aero7
lkt7cZAe1Q0038JUuE/QGlnK4lESK9UkSIQAjSaAlTsrcfwtQxB2EjoOoLhwH5mvxUEmcNGNnXUc
9xj3M5BD3zBz3Ft7G3YMMDwB1+zC2l+0UG0MGVjMVaeoy32VVNvxgX7jk22OXG1iaOB+PY9kdk+O
X+52BGSf/rD6X0EnqY7XuRPkMGgjtpZeAYxRQnFtCZgDY4wYheuxqSSpdF49yNczSPLkgB3CeCfS
+9NTKN7aC6hBbmW/8yYh6OvSiCEwY0lFS/T+7iaVxr1loE4zI1y/FFp4Pe1qfLlLttVlkygga2UU
SCunTQ8UB/M5IXWKkhMOO11dP4niWwb39Y7pCWpau7mwbXOKfRPX96cgHnQJK5uG+BesDD1oYnX0
6frN7FOnTSHKruRIwuI8KnOQ/I+owmyz71wiv5LMQt+yM47UrEjB/EZa5X8dpEwOZvkdqL7utcyo
l0XH5kWMXdW856LL/FYftAqJIDAmtX1TXF/rbP6mPyN/IlDC0gjP84Uzd/a2UyTIWr+wk49Ek3vQ
/uDamq6QrwAxVmNh5Tset5Vhpc1e1kb7mRMZIzxSP8JcTuYd45oFKi98I8YjvueHVZce1g7OudQP
SbFQoJvdT46iBg1TTatlltpOiH2mFaxWVS0xYjAjBgkqhkiG9w0BCRUxFgQUdA9eVqvETX4an/c8
p8SsTugkit8wOwYJKoZIhvcNAQkUMS4eLABGAHIAaQBlAG4AZABsAHkAIABuAGEAbQBlACAAZgBv
AHIAIABjAGUAcgB0MDEwITAJBgUrDgMCGgUABBRFsNz3Zd1O1GI8GTuFwCWuDOjEEwQIuBEfIcAy
HQ8CAggA`,
}

View File

@ -1,3 +1,6 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package pkcs12 package pkcs12
import ( import (
@ -28,7 +31,7 @@ var (
// marshalPKCS8PrivateKey converts a private key to PKCS#8 encoded form. // marshalPKCS8PrivateKey converts a private key to PKCS#8 encoded form.
// See http://www.rsa.com/rsalabs/node.asp?id=2130 and RFC5208. // See http://www.rsa.com/rsalabs/node.asp?id=2130 and RFC5208.
func marshalPKCS8PrivateKey(key interface{}) ([]byte, error) { func marshalPKCS8PrivateKey(key interface{}) (der []byte, err error) {
pkcs := pkcs8{ pkcs := pkcs8{
Version: 0, Version: 0,
} }

View File

@ -35,7 +35,7 @@ func TestRoundTripPkcs8Rsa(t *testing.T) {
} }
if actualPrivateKey.Validate() != nil { if actualPrivateKey.Validate() != nil {
t.Fatal("private key did not validate") t.Fatalf("private key did not validate")
} }
if actualPrivateKey.N.Cmp(privateKey.N) != 0 { if actualPrivateKey.N.Cmp(privateKey.N) != 0 {

View File

@ -0,0 +1,27 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package rc2
import (
"testing"
)
func BenchmarkEncrypt(b *testing.B) {
r, _ := New([]byte{0, 0, 0, 0, 0, 0, 0, 0}, 64)
b.ResetTimer()
var src [8]byte
for i := 0; i < b.N; i++ {
r.Encrypt(src[:], src[:])
}
}
func BenchmarkDecrypt(b *testing.B) {
r, _ := New([]byte{0, 0, 0, 0, 0, 0, 0, 0}, 64)
b.ResetTimer()
var src [8]byte
for i := 0; i < b.N; i++ {
r.Decrypt(src[:], src[:])
}
}

View File

@ -1,3 +1,7 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package rc2 implements the RC2 cipher // Package rc2 implements the RC2 cipher
/* /*
https://www.ietf.org/rfc/rfc2268.txt https://www.ietf.org/rfc/rfc2268.txt
@ -10,7 +14,6 @@ package rc2
import ( import (
"crypto/cipher" "crypto/cipher"
"encoding/binary" "encoding/binary"
"strconv"
) )
// The rc2 block size in bytes // The rc2 block size in bytes
@ -20,34 +23,15 @@ type rc2Cipher struct {
k [64]uint16 k [64]uint16
} }
// KeySizeError indicates the supplied key was invalid
type KeySizeError int
func (k KeySizeError) Error() string { return "rc2: invalid key size " + strconv.Itoa(int(k)) }
// EffectiveKeySizeError indicates the supplied effective key length was invalid
type EffectiveKeySizeError int
func (k EffectiveKeySizeError) Error() string {
return "rc2: invalid effective key size " + strconv.Itoa(int(k))
}
// New returns a new rc2 cipher with the given key and effective key length t1 // New returns a new rc2 cipher with the given key and effective key length t1
func New(key []byte, t1 int) (cipher.Block, error) { func New(key []byte, t1 int) (cipher.Block, error) {
if l := len(key); l == 0 || l > 128 { // TODO(dgryski): error checking for key length
return nil, KeySizeError(l)
}
if t1 < 8 || t1 > 1024 {
return nil, EffectiveKeySizeError(t1)
}
return &rc2Cipher{ return &rc2Cipher{
k: expandKey(key, t1), k: expandKey(key, t1),
}, nil }, nil
} }
func (c *rc2Cipher) BlockSize() int { return BlockSize } func (*rc2Cipher) BlockSize() int { return BlockSize }
var piTable = [256]byte{ var piTable = [256]byte{
0xd9, 0x78, 0xf9, 0xc4, 0x19, 0xdd, 0xb5, 0xed, 0x28, 0xe9, 0xfd, 0x79, 0x4a, 0xa0, 0xd8, 0x9d, 0xd9, 0x78, 0xf9, 0xc4, 0x19, 0xdd, 0xb5, 0xed, 0x28, 0xe9, 0xfd, 0x79, 0x4a, 0xa0, 0xd8, 0x9d,
@ -109,7 +93,6 @@ func (c *rc2Cipher) Encrypt(dst, src []byte) {
var j int var j int
// These three mix blocks have not been extracted to a common function for to performance reasons.
for j <= 16 { for j <= 16 {
// mix r0 // mix r0
r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1) r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1)
@ -130,6 +113,7 @@ func (c *rc2Cipher) Encrypt(dst, src []byte) {
r3 = r3 + c.k[j] + (r2 & r1) + ((^r2) & r0) r3 = r3 + c.k[j] + (r2 & r1) + ((^r2) & r0)
r3 = rotl16(r3, 5) r3 = rotl16(r3, 5)
j++ j++
} }
r0 = r0 + c.k[r3&63] r0 = r0 + c.k[r3&63]
@ -138,6 +122,7 @@ func (c *rc2Cipher) Encrypt(dst, src []byte) {
r3 = r3 + c.k[r2&63] r3 = r3 + c.k[r2&63]
for j <= 40 { for j <= 40 {
// mix r0 // mix r0
r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1) r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1)
r0 = rotl16(r0, 1) r0 = rotl16(r0, 1)
@ -157,6 +142,7 @@ func (c *rc2Cipher) Encrypt(dst, src []byte) {
r3 = r3 + c.k[j] + (r2 & r1) + ((^r2) & r0) r3 = r3 + c.k[j] + (r2 & r1) + ((^r2) & r0)
r3 = rotl16(r3, 5) r3 = rotl16(r3, 5)
j++ j++
} }
r0 = r0 + c.k[r3&63] r0 = r0 + c.k[r3&63]
@ -165,6 +151,7 @@ func (c *rc2Cipher) Encrypt(dst, src []byte) {
r3 = r3 + c.k[r2&63] r3 = r3 + c.k[r2&63]
for j <= 60 { for j <= 60 {
// mix r0 // mix r0
r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1) r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1)
r0 = rotl16(r0, 1) r0 = rotl16(r0, 1)
@ -248,6 +235,7 @@ func (c *rc2Cipher) Decrypt(dst, src []byte) {
r0 = rotl16(r0, 16-1) r0 = rotl16(r0, 16-1)
r0 = r0 - c.k[j] - (r3 & r2) - ((^r3) & r1) r0 = r0 - c.k[j] - (r3 & r2) - ((^r3) & r1)
j-- j--
} }
r3 = r3 - c.k[r2&63] r3 = r3 - c.k[r2&63]
@ -256,6 +244,7 @@ func (c *rc2Cipher) Decrypt(dst, src []byte) {
r0 = r0 - c.k[r3&63] r0 = r0 - c.k[r3&63]
for j >= 0 { for j >= 0 {
// unmix r3 // unmix r3
r3 = rotl16(r3, 16-5) r3 = rotl16(r3, 16-5)
r3 = r3 - c.k[j] - (r2 & r1) - ((^r2) & r0) r3 = r3 - c.k[j] - (r2 & r1) - ((^r2) & r0)
@ -275,6 +264,7 @@ func (c *rc2Cipher) Decrypt(dst, src []byte) {
r0 = rotl16(r0, 16-1) r0 = rotl16(r0, 16-1)
r0 = r0 - c.k[j] - (r3 & r2) - ((^r3) & r1) r0 = r0 - c.k[j] - (r3 & r2) - ((^r3) & r1)
j-- j--
} }
binary.LittleEndian.PutUint16(dst[0:], r0) binary.LittleEndian.PutUint16(dst[0:], r0)

View File

@ -1,3 +1,7 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package rc2 package rc2
import ( import (
@ -8,6 +12,7 @@ import (
func TestEncryptDecrypt(t *testing.T) { func TestEncryptDecrypt(t *testing.T) {
// TODO(dgryski): add the rest of the test vectors from the RFC
var tests = []struct { var tests = []struct {
key string key string
plain string plain string
@ -86,20 +91,3 @@ func TestEncryptDecrypt(t *testing.T) {
} }
} }
} }
func BenchmarkEncrypt(b *testing.B) {
r, _ := New([]byte{0, 0, 0, 0, 0, 0, 0, 0}, 64)
b.ResetTimer()
var src [8]byte
for i := 0; i < b.N; i++ {
r.Encrypt(src[:], src[:])
}
}
func BenchmarkDecrypt(b *testing.B) {
r, _ := New([]byte{0, 0, 0, 0, 0, 0, 0, 0}, 64)
b.ResetTimer()
var src [8]byte
for i := 0; i < b.N; i++ {
r.Decrypt(src[:], src[:])
}
}

View File

@ -1,17 +1,21 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package pkcs12 package pkcs12
import ( import (
"crypto/x509"
"crypto/x509/pkix" "crypto/x509/pkix"
"encoding/asn1" "encoding/asn1"
"errors" "errors"
) )
//see https://tools.ietf.org/html/rfc7292#appendix-D
var ( var (
oidPkcs8ShroudedKeyBagType = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 12, 10, 1, 2} // see https://tools.ietf.org/html/rfc7292#appendix-D
oidCertBagType = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 12, 10, 1, 3} oidCertTypeX509Certificate = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 9, 22, 1})
oidPKCS8ShroudedKeyBag = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 10, 1, 2})
oidCertTypeX509Certificate = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 22, 1} oidCertBag = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 10, 1, 3})
) )
type certBag struct { type certBag struct {
@ -52,7 +56,7 @@ func encodePkcs8ShroudedKeyBag(privateKey interface{}, password []byte) (bytes [
pkinfo := encryptedPrivateKeyInfo{ pkinfo := encryptedPrivateKeyInfo{
AlgorithmIdentifier: pkix.AlgorithmIdentifier{ AlgorithmIdentifier: pkix.AlgorithmIdentifier{
Algorithm: oidPbeWithSHAAnd3KeyTripleDESCBC, Algorithm: oidPBEWithSHAAnd3KeyTripleDESCBC,
Parameters: params, Parameters: params,
}, },
EncryptedData: pkData, EncryptedData: pkData,
@ -65,3 +69,37 @@ func encodePkcs8ShroudedKeyBag(privateKey interface{}, password []byte) (bytes [
return bytes, err return bytes, err
} }
func decodePkcs8ShroudedKeyBag(asn1Data, password []byte) (privateKey interface{}, err error) {
pkinfo := new(encryptedPrivateKeyInfo)
if err = unmarshal(asn1Data, pkinfo); err != nil {
return nil, errors.New("pkcs12: error decoding PKCS#8 shrouded key bag: " + err.Error())
}
pkData, err := pbDecrypt(pkinfo, password)
if err != nil {
return nil, errors.New("pkcs12: error decrypting PKCS#8 shrouded key bag: " + err.Error())
}
ret := new(asn1.RawValue)
if err = unmarshal(pkData, ret); err != nil {
return nil, errors.New("pkcs12: error unmarshaling decrypted private key: " + err.Error())
}
if privateKey, err = x509.ParsePKCS8PrivateKey(pkData); err != nil {
return nil, errors.New("pkcs12: error parsing PKCS#8 private key: " + err.Error())
}
return privateKey, nil
}
func decodeCertBag(asn1Data []byte) (x509Certificates []byte, err error) {
bag := new(certBag)
if err := unmarshal(asn1Data, bag); err != nil {
return nil, errors.New("pkcs12: error decoding cert bag: " + err.Error())
}
if !bag.Id.Equal(oidCertTypeX509Certificate) {
return nil, NotImplementedError("only X509 certificates are supported")
}
return bag.Data, nil
}

View File

@ -1,39 +1,15 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package pkcs12 package pkcs12
import ( import (
"crypto/rand" "crypto/rand"
"crypto/rsa" "crypto/rsa"
"crypto/x509"
"encoding/asn1" "encoding/asn1"
"fmt"
"testing" "testing"
) )
func decodePkcs8ShroudedKeyBag(asn1Data, password []byte) (privateKey interface{}, err error) {
pkinfo := new(encryptedPrivateKeyInfo)
if _, err = asn1.Unmarshal(asn1Data, pkinfo); err != nil {
err = fmt.Errorf("error decoding PKCS8 shrouded key bag: %v", err)
return nil, err
}
pkData, err := pbDecrypt(pkinfo, password)
if err != nil {
err = fmt.Errorf("error decrypting PKCS8 shrouded key bag: %v", err)
return
}
rv := new(asn1.RawValue)
if _, err = asn1.Unmarshal(pkData, rv); err != nil {
err = fmt.Errorf("could not decode decrypted private key data")
}
if privateKey, err = x509.ParsePKCS8PrivateKey(pkData); err != nil {
err = fmt.Errorf("error parsing PKCS8 private key: %v", err)
return nil, err
}
return
}
// Assert the default algorithm parameters are in the correct order, // Assert the default algorithm parameters are in the correct order,
// and default to the correct value. Defaults are based on OpenSSL. // and default to the correct value. Defaults are based on OpenSSL.
// 1. IterationCount, defaults to 2,048 long. // 1. IterationCount, defaults to 2,048 long.
@ -61,7 +37,7 @@ func TestDefaultAlgorithmParametersPkcs8ShroudedKeyBag(t *testing.T) {
} }
var params pbeParams var params pbeParams
rest, err = asn1.Unmarshal(pkinfo.GetAlgorithm().Parameters.FullBytes, &params) rest, err = asn1.Unmarshal(pkinfo.Algorithm().Parameters.FullBytes, &params)
if err != nil { if err != nil {
t.Fatalf("failed to unmarshal encryptedPrivateKeyInfo %s", err) t.Fatalf("failed to unmarshal encryptedPrivateKeyInfo %s", err)
} }
@ -97,6 +73,6 @@ func TestRoundTripPkcs8ShroudedKeyBag(t *testing.T) {
actualPrivateKey := key.(*rsa.PrivateKey) actualPrivateKey := key.(*rsa.PrivateKey)
if actualPrivateKey.D.Cmp(privateKey.D) != 0 { if actualPrivateKey.D.Cmp(privateKey.D) != 0 {
t.Fatal("failed to round-trip rsa.PrivateKey.D") t.Fatalf("failed to round-trip rsa.PrivateKey.D")
} }
} }