Added support for ECDH in enveloped data with ECDSA certificates.
- fixed parsing of choice(was compatible with Apple mail.app which tagged kari explicitly) - minor fixes
This commit is contained in:
parent
58514b0041
commit
5f34d82562
@ -19,7 +19,7 @@ It supports enveloped data with AES in CBC mode. Decryption also works with (3)D
|
|||||||
This is covered in
|
This is covered in
|
||||||
- Cryptographic Message Syntax (CMS) Authenticated-Enveloped-Data Content Type [rfc5083](https://tools.ietf.org/html/rfc5083)
|
- Cryptographic Message Syntax (CMS) Authenticated-Enveloped-Data Content Type [rfc5083](https://tools.ietf.org/html/rfc5083)
|
||||||
- Using ChaCha20-Poly1305 Authenticated Encryption in the Cryptographic Message Syntax (CMS) [rfc8103](https://tools.ietf.org/html/rfc8103)
|
- Using ChaCha20-Poly1305 Authenticated Encryption in the Cryptographic Message Syntax (CMS) [rfc8103](https://tools.ietf.org/html/rfc8103)
|
||||||
- Using AES-CCM and AES-GCM Authenticated Encryption in the Cryptographic Message Syntax (CMS) [rfc5084](https://tools.ietf.org/html/rfc 5084)
|
- Using AES-CCM and AES-GCM Authenticated Encryption in the Cryptographic Message Syntax (CMS) [rfc5084](https://tools.ietf.org/html/rfc5084)
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
|
18
asn1/asn1.go
18
asn1/asn1.go
@ -760,7 +760,7 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if params.explicit {
|
if params.explicit && !params.choice {
|
||||||
expectedClass := ClassContextSpecific
|
expectedClass := ClassContextSpecific
|
||||||
if params.application {
|
if params.application {
|
||||||
expectedClass = ClassApplication
|
expectedClass = ClassApplication
|
||||||
@ -968,19 +968,13 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
|
|||||||
}
|
}
|
||||||
|
|
||||||
innerOffset := 0
|
innerOffset := 0
|
||||||
|
if params.choice {
|
||||||
|
if !params.explicit {
|
||||||
|
innerBytes = bytes[initOffset:]
|
||||||
|
}
|
||||||
|
}
|
||||||
for i := 0; i < structType.NumField(); i++ {
|
for i := 0; i < structType.NumField(); i++ {
|
||||||
field := structType.Field(i)
|
field := structType.Field(i)
|
||||||
if params.choice {
|
|
||||||
tag := parseFieldParameters(field.Tag.Get("asn1")).tag
|
|
||||||
if tag != nil && t.tag == *tag || t.class != ClassContextSpecific && tag == nil {
|
|
||||||
if tag == nil || params.set {
|
|
||||||
innerBytes = bytes[initOffset:offset]
|
|
||||||
}
|
|
||||||
innerOffset, err = parseField(val.Field(i), innerBytes, innerOffset, fieldParameters{})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if i == 0 && field.Type == rawContentsType {
|
if i == 0 && field.Type == rawContentsType {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -482,9 +482,6 @@ func makeBody(value reflect.Value, params fieldParameters) (e encoder, err error
|
|||||||
m := make([]encoder, n1)
|
m := make([]encoder, n1)
|
||||||
for i := 0; i < n1; i++ {
|
for i := 0; i < n1; i++ {
|
||||||
fp := parseFieldParameters(t.Field(i + startingField).Tag.Get("asn1"))
|
fp := parseFieldParameters(t.Field(i + startingField).Tag.Get("asn1"))
|
||||||
if params.explicit && params.choice {
|
|
||||||
fp.explicit = true
|
|
||||||
}
|
|
||||||
m[i], err = makeField(v.Field(i+startingField), fp)
|
m[i], err = makeField(v.Field(i+startingField), fp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -501,9 +498,6 @@ func makeBody(value reflect.Value, params fieldParameters) (e encoder, err error
|
|||||||
|
|
||||||
var fp fieldParameters
|
var fp fieldParameters
|
||||||
fp.choice = params.choice
|
fp.choice = params.choice
|
||||||
if params.choice && params.set {
|
|
||||||
fp.explicit = true
|
|
||||||
}
|
|
||||||
|
|
||||||
switch l := v.Len(); l {
|
switch l := v.Len(); l {
|
||||||
case 0:
|
case 0:
|
||||||
|
12
cms/cms.go
12
cms/cms.go
@ -65,7 +65,11 @@ func (cms *CMS) Encrypt(data []byte, recipients []*x509.Certificate) (der []byte
|
|||||||
var reciInfos []protocol.RecipientInfo
|
var reciInfos []protocol.RecipientInfo
|
||||||
|
|
||||||
for _, recipient := range recipients {
|
for _, recipient := range recipients {
|
||||||
rInfo := protocol.NewRecipientInfo(recipient, key)
|
var rInfo protocol.RecipientInfo
|
||||||
|
rInfo, err = protocol.NewRecipientInfo(recipient, key)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
reciInfos = append(reciInfos, rInfo)
|
reciInfos = append(reciInfos, rInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,7 +94,11 @@ func (cms *CMS) AuthEncrypt(data []byte, recipients []*x509.Certificate) (der []
|
|||||||
var reciInfos []protocol.RecipientInfo
|
var reciInfos []protocol.RecipientInfo
|
||||||
|
|
||||||
for _, recipient := range recipients {
|
for _, recipient := range recipients {
|
||||||
rInfo := protocol.NewRecipientInfo(recipient, key)
|
var rInfo protocol.RecipientInfo
|
||||||
|
rInfo, err = protocol.NewRecipientInfo(recipient, key)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
reciInfos = append(reciInfos, rInfo)
|
reciInfos = append(reciInfos, rInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,10 +3,13 @@ package cms
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto"
|
"crypto"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/rand"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"crypto/x509/pkix"
|
"crypto/x509/pkix"
|
||||||
"log"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
openssl "github.com/InfiniteLoopSpace/go_S-MIME/openssl"
|
openssl "github.com/InfiniteLoopSpace/go_S-MIME/openssl"
|
||||||
@ -26,62 +29,103 @@ var (
|
|||||||
CommonName: "leaf.example.com",
|
CommonName: "leaf.example.com",
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
ecKey, _ = ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
|
||||||
|
|
||||||
|
leafECC = intermediate.Issue(pki.Subject(pkix.Name{
|
||||||
|
CommonName: "leaf.example.com",
|
||||||
|
}), pki.PrivateKey(ecKey))
|
||||||
|
|
||||||
keyPair = tls.Certificate{
|
keyPair = tls.Certificate{
|
||||||
Certificate: [][]byte{leaf.Certificate.Raw, intermediate.Certificate.Raw, root.Certificate.Raw},
|
Certificate: [][]byte{leaf.Certificate.Raw, intermediate.Certificate.Raw, root.Certificate.Raw},
|
||||||
PrivateKey: leaf.PrivateKey.(crypto.PrivateKey),
|
PrivateKey: leaf.PrivateKey.(crypto.PrivateKey),
|
||||||
|
Leaf: leaf.Certificate,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
keyPairECC = tls.Certificate{
|
||||||
|
Certificate: [][]byte{leafECC.Certificate.Raw, intermediate.Certificate.Raw, root.Certificate.Raw},
|
||||||
|
PrivateKey: leafECC.PrivateKey.(crypto.PrivateKey),
|
||||||
|
Leaf: leafECC.Certificate,
|
||||||
|
}
|
||||||
|
|
||||||
|
keyPairs = []tls.Certificate{}
|
||||||
|
|
||||||
|
keyPairsOpenssl = []tls.Certificate{}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
|
||||||
|
keyPairs = []tls.Certificate{keyPair, keyPairECC}
|
||||||
|
|
||||||
|
version, err := openssl.Openssl(nil, "version")
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
keyPairsOpenssl = append(keyPairsOpenssl, keyPair)
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(string(version), "OpenSSL 1.1") {
|
||||||
|
openssl.SMIME = "cms"
|
||||||
|
keyPairsOpenssl = append(keyPairsOpenssl, keyPairECC)
|
||||||
|
}
|
||||||
|
|
||||||
|
m.Run()
|
||||||
|
}
|
||||||
|
|
||||||
func TestAuthEnrypt(t *testing.T) {
|
func TestAuthEnrypt(t *testing.T) {
|
||||||
|
|
||||||
cms, err := New(keyPair)
|
for _, keypair := range keyPairs {
|
||||||
|
cms, err := New(keypair)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
plaintext := []byte("Hallo Welt!")
|
plaintext := []byte("Hallo Welt!")
|
||||||
|
|
||||||
ciphertext, err := cms.AuthEncrypt(plaintext, []*x509.Certificate{leaf.Certificate})
|
ciphertext, err := cms.AuthEncrypt(plaintext, []*x509.Certificate{keypair.Leaf})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
plain, err := cms.AuthDecrypt(ciphertext)
|
plain, err := cms.AuthDecrypt(ciphertext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !bytes.Equal(plaintext, plain) {
|
if !bytes.Equal(plaintext, plain) {
|
||||||
t.Fatal("Encryption and decryption are not inverse")
|
t.Fatal("Encryption and decryption are not inverse")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEnryptDecrypt(t *testing.T) {
|
func TestEnryptDecrypt(t *testing.T) {
|
||||||
|
|
||||||
cms, err := New(keyPair)
|
for _, keypair := range keyPairs {
|
||||||
|
cms, err := New(keypair)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
plaintext := []byte("Hallo Welt!")
|
plaintext := []byte("Hallo Welt!")
|
||||||
|
|
||||||
ciphertext, err := cms.Encrypt(plaintext, []*x509.Certificate{leaf.Certificate})
|
ciphertext, err := cms.Encrypt(plaintext, []*x509.Certificate{keypair.Leaf})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
plain, err := cms.Decrypt(ciphertext)
|
plain, err := cms.Decrypt(ciphertext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !bytes.Equal(plaintext, plain) {
|
if !bytes.Equal(plaintext, plain) {
|
||||||
t.Fatal("Encryption and decryption are not inverse")
|
t.Fatal("Encryption and decryption are not inverse")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSignVerify(t *testing.T) {
|
func TestSignVerify(t *testing.T) {
|
||||||
cms, err := New(keyPair)
|
|
||||||
|
for _, keypair := range keyPairs {
|
||||||
|
cms, err := New(keypair)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
@ -99,17 +143,20 @@ func TestSignVerify(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEncryptOpenSSL(t *testing.T) {
|
func TestEncryptOpenSSL(t *testing.T) {
|
||||||
|
|
||||||
|
for _, keypair := range keyPairsOpenssl {
|
||||||
message := []byte("Hallo Welt!")
|
message := []byte("Hallo Welt!")
|
||||||
|
|
||||||
der, err := openssl.Encrypt(message, leaf.Certificate, "-outform", "DER")
|
der, err := openssl.Encrypt(message, keypair.Leaf, "-outform", "DER")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cms, err := New(keyPair)
|
cms, err := New(keypair)
|
||||||
plain, err := cms.Decrypt(der)
|
plain, err := cms.Decrypt(der)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
@ -118,18 +165,21 @@ func TestEncryptOpenSSL(t *testing.T) {
|
|||||||
if !bytes.Equal(message, plain) {
|
if !bytes.Equal(message, plain) {
|
||||||
t.Fatal("Encryption and decryption are not inverse")
|
t.Fatal("Encryption and decryption are not inverse")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDecryptOpenSSL(t *testing.T) {
|
func TestDecryptOpenSSL(t *testing.T) {
|
||||||
|
|
||||||
|
for _, keypair := range keyPairsOpenssl {
|
||||||
message := []byte("Hallo Welt!")
|
message := []byte("Hallo Welt!")
|
||||||
|
|
||||||
cms, _ := New()
|
cms, _ := New()
|
||||||
ciphertext, err := cms.Encrypt(message, []*x509.Certificate{leaf.Certificate})
|
ciphertext, err := cms.Encrypt(message, []*x509.Certificate{keypair.Leaf})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
plain, err := openssl.Decrypt(ciphertext, leaf.PrivateKey, "-inform", "DER")
|
plain, err := openssl.Decrypt(ciphertext, keypair.PrivateKey, "-inform", "DER")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
@ -137,12 +187,15 @@ func TestDecryptOpenSSL(t *testing.T) {
|
|||||||
if !bytes.Equal(message, plain) {
|
if !bytes.Equal(message, plain) {
|
||||||
t.Fatal("Encryption and decryption are not inverse")
|
t.Fatal("Encryption and decryption are not inverse")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSignOpenSSL(t *testing.T) {
|
func TestSignOpenSSL(t *testing.T) {
|
||||||
|
|
||||||
|
for _, keypair := range keyPairsOpenssl {
|
||||||
message := []byte("Hallo Welt")
|
message := []byte("Hallo Welt")
|
||||||
|
|
||||||
sig, err := openssl.SignDetached(message, leaf.Certificate, leaf.PrivateKey, []*x509.Certificate{intermediate.Certificate}, "-outform", "DER")
|
sig, err := openssl.SignDetached(message, keypair.Leaf, keypair.PrivateKey, []*x509.Certificate{intermediate.Certificate}, "-outform", "DER")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
@ -157,10 +210,13 @@ func TestSignOpenSSL(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestVerifyOpenSSL(t *testing.T) {
|
func TestVerifyOpenSSL(t *testing.T) {
|
||||||
cms, err := New(keyPair)
|
|
||||||
|
for _, keypair := range keyPairsOpenssl {
|
||||||
|
cms, err := New(keypair)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
@ -184,4 +240,5 @@ func TestVerifyOpenSSL(t *testing.T) {
|
|||||||
if !bytes.Equal(msg, sig) {
|
if !bytes.Equal(msg, sig) {
|
||||||
t.Fatal("Signed message and message do not agree!")
|
t.Fatal("Signed message and message do not agree!")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
218
cms/protocol/ecdh.go
Normal file
218
cms/protocol/ecdh.go
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
package protocol
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"crypto/x509/pkix"
|
||||||
|
"encoding/asn1"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/InfiniteLoopSpace/go_S-MIME/oid"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errUnsupported = errors.New("Unsupported hash function")
|
||||||
|
|
||||||
|
// ECDHsharedSecret computes shared secret with ephemeral static ECDH
|
||||||
|
func ECDHsharedSecret(curve elliptic.Curve, priv []byte, pubX, pubY *big.Int) []byte {
|
||||||
|
|
||||||
|
x, _ := curve.ScalarMult(pubX, pubY, priv)
|
||||||
|
|
||||||
|
return x.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ANSIx963KDF implents ANSI X9.63 key derivation function
|
||||||
|
func ANSIx963KDF(sharedSecret, sharedInfo []byte, keyLen int, hash crypto.Hash) (key []byte, err error) {
|
||||||
|
|
||||||
|
ctr := make([]byte, 4)
|
||||||
|
ctr[3] = 0x01
|
||||||
|
if hash == 0 || !hash.Available() {
|
||||||
|
return nil, errUnsupported
|
||||||
|
}
|
||||||
|
h := hash.New()
|
||||||
|
|
||||||
|
for i := 0; i < keyLen/hash.Size()+1; i++ {
|
||||||
|
h.Reset()
|
||||||
|
h.Write(sharedSecret)
|
||||||
|
h.Write(ctr)
|
||||||
|
h.Write(sharedInfo)
|
||||||
|
key = append(key, h.Sum(nil)...)
|
||||||
|
|
||||||
|
// Increment counter
|
||||||
|
for i := len(ctr) - 1; i >= 0; i-- {
|
||||||
|
ctr[i]++
|
||||||
|
if ctr[i] != 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return key[:keyLen], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func encryptKeyECDH(key []byte, recipient *x509.Certificate) (kari KeyAgreeRecipientInfo, err error) {
|
||||||
|
|
||||||
|
keyWrapAlgorithm := oid.KeyWrap{KeyWrapAlgorithm: oid.AES128Wrap}
|
||||||
|
keyEncryptionAlgorithm := oid.DHSinglePassstdDHsha256kdfscheme
|
||||||
|
hash := oid.KDFHashAlgorithm[keyEncryptionAlgorithm.String()]
|
||||||
|
|
||||||
|
kari.UKM = make([]byte, 8)
|
||||||
|
rand.Read(kari.UKM)
|
||||||
|
|
||||||
|
kari.Version = 3
|
||||||
|
kari.Originator.OriginatorKey.Algorithm = pkix.AlgorithmIdentifier{Algorithm: oid.ECPublicKey}
|
||||||
|
|
||||||
|
// check recipient key
|
||||||
|
|
||||||
|
if recipient.PublicKeyAlgorithm != x509.ECDSA {
|
||||||
|
err = errors.New("Recipient certficiate has wrong public key algorithm, expected ECDSA")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pubKey, ok := recipient.PublicKey.(*ecdsa.PublicKey)
|
||||||
|
if !ok {
|
||||||
|
err = errors.New("Can not parse public key of recipient")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// genrate ephemeral public key and key encryption key
|
||||||
|
|
||||||
|
priv, x, y, err := elliptic.GenerateKey(pubKey.Curve, rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ephPubKey := elliptic.Marshal(pubKey.Curve, x, y)
|
||||||
|
kari.Originator.OriginatorKey.PublicKey = asn1.BitString{Bytes: ephPubKey, BitLength: len(ephPubKey) * 8}
|
||||||
|
|
||||||
|
sharedSecret := ECDHsharedSecret(pubKey.Curve, priv, pubKey.X, pubKey.Y)
|
||||||
|
|
||||||
|
keyLenBigEnd := make([]byte, 4)
|
||||||
|
binary.BigEndian.PutUint32(keyLenBigEnd, uint32(keyWrapAlgorithm.KeyLen())*8)
|
||||||
|
sharedInfo := ECCCMSSharedInfo{KeyInfo: keyWrapAlgorithm.AlgorithmIdentifier(),
|
||||||
|
SuppPubInfo: keyLenBigEnd,
|
||||||
|
EntityUInfo: kari.UKM}
|
||||||
|
|
||||||
|
sharedInfoDER, err := asn1.Marshal(sharedInfo)
|
||||||
|
|
||||||
|
kek, err := ANSIx963KDF(sharedSecret, sharedInfoDER, keyWrapAlgorithm.KeyLen(), hash)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// encrypt key
|
||||||
|
|
||||||
|
keyWrapAlgorithm.KEK = kek
|
||||||
|
encKey, err := keyWrapAlgorithm.Wrap(key)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
keyWrapAlgorithmIdentifier, err := RawValue(keyWrapAlgorithm.AlgorithmIdentifier())
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
kari.KeyEncryptionAlgorithm = pkix.AlgorithmIdentifier{Algorithm: keyEncryptionAlgorithm,
|
||||||
|
Parameters: keyWrapAlgorithmIdentifier}
|
||||||
|
|
||||||
|
ias, err := NewIssuerAndSerialNumber(recipient)
|
||||||
|
karID := KeyAgreeRecipientIdentifier{IAS: ias}
|
||||||
|
|
||||||
|
kari.RecipientEncryptedKeys = append(kari.RecipientEncryptedKeys, RecipientEncryptedKey{RID: karID, EncryptedKey: encKey})
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ECCCMSSharedInfo ECC-CMS-SharedInfo ::= SEQUENCE {
|
||||||
|
// keyInfo AlgorithmIdentifier,
|
||||||
|
// entityUInfo [0] EXPLICIT OCTET STRING OPTIONAL,
|
||||||
|
// suppPubInfo [2] EXPLICIT OCTET STRING }
|
||||||
|
type ECCCMSSharedInfo struct {
|
||||||
|
KeyInfo pkix.AlgorithmIdentifier
|
||||||
|
EntityUInfo []byte `asn1:"optional,explicit,tag:0"`
|
||||||
|
SuppPubInfo []byte `asn1:"explicit,tag:2"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kari *KeyAgreeRecipientInfo) decryptKey(keyPair tls.Certificate) (key []byte, err error) {
|
||||||
|
|
||||||
|
// check for ec key
|
||||||
|
|
||||||
|
if kari.Version != 3 {
|
||||||
|
err = errors.New("Version not supported")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !kari.Originator.OriginatorKey.Algorithm.Algorithm.Equal(oid.ECPublicKey) {
|
||||||
|
err = errors.New("Orginator key algorithm not supported")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pubKey, ok := keyPair.Leaf.PublicKey.(*ecdsa.PublicKey)
|
||||||
|
if !ok {
|
||||||
|
err = errors.New("Can not parse public key of recipient")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
x, y := elliptic.Unmarshal(pubKey.Curve, kari.Originator.OriginatorKey.PublicKey.Bytes)
|
||||||
|
|
||||||
|
// genrate ephemeral public key and key encryption key
|
||||||
|
|
||||||
|
priv := keyPair.PrivateKey.(*ecdsa.PrivateKey)
|
||||||
|
|
||||||
|
privateKeyBytes := keyPair.PrivateKey.(*ecdsa.PrivateKey).D.Bytes()
|
||||||
|
paddedPrivateKey := make([]byte, (priv.Curve.Params().N.BitLen()+7)/8)
|
||||||
|
copy(paddedPrivateKey[len(paddedPrivateKey)-len(privateKeyBytes):], privateKeyBytes)
|
||||||
|
|
||||||
|
sharedSecret := ECDHsharedSecret(pubKey.Curve, paddedPrivateKey, x, y)
|
||||||
|
|
||||||
|
hash, ok := oid.KDFHashAlgorithm[kari.KeyEncryptionAlgorithm.Algorithm.String()]
|
||||||
|
if !ok {
|
||||||
|
err = errors.New("Unsupported key derivation hash algorithm")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var keyWrapAlgorithmIdentifier pkix.AlgorithmIdentifier
|
||||||
|
asn1.Unmarshal(kari.KeyEncryptionAlgorithm.Parameters.FullBytes, &keyWrapAlgorithmIdentifier)
|
||||||
|
keyWrapAlgorithm := oid.KeyWrap{KeyWrapAlgorithm: keyWrapAlgorithmIdentifier.Algorithm}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
keyLenBigEnd := make([]byte, 4)
|
||||||
|
binary.BigEndian.PutUint32(keyLenBigEnd, uint32(keyWrapAlgorithm.KeyLen())*8)
|
||||||
|
sharedInfo := ECCCMSSharedInfo{KeyInfo: keyWrapAlgorithmIdentifier,
|
||||||
|
SuppPubInfo: keyLenBigEnd,
|
||||||
|
EntityUInfo: kari.UKM}
|
||||||
|
|
||||||
|
sharedInfoDER, err := asn1.Marshal(sharedInfo)
|
||||||
|
|
||||||
|
kek, err := ANSIx963KDF(sharedSecret, sharedInfoDER, keyWrapAlgorithm.KeyLen(), hash)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
keyWrapAlgorithm.KEK = kek
|
||||||
|
|
||||||
|
// encrypt key
|
||||||
|
|
||||||
|
ias, err := NewIssuerAndSerialNumber(keyPair.Leaf)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range kari.RecipientEncryptedKeys {
|
||||||
|
if kari.RecipientEncryptedKeys[i].RID.IAS.Equal(ias) {
|
||||||
|
key, err = keyWrapAlgorithm.UnWrap(kari.RecipientEncryptedKeys[i].EncryptedKey)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ErrNoKeyFound
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
@ -58,7 +58,7 @@ func (ed *EnvelopedData) decryptKey(keyPair tls.Certificate) (key []byte, err er
|
|||||||
for i := range ed.RecipientInfos {
|
for i := range ed.RecipientInfos {
|
||||||
|
|
||||||
key, err = ed.RecipientInfos[i].decryptKey(keyPair)
|
key, err = ed.RecipientInfos[i].decryptKey(keyPair)
|
||||||
if key != nil {
|
if key != nil || err != ErrNoKeyFound {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,8 +33,14 @@ type RecipientInfo struct {
|
|||||||
|
|
||||||
func (recInfo *RecipientInfo) decryptKey(keyPair tls.Certificate) (key []byte, err error) {
|
func (recInfo *RecipientInfo) decryptKey(keyPair tls.Certificate) (key []byte, err error) {
|
||||||
|
|
||||||
return recInfo.KTRI.decryptKey(keyPair)
|
key, err = recInfo.KTRI.decryptKey(keyPair)
|
||||||
|
if key != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
key, err = recInfo.KARI.decryptKey(keyPair)
|
||||||
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//KeyTransRecipientInfo ::= SEQUENCE {
|
//KeyTransRecipientInfo ::= SEQUENCE {
|
||||||
@ -102,7 +108,7 @@ type RecipientIdentifier struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewRecipientInfo creates RecipientInfo for giben recipient and key.
|
// NewRecipientInfo creates RecipientInfo for giben recipient and key.
|
||||||
func NewRecipientInfo(recipient *x509.Certificate, key []byte) RecipientInfo {
|
func NewRecipientInfo(recipient *x509.Certificate, key []byte) (info RecipientInfo, err error) {
|
||||||
version := 0 //issuerAndSerialNumber
|
version := 0 //issuerAndSerialNumber
|
||||||
|
|
||||||
rid := RecipientIdentifier{}
|
rid := RecipientIdentifier{}
|
||||||
@ -118,24 +124,35 @@ func NewRecipientInfo(recipient *x509.Certificate, key []byte) RecipientInfo {
|
|||||||
rid.SKI = recipient.SubjectKeyId
|
rid.SKI = recipient.SubjectKeyId
|
||||||
}
|
}
|
||||||
|
|
||||||
kea := oid.PublicKeyAlgorithmToEncrytionAlgorithm[recipient.PublicKeyAlgorithm]
|
switch recipient.PublicKeyAlgorithm {
|
||||||
if _, ok := oid.PublicKeyAlgorithmToEncrytionAlgorithm[recipient.PublicKeyAlgorithm]; !ok {
|
case x509.RSA:
|
||||||
log.Fatal("NewRecipientInfo: PublicKeyAlgorithm not supported")
|
var encrypted []byte
|
||||||
|
encrypted, err = encryptKeyRSA(key, recipient)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
info = RecipientInfo{
|
||||||
encrypted, _ := encryptKey(key, recipient)
|
|
||||||
|
|
||||||
info := RecipientInfo{
|
|
||||||
KTRI: KeyTransRecipientInfo{
|
KTRI: KeyTransRecipientInfo{
|
||||||
Version: version,
|
Version: version,
|
||||||
Rid: rid,
|
Rid: rid,
|
||||||
KeyEncryptionAlgorithm: kea,
|
KeyEncryptionAlgorithm: pkix.AlgorithmIdentifier{Algorithm: oid.EncryptionAlgorithmRSA},
|
||||||
EncryptedKey: encrypted,
|
EncryptedKey: encrypted,
|
||||||
}}
|
}}
|
||||||
return info
|
case x509.ECDSA:
|
||||||
|
var kari KeyAgreeRecipientInfo
|
||||||
|
kari, err = encryptKeyECDH(key, recipient)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
info = RecipientInfo{KARI: kari}
|
||||||
|
default:
|
||||||
|
err = errors.New("Public key algorithm not supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func encryptKey(key []byte, recipient *x509.Certificate) ([]byte, error) {
|
func encryptKeyRSA(key []byte, recipient *x509.Certificate) ([]byte, error) {
|
||||||
if pub := recipient.PublicKey.(*rsa.PublicKey); pub != nil {
|
if pub := recipient.PublicKey.(*rsa.PublicKey); pub != nil {
|
||||||
return rsa.EncryptPKCS1v15(rand.Reader, pub, key)
|
return rsa.EncryptPKCS1v15(rand.Reader, pub, key)
|
||||||
}
|
}
|
||||||
|
@ -379,8 +379,12 @@ func (sd *SignedData) Verify(Opts x509.VerifyOptions, detached []byte) (chains [
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var sigAlg x509.SignatureAlgorithm
|
||||||
err = cert.CheckSignature(signer.X509SignatureAlgorithm(), signedMessage, signer.Signature)
|
sigAlg, err = signer.X509SignatureAlgorithm()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = cert.CheckSignature(sigAlg, signedMessage, signer.Signature)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"crypto/x509/pkix"
|
"crypto/x509/pkix"
|
||||||
"encoding/asn1"
|
"encoding/asn1"
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
asn "github.com/InfiniteLoopSpace/go_S-MIME/asn1"
|
asn "github.com/InfiniteLoopSpace/go_S-MIME/asn1"
|
||||||
@ -83,13 +84,18 @@ func (si SignerInfo) Hash() (crypto.Hash, error) {
|
|||||||
|
|
||||||
// X509SignatureAlgorithm gets the x509.SignatureAlgorithm that should be used
|
// X509SignatureAlgorithm gets the x509.SignatureAlgorithm that should be used
|
||||||
// for verifying this SignerInfo's signature.
|
// for verifying this SignerInfo's signature.
|
||||||
func (si SignerInfo) X509SignatureAlgorithm() x509.SignatureAlgorithm {
|
func (si SignerInfo) X509SignatureAlgorithm() (sigAlg x509.SignatureAlgorithm, err error) {
|
||||||
var (
|
var (
|
||||||
sigOID = si.SignatureAlgorithm.Algorithm.String()
|
sigOID = si.SignatureAlgorithm.Algorithm.String()
|
||||||
digestOID = si.DigestAlgorithm.Algorithm.String()
|
digestOID = si.DigestAlgorithm.Algorithm.String()
|
||||||
)
|
)
|
||||||
|
sigAlg, ok := oid.SignatureAlgorithms[sigOID][digestOID]
|
||||||
|
|
||||||
return oid.SignatureAlgorithms[sigOID][digestOID]
|
if !ok {
|
||||||
|
err = fmt.Errorf("Signature algorithm with OID %s in combination with digest with OID %s not supported", sigOID, digestOID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetContentTypeAttribute gets the signed ContentType attribute from the
|
// GetContentTypeAttribute gets the signed ContentType attribute from the
|
||||||
|
163
oid/key_wrap.go
Normal file
163
oid/key_wrap.go
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
package oid
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/subtle"
|
||||||
|
"crypto/x509/pkix"
|
||||||
|
"encoding/asn1"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// KeyWrap wraps and unwraps key with the key encrytion key (KEK) for a given (KeyWrapAlgorithm)
|
||||||
|
type KeyWrap struct {
|
||||||
|
KEK []byte
|
||||||
|
KeyWrapAlgorithm asn1.ObjectIdentifier
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrap wraps the content encryption key (cek)
|
||||||
|
func (kw *KeyWrap) Wrap(cek []byte) (ciphertext []byte, err error) {
|
||||||
|
|
||||||
|
var block cipher.Block
|
||||||
|
switch kw.KeyWrapAlgorithm.String() {
|
||||||
|
case AES128Wrap.String(), AES192Wrap.String(), AES256Wrap.String():
|
||||||
|
block, err = aes.NewCipher(kw.KEK)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Wrap(block, cek)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnWrap unwraps the encrypted key (encKey)
|
||||||
|
func (kw *KeyWrap) UnWrap(encKey []byte) (cek []byte, err error) {
|
||||||
|
|
||||||
|
var block cipher.Block
|
||||||
|
switch kw.KeyWrapAlgorithm.String() {
|
||||||
|
case AES128Wrap.String(), AES192Wrap.String(), AES256Wrap.String():
|
||||||
|
block, err = aes.NewCipher(kw.KEK)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Unwrap(block, encKey)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyLen returns the key lenght of the key wrap algorithm
|
||||||
|
func (kw *KeyWrap) KeyLen() (len int) {
|
||||||
|
|
||||||
|
switch kw.KeyWrapAlgorithm.String() {
|
||||||
|
case AES128Wrap.String():
|
||||||
|
len = 16
|
||||||
|
case AES192Wrap.String():
|
||||||
|
len = 24
|
||||||
|
case AES256Wrap.String():
|
||||||
|
len = 32
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// AlgorithmIdentifier returns the OID of the key wrap algorithm
|
||||||
|
func (kw *KeyWrap) AlgorithmIdentifier() (algID pkix.AlgorithmIdentifier) {
|
||||||
|
|
||||||
|
switch kw.KeyWrapAlgorithm.String() {
|
||||||
|
case AES128Wrap.String(), AES192Wrap.String(), AES256Wrap.String():
|
||||||
|
algID = pkix.AlgorithmIdentifier{Algorithm: kw.KeyWrapAlgorithm}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// defaultIV from RFC-3394
|
||||||
|
var defaultIV = []byte{0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6}
|
||||||
|
|
||||||
|
// Wrap encrypts the content encryption key (cek) with the given AES cipher (block), using the AES Key Wrap algorithm (RFC-3394)
|
||||||
|
func Wrap(block cipher.Block, cek []byte) (encKey []byte, err error) {
|
||||||
|
if len(cek)%8 != 0 {
|
||||||
|
return nil, errors.New("Lenght of cek must be in 8-byte blocks")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. Initialize variables
|
||||||
|
|
||||||
|
// Set A = IV, an initial value (see 2.2.3)
|
||||||
|
B := make([]byte, 16)
|
||||||
|
copy(B, defaultIV)
|
||||||
|
|
||||||
|
// For i = 1 to n
|
||||||
|
// R[i] = P[i]
|
||||||
|
encKey = make([]byte, len(cek)+8)
|
||||||
|
copy(encKey[8:], cek)
|
||||||
|
|
||||||
|
n := len(cek) / 8
|
||||||
|
|
||||||
|
// 2. Calculate intermediate values.
|
||||||
|
for j := 0; j <= 5; j++ {
|
||||||
|
for i := 1; i <= n; i++ {
|
||||||
|
|
||||||
|
// B = AES(K, A | R[i])
|
||||||
|
copy(B[8:], encKey[i*8:(i+1)*8])
|
||||||
|
block.Encrypt(B, B)
|
||||||
|
|
||||||
|
// A = MSB(64, B) ^ t where t = (n*j)+i
|
||||||
|
t := uint64(n*j + i)
|
||||||
|
b := binary.BigEndian.Uint64(B[:8]) ^ t
|
||||||
|
binary.BigEndian.PutUint64(B[:8], b)
|
||||||
|
|
||||||
|
// R[i] = LSB(64, B)
|
||||||
|
copy(encKey[i*8:(i+1)*8], B[8:])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Output the results.
|
||||||
|
copy(encKey[:8], B[:8])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unwrap decrypts the provided encrypted key (encKey) with the given AES cipher (block), using the AES Key Wrap algorithm (RFC-3394).
|
||||||
|
// Returns an error if validation fails.
|
||||||
|
func Unwrap(block cipher.Block, encKey []byte) (cek []byte, err error) {
|
||||||
|
if len(cek)%8 != 0 {
|
||||||
|
return nil, errors.New("Length of encKey must multiple 8-bytes")
|
||||||
|
}
|
||||||
|
|
||||||
|
//Initialize variables
|
||||||
|
B := make([]byte, 16)
|
||||||
|
copy(B, encKey[:8])
|
||||||
|
|
||||||
|
cek = make([]byte, len(encKey)-8)
|
||||||
|
copy(cek, encKey[8:])
|
||||||
|
|
||||||
|
n := (len(encKey) / 8) - 1
|
||||||
|
|
||||||
|
//Compute intermediate values
|
||||||
|
for j := 5; j >= 0; j-- {
|
||||||
|
for i := n; i >= 1; i-- {
|
||||||
|
|
||||||
|
// B = AES-1(K, (A ^ t) | R[i]) where t = n*j+i
|
||||||
|
copy(B[8:], cek[(i-1)*8:i*8])
|
||||||
|
t := uint64(n*j + i)
|
||||||
|
b := binary.BigEndian.Uint64(B[:8]) ^ t
|
||||||
|
binary.BigEndian.PutUint64(B[:8], b)
|
||||||
|
|
||||||
|
block.Decrypt(B, B)
|
||||||
|
|
||||||
|
// A = MSB(64, B)
|
||||||
|
// R[i] = LSB(64, B)
|
||||||
|
copy(cek[(i-1)*8:i*8], B[8:])
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if subtle.ConstantTimeCompare(B[:8], defaultIV) != 1 {
|
||||||
|
return nil, errors.New("Integrity check failed - unexpected IV")
|
||||||
|
}
|
||||||
|
|
||||||
|
//Output
|
||||||
|
return
|
||||||
|
}
|
47
oid/oid.go
47
oid/oid.go
@ -30,6 +30,11 @@ var (
|
|||||||
var (
|
var (
|
||||||
SignatureAlgorithmRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
|
SignatureAlgorithmRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
|
||||||
SignatureAlgorithmECDSA = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1}
|
SignatureAlgorithmECDSA = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1}
|
||||||
|
SignatureAlgorithmECDSAwithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1}
|
||||||
|
SignatureAlgorithmECDSAwithSHA224 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 1}
|
||||||
|
SignatureAlgorithmECDSAwithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2}
|
||||||
|
SignatureAlgorithmECDSAwithSHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3}
|
||||||
|
SignatureAlgorithmECDSAwithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Public Key Encryption OIDs
|
// Public Key Encryption OIDs
|
||||||
@ -51,6 +56,27 @@ var (
|
|||||||
SubjectKeyIdentifier = asn1.ObjectIdentifier{2, 5, 29, 14}
|
SubjectKeyIdentifier = asn1.ObjectIdentifier{2, 5, 29, 14}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Elliptic curve public key OID
|
||||||
|
var (
|
||||||
|
ECPublicKey = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1}
|
||||||
|
)
|
||||||
|
|
||||||
|
// DH Key Derivation Schemes OIDs
|
||||||
|
var (
|
||||||
|
DHSinglePassstdDHsha1kdfscheme = asn1.ObjectIdentifier{1, 3, 133, 16, 840, 63, 0, 2}
|
||||||
|
DHSinglePassstdDHsha224kdfscheme = asn1.ObjectIdentifier{1, 3, 132, 1, 11, 0}
|
||||||
|
DHSinglePassstdDHsha256kdfscheme = asn1.ObjectIdentifier{1, 3, 132, 1, 11, 1}
|
||||||
|
DHSinglePassstdDHsha384kdfscheme = asn1.ObjectIdentifier{1, 3, 132, 1, 11, 2}
|
||||||
|
DHSinglePassstdDHsha512kdfscheme = asn1.ObjectIdentifier{1, 3, 132, 1, 11, 3}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Key wrap algorithm OIDs
|
||||||
|
var (
|
||||||
|
AES128Wrap = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 5}
|
||||||
|
AES192Wrap = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 25}
|
||||||
|
AES256Wrap = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 45}
|
||||||
|
)
|
||||||
|
|
||||||
// DigestAlgorithmToHash maps digest OIDs to crypto.Hash values.
|
// DigestAlgorithmToHash maps digest OIDs to crypto.Hash values.
|
||||||
var DigestAlgorithmToHash = map[string]crypto.Hash{
|
var DigestAlgorithmToHash = map[string]crypto.Hash{
|
||||||
DigestAlgorithmSHA1.String(): crypto.SHA1,
|
DigestAlgorithmSHA1.String(): crypto.SHA1,
|
||||||
@ -113,6 +139,18 @@ var SignatureAlgorithms = map[string]map[string]x509.SignatureAlgorithm{
|
|||||||
DigestAlgorithmSHA384.String(): x509.ECDSAWithSHA384,
|
DigestAlgorithmSHA384.String(): x509.ECDSAWithSHA384,
|
||||||
DigestAlgorithmSHA512.String(): x509.ECDSAWithSHA512,
|
DigestAlgorithmSHA512.String(): x509.ECDSAWithSHA512,
|
||||||
},
|
},
|
||||||
|
SignatureAlgorithmECDSAwithSHA1.String(): map[string]x509.SignatureAlgorithm{
|
||||||
|
DigestAlgorithmSHA1.String(): x509.ECDSAWithSHA1,
|
||||||
|
},
|
||||||
|
SignatureAlgorithmECDSAwithSHA256.String(): map[string]x509.SignatureAlgorithm{
|
||||||
|
DigestAlgorithmSHA256.String(): x509.ECDSAWithSHA256,
|
||||||
|
},
|
||||||
|
SignatureAlgorithmECDSAwithSHA384.String(): map[string]x509.SignatureAlgorithm{
|
||||||
|
DigestAlgorithmSHA384.String(): x509.ECDSAWithSHA384,
|
||||||
|
},
|
||||||
|
SignatureAlgorithmECDSAwithSHA512.String(): map[string]x509.SignatureAlgorithm{
|
||||||
|
DigestAlgorithmSHA512.String(): x509.ECDSAWithSHA512,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// PublicKeyAlgorithmToSignatureAlgorithm maps certificate public key
|
// PublicKeyAlgorithmToSignatureAlgorithm maps certificate public key
|
||||||
@ -127,3 +165,12 @@ var PublicKeyAlgorithmToSignatureAlgorithm = map[x509.PublicKeyAlgorithm]pkix.Al
|
|||||||
var PublicKeyAlgorithmToEncrytionAlgorithm = map[x509.PublicKeyAlgorithm]pkix.AlgorithmIdentifier{
|
var PublicKeyAlgorithmToEncrytionAlgorithm = map[x509.PublicKeyAlgorithm]pkix.AlgorithmIdentifier{
|
||||||
x509.RSA: pkix.AlgorithmIdentifier{Algorithm: EncryptionAlgorithmRSA},
|
x509.RSA: pkix.AlgorithmIdentifier{Algorithm: EncryptionAlgorithmRSA},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// KDFHashAlgorithm key derivation schemes to its hash algorithms
|
||||||
|
var KDFHashAlgorithm = map[string]crypto.Hash{
|
||||||
|
DHSinglePassstdDHsha1kdfscheme.String(): crypto.SHA1,
|
||||||
|
DHSinglePassstdDHsha224kdfscheme.String(): crypto.SHA224,
|
||||||
|
DHSinglePassstdDHsha256kdfscheme.String(): crypto.SHA256,
|
||||||
|
DHSinglePassstdDHsha384kdfscheme.String(): crypto.SHA384,
|
||||||
|
DHSinglePassstdDHsha512kdfscheme.String(): crypto.SHA512,
|
||||||
|
}
|
||||||
|
@ -32,9 +32,10 @@ var (
|
|||||||
AEADChaCha20Poly1305 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 16, 3, 18}
|
AEADChaCha20Poly1305 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 16, 3, 18}
|
||||||
)
|
)
|
||||||
|
|
||||||
var symmetricKeyLen = map[string]int{
|
// SymmetricKeyLen maps the encryption algorithm to its key length
|
||||||
EncryptionAlgorithmDESCBC.String(): 7,
|
var SymmetricKeyLen = map[string]int{
|
||||||
EncryptionAlgorithmDESEDE3CBC.String(): 21,
|
EncryptionAlgorithmDESCBC.String(): 8,
|
||||||
|
EncryptionAlgorithmDESEDE3CBC.String(): 24,
|
||||||
EncryptionAlgorithmAES128CBC.String(): 16,
|
EncryptionAlgorithmAES128CBC.String(): 16,
|
||||||
EncryptionAlgorithmAES256CBC.String(): 32,
|
EncryptionAlgorithmAES256CBC.String(): 32,
|
||||||
//AEAD
|
//AEAD
|
||||||
@ -46,7 +47,7 @@ var symmetricKeyLen = map[string]int{
|
|||||||
func (e *EncryptionAlgorithm) Encrypt(plaintext []byte) (ciphertext []byte, err error) {
|
func (e *EncryptionAlgorithm) Encrypt(plaintext []byte) (ciphertext []byte, err error) {
|
||||||
|
|
||||||
if e.Key == nil {
|
if e.Key == nil {
|
||||||
e.Key = make([]byte, symmetricKeyLen[e.EncryptionAlgorithmIdentifier.String()])
|
e.Key = make([]byte, SymmetricKeyLen[e.EncryptionAlgorithmIdentifier.String()])
|
||||||
rand.Read(e.Key)
|
rand.Read(e.Key)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,7 +72,7 @@ func (e *EncryptionAlgorithm) Encrypt(plaintext []byte) (ciphertext []byte, err
|
|||||||
switch e.EncryptionAlgorithmIdentifier.String() {
|
switch e.EncryptionAlgorithmIdentifier.String() {
|
||||||
case EncryptionAlgorithmAES128CBC.String(), EncryptionAlgorithmAES256CBC.String():
|
case EncryptionAlgorithmAES128CBC.String(), EncryptionAlgorithmAES256CBC.String():
|
||||||
if e.IV == nil {
|
if e.IV == nil {
|
||||||
e.IV = make([]byte, len(e.Key))
|
e.IV = make([]byte, blockCipher.BlockSize())
|
||||||
rand.Read(e.IV)
|
rand.Read(e.IV)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,7 +181,6 @@ func (e *EncryptionAlgorithm) Decrypt(ciphertext []byte) (plaintext []byte, err
|
|||||||
switch e.EncryptionAlgorithmIdentifier.String() {
|
switch e.EncryptionAlgorithmIdentifier.String() {
|
||||||
case EncryptionAlgorithmAES128CBC.String(), EncryptionAlgorithmAES256CBC.String(), EncryptionAlgorithmDESCBC.String(), EncryptionAlgorithmDESEDE3CBC.String():
|
case EncryptionAlgorithmAES128CBC.String(), EncryptionAlgorithmAES256CBC.String(), EncryptionAlgorithmDESCBC.String(), EncryptionAlgorithmDESEDE3CBC.String():
|
||||||
e.IV = e.ContentEncryptionAlgorithmIdentifier.Parameters.Bytes
|
e.IV = e.ContentEncryptionAlgorithmIdentifier.Parameters.Bytes
|
||||||
|
|
||||||
blockMode = cipher.NewCBCDecrypter(blockCipher, e.IV)
|
blockMode = cipher.NewCBCDecrypter(blockCipher, e.IV)
|
||||||
case EncryptionAlgorithmAES128GCM.String():
|
case EncryptionAlgorithmAES128GCM.String():
|
||||||
aead, err = cipher.NewGCM(blockCipher)
|
aead, err = cipher.NewGCM(blockCipher)
|
||||||
@ -198,7 +198,6 @@ func (e *EncryptionAlgorithm) Decrypt(ciphertext []byte) (plaintext []byte, err
|
|||||||
case EncryptionAlgorithmAES128CBC.String(), EncryptionAlgorithmAES256CBC.String(), EncryptionAlgorithmDESCBC.String(), EncryptionAlgorithmDESEDE3CBC.String():
|
case EncryptionAlgorithmAES128CBC.String(), EncryptionAlgorithmAES256CBC.String(), EncryptionAlgorithmDESCBC.String(), EncryptionAlgorithmDESEDE3CBC.String():
|
||||||
plaintext = make([]byte, len(ciphertext))
|
plaintext = make([]byte, len(ciphertext))
|
||||||
blockMode.CryptBlocks(plaintext, ciphertext)
|
blockMode.CryptBlocks(plaintext, ciphertext)
|
||||||
|
|
||||||
return unpad(plaintext, blockMode.BlockSize())
|
return unpad(plaintext, blockMode.BlockSize())
|
||||||
case EncryptionAlgorithmAES128GCM.String(), AEADChaCha20Poly1305.String():
|
case EncryptionAlgorithmAES128GCM.String(), AEADChaCha20Poly1305.String():
|
||||||
var cipher []byte
|
var cipher []byte
|
||||||
@ -247,6 +246,9 @@ func unpad(data []byte, blocklen int) ([]byte, error) {
|
|||||||
|
|
||||||
// the last byte is the length of padding
|
// the last byte is the length of padding
|
||||||
padlen := int(data[len(data)-1])
|
padlen := int(data[len(data)-1])
|
||||||
|
if padlen > blocklen {
|
||||||
|
return nil, fmt.Errorf("pad len %d is bigger than block len len %d", padlen, blocklen)
|
||||||
|
}
|
||||||
|
|
||||||
// check padding integrity, all bytes should be the same
|
// check padding integrity, all bytes should be the same
|
||||||
pad := data[len(data)-padlen:]
|
pad := data[len(data)-padlen:]
|
||||||
|
@ -4,26 +4,29 @@ package openssl
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto"
|
"crypto"
|
||||||
"crypto/rsa"
|
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// SMIME is the commpand used for openssl smime, can be replaces with cms
|
||||||
|
var SMIME = "smime"
|
||||||
|
|
||||||
//Encrypt a message with openssl
|
//Encrypt a message with openssl
|
||||||
func Encrypt(in []byte, cert *x509.Certificate, opts ...string) (der []byte, err error) {
|
func Encrypt(in []byte, cert *x509.Certificate, opts ...string) (der []byte, err error) {
|
||||||
|
|
||||||
tmp, err := ioutil.TempFile("", "example")
|
tmpKey, err := ioutil.TempFile("", "example")
|
||||||
defer os.Remove(tmp.Name())
|
defer os.Remove(tmpKey.Name())
|
||||||
|
|
||||||
pem.Encode(tmp, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw})
|
pem.Encode(tmpKey, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw})
|
||||||
|
|
||||||
param := []string{"smime", "-encrypt", "-aes128"}
|
param := []string{SMIME, "-encrypt", "-aes128"}
|
||||||
param = append(param, opts...)
|
param = append(param, opts...)
|
||||||
param = append(param, tmp.Name())
|
param = append(param, tmpKey.Name())
|
||||||
der, err = openssl(in, param...)
|
der, err = openssl(in, param...)
|
||||||
|
|
||||||
return
|
return
|
||||||
@ -32,20 +35,24 @@ func Encrypt(in []byte, cert *x509.Certificate, opts ...string) (der []byte, err
|
|||||||
//Decrypt a message with openssl
|
//Decrypt a message with openssl
|
||||||
func Decrypt(in []byte, key crypto.PrivateKey, opts ...string) (plain []byte, err error) {
|
func Decrypt(in []byte, key crypto.PrivateKey, opts ...string) (plain []byte, err error) {
|
||||||
|
|
||||||
tmp, err := ioutil.TempFile("", "example")
|
tmpKey, err := ioutil.TempFile("", "example")
|
||||||
defer os.Remove(tmp.Name())
|
defer os.Remove(tmpKey.Name())
|
||||||
|
|
||||||
pem.Encode(tmp, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key.(*rsa.PrivateKey))})
|
keyDER, err := x509.MarshalPKCS8PrivateKey(key)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pem.Encode(tmpKey, &pem.Block{Type: "PRIVATE KEY", Bytes: keyDER})
|
||||||
|
|
||||||
param := []string{"smime", "-decrypt"}
|
param := []string{SMIME, "-decrypt"}
|
||||||
param = append(param, opts...)
|
param = append(param, opts...)
|
||||||
param = append(param, []string{"-decrypt", "-inkey", tmp.Name()}...)
|
param = append(param, []string{"-inkey", tmpKey.Name()}...)
|
||||||
plain, err = openssl(in, param...)
|
plain, err = openssl(in, param...)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//Create a detached signature with openssl
|
// SignDetached creates a detached signature with openssl
|
||||||
func SignDetached(in []byte, cert *x509.Certificate, key crypto.PrivateKey, interm []*x509.Certificate, opts ...string) (plain []byte, err error) {
|
func SignDetached(in []byte, cert *x509.Certificate, key crypto.PrivateKey, interm []*x509.Certificate, opts ...string) (plain []byte, err error) {
|
||||||
|
|
||||||
tmpCert, err := ioutil.TempFile("", "example")
|
tmpCert, err := ioutil.TempFile("", "example")
|
||||||
@ -56,7 +63,11 @@ func SignDetached(in []byte, cert *x509.Certificate, key crypto.PrivateKey, inte
|
|||||||
tmpKey, err := ioutil.TempFile("", "example")
|
tmpKey, err := ioutil.TempFile("", "example")
|
||||||
defer os.Remove(tmpKey.Name())
|
defer os.Remove(tmpKey.Name())
|
||||||
|
|
||||||
pem.Encode(tmpKey, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key.(*rsa.PrivateKey))})
|
keyDER, err := x509.MarshalPKCS8PrivateKey(key)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pem.Encode(tmpKey, &pem.Block{Type: "PRIVATE KEY", Bytes: keyDER})
|
||||||
|
|
||||||
tmpInterm, err := ioutil.TempFile("", "example")
|
tmpInterm, err := ioutil.TempFile("", "example")
|
||||||
defer os.Remove(tmpInterm.Name())
|
defer os.Remove(tmpInterm.Name())
|
||||||
@ -65,7 +76,7 @@ func SignDetached(in []byte, cert *x509.Certificate, key crypto.PrivateKey, inte
|
|||||||
pem.Encode(tmpInterm, &pem.Block{Type: "CERTIFICATE", Bytes: i.Raw})
|
pem.Encode(tmpInterm, &pem.Block{Type: "CERTIFICATE", Bytes: i.Raw})
|
||||||
}
|
}
|
||||||
|
|
||||||
param := []string{"smime", "-sign", "-nodetach"}
|
param := []string{SMIME, "-sign", "-nodetach"}
|
||||||
param = append(param, opts...)
|
param = append(param, opts...)
|
||||||
param = append(param, []string{"-signer", tmpCert.Name(), "-inkey", tmpKey.Name(), "-certfile", tmpInterm.Name()}...)
|
param = append(param, []string{"-signer", tmpCert.Name(), "-inkey", tmpKey.Name(), "-certfile", tmpInterm.Name()}...)
|
||||||
plain, err = openssl(in, param...)
|
plain, err = openssl(in, param...)
|
||||||
@ -73,7 +84,7 @@ func SignDetached(in []byte, cert *x509.Certificate, key crypto.PrivateKey, inte
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//Create a signature with openssl
|
// Sign creates a signature with openssl
|
||||||
func Sign(in []byte, cert *x509.Certificate, key crypto.PrivateKey, interm []*x509.Certificate, opts ...string) (plain []byte, err error) {
|
func Sign(in []byte, cert *x509.Certificate, key crypto.PrivateKey, interm []*x509.Certificate, opts ...string) (plain []byte, err error) {
|
||||||
|
|
||||||
tmpCert, err := ioutil.TempFile("", "example")
|
tmpCert, err := ioutil.TempFile("", "example")
|
||||||
@ -84,7 +95,11 @@ func Sign(in []byte, cert *x509.Certificate, key crypto.PrivateKey, interm []*x5
|
|||||||
tmpKey, err := ioutil.TempFile("", "example")
|
tmpKey, err := ioutil.TempFile("", "example")
|
||||||
defer os.Remove(tmpKey.Name())
|
defer os.Remove(tmpKey.Name())
|
||||||
|
|
||||||
pem.Encode(tmpKey, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key.(*rsa.PrivateKey))})
|
keyDER, err := x509.MarshalPKCS8PrivateKey(key)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pem.Encode(tmpKey, &pem.Block{Type: "PRIVATE KEY", Bytes: keyDER})
|
||||||
|
|
||||||
tmpInterm, err := ioutil.TempFile("", "example")
|
tmpInterm, err := ioutil.TempFile("", "example")
|
||||||
defer os.Remove(tmpInterm.Name())
|
defer os.Remove(tmpInterm.Name())
|
||||||
@ -93,7 +108,7 @@ func Sign(in []byte, cert *x509.Certificate, key crypto.PrivateKey, interm []*x5
|
|||||||
pem.Encode(tmpInterm, &pem.Block{Type: "CERTIFICATE", Bytes: i.Raw})
|
pem.Encode(tmpInterm, &pem.Block{Type: "CERTIFICATE", Bytes: i.Raw})
|
||||||
}
|
}
|
||||||
|
|
||||||
param := []string{"smime", "-sign"}
|
param := []string{SMIME, "-sign"}
|
||||||
param = append(param, opts...)
|
param = append(param, opts...)
|
||||||
param = append(param, []string{"-signer", tmpCert.Name(), "-inkey", tmpKey.Name(), "-certfile", tmpInterm.Name()}...)
|
param = append(param, []string{"-signer", tmpCert.Name(), "-inkey", tmpKey.Name(), "-certfile", tmpInterm.Name()}...)
|
||||||
plain, err = openssl(in, param...)
|
plain, err = openssl(in, param...)
|
||||||
@ -109,7 +124,7 @@ func Verify(in []byte, ca *x509.Certificate, opts ...string) (plain []byte, err
|
|||||||
|
|
||||||
pem.Encode(tmpCA, &pem.Block{Type: "CERTIFICATE", Bytes: ca.Raw})
|
pem.Encode(tmpCA, &pem.Block{Type: "CERTIFICATE", Bytes: ca.Raw})
|
||||||
|
|
||||||
param := []string{"smime", "-verify"}
|
param := []string{SMIME, "-verify"}
|
||||||
param = append(param, opts...)
|
param = append(param, opts...)
|
||||||
param = append(param, []string{"-CAfile", tmpCA.Name()}...)
|
param = append(param, []string{"-CAfile", tmpCA.Name()}...)
|
||||||
plain, err = openssl(in, param...)
|
plain, err = openssl(in, param...)
|
||||||
@ -117,6 +132,11 @@ func Verify(in []byte, ca *x509.Certificate, opts ...string) (plain []byte, err
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Openssl runs the openssl command with given args
|
||||||
|
func Openssl(stdin []byte, args ...string) ([]byte, error) {
|
||||||
|
return openssl(stdin, args...)
|
||||||
|
}
|
||||||
|
|
||||||
func openssl(stdin []byte, args ...string) ([]byte, error) {
|
func openssl(stdin []byte, args ...string) ([]byte, error) {
|
||||||
cmd := exec.Command("openssl", args...)
|
cmd := exec.Command("openssl", args...)
|
||||||
|
|
||||||
@ -133,5 +153,9 @@ func openssl(stdin []byte, args ...string) ([]byte, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if strings.Contains(errs.String(), "Error") {
|
||||||
|
return nil, fmt.Errorf("error running %s (%s):\n ", cmd.Args, errs.String())
|
||||||
|
}
|
||||||
|
|
||||||
return out.Bytes(), nil
|
return out.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
@ -234,7 +234,7 @@ Y0ZB9qANMAsGA1UdDzEEAwIAEA==
|
|||||||
-----END PRIVATE KEY-----`
|
-----END PRIVATE KEY-----`
|
||||||
|
|
||||||
//https://github.com/fullsailor/pkcs7/issues/9
|
//https://github.com/fullsailor/pkcs7/issues/9
|
||||||
func TestiTunesReceipt(t *testing.T) {
|
func TestSampleiTunesReceipt(t *testing.T) {
|
||||||
|
|
||||||
b, err := base64.StdEncoding.DecodeString(strings.TrimSpace(iTunesReceipt))
|
b, err := base64.StdEncoding.DecodeString(strings.TrimSpace(iTunesReceipt))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user