Merge pull request #5301 from hashicorp/pkcs12redux

Replace pkcs12 code with upstream
This commit is contained in:
Matthew Hooker 2017-09-06 10:52:11 -07:00 committed by GitHub
commit 3eeb722897
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
import (
@ -5,6 +9,7 @@ import (
"unicode/utf16"
)
// bmpString returns s encoded in UCS-2 with a zero terminator.
func bmpString(s string) ([]byte, error) {
// References:
// 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
// - 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 {
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
import (
"bytes"
"errors"
"encoding/hex"
"testing"
"unicode/utf16"
)
func decodeBMPString(bmpString []byte) (string, error) {
if len(bmpString)%2 != 0 {
return "", errors.New("expected BMP byte string to be an even length")
}
// strip terminator if present
if terminator := bmpString[len(bmpString)-2:]; terminator[0] == terminator[1] && terminator[1] == 0 {
bmpString = bmpString[:len(bmpString)-2]
}
s := make([]uint16, 0, len(bmpString)/2)
for len(bmpString) > 0 {
s = append(s, uint16(bmpString[0])*265+uint16(bmpString[1]))
bmpString = bmpString[2:]
}
return string(utf16.Decode(s)), nil
var bmpStringTests = []struct {
in string
expectedHex string
shouldFail bool
}{
{"", "0000", false},
// Example from https://tools.ietf.org/html/rfc7292#appendix-B.
{"Beavis", "0042006500610076006900730000", false},
// Some characters from the "Letterlike Symbols Unicode block".
{"\u2115 - Double-struck N", "21150020002d00200044006f00750062006c0065002d00730074007200750063006b0020004e0000", false},
// any character outside the BMP should trigger an error.
{"\U0001f000 East wind (Mahjong)", "", true},
}
func TestBMPStringDecode(t *testing.T) {
_, err := decodeBMPString([]byte("a"))
if err == nil {
t.Fatal("expected decode to fail, but it succeeded")
if _, err := decodeBMPString([]byte("a")); err == nil {
t.Fatalf("expected decode to fail, but it succeeded")
}
}
func TestBMPString(t *testing.T) {
str, err := bmpString("")
if !bytes.Equal(str, []byte{0, 0}) {
t.Errorf("expected empty string to return double 0, but found: % x", str)
}
for i, test := range bmpStringTests {
expected, err := hex.DecodeString(test.expectedHex)
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
str, err = bmpString("Beavis")
if !bytes.Equal(str, []byte{0x00, 0x42, 0x00, 0x65, 0x00, 0x61, 0x00, 0x0076, 0x00, 0x69, 0x00, 0x73, 0x00, 0x00}) {
t.Errorf("expected 'Beavis' to return 0x00 0x42 0x00 0x65 0x00 0x61 0x00 0x76 0x00 0x69 0x00 0x73 0x00 0x00, but found: % x", str)
}
if err != nil {
t.Errorf("err: %v", err)
out, err := bmpString(test.in)
if err == nil && test.shouldFail {
t.Errorf("#%d: expected to fail, but produced %x", i, out)
continue
}
// some characters from the "Letterlike Symbols Unicode block"
tst := "\u2115 - Double-struck N"
str, err = bmpString(tst)
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)
if err != nil && !test.shouldFail {
t.Errorf("#%d: failed unexpectedly: %s", i, err)
continue
}
// some character outside the BMP should error
tst = "\U0001f000 East wind (Mahjong)"
_, err = bmpString(tst)
if err == nil {
t.Errorf("expected '%s' to throw error because the first character is not in the BMP", tst)
if !test.shouldFail {
if !bytes.Equal(out, expected) {
t.Errorf("#%d: expected %s, got %x", i, test.expectedHex, out)
continue
}
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
@ -15,31 +17,52 @@ import (
"github.com/hashicorp/packer/builder/azure/pkcs12/rc2"
)
const (
pbeWithSHAAnd3KeyTripleDESCBC = "pbeWithSHAAnd3-KeyTripleDES-CBC"
pbewithSHAAnd40BitRC2CBC = "pbewithSHAAnd40BitRC2-CBC"
)
const (
pbeIterationCount = 2048
pbeSaltSizeBytes = 8
)
var (
oidPbeWithSHAAnd3KeyTripleDESCBC = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 12, 1, 3}
oidPbewithSHAAnd40BitRC2CBC = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 12, 1, 6}
oidPBEWithSHAAnd3KeyTripleDESCBC = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 1, 3})
oidPBEWithSHAAnd40BitRC2CBC = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 1, 6})
)
var algByOID = map[string]string{
oidPbeWithSHAAnd3KeyTripleDESCBC.String(): pbeWithSHAAnd3KeyTripleDESCBC,
oidPbewithSHAAnd40BitRC2CBC.String(): pbewithSHAAnd40BitRC2CBC,
// pbeCipher is an abstraction of a PKCS#12 cipher.
type pbeCipher interface {
// 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){
pbeWithSHAAnd3KeyTripleDESCBC: des.NewTripleDESCipher,
pbewithSHAAnd40BitRC2CBC: func(key []byte) (cipher.Block, error) {
type shaWithTripleDESCBC struct{}
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)
},
}
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 {
@ -47,6 +70,67 @@ type pbeParams struct {
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 {
paddingLength := blockSize - len(src)%blockSize
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) {
_, err = io.ReadFull(rand.Reader, salt)
if err != nil {
if _, err := io.ReadFull(rand.Reader, salt); err != nil {
return nil, errors.New("pkcs12: failed to create a random salt value: " + err.Error())
}
key := deriveKeyByAlg[pbeWithSHAAnd3KeyTripleDESCBC](salt, password, iterations)
iv := deriveIVByAlg[pbeWithSHAAnd3KeyTripleDESCBC](salt, password, iterations)
cipherType := shaWithTripleDESCBC{}
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 {
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
}
// decryptable abstracts a object that contains ciphertext.
type decryptable interface {
GetAlgorithm() pkix.AlgorithmIdentifier
GetData() []byte
Algorithm() pkix.AlgorithmIdentifier
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
import (
"bytes"
"crypto/cipher"
"crypto/rand"
"crypto/x509/pkix"
"encoding/asn1"
"io"
"testing"
)
func pbDecrypterFor(algorithm pkix.AlgorithmIdentifier, password []byte) (cipher.BlockMode, error) {
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
}
var sha1WithTripleDES = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 1, 3})
func TestPbDecrypterFor(t *testing.T) {
params, _ := asn1.Marshal(pbeParams{
@ -70,70 +29,122 @@ func TestPbDecrypterFor(t *testing.T) {
pass, _ := bmpString("Sesame open")
_, err := pbDecrypterFor(alg, pass)
_, _, err := pbDecrypterFor(alg, pass)
if _, ok := err.(NotImplementedError); !ok {
t.Errorf("expected not implemented error, got: %T %s", err, err)
}
alg.Algorithm = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 1, 3})
cbc, err := pbDecrypterFor(alg, pass)
alg.Algorithm = sha1WithTripleDES
cbc, blockSize, err := pbDecrypterFor(alg, pass)
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}
expectedM := []byte{185, 73, 135, 249, 137, 1, 122, 247}
cbc.CryptBlocks(M, M)
plaintext := []byte{1, 2, 3, 4, 5, 6, 7, 8}
expectedCiphertext := []byte{185, 73, 135, 249, 137, 1, 122, 247}
ciphertext := make([]byte, len(plaintext))
cbc.CryptBlocks(ciphertext, plaintext)
if !bytes.Equal(M, expectedM) {
t.Errorf("expected M to be '%d', but found '%d", expectedM, M)
if bytes.Compare(ciphertext, expectedCiphertext) != 0 {
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) {
salt := []byte("\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8")
tests := [][]byte{
[]byte("\x33\x73\xf3\x9f\xda\x49\xae\xfc\xa0\x9a\xdf\x5a\x58\xa0\xea\x46"), // 7 padding bytes
[]byte("\x33\x73\xf3\x9f\xda\x49\xae\xfc\x96\x24\x2f\x71\x7e\x32\x3f\xe7"), // 8 padding bytes
[]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 ]
}
expected := []interface{}{
[]byte("A secret!"),
[]byte("A secret"),
ErrDecryption,
ErrDecryption,
for i, test := range pbDecryptTests {
decryptable := makeTestDecryptable(test.in, salt)
password, _ := bmpString("sesame")
plaintext, err := pbDecrypt(decryptable, password)
if err != test.expectedError {
t.Errorf("#%d: got error %q, but wanted %q", i, err, test.expectedError)
continue
}
for i, c := range tests {
td := testDecryptable{
data: c,
if !bytes.Equal(plaintext, test.expected) {
t.Errorf("#%d: got %x, but wanted %x", i, plaintext, test.expected)
}
}
}
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: asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 1, 3}), // SHA1/3TDES
Algorithm: sha1WithTripleDES,
Parameters: pbeParams{
Salt: []byte("\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8"),
Salt: salt,
Iterations: 4096,
}.RawASN1(),
},
}
p, _ := bmpString("sesame")
m, err := pbDecrypt(td, p)
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)
}
}
}
return decryptable
}
type testDecryptable struct {
@ -141,8 +152,8 @@ type testDecryptable struct {
algorithm pkix.AlgorithmIdentifier
}
func (d testDecryptable) GetAlgorithm() pkix.AlgorithmIdentifier { return d.algorithm }
func (d testDecryptable) GetData() []byte { return d.data }
func (d testDecryptable) Algorithm() pkix.AlgorithmIdentifier { return d.algorithm }
func (d testDecryptable) Data() []byte { return d.data }
func (params pbeParams) RawASN1() (raw asn1.RawValue) {
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
import "errors"
@ -16,7 +20,7 @@ type NotImplementedError string
type EncodeError string
func (e NotImplementedError) Error() string {
return string(e)
return "pkcs12: " + string(e)
}
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
import (
@ -7,10 +11,6 @@ import (
"encoding/asn1"
)
var (
oidSha1Algorithm = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 26}
)
type macData struct {
Mac digestInfo
MacSalt []byte
@ -23,6 +23,23 @@ type digestInfo struct {
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 {
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
import (
"crypto/hmac"
"encoding/asn1"
"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) {
td := macData{
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
import (
"bytes"
"crypto/sha1"
"math/big"
)
var (
deriveKeyByAlg = 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, 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)
},
}
one = big.NewInt(1)
)
// sha1Sum returns the SHA-1 hash of in.
func sha1Sum(in []byte) []byte {
sum := sha1.Sum(in)
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) {
// 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
// copies of ID.
D := []byte{}
var D []byte
for i := 0; i < v; i++ {
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
// string, then so is S.
S := []byte{}
{
s := len(salt)
times := s / v
if s%v > 0 {
times++
}
for len(S) < times*v {
S = append(S, salt...)
}
S = S[:times*v]
}
S := fillWithRepeats(salt, v)
// 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
// may be truncated to create P). Note that if the password is the
// empty string, then so is P.
P := []byte{}
{
s := len(password)
times := s / v
if s%v > 0 {
times++
}
for len(P) < times*v {
P = append(P, password...)
}
P = P[:times*v]
}
P := fillWithRepeats(password, v)
// 4. Set I=S||P to be the concatenation of S and P.
I := append(S, P...)
// 5. Set c=ceiling(n/u).
c := size / u
if size%u > 0 {
c++
}
c := (size + u - 1) / u
// 6. For i=1, 2, ..., c, do the following:
A := make([]byte, c*20)
var IjBuf []byte
for i := 0; i < c; i++ {
// A. Set A2=H^r(D||I). (i.e., the r-th hash of D||1,
// H(H(H(... H(D||I))))
Ai := hash(append(D, I...))
for j := 1; j < r; j++ {
Ai = hash(Ai[:])
Ai = hash(Ai)
}
copy(A[i*20:], Ai[:])
if i < c-1 { // skip on last iteration
// 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).
B := []byte{}
var B []byte
for len(B) < v {
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
// setting I_j=(I_j+B+1) mod 2^v for each j.
{
Bbi := new(big.Int)
Bbi.SetBytes(B)
one := big.NewInt(1)
Bbi := new(big.Int).SetBytes(B)
Ij := new(big.Int)
for j := 0; j < len(I)/v; j++ {
Ij := new(big.Int)
Ij.SetBytes(I[j*v : (j+1)*v])
Ij.Add(Ij, Bbi)
Ij.Add(Ij, one)
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 {
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)
}
}
@ -174,9 +160,7 @@ func pbkdf(hash func([]byte) []byte, u, v int, salt, password []byte, r int, ID
// bit string, A.
// 8. Use the first n bits of A as the output of this entire process.
A = A[:size]
return A
return A[:size]
// 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

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
import (
@ -6,13 +10,25 @@ import (
)
func TestThatPBKDFWorksCorrectlyForLongKeys(t *testing.T) {
pbkdf := deriveKeyByAlg[pbeWithSHAAnd3KeyTripleDESCBC]
cipherInfo := shaWithTripleDESCBC{}
salt := []byte("\xff\xff\xff\xff\xff\xff\xff\xff")
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) {
t.Fatalf("expected key '% x', but found '% x'", 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'", 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.
// It is intended for decoding P12/PFX-stored certificate+key for use with the crypto/tls package.
// This implementation is distilled from https://tools.ietf.org/html/rfc7292
// and referenced documents. It is intended for decoding P12/PFX-stored
// certificates and keys for use with the crypto/tls package.
package pkcs12
import (
"crypto/ecdsa"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/hex"
"encoding/pem"
"errors"
"io"
)
var (
oidLocalKeyID = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 21}
oidDataContentType = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 1}
oidDataContentType = asn1.ObjectIdentifier([]int{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}
)
@ -30,6 +44,23 @@ type contentInfo struct {
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 {
Id asn1.ObjectIdentifier
Value asn1.RawValue `asn1:"tag:0,explicit"`
@ -38,7 +69,7 @@ type safeBag struct {
type pkcs12Attribute struct {
Id asn1.ObjectIdentifier
Value asn1.RawValue `ans1:"set"`
Value asn1.RawValue `asn1:"set"`
}
type encryptedPrivateKeyInfo struct {
@ -46,8 +77,19 @@ type encryptedPrivateKeyInfo struct {
EncryptedData []byte
}
func (i encryptedPrivateKeyInfo) GetAlgorithm() pkix.AlgorithmIdentifier { return i.AlgorithmIdentifier }
func (i encryptedPrivateKeyInfo) GetData() []byte { return i.EncryptedData }
func (i encryptedPrivateKeyInfo) Algorithm() pkix.AlgorithmIdentifier {
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
// trailing data after unmarshaling.
@ -62,6 +104,168 @@ func unmarshal(in []byte, out interface{}) error {
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) {
octetString := asn1.RawValue{Tag: 4, Class: 0, IsCompound: false, Bytes: id}
bytes, err := asn1.Marshal(octetString)
@ -115,7 +319,7 @@ func makeCertBagContentInfo(derBytes []byte) (*contentInfo, error) {
return nil, EncodeError("encoding cert bag: " + err.Error())
}
certSafeBags, err := makeSafeBags(oidCertBagType, bytes)
certSafeBags, err := makeSafeBags(oidCertBag, bytes)
if err != nil {
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())
}
safeBags, err := makeSafeBags(oidPkcs8ShroudedKeyBagType, shroudedKeyBagBytes)
safeBags, err := makeSafeBags(oidPKCS8ShroudedKeyBag, shroudedKeyBagBytes)
if err != nil {
return nil, EncodeError("safe bags: " + err.Error())
}
@ -183,7 +387,7 @@ func makeSalt(saltByteCount int) ([]byte, error) {
//
// derBytes is a DER encoded certificate.
// 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)
if err != nil {
return nil, ErrIncorrectPassword
@ -230,7 +434,7 @@ func Encode(derBytes []byte, privateKey interface{}, password string) ([]byte, e
MacSalt: salt,
Mac: digestInfo{
Algorithm: pkix.AlgorithmIdentifier{
Algorithm: oidSha1Algorithm,
Algorithm: oidSHA1,
},
Digest: digest,
},
@ -244,3 +448,83 @@ func Encode(derBytes []byte, privateKey interface{}, password string) ([]byte, e
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
import (
@ -5,16 +9,36 @@ import (
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/base64"
"encoding/pem"
"fmt"
"math/big"
"testing"
"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) {
privateKey, err := rsa.GenerateKey(rand.Reader, 512)
if err != nil {
@ -25,7 +49,7 @@ func TestPfxRoundTriRsa(t *testing.T) {
actualPrivateKey, ok := key.(*rsa.PrivateKey)
if !ok {
t.Fatal("failed to decode private key")
t.Fatalf("failed to decode private key")
}
if privateKey.D.Cmp(actualPrivateKey.D) != 0 {
@ -62,7 +86,7 @@ func testPfxRoundTrip(t *testing.T, privateKey interface{}) interface{} {
t.Fatal(err.Error())
}
key, _, err := gopkcs12.Decode(bytes, "sesame")
key, _, err := Decode(bytes, "sesame")
if err != nil {
t.Fatalf(err.Error())
}
@ -70,6 +94,61 @@ func testPfxRoundTrip(t *testing.T, privateKey interface{}) interface{} {
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) {
t, _ := time.Parse("2006-01-02", "2016-01-01")
notBefore := t
@ -115,3 +194,54 @@ func newCertificate(hostname string, privateKey interface{}) ([]byte, error) {
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": `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
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
import (
@ -28,7 +31,7 @@ var (
// marshalPKCS8PrivateKey converts a private key to PKCS#8 encoded form.
// 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{
Version: 0,
}

View File

@ -35,7 +35,7 @@ func TestRoundTripPkcs8Rsa(t *testing.T) {
}
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 {

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
/*
https://www.ietf.org/rfc/rfc2268.txt
@ -10,7 +14,6 @@ package rc2
import (
"crypto/cipher"
"encoding/binary"
"strconv"
)
// The rc2 block size in bytes
@ -20,34 +23,15 @@ type rc2Cipher struct {
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
func New(key []byte, t1 int) (cipher.Block, error) {
if l := len(key); l == 0 || l > 128 {
return nil, KeySizeError(l)
}
if t1 < 8 || t1 > 1024 {
return nil, EffectiveKeySizeError(t1)
}
// TODO(dgryski): error checking for key length
return &rc2Cipher{
k: expandKey(key, t1),
}, nil
}
func (c *rc2Cipher) BlockSize() int { return BlockSize }
func (*rc2Cipher) BlockSize() int { return BlockSize }
var piTable = [256]byte{
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
// These three mix blocks have not been extracted to a common function for to performance reasons.
for j <= 16 {
// mix r0
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 = rotl16(r3, 5)
j++
}
r0 = r0 + c.k[r3&63]
@ -138,6 +122,7 @@ func (c *rc2Cipher) Encrypt(dst, src []byte) {
r3 = r3 + c.k[r2&63]
for j <= 40 {
// mix r0
r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1)
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 = rotl16(r3, 5)
j++
}
r0 = r0 + c.k[r3&63]
@ -165,6 +151,7 @@ func (c *rc2Cipher) Encrypt(dst, src []byte) {
r3 = r3 + c.k[r2&63]
for j <= 60 {
// mix r0
r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1)
r0 = rotl16(r0, 1)
@ -248,6 +235,7 @@ func (c *rc2Cipher) Decrypt(dst, src []byte) {
r0 = rotl16(r0, 16-1)
r0 = r0 - c.k[j] - (r3 & r2) - ((^r3) & r1)
j--
}
r3 = r3 - c.k[r2&63]
@ -256,6 +244,7 @@ func (c *rc2Cipher) Decrypt(dst, src []byte) {
r0 = r0 - c.k[r3&63]
for j >= 0 {
// unmix r3
r3 = rotl16(r3, 16-5)
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 = r0 - c.k[j] - (r3 & r2) - ((^r3) & r1)
j--
}
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
import (
@ -8,6 +12,7 @@ import (
func TestEncryptDecrypt(t *testing.T) {
// TODO(dgryski): add the rest of the test vectors from the RFC
var tests = []struct {
key 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
import (
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"errors"
)
//see https://tools.ietf.org/html/rfc7292#appendix-D
var (
oidPkcs8ShroudedKeyBagType = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 12, 10, 1, 2}
oidCertBagType = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 12, 10, 1, 3}
oidCertTypeX509Certificate = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 22, 1}
// see https://tools.ietf.org/html/rfc7292#appendix-D
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})
oidCertBag = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 10, 1, 3})
)
type certBag struct {
@ -52,7 +56,7 @@ func encodePkcs8ShroudedKeyBag(privateKey interface{}, password []byte) (bytes [
pkinfo := encryptedPrivateKeyInfo{
AlgorithmIdentifier: pkix.AlgorithmIdentifier{
Algorithm: oidPbeWithSHAAnd3KeyTripleDESCBC,
Algorithm: oidPBEWithSHAAnd3KeyTripleDESCBC,
Parameters: params,
},
EncryptedData: pkData,
@ -65,3 +69,37 @@ func encodePkcs8ShroudedKeyBag(privateKey interface{}, password []byte) (bytes [
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
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/asn1"
"fmt"
"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,
// and default to the correct value. Defaults are based on OpenSSL.
// 1. IterationCount, defaults to 2,048 long.
@ -61,7 +37,7 @@ func TestDefaultAlgorithmParametersPkcs8ShroudedKeyBag(t *testing.T) {
}
var params pbeParams
rest, err = asn1.Unmarshal(pkinfo.GetAlgorithm().Parameters.FullBytes, &params)
rest, err = asn1.Unmarshal(pkinfo.Algorithm().Parameters.FullBytes, &params)
if err != nil {
t.Fatalf("failed to unmarshal encryptedPrivateKeyInfo %s", err)
}
@ -97,6 +73,6 @@ func TestRoundTripPkcs8ShroudedKeyBag(t *testing.T) {
actualPrivateKey := key.(*rsa.PrivateKey)
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")
}
}