mirror of
https://github.com/emmansun/gmsm.git
synced 2025-04-26 12:16:20 +08:00
merge pkcs7
This commit is contained in:
parent
7257b0151b
commit
5d41aaa348
@ -24,6 +24,8 @@
|
||||
|
||||
* **SMX509** - a fork of golang X509 that supports ShangMi.
|
||||
|
||||
* **PKCS7** - a fork of [mozilla-services/pkcs7](https://github.com/mozilla-services/pkcs7) that supports ShangMi.
|
||||
|
||||
* **PKCS8** - a fork of [youmark/pkcs8](https://github.com/youmark/pkcs8) that supports ShangMi.
|
||||
|
||||
* **ECDH** - a similar implementation of golang ECDH that supports SM2 ECDH & SM2MQV without usage of **big.Int**, a replacement of SM2 key exchange. For detail, pleaes refer [is my code constant time?](https://github.com/emmansun/gmsm/wiki/is-my-code-constant-time%3F)
|
||||
@ -31,9 +33,8 @@
|
||||
* **DRBG** - Random Number Generation Using Deterministic Random Bit Generators, for detail, please reference **NIST Special Publication 800-90A** and **GM/T 0105-2021**: CTR-DRBG using derivation function and HASH-DRBG. NIST related implementations are tested with part of NIST provided test vectors. You can also use [randomness](https://github.com/Trisia/randomness) tool to check the generated random bits.
|
||||
|
||||
## Some Related Projects
|
||||
* **[PKCS12](https://github.com/emmansun/go-pkcs12)** - pkcs12 supports ShangMi, a fork of [SSLMate/go-pkcs12](https://github.com/SSLMate/go-pkcs12).
|
||||
* **[PKCS7](https://github.com/emmansun/pkcs7)** - pkcs7 supports ShangMi (not supports SM9 yet), a fork of [mozilla-services/pkcs7](https://github.com/mozilla-services/pkcs7).
|
||||
* **[TLCP](https://github.com/Trisia/gotlcp)** - An implementation of GB/T 38636-2020 Information security technology Transport Layer Cryptography Protocol (TLCP).
|
||||
* **[PKCS12](https://github.com/emmansun/go-pkcs12)** - pkcs12 supports ShangMi, a fork of [SSLMate/go-pkcs12](https://github.com/SSLMate/go-pkcs12).
|
||||
* **[MKSMCERT](https://github.com/emmansun/mksmcert)** - A simple tool for making locally-trusted development ShangMi certificates, a fork of [FiloSottile/mkcert](https://github.com/FiloSottile/mkcert).
|
||||
|
||||
## Disclaimer
|
||||
|
@ -1,4 +1,5 @@
|
||||
package pkcs8
|
||||
// Package pkcs implements ciphers used by PKCS#7 & PKCS#8.
|
||||
package pkcs
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
@ -6,32 +7,92 @@ import (
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
smcipher "github.com/emmansun/gmsm/cipher"
|
||||
"github.com/emmansun/gmsm/padding"
|
||||
)
|
||||
|
||||
func genRandom(len int) ([]byte, error) {
|
||||
value := make([]byte, len)
|
||||
_, err := rand.Read(value)
|
||||
return value, err
|
||||
// Cipher represents a cipher for encrypting the key material.
|
||||
type Cipher interface {
|
||||
// KeySize returns the key size of the cipher, in bytes.
|
||||
KeySize() int
|
||||
// Encrypt encrypts the key material.
|
||||
Encrypt(key, plaintext []byte) (*pkix.AlgorithmIdentifier, []byte, error)
|
||||
// Decrypt decrypts the key material.
|
||||
Decrypt(key []byte, parameters *asn1.RawValue, encryptedKey []byte) ([]byte, error)
|
||||
// OID returns the OID of the cipher specified.
|
||||
OID() asn1.ObjectIdentifier
|
||||
}
|
||||
|
||||
type cbcBlockCipher struct {
|
||||
var ciphers = make(map[string]func() Cipher)
|
||||
|
||||
// RegisterCipher registers a function that returns a new instance of the given
|
||||
// cipher. This allows the library to support client-provided ciphers.
|
||||
func RegisterCipher(oid asn1.ObjectIdentifier, cipher func() Cipher) {
|
||||
ciphers[oid.String()] = cipher
|
||||
}
|
||||
|
||||
func GetCipher(oid asn1.ObjectIdentifier) (Cipher, error) {
|
||||
newCipher, ok := ciphers[oid.String()]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("pkcs: unsupported cipher (OID: %s)", oid)
|
||||
}
|
||||
return newCipher(), nil
|
||||
}
|
||||
|
||||
type baseBlockCipher struct {
|
||||
oid asn1.ObjectIdentifier
|
||||
ivSize int
|
||||
keySize int
|
||||
newBlock func(key []byte) (cipher.Block, error)
|
||||
}
|
||||
|
||||
func (c cbcBlockCipher) KeySize() int {
|
||||
return c.keySize
|
||||
func (b *baseBlockCipher) KeySize() int {
|
||||
return b.keySize
|
||||
}
|
||||
|
||||
func (c cbcBlockCipher) OID() asn1.ObjectIdentifier {
|
||||
return c.oid
|
||||
func (b *baseBlockCipher) OID() asn1.ObjectIdentifier {
|
||||
return b.oid
|
||||
}
|
||||
|
||||
func (c cbcBlockCipher) Encrypt(key, plaintext []byte) (*pkix.AlgorithmIdentifier, []byte, error) {
|
||||
type ecbBlockCipher struct {
|
||||
baseBlockCipher
|
||||
}
|
||||
|
||||
func (ecb *ecbBlockCipher) Encrypt(key, plaintext []byte) (*pkix.AlgorithmIdentifier, []byte, error) {
|
||||
block, err := ecb.newBlock(key)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
mode := smcipher.NewECBEncrypter(block)
|
||||
ciphertext := make([]byte, len(plaintext))
|
||||
mode.CryptBlocks(ciphertext, plaintext)
|
||||
|
||||
encryptionScheme := pkix.AlgorithmIdentifier{
|
||||
Algorithm: ecb.oid,
|
||||
}
|
||||
|
||||
return &encryptionScheme, ciphertext, nil
|
||||
}
|
||||
|
||||
func (ecb *ecbBlockCipher) Decrypt(key []byte, parameters *asn1.RawValue, ciphertext []byte) ([]byte, error) {
|
||||
block, err := ecb.newBlock(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mode := smcipher.NewECBDecrypter(block)
|
||||
plaintext := make([]byte, len(ciphertext))
|
||||
mode.CryptBlocks(plaintext, ciphertext)
|
||||
return plaintext, nil
|
||||
}
|
||||
|
||||
|
||||
type cbcBlockCipher struct {
|
||||
baseBlockCipher
|
||||
ivSize int
|
||||
}
|
||||
|
||||
func (c *cbcBlockCipher) Encrypt(key, plaintext []byte) (*pkix.AlgorithmIdentifier, []byte, error) {
|
||||
block, err := c.newBlock(key)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@ -58,7 +119,7 @@ func (c cbcBlockCipher) Encrypt(key, plaintext []byte) (*pkix.AlgorithmIdentifie
|
||||
return &encryptionScheme, ciphertext, nil
|
||||
}
|
||||
|
||||
func (c cbcBlockCipher) Decrypt(key []byte, parameters *asn1.RawValue, encryptedKey []byte) ([]byte, error) {
|
||||
func (c *cbcBlockCipher) Decrypt(key []byte, parameters *asn1.RawValue, encryptedKey []byte) ([]byte, error) {
|
||||
block, err := c.newBlock(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -66,7 +127,7 @@ func (c cbcBlockCipher) Decrypt(key []byte, parameters *asn1.RawValue, encrypted
|
||||
|
||||
var iv []byte
|
||||
if _, err := asn1.Unmarshal(parameters.FullBytes, &iv); err != nil {
|
||||
return nil, errors.New("pkcs8: invalid cipher parameters")
|
||||
return nil, errors.New("pkcs: invalid cipher parameters")
|
||||
}
|
||||
|
||||
return cbcDecrypt(block, key, iv, encryptedKey)
|
||||
@ -90,10 +151,8 @@ func cbcDecrypt(block cipher.Block, key, iv, ciphertext []byte) ([]byte, error)
|
||||
}
|
||||
|
||||
type gcmBlockCipher struct {
|
||||
oid asn1.ObjectIdentifier
|
||||
baseBlockCipher
|
||||
nonceSize int
|
||||
keySize int
|
||||
newBlock func(key []byte) (cipher.Block, error)
|
||||
}
|
||||
|
||||
// http://javadoc.iaik.tugraz.at/iaik_jce/current/index.html?iaik/security/cipher/GCMParameters.html
|
||||
@ -102,15 +161,7 @@ type gcmParameters struct {
|
||||
ICVLen int
|
||||
}
|
||||
|
||||
func (c gcmBlockCipher) KeySize() int {
|
||||
return c.keySize
|
||||
}
|
||||
|
||||
func (c gcmBlockCipher) OID() asn1.ObjectIdentifier {
|
||||
return c.oid
|
||||
}
|
||||
|
||||
func (c gcmBlockCipher) Encrypt(key, plaintext []byte) (*pkix.AlgorithmIdentifier, []byte, error) {
|
||||
func (c *gcmBlockCipher) Encrypt(key, plaintext []byte) (*pkix.AlgorithmIdentifier, []byte, error) {
|
||||
block, err := c.newBlock(key)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@ -142,7 +193,7 @@ func (c gcmBlockCipher) Encrypt(key, plaintext []byte) (*pkix.AlgorithmIdentifie
|
||||
return &encryptionAlgorithm, ciphertext, nil
|
||||
}
|
||||
|
||||
func (c gcmBlockCipher) Decrypt(key []byte, parameters *asn1.RawValue, encryptedKey []byte) ([]byte, error) {
|
||||
func (c *gcmBlockCipher) Decrypt(key []byte, parameters *asn1.RawValue, encryptedKey []byte) ([]byte, error) {
|
||||
block, err := c.newBlock(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -157,8 +208,14 @@ func (c gcmBlockCipher) Decrypt(key []byte, parameters *asn1.RawValue, encrypted
|
||||
return nil, err
|
||||
}
|
||||
if params.ICVLen != aead.Overhead() {
|
||||
return nil, errors.New("pkcs8: invalid tag size")
|
||||
return nil, errors.New("pkcs: invalid tag size")
|
||||
}
|
||||
|
||||
return aead.Open(nil, params.Nonce, encryptedKey, nil)
|
||||
}
|
||||
|
||||
func genRandom(len int) ([]byte, error) {
|
||||
value := make([]byte, len)
|
||||
_, err := rand.Read(value)
|
||||
return value, err
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package pkcs8
|
||||
package pkcs
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
@ -16,69 +16,81 @@ var (
|
||||
|
||||
func init() {
|
||||
RegisterCipher(oidAES128CBC, func() Cipher {
|
||||
return &AES128CBC
|
||||
return AES128CBC
|
||||
})
|
||||
RegisterCipher(oidAES128GCM, func() Cipher {
|
||||
return &AES128GCM
|
||||
return AES128GCM
|
||||
})
|
||||
RegisterCipher(oidAES192CBC, func() Cipher {
|
||||
return &AES192CBC
|
||||
return AES192CBC
|
||||
})
|
||||
RegisterCipher(oidAES192GCM, func() Cipher {
|
||||
return &AES192GCM
|
||||
return AES192GCM
|
||||
})
|
||||
RegisterCipher(oidAES256CBC, func() Cipher {
|
||||
return &AES256CBC
|
||||
return AES256CBC
|
||||
})
|
||||
RegisterCipher(oidAES256GCM, func() Cipher {
|
||||
return &AES256GCM
|
||||
return AES256GCM
|
||||
})
|
||||
}
|
||||
|
||||
// AES128CBC is the 128-bit key AES cipher in CBC mode.
|
||||
var AES128CBC = cbcBlockCipher{
|
||||
ivSize: aes.BlockSize,
|
||||
var AES128CBC = &cbcBlockCipher{
|
||||
baseBlockCipher: baseBlockCipher{
|
||||
keySize: 16,
|
||||
newBlock: aes.NewCipher,
|
||||
oid: oidAES128CBC,
|
||||
},
|
||||
ivSize: aes.BlockSize,
|
||||
}
|
||||
|
||||
// AES128GCM is the 128-bit key AES cipher in GCM mode.
|
||||
var AES128GCM = gcmBlockCipher{
|
||||
nonceSize: 12,
|
||||
var AES128GCM = &gcmBlockCipher{
|
||||
baseBlockCipher: baseBlockCipher{
|
||||
keySize: 16,
|
||||
newBlock: aes.NewCipher,
|
||||
oid: oidAES128GCM,
|
||||
},
|
||||
nonceSize: 12,
|
||||
}
|
||||
|
||||
// AES192CBC is the 192-bit key AES cipher in CBC mode.
|
||||
var AES192CBC = cbcBlockCipher{
|
||||
ivSize: aes.BlockSize,
|
||||
var AES192CBC = &cbcBlockCipher{
|
||||
baseBlockCipher: baseBlockCipher{
|
||||
keySize: 24,
|
||||
newBlock: aes.NewCipher,
|
||||
oid: oidAES192CBC,
|
||||
},
|
||||
ivSize: aes.BlockSize,
|
||||
}
|
||||
|
||||
// AES192GCM is the 912-bit key AES cipher in GCM mode.
|
||||
var AES192GCM = gcmBlockCipher{
|
||||
nonceSize: 12,
|
||||
var AES192GCM = &gcmBlockCipher{
|
||||
baseBlockCipher: baseBlockCipher{
|
||||
keySize: 24,
|
||||
newBlock: aes.NewCipher,
|
||||
oid: oidAES192GCM,
|
||||
},
|
||||
nonceSize: 12,
|
||||
}
|
||||
|
||||
// AES256CBC is the 256-bit key AES cipher in CBC mode.
|
||||
var AES256CBC = cbcBlockCipher{
|
||||
ivSize: aes.BlockSize,
|
||||
var AES256CBC = &cbcBlockCipher{
|
||||
baseBlockCipher: baseBlockCipher{
|
||||
keySize: 32,
|
||||
newBlock: aes.NewCipher,
|
||||
oid: oidAES256CBC,
|
||||
},
|
||||
ivSize: aes.BlockSize,
|
||||
}
|
||||
|
||||
// AES256GCM is the 256-bit key AES cipher in GCM mode.
|
||||
var AES256GCM = gcmBlockCipher{
|
||||
nonceSize: 12,
|
||||
var AES256GCM = &gcmBlockCipher{
|
||||
baseBlockCipher: baseBlockCipher{
|
||||
keySize: 32,
|
||||
newBlock: aes.NewCipher,
|
||||
oid: oidAES256GCM,
|
||||
},
|
||||
nonceSize: 12,
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package pkcs8
|
||||
package pkcs
|
||||
|
||||
import (
|
||||
"crypto/des"
|
||||
@ -12,25 +12,29 @@ var (
|
||||
|
||||
func init() {
|
||||
RegisterCipher(oidDESCBC, func() Cipher {
|
||||
return &DESCBC
|
||||
return DESCBC
|
||||
})
|
||||
|
||||
RegisterCipher(oidDESEDE3CBC, func() Cipher {
|
||||
return &TripleDESCBC
|
||||
return TripleDESCBC
|
||||
})
|
||||
}
|
||||
|
||||
var DESCBC = cbcBlockCipher{
|
||||
ivSize: des.BlockSize,
|
||||
var DESCBC = &cbcBlockCipher{
|
||||
baseBlockCipher: baseBlockCipher{
|
||||
keySize: 8,
|
||||
newBlock: des.NewCipher,
|
||||
oid: oidDESCBC,
|
||||
},
|
||||
ivSize: des.BlockSize,
|
||||
}
|
||||
|
||||
// TripleDESCBC is the 168-bit key 3DES cipher in CBC mode.
|
||||
var TripleDESCBC = cbcBlockCipher{
|
||||
ivSize: des.BlockSize,
|
||||
var TripleDESCBC = &cbcBlockCipher{
|
||||
baseBlockCipher: baseBlockCipher{
|
||||
keySize: 24,
|
||||
newBlock: des.NewTripleDESCipher,
|
||||
oid: oidDESEDE3CBC,
|
||||
},
|
||||
ivSize: des.BlockSize,
|
||||
}
|
67
pkcs/cipher_sm4.go
Normal file
67
pkcs/cipher_sm4.go
Normal file
@ -0,0 +1,67 @@
|
||||
package pkcs
|
||||
|
||||
import (
|
||||
"encoding/asn1"
|
||||
|
||||
"github.com/emmansun/gmsm/sm4"
|
||||
)
|
||||
|
||||
var (
|
||||
oidSM4CBC = asn1.ObjectIdentifier{1, 2, 156, 10197, 1, 104, 2}
|
||||
oidSM4GCM = asn1.ObjectIdentifier{1, 2, 156, 10197, 1, 104, 8}
|
||||
oidSM4ECB = asn1.ObjectIdentifier{1, 2, 156, 10197, 1, 104, 1}
|
||||
oidSM4 = asn1.ObjectIdentifier{1, 2, 156, 10197, 1, 104}
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegisterCipher(oidSM4CBC, func() Cipher {
|
||||
return SM4CBC
|
||||
})
|
||||
RegisterCipher(oidSM4GCM, func() Cipher {
|
||||
return SM4GCM
|
||||
})
|
||||
RegisterCipher(oidSM4, func() Cipher {
|
||||
return SM4
|
||||
})
|
||||
RegisterCipher(oidSM4ECB, func() Cipher {
|
||||
return SM4ECB
|
||||
})
|
||||
}
|
||||
|
||||
// SM4 is the 128-bit key SM4 cipher in ECB mode.
|
||||
var SM4 = &ecbBlockCipher{
|
||||
baseBlockCipher: baseBlockCipher{
|
||||
keySize: 16,
|
||||
newBlock: sm4.NewCipher,
|
||||
oid: oidSM4,
|
||||
},
|
||||
}
|
||||
|
||||
// SM4ECB is the 128-bit key SM4 cipher in ECB mode.
|
||||
var SM4ECB = &ecbBlockCipher{
|
||||
baseBlockCipher: baseBlockCipher{
|
||||
keySize: 16,
|
||||
newBlock: sm4.NewCipher,
|
||||
oid: oidSM4ECB,
|
||||
},
|
||||
}
|
||||
|
||||
// SM4CBC is the 128-bit key SM4 cipher in CBC mode.
|
||||
var SM4CBC = &cbcBlockCipher{
|
||||
baseBlockCipher: baseBlockCipher{
|
||||
keySize: 16,
|
||||
newBlock: sm4.NewCipher,
|
||||
oid: oidSM4CBC,
|
||||
},
|
||||
ivSize: sm4.BlockSize,
|
||||
}
|
||||
|
||||
// SM4GCM is the 128-bit key SM4 cipher in GCM mode.
|
||||
var SM4GCM = &gcmBlockCipher{
|
||||
baseBlockCipher: baseBlockCipher{
|
||||
keySize: 16,
|
||||
newBlock: sm4.NewCipher,
|
||||
oid: oidSM4GCM,
|
||||
},
|
||||
nonceSize: 12,
|
||||
}
|
21
pkcs7/LICENSE
Normal file
21
pkcs7/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Andrew Smith
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
64
pkcs7/README.md
Normal file
64
pkcs7/README.md
Normal file
@ -0,0 +1,64 @@
|
||||
pkcs7 implements parsing and creating signed and enveloped messages.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/emmansun/gmsm/pkcs7"
|
||||
)
|
||||
|
||||
func SignAndDetach(content []byte, cert *x509.Certificate, privkey *rsa.PrivateKey) (signed []byte, err error) {
|
||||
toBeSigned, err := NewSignedData(content)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Cannot initialize signed data: %s", err)
|
||||
return
|
||||
}
|
||||
if err = toBeSigned.AddSigner(cert, privkey, SignerInfoConfig{}); err != nil {
|
||||
err = fmt.Errorf("Cannot add signer: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Detach signature, omit if you want an embedded signature
|
||||
toBeSigned.Detach()
|
||||
|
||||
signed, err = toBeSigned.Finish()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Cannot finish signing data: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Verify the signature
|
||||
pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: signed})
|
||||
p7, err := pkcs7.Parse(signed)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Cannot parse our signed data: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
// since the signature was detached, reattach the content here
|
||||
p7.Content = content
|
||||
|
||||
if bytes.Compare(content, p7.Content) != 0 {
|
||||
err = fmt.Errorf("Our content was not in the parsed data:\n\tExpected: %s\n\tActual: %s", content, p7.Content)
|
||||
return
|
||||
}
|
||||
if err = p7.Verify(); err != nil {
|
||||
err = fmt.Errorf("Cannot verify our signed data: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
return signed, nil
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Credits
|
||||
This is a fork of [mozilla-services/pkcs7](https://github.com/mozilla-services/pkcs7)
|
271
pkcs7/ber.go
Normal file
271
pkcs7/ber.go
Normal file
@ -0,0 +1,271 @@
|
||||
package pkcs7
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
)
|
||||
|
||||
var encodeIndent = 0
|
||||
|
||||
type asn1Object interface {
|
||||
EncodeTo(writer *bytes.Buffer) error
|
||||
}
|
||||
|
||||
type asn1Structured struct {
|
||||
tagBytes []byte
|
||||
content []asn1Object
|
||||
}
|
||||
|
||||
func (s asn1Structured) EncodeTo(out *bytes.Buffer) error {
|
||||
//fmt.Printf("%s--> tag: % X\n", strings.Repeat("| ", encodeIndent), s.tagBytes)
|
||||
encodeIndent++
|
||||
inner := new(bytes.Buffer)
|
||||
for _, obj := range s.content {
|
||||
err := obj.EncodeTo(inner)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
encodeIndent--
|
||||
out.Write(s.tagBytes)
|
||||
encodeLength(out, inner.Len())
|
||||
out.Write(inner.Bytes())
|
||||
return nil
|
||||
}
|
||||
|
||||
type asn1Primitive struct {
|
||||
tagBytes []byte
|
||||
length int
|
||||
content []byte
|
||||
}
|
||||
|
||||
func (p asn1Primitive) EncodeTo(out *bytes.Buffer) error {
|
||||
_, err := out.Write(p.tagBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = encodeLength(out, p.length); err != nil {
|
||||
return err
|
||||
}
|
||||
//fmt.Printf("%s--> tag: % X length: %d\n", strings.Repeat("| ", encodeIndent), p.tagBytes, p.length)
|
||||
//fmt.Printf("%s--> content length: %d\n", strings.Repeat("| ", encodeIndent), len(p.content))
|
||||
out.Write(p.content)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ber2der(ber []byte) ([]byte, error) {
|
||||
if len(ber) == 0 {
|
||||
return nil, errors.New("ber2der: input ber is empty")
|
||||
}
|
||||
//fmt.Printf("--> ber2der: Transcoding %d bytes\n", len(ber))
|
||||
out := new(bytes.Buffer)
|
||||
|
||||
obj, _, err := readObject(ber, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
obj.EncodeTo(out)
|
||||
|
||||
// if offset < len(ber) {
|
||||
// return nil, fmt.Errorf("ber2der: Content longer than expected. Got %d, expected %d", offset, len(ber))
|
||||
//}
|
||||
|
||||
return out.Bytes(), nil
|
||||
}
|
||||
|
||||
// encodes lengths that are longer than 127 into string of bytes
|
||||
func marshalLongLength(out *bytes.Buffer, i int) (err error) {
|
||||
n := lengthLength(i)
|
||||
|
||||
for ; n > 0; n-- {
|
||||
err = out.WriteByte(byte(i >> uint((n-1)*8)))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// computes the byte length of an encoded length value
|
||||
func lengthLength(i int) (numBytes int) {
|
||||
numBytes = 1
|
||||
for i > 255 {
|
||||
numBytes++
|
||||
i >>= 8
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// encodes the length in DER format
|
||||
// If the length fits in 7 bits, the value is encoded directly.
|
||||
//
|
||||
// Otherwise, the number of bytes to encode the length is first determined.
|
||||
// This number is likely to be 4 or less for a 32bit length. This number is
|
||||
// added to 0x80. The length is encoded in big endian encoding follow after
|
||||
//
|
||||
// Examples:
|
||||
// length | byte 1 | bytes n
|
||||
// 0 | 0x00 | -
|
||||
// 120 | 0x78 | -
|
||||
// 200 | 0x81 | 0xC8
|
||||
// 500 | 0x82 | 0x01 0xF4
|
||||
//
|
||||
func encodeLength(out *bytes.Buffer, length int) (err error) {
|
||||
if length >= 128 {
|
||||
l := lengthLength(length)
|
||||
err = out.WriteByte(0x80 | byte(l))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = marshalLongLength(out, length)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
err = out.WriteByte(byte(length))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func readObject(ber []byte, offset int) (asn1Object, int, error) {
|
||||
berLen := len(ber)
|
||||
if offset >= berLen {
|
||||
return nil, 0, errors.New("ber2der: offset is after end of ber data")
|
||||
}
|
||||
tagStart := offset
|
||||
b := ber[offset]
|
||||
offset++
|
||||
if offset >= berLen {
|
||||
return nil, 0, errors.New("ber2der: cannot move offset forward, end of ber data reached")
|
||||
}
|
||||
tag := b & 0x1F // last 5 bits
|
||||
if tag == 0x1F {
|
||||
tag = 0
|
||||
for ber[offset] >= 0x80 {
|
||||
tag = tag<<7 + ber[offset] - 0x80
|
||||
offset++
|
||||
if offset > berLen {
|
||||
return nil, 0, errors.New("ber2der: cannot move offset forward, end of ber data reached")
|
||||
}
|
||||
}
|
||||
// jvehent 20170227: this doesn't appear to be used anywhere...
|
||||
//tag = tag*128 + ber[offset] - 0x80
|
||||
offset++
|
||||
if offset > berLen {
|
||||
return nil, 0, errors.New("ber2der: cannot move offset forward, end of ber data reached")
|
||||
}
|
||||
}
|
||||
tagEnd := offset
|
||||
|
||||
kind := b & 0x20
|
||||
if kind == 0 {
|
||||
debugprint("--> Primitive\n")
|
||||
} else {
|
||||
debugprint("--> Constructed\n")
|
||||
}
|
||||
// read length
|
||||
var length int
|
||||
l := ber[offset]
|
||||
offset++
|
||||
if offset > berLen {
|
||||
return nil, 0, errors.New("ber2der: cannot move offset forward, end of ber data reached")
|
||||
}
|
||||
indefinite := false
|
||||
if l > 0x80 {
|
||||
numberOfBytes := (int)(l & 0x7F)
|
||||
if numberOfBytes > 4 { // int is only guaranteed to be 32bit
|
||||
return nil, 0, errors.New("ber2der: BER tag length too long")
|
||||
}
|
||||
if numberOfBytes == 4 && (int)(ber[offset]) > 0x7F {
|
||||
return nil, 0, errors.New("ber2der: BER tag length is negative")
|
||||
}
|
||||
if (int)(ber[offset]) == 0x0 {
|
||||
return nil, 0, errors.New("ber2der: BER tag length has leading zero")
|
||||
}
|
||||
debugprint("--> (compute length) indicator byte: %x\n", l)
|
||||
debugprint("--> (compute length) length bytes: % X\n", ber[offset:offset+numberOfBytes])
|
||||
for i := 0; i < numberOfBytes; i++ {
|
||||
length = length<<8 + (int)(ber[offset])
|
||||
offset++
|
||||
if offset > berLen {
|
||||
return nil, 0, errors.New("ber2der: cannot move offset forward, end of ber data reached")
|
||||
}
|
||||
}
|
||||
} else if l == 0x80 {
|
||||
indefinite = true
|
||||
} else {
|
||||
length = (int)(l)
|
||||
}
|
||||
if length < 0 {
|
||||
return nil, 0, errors.New("ber2der: invalid negative value found in BER tag length")
|
||||
}
|
||||
//fmt.Printf("--> length : %d\n", length)
|
||||
contentEnd := offset + length
|
||||
if contentEnd > len(ber) {
|
||||
return nil, 0, errors.New("ber2der: BER tag length is more than available data")
|
||||
}
|
||||
debugprint("--> content start : %d\n", offset)
|
||||
debugprint("--> content end : %d\n", contentEnd)
|
||||
debugprint("--> content : % X\n", ber[offset:contentEnd])
|
||||
var obj asn1Object
|
||||
if indefinite && kind == 0 {
|
||||
return nil, 0, errors.New("ber2der: Indefinite form tag must have constructed encoding")
|
||||
}
|
||||
if kind == 0 {
|
||||
obj = asn1Primitive{
|
||||
tagBytes: ber[tagStart:tagEnd],
|
||||
length: length,
|
||||
content: ber[offset:contentEnd],
|
||||
}
|
||||
} else {
|
||||
var subObjects []asn1Object
|
||||
for (offset < contentEnd) || indefinite {
|
||||
var subObj asn1Object
|
||||
var err error
|
||||
subObj, offset, err = readObject(ber, offset)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
subObjects = append(subObjects, subObj)
|
||||
|
||||
if indefinite {
|
||||
terminated, err := isIndefiniteTermination(ber, offset)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
if terminated {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
obj = asn1Structured{
|
||||
tagBytes: ber[tagStart:tagEnd],
|
||||
content: subObjects,
|
||||
}
|
||||
}
|
||||
|
||||
// Apply indefinite form length with 0x0000 terminator.
|
||||
if indefinite {
|
||||
contentEnd = offset + 2
|
||||
}
|
||||
|
||||
return obj, contentEnd, nil
|
||||
}
|
||||
|
||||
func isIndefiniteTermination(ber []byte, offset int) (bool, error) {
|
||||
if len(ber)-offset < 2 {
|
||||
return false, errors.New("ber2der: Invalid BER format")
|
||||
}
|
||||
|
||||
return bytes.Index(ber[offset:], []byte{0x0, 0x0}) == 0, nil
|
||||
}
|
||||
|
||||
func debugprint(format string, a ...interface{}) {
|
||||
//fmt.Printf(format, a)
|
||||
}
|
186
pkcs7/ber_test.go
Normal file
186
pkcs7/ber_test.go
Normal file
@ -0,0 +1,186 @@
|
||||
package pkcs7
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/asn1"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBer2Der(t *testing.T) {
|
||||
// indefinite length fixture
|
||||
ber := []byte{0x30, 0x80, 0x02, 0x01, 0x01, 0x00, 0x00}
|
||||
expected := []byte{0x30, 0x03, 0x02, 0x01, 0x01}
|
||||
der, err := ber2der(ber)
|
||||
if err != nil {
|
||||
t.Fatalf("ber2der failed with error: %v", err)
|
||||
}
|
||||
if !bytes.Equal(der, expected) {
|
||||
t.Errorf("ber2der result did not match.\n\tExpected: % X\n\tActual: % X", expected, der)
|
||||
}
|
||||
|
||||
if der2, err := ber2der(der); err != nil {
|
||||
t.Errorf("ber2der on DER bytes failed with error: %v", err)
|
||||
} else {
|
||||
if !bytes.Equal(der, der2) {
|
||||
t.Error("ber2der is not idempotent")
|
||||
}
|
||||
}
|
||||
var thing struct {
|
||||
Number int
|
||||
}
|
||||
rest, err := asn1.Unmarshal(der, &thing)
|
||||
if err != nil {
|
||||
t.Errorf("Cannot parse resulting DER because: %v", err)
|
||||
} else if len(rest) > 0 {
|
||||
t.Errorf("Resulting DER has trailing data: % X", rest)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBer2Der_Negatives(t *testing.T) {
|
||||
fixtures := []struct {
|
||||
Input []byte
|
||||
ErrorContains string
|
||||
}{
|
||||
{[]byte{0x30, 0x85}, "tag length too long"},
|
||||
{[]byte{0x30, 0x84, 0x80, 0x0, 0x0, 0x0}, "length is negative"},
|
||||
{[]byte{0x30, 0x82, 0x0, 0x1}, "length has leading zero"},
|
||||
{[]byte{0x30, 0x80, 0x1, 0x2, 0x1, 0x2}, "Invalid BER format"},
|
||||
{[]byte{0x30, 0x80, 0x1, 0x2}, "BER tag length is more than available data"},
|
||||
{[]byte{0x30, 0x03, 0x01, 0x02}, "length is more than available data"},
|
||||
{[]byte{0x30}, "end of ber data reached"},
|
||||
}
|
||||
|
||||
for _, fixture := range fixtures {
|
||||
_, err := ber2der(fixture.Input)
|
||||
if err == nil {
|
||||
t.Errorf("No error thrown. Expected: %s", fixture.ErrorContains)
|
||||
}
|
||||
if !strings.Contains(err.Error(), fixture.ErrorContains) {
|
||||
t.Errorf("Unexpected error thrown.\n\tExpected: /%s/\n\tActual: %s", fixture.ErrorContains, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBer2Der_NestedMultipleIndefinite(t *testing.T) {
|
||||
// indefinite length fixture
|
||||
ber := []byte{0x30, 0x80, 0x30, 0x80, 0x02, 0x01, 0x01, 0x00, 0x00, 0x30, 0x80, 0x02, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00}
|
||||
expected := []byte{0x30, 0x0A, 0x30, 0x03, 0x02, 0x01, 0x01, 0x30, 0x03, 0x02, 0x01, 0x02}
|
||||
|
||||
der, err := ber2der(ber)
|
||||
if err != nil {
|
||||
t.Fatalf("ber2der failed with error: %v", err)
|
||||
}
|
||||
if !bytes.Equal(der, expected) {
|
||||
t.Errorf("ber2der result did not match.\n\tExpected: % X\n\tActual: % X", expected, der)
|
||||
}
|
||||
|
||||
if der2, err := ber2der(der); err != nil {
|
||||
t.Errorf("ber2der on DER bytes failed with error: %v", err)
|
||||
} else {
|
||||
if !bytes.Equal(der, der2) {
|
||||
t.Error("ber2der is not idempotent")
|
||||
}
|
||||
}
|
||||
var thing struct {
|
||||
Nest1 struct {
|
||||
Number int
|
||||
}
|
||||
Nest2 struct {
|
||||
Number int
|
||||
}
|
||||
}
|
||||
rest, err := asn1.Unmarshal(der, &thing)
|
||||
if err != nil {
|
||||
t.Errorf("Cannot parse resulting DER because: %v", err)
|
||||
} else if len(rest) > 0 {
|
||||
t.Errorf("Resulting DER has trailing data: % X", rest)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyIndefiniteLengthBer(t *testing.T) {
|
||||
decoded := mustDecodePEM([]byte(testPKCS7))
|
||||
|
||||
_, err := ber2der(decoded)
|
||||
if err != nil {
|
||||
t.Errorf("cannot parse indefinite length ber: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func mustDecodePEM(data []byte) []byte {
|
||||
var block *pem.Block
|
||||
block, rest := pem.Decode(data)
|
||||
if len(rest) != 0 {
|
||||
panic(fmt.Errorf("unexpected remaining PEM block during decode"))
|
||||
}
|
||||
return block.Bytes
|
||||
}
|
||||
|
||||
const testPKCS7 = `
|
||||
-----BEGIN PKCS7-----
|
||||
MIAGCSqGSIb3DQEHAqCAMIACAQExDzANBglghkgBZQMEAgEFADCABgkqhkiG9w0B
|
||||
BwGggCSABIIDfXsiQWdlbnRBY3Rpb25PdmVycmlkZXMiOnsiQWdlbnRPdmVycmlk
|
||||
ZXMiOnsiRmlsZUV4aXN0c0JlaGF2aW9yIjoiT1ZFUldSSVRFIn19LCJBcHBsaWNh
|
||||
dGlvbklkIjoiZTA0NDIzZTQtN2E2Ny00ZjljLWIyOTEtOTllNjNjMWMyMTU4Iiwi
|
||||
QXBwbGljYXRpb25OYW1lIjoibWthbmlhLXhyZF9zYW0uY2R3c19lY2hvc2VydmVy
|
||||
IiwiRGVwbG95bWVudENyZWF0b3IiOiJ1c2VyIiwiRGVwbG95bWVudEdyb3VwSWQi
|
||||
OiJmYWI5MjEwZi1mNmM3LTQyODUtYWEyZC03Mzc2MGQ4ODE3NmEiLCJEZXBsb3lt
|
||||
ZW50R3JvdXBOYW1lIjoibWthbmlhLXhyZF9zYW0uY2R3c19lY2hvc2VydmVyX2Rn
|
||||
IiwiRGVwbG95bWVudElkIjoiZC1UREUxVTNXREEiLCJEZXBsb3ltZW50VHlwZSI6
|
||||
IklOX1BMQUNFIiwiR2l0SHViQWNjZXNzVG9rZW4iOm51bGwsIkluc3RhbmNlR3Jv
|
||||
dXBJZCI6ImZhYjkyMTBmLWY2YzctNDI4NS1hYTJkLTczNzYwZDg4MTc2YSIsIlJl
|
||||
dmlzaW9uIjp7IkFwcFNwZWNDb250ZW50IjpudWxsLCJDb2RlQ29tbWl0UmV2aXNp
|
||||
b24iOm51bGwsIkdpdEh1YlJldmlzaW9uIjpudWxsLCJHaXRSZXZpc2lvbiI6bnVs
|
||||
bCwiUmV2aXNpb25UeXBlIjoiUzMiLCJTM1JldmlzaW9uIjp7IkJ1Y2tldCI6Im1r
|
||||
YW5pYS1jZHdzLWRlcGxveS1idWNrZXQiLCJCdW5kbGVUeXBlIjoiemlwIiwiRVRh
|
||||
ZyI6bnVsbCwiS2V5IjoieHJkOjpzYW0uY2R3czo6ZWNob3NlcnZlcjo6MTo6Lnpp
|
||||
cCIsIlZlcnNpb24iOm51bGx9fSwiUzNSZXZpc2lvbiI6eyJCdWNrZXQiOiJta2Fu
|
||||
aWEtY2R3cy1kZXBsb3ktYnVja2V0IiwiQnVuZGxlVHlwZSI6InppcCIsIkVUYWci
|
||||
Om51bGwsIktleSI6InhyZDo6c2FtLmNkd3M6OmVjaG9zZXJ2ZXI6OjE6Oi56aXAi
|
||||
LCJWZXJzaW9uIjpudWxsfSwiVGFyZ2V0UmV2aXNpb24iOm51bGx9AAAAAAAAoIAw
|
||||
ggWbMIIEg6ADAgECAhAGrjFMK45t2jcNHtjY1DjEMA0GCSqGSIb3DQEBCwUAMEYx
|
||||
CzAJBgNVBAYTAlVTMQ8wDQYDVQQKEwZBbWF6b24xFTATBgNVBAsTDFNlcnZlciBD
|
||||
QSAxQjEPMA0GA1UEAxMGQW1hem9uMB4XDTIwMTExMjAwMDAwMFoXDTIxMTAxNTIz
|
||||
NTk1OVowNDEyMDAGA1UEAxMpY29kZWRlcGxveS1zaWduZXItdXMtZWFzdC0yLmFt
|
||||
YXpvbmF3cy5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDit4f+
|
||||
I4BSv4rBV/8bJ+f4KqBwTCt9iJeau/r9liQfMgj/C1M2E+aa++u8BtY/LQstB44v
|
||||
v6KqcaiOyWpkD9OsUty9qb4eNTPF2Y4jpNsi/Hfw0phsd9gLun2foppILmL4lZIG
|
||||
lBhTeEwv6qV4KbyXOG9abHOX32+jVFtM1rbzHNFvz90ysfZp16TBAi7IRKEZeXvd
|
||||
MvlJJMAJtAoblxiDIS3A1csY1G4XHYET8xIoCop3mqEZEtAxUUP2epdXXdhD2U0G
|
||||
7alSRS54o91QW1Dp3A13lu1A1nds9CkWlPkDTpKSUG/qN5y5+6dCCGaydgL5krMs
|
||||
R79bCrR1sEKm5hi1AgMBAAGjggKVMIICkTAfBgNVHSMEGDAWgBRZpGYGUqB7lZI8
|
||||
o5QHJ5Z0W/k90DAdBgNVHQ4EFgQUPF5qTbnTDYhmp7tGmmL/jTmLoHMwNAYDVR0R
|
||||
BC0wK4IpY29kZWRlcGxveS1zaWduZXItdXMtZWFzdC0yLmFtYXpvbmF3cy5jb20w
|
||||
DgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjA7
|
||||
BgNVHR8ENDAyMDCgLqAshipodHRwOi8vY3JsLnNjYTFiLmFtYXpvbnRydXN0LmNv
|
||||
bS9zY2ExYi5jcmwwIAYDVR0gBBkwFzALBglghkgBhv1sAQIwCAYGZ4EMAQIBMHUG
|
||||
CCsGAQUFBwEBBGkwZzAtBggrBgEFBQcwAYYhaHR0cDovL29jc3Auc2NhMWIuYW1h
|
||||
em9udHJ1c3QuY29tMDYGCCsGAQUFBzAChipodHRwOi8vY3J0LnNjYTFiLmFtYXpv
|
||||
bnRydXN0LmNvbS9zY2ExYi5jcnQwDAYDVR0TAQH/BAIwADCCAQQGCisGAQQB1nkC
|
||||
BAIEgfUEgfIA8AB2APZclC/RdzAiFFQYCDCUVo7jTRMZM7/fDC8gC8xO8WTjAAAB
|
||||
dboejIcAAAQDAEcwRQIgeqoKXbST17TCEzM1BMWx/jjyVQVBIN3LG17U4OaV364C
|
||||
IQDPUSJZhJm7uqGea6+VwqeDe/vGuGSuJzkDwTIOeIXPaAB2AFzcQ5L+5qtFRLFe
|
||||
mtRW5hA3+9X6R9yhc5SyXub2xw7KAAABdboejNQAAAQDAEcwRQIgEKIAwwhjUcq2
|
||||
iwzBAagdy+fTiKnBY1Yjf6wOeRpwXfMCIQC8wM3nxiWrGgIpdzzgDvFhZZTV3N81
|
||||
JWcYAu+srIVOhTANBgkqhkiG9w0BAQsFAAOCAQEAer9kml53XFy4ZSVzCbdsIFYP
|
||||
Ohu7LDf5iffHBVZFnGOEVOmiPYYkNwi9R6EHIYaAs7G7GGLCp/6tdc+G4eF1j6wB
|
||||
IkmXZcxMTxk/87R+S+36yDLg1GBZvqttLfexj0TRVAfVLJc7FjLXAW2+wi7YyNe8
|
||||
X17lWBwHxa1r5KgweJshGzYVUsgMTSx0aJ+93ZnqplBp9x+9DSQNqqNlBgxFANxs
|
||||
ux+dfpduyLd8VLqtlECGC07tYE4mBaAjMiNjCZRWMp8ya/Z6J/bJZ27IDGA4dXzm
|
||||
l9NNnlbuUDAenAByUqE+0b78J6EmmdAVf+N8siriMg02FdP3lAXJLE8tDeZp8AAA
|
||||
MYICIDCCAhwCAQEwWjBGMQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRUw
|
||||
EwYDVQQLEwxTZXJ2ZXIgQ0EgMUIxDzANBgNVBAMTBkFtYXpvbgIQBq4xTCuObdo3
|
||||
DR7Y2NQ4xDANBglghkgBZQMEAgEFAKCBmDAYBgkqhkiG9w0BCQMxCwYJKoZIhvcN
|
||||
AQcBMBwGCSqGSIb3DQEJBTEPFw0yMTA2MjQxOTU1MzFaMC0GCSqGSIb3DQEJNDEg
|
||||
MB4wDQYJYIZIAWUDBAIBBQChDQYJKoZIhvcNAQELBQAwLwYJKoZIhvcNAQkEMSIE
|
||||
IP7gMuT2H0/AhgPgj3Eo0NWCIdQOBjJO18coNKIaOnJYMA0GCSqGSIb3DQEBCwUA
|
||||
BIIBAJX+e87q0YvRon9/ENTvE0FoYMzYblID2Reek6L217ZlZ6pUuRsc4ghhJ5Yh
|
||||
WZeOCaLwi4mrnQ5/+DGKkJ4a/w5sqFTwtJIGIIAuDCn/uDm8kIDUVkbeznSOLoPA
|
||||
67cxiqgIdqZ5pqUoid2YsDj20owrGDG4wUF6ZvhM9g/5va3CAhxqvTE2HwjhHTfz
|
||||
Cgl8Nlvalz7YxXEf2clFEiEVa1fVaGMl9pCyedAmTfd6hoivcpAsopvXfVaaaR2y
|
||||
iuZidpUfFhSk+Ls7TU/kB74ckfUGj5q/5HcKJgb/S+FYUV7eu0ewzTyW1uRl/d0U
|
||||
Tb7e7EjgDGJsjOTMdTrMfv8ho8kAAAAAAAA=
|
||||
-----END PKCS7-----
|
||||
`
|
88
pkcs7/decrypt.go
Normal file
88
pkcs7/decrypt.go
Normal file
@ -0,0 +1,88 @@
|
||||
package pkcs7
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"encoding/asn1"
|
||||
"errors"
|
||||
|
||||
"github.com/emmansun/gmsm/pkcs"
|
||||
"github.com/emmansun/gmsm/smx509"
|
||||
)
|
||||
|
||||
// ErrUnsupportedAlgorithm tells you when our quick dev assumptions have failed
|
||||
var ErrUnsupportedAlgorithm = errors.New("pkcs7: cannot decrypt data: only RSA, SM2, DES, DES-EDE3, AES and SM4 supported")
|
||||
|
||||
// ErrNotEncryptedContent is returned when attempting to Decrypt data that is not encrypted data
|
||||
var ErrNotEncryptedContent = errors.New("pkcs7: content data is NOT a decryptable data type")
|
||||
|
||||
type decryptable interface {
|
||||
GetRecipient(cert *smx509.Certificate) *recipientInfo
|
||||
GetEncryptedContentInfo() *encryptedContentInfo
|
||||
}
|
||||
|
||||
// Decrypt decrypts encrypted content info for recipient cert and private key
|
||||
func (p7 *PKCS7) Decrypt(cert *smx509.Certificate, pkey crypto.PrivateKey) ([]byte, error) {
|
||||
decryptableData, ok := p7.raw.(decryptable)
|
||||
if !ok {
|
||||
return nil, ErrNotEncryptedContent
|
||||
}
|
||||
recipient := decryptableData.GetRecipient(cert)
|
||||
if recipient == nil {
|
||||
return nil, errors.New("pkcs7: no enveloped recipient for provided certificate")
|
||||
}
|
||||
|
||||
switch pkey := pkey.(type) {
|
||||
case crypto.Decrypter:
|
||||
// Generic case to handle anything that provides the crypto.Decrypter interface.
|
||||
contentKey, err := pkey.Decrypt(rand.Reader, recipient.EncryptedKey, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return decryptableData.GetEncryptedContentInfo().decrypt(contentKey)
|
||||
}
|
||||
return nil, ErrUnsupportedAlgorithm
|
||||
}
|
||||
|
||||
// DecryptUsingPSK decrypts encrypted data using caller provided
|
||||
// pre-shared secret
|
||||
func (p7 *PKCS7) DecryptUsingPSK(key []byte) ([]byte, error) {
|
||||
data, ok := p7.raw.(encryptedData)
|
||||
if !ok {
|
||||
return nil, ErrNotEncryptedContent
|
||||
}
|
||||
return data.EncryptedContentInfo.decrypt(key)
|
||||
}
|
||||
|
||||
func (eci encryptedContentInfo) getCiphertext() (ciphertext []byte) {
|
||||
// EncryptedContent can either be constructed of multple OCTET STRINGs
|
||||
// or _be_ a tagged OCTET STRING
|
||||
if eci.EncryptedContent.IsCompound {
|
||||
// Complex case to concat all of the children OCTET STRINGs
|
||||
var buf bytes.Buffer
|
||||
cypherbytes := eci.EncryptedContent.Bytes
|
||||
for {
|
||||
var part []byte
|
||||
cypherbytes, _ = asn1.Unmarshal(cypherbytes, &part)
|
||||
buf.Write(part)
|
||||
if cypherbytes == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
ciphertext = buf.Bytes()
|
||||
} else {
|
||||
// Simple case, the bytes _are_ the cyphertext
|
||||
ciphertext = eci.EncryptedContent.Bytes
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (eci encryptedContentInfo) decrypt(key []byte) ([]byte, error) {
|
||||
alg := eci.ContentEncryptionAlgorithm.Algorithm
|
||||
cipher, err := pkcs.GetCipher(alg)
|
||||
if err != nil {
|
||||
return nil, ErrUnsupportedAlgorithm
|
||||
}
|
||||
return cipher.Decrypt(key, &eci.ContentEncryptionAlgorithm.Parameters, eci.getCiphertext())
|
||||
}
|
61
pkcs7/decrypt_test.go
Normal file
61
pkcs7/decrypt_test.go
Normal file
@ -0,0 +1,61 @@
|
||||
package pkcs7
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDecrypt(t *testing.T) {
|
||||
fixture := UnmarshalTestFixture(EncryptedTestFixture)
|
||||
p7, err := Parse(fixture.Input)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
content, err := p7.Decrypt(fixture.Certificate, fixture.PrivateKey)
|
||||
if err != nil {
|
||||
t.Errorf("Cannot Decrypt with error: %v", err)
|
||||
}
|
||||
expected := []byte("This is a test")
|
||||
if !bytes.Equal(content, expected) {
|
||||
t.Errorf("Decrypted result does not match.\n\tExpected:%s\n\tActual:%s", expected, content)
|
||||
}
|
||||
}
|
||||
|
||||
// echo -n "This is a test" > test.txt
|
||||
// openssl cms -encrypt -in test.txt cert.pem
|
||||
var EncryptedTestFixture = `
|
||||
-----BEGIN PKCS7-----
|
||||
MIIBGgYJKoZIhvcNAQcDoIIBCzCCAQcCAQAxgcwwgckCAQAwMjApMRAwDgYDVQQK
|
||||
EwdBY21lIENvMRUwEwYDVQQDEwxFZGRhcmQgU3RhcmsCBQDL+CvWMA0GCSqGSIb3
|
||||
DQEBAQUABIGAyFz7bfI2noUs4FpmYfztm1pVjGyB00p9x0H3gGHEYNXdqlq8VG8d
|
||||
iq36poWtEkatnwsOlURWZYECSi0g5IAL0U9sj82EN0xssZNaK0S5FTGnB3DPvYgt
|
||||
HJvcKq7YvNLKMh4oqd17C6GB4oXyEBDj0vZnL7SUoCAOAWELPeC8CTUwMwYJKoZI
|
||||
hvcNAQcBMBQGCCqGSIb3DQMHBAhEowTkot3a7oAQFD//J/IhFnk+JbkH7HZQFA==
|
||||
-----END PKCS7-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIB1jCCAUGgAwIBAgIFAMv4K9YwCwYJKoZIhvcNAQELMCkxEDAOBgNVBAoTB0Fj
|
||||
bWUgQ28xFTATBgNVBAMTDEVkZGFyZCBTdGFyazAeFw0xNTA1MDYwMzU2NDBaFw0x
|
||||
NjA1MDYwMzU2NDBaMCUxEDAOBgNVBAoTB0FjbWUgQ28xETAPBgNVBAMTCEpvbiBT
|
||||
bm93MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDK6NU0R0eiCYVquU4RcjKc
|
||||
LzGfx0aa1lMr2TnLQUSeLFZHFxsyyMXXuMPig3HK4A7SGFHupO+/1H/sL4xpH5zg
|
||||
8+Zg2r8xnnney7abxcuv0uATWSIeKlNnb1ZO1BAxFnESc3GtyOCr2dUwZHX5mRVP
|
||||
+Zxp2ni5qHNraf3wE2VPIQIDAQABoxIwEDAOBgNVHQ8BAf8EBAMCAKAwCwYJKoZI
|
||||
hvcNAQELA4GBAIr2F7wsqmEU/J/kLyrCgEVXgaV/sKZq4pPNnzS0tBYk8fkV3V18
|
||||
sBJyHKRLL/wFZASvzDcVGCplXyMdAOCyfd8jO3F9Ac/xdlz10RrHJT75hNu3a7/n
|
||||
9KNwKhfN4A1CQv2x372oGjRhCW5bHNCWx4PIVeNzCyq/KZhyY9sxHE6f
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIICXgIBAAKBgQDK6NU0R0eiCYVquU4RcjKcLzGfx0aa1lMr2TnLQUSeLFZHFxsy
|
||||
yMXXuMPig3HK4A7SGFHupO+/1H/sL4xpH5zg8+Zg2r8xnnney7abxcuv0uATWSIe
|
||||
KlNnb1ZO1BAxFnESc3GtyOCr2dUwZHX5mRVP+Zxp2ni5qHNraf3wE2VPIQIDAQAB
|
||||
AoGBALyvnSt7KUquDen7nXQtvJBudnf9KFPt//OjkdHHxNZNpoF/JCSqfQeoYkeu
|
||||
MdAVYNLQGMiRifzZz4dDhA9xfUAuy7lcGQcMCxEQ1dwwuFaYkawbS0Tvy2PFlq2d
|
||||
H5/HeDXU4EDJ3BZg0eYj2Bnkt1sJI35UKQSxblQ0MY2q0uFBAkEA5MMOogkgUx1C
|
||||
67S1tFqMUSM8D0mZB0O5vOJZC5Gtt2Urju6vywge2ArExWRXlM2qGl8afFy2SgSv
|
||||
Xk5eybcEiQJBAOMRwwbEoW5NYHuFFbSJyWll4n71CYuWuQOCzehDPyTb80WFZGLV
|
||||
i91kFIjeERyq88eDE5xVB3ZuRiXqaShO/9kCQQCKOEkpInaDgZSjskZvuJ47kByD
|
||||
6CYsO4GIXQMMeHML8ncFH7bb6AYq5ybJVb2NTU7QLFJmfeYuhvIm+xdOreRxAkEA
|
||||
o5FC5Jg2FUfFzZSDmyZ6IONUsdF/i78KDV5nRv1R+hI6/oRlWNCtTNBv/lvBBd6b
|
||||
dseUE9QoaQZsn5lpILEvmQJAZ0B+Or1rAYjnbjnUhdVZoy9kC4Zov+4UH3N/BtSy
|
||||
KJRWUR0wTWfZBPZ5hAYZjTBEAFULaYCXlQKsODSp0M1aQA==
|
||||
-----END PRIVATE KEY-----`
|
228
pkcs7/encrypt.go
Normal file
228
pkcs7/encrypt.go
Normal file
@ -0,0 +1,228 @@
|
||||
package pkcs7
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"errors"
|
||||
|
||||
"github.com/emmansun/gmsm/pkcs"
|
||||
"github.com/emmansun/gmsm/sm2"
|
||||
"github.com/emmansun/gmsm/smx509"
|
||||
)
|
||||
|
||||
type envelopedData struct {
|
||||
Version int
|
||||
RecipientInfos []recipientInfo `asn1:"set"`
|
||||
EncryptedContentInfo encryptedContentInfo
|
||||
}
|
||||
|
||||
type encryptedData struct {
|
||||
Version int
|
||||
EncryptedContentInfo encryptedContentInfo
|
||||
}
|
||||
|
||||
type recipientInfo struct {
|
||||
Version int
|
||||
IssuerAndSerialNumber issuerAndSerial
|
||||
KeyEncryptionAlgorithm pkix.AlgorithmIdentifier
|
||||
EncryptedKey []byte
|
||||
}
|
||||
|
||||
type encryptedContentInfo struct {
|
||||
ContentType asn1.ObjectIdentifier
|
||||
ContentEncryptionAlgorithm pkix.AlgorithmIdentifier
|
||||
EncryptedContent asn1.RawValue `asn1:"tag:0,optional"`
|
||||
}
|
||||
|
||||
func (data envelopedData) GetRecipient(cert *smx509.Certificate) *recipientInfo {
|
||||
for _, recp := range data.RecipientInfos {
|
||||
if isCertMatchForIssuerAndSerial(cert, recp.IssuerAndSerialNumber) {
|
||||
return &recp
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (data envelopedData) GetEncryptedContentInfo() *encryptedContentInfo {
|
||||
return &data.EncryptedContentInfo
|
||||
}
|
||||
|
||||
// ErrUnsupportedEncryptionAlgorithm is returned when attempting to encrypt
|
||||
// content with an unsupported algorithm.
|
||||
var ErrUnsupportedEncryptionAlgorithm = errors.New("pkcs7: cannot encrypt content: only DES-CBC, AES-CBC, AES-GCM, SM4-CBC and SM4-GCM supported")
|
||||
|
||||
// ErrPSKNotProvided is returned when attempting to encrypt
|
||||
// using a PSK without actually providing the PSK.
|
||||
var ErrPSKNotProvided = errors.New("pkcs7: cannot encrypt content: PSK not provided")
|
||||
|
||||
// Encrypt creates and returns an envelope data PKCS7 structure with encrypted
|
||||
// recipient keys for each recipient public key.
|
||||
//
|
||||
// The algorithm used to perform encryption is determined by the argument cipher
|
||||
//
|
||||
// TODO(fullsailor): Add support for encrypting content with other algorithms
|
||||
func Encrypt(cipher pkcs.Cipher, content []byte, recipients []*smx509.Certificate) ([]byte, error) {
|
||||
return encrypt(cipher, content, recipients, false)
|
||||
}
|
||||
|
||||
// EncryptSM creates and returns an envelope data PKCS7 structure with encrypted
|
||||
// recipient keys for each recipient public key.
|
||||
// The OIDs use GM/T 0010 - 2012 set
|
||||
//
|
||||
// The algorithm used to perform encryption is determined by the argument cipher
|
||||
//
|
||||
func EncryptSM(cipher pkcs.Cipher, content []byte, recipients []*smx509.Certificate) ([]byte, error) {
|
||||
return encrypt(cipher, content, recipients, true)
|
||||
}
|
||||
|
||||
func encrypt(cipher pkcs.Cipher, content []byte, recipients []*smx509.Certificate, isSM bool) ([]byte, error) {
|
||||
var key []byte
|
||||
var err error
|
||||
|
||||
// Create key
|
||||
key = make([]byte, cipher.KeySize())
|
||||
_, err = rand.Read(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
id, ciphertext, err := cipher.Encrypt(key, content)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
envelope := envelopedData{
|
||||
Version: 0,
|
||||
EncryptedContentInfo: encryptedContentInfo{
|
||||
ContentType: OIDData,
|
||||
ContentEncryptionAlgorithm: *id,
|
||||
EncryptedContent: marshalEncryptedContent(ciphertext),
|
||||
},
|
||||
}
|
||||
|
||||
if isSM {
|
||||
envelope.EncryptedContentInfo.ContentType = SM2OIDData
|
||||
}
|
||||
|
||||
// Prepare each recipient's encrypted cipher key
|
||||
recipientInfos := make([]recipientInfo, len(recipients))
|
||||
for i, recipient := range recipients {
|
||||
encrypted, err := encryptKey(key, recipient)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ias, err := cert2issuerAndSerial(recipient)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var keyEncryptionAlgorithm asn1.ObjectIdentifier = OIDEncryptionAlgorithmRSA
|
||||
if recipient.SignatureAlgorithm == smx509.SM2WithSM3 {
|
||||
keyEncryptionAlgorithm = OIDKeyEncryptionAlgorithmSM2
|
||||
} else if isSM {
|
||||
return nil, errors.New("pkcs7: Shangmi does not support RSA")
|
||||
}
|
||||
|
||||
info := recipientInfo{
|
||||
Version: 0,
|
||||
IssuerAndSerialNumber: ias,
|
||||
KeyEncryptionAlgorithm: pkix.AlgorithmIdentifier{
|
||||
Algorithm: keyEncryptionAlgorithm,
|
||||
},
|
||||
EncryptedKey: encrypted,
|
||||
}
|
||||
recipientInfos[i] = info
|
||||
}
|
||||
|
||||
envelope.RecipientInfos = recipientInfos
|
||||
|
||||
innerContent, err := asn1.Marshal(envelope)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Prepare outer payload structure
|
||||
wrapper := contentInfo{
|
||||
ContentType: OIDEnvelopedData,
|
||||
Content: asn1.RawValue{Class: 2, Tag: 0, IsCompound: true, Bytes: innerContent},
|
||||
}
|
||||
|
||||
if isSM {
|
||||
wrapper.ContentType = SM2OIDEnvelopedData
|
||||
}
|
||||
|
||||
return asn1.Marshal(wrapper)
|
||||
}
|
||||
|
||||
// EncryptUsingPSK creates and returns an encrypted data PKCS7 structure,
|
||||
// encrypted using caller provided pre-shared secret.
|
||||
func EncryptUsingPSK(cipher pkcs.Cipher, content []byte, key []byte) ([]byte, error) {
|
||||
return encryptUsingPSK(false, cipher, content, key)
|
||||
}
|
||||
|
||||
// EncryptSMUsingPSK creates and returns an encrypted data PKCS7 structure,
|
||||
// encrypted using caller provided pre-shared secret.
|
||||
// This method uses China Standard OID
|
||||
func EncryptSMUsingPSK(cipher pkcs.Cipher, content []byte, key []byte) ([]byte, error) {
|
||||
return encryptUsingPSK(true, cipher, content, key)
|
||||
}
|
||||
|
||||
func encryptUsingPSK(isSM bool, cipher pkcs.Cipher, content []byte, key []byte) ([]byte, error) {
|
||||
var err error
|
||||
|
||||
if key == nil {
|
||||
return nil, ErrPSKNotProvided
|
||||
}
|
||||
|
||||
id, ciphertext, err := cipher.Encrypt(key, content)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Prepare encrypted-data content
|
||||
ed := encryptedData{
|
||||
Version: 0,
|
||||
EncryptedContentInfo: encryptedContentInfo{
|
||||
ContentType: OIDData,
|
||||
ContentEncryptionAlgorithm: *id,
|
||||
EncryptedContent: marshalEncryptedContent(ciphertext),
|
||||
},
|
||||
}
|
||||
if isSM {
|
||||
ed.EncryptedContentInfo.ContentType = SM2OIDData
|
||||
}
|
||||
|
||||
innerContent, err := asn1.Marshal(ed)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var contentType asn1.ObjectIdentifier = OIDEncryptedData
|
||||
if isSM {
|
||||
contentType = SM2OIDEncryptedData
|
||||
}
|
||||
// Prepare outer payload structure
|
||||
wrapper := contentInfo{
|
||||
ContentType: contentType,
|
||||
Content: asn1.RawValue{Class: 2, Tag: 0, IsCompound: true, Bytes: innerContent},
|
||||
}
|
||||
|
||||
return asn1.Marshal(wrapper)
|
||||
}
|
||||
|
||||
func marshalEncryptedContent(content []byte) asn1.RawValue {
|
||||
asn1Content, _ := asn1.Marshal(content)
|
||||
return asn1.RawValue{Tag: 0, Class: 2, Bytes: asn1Content, IsCompound: true}
|
||||
}
|
||||
|
||||
func encryptKey(key []byte, recipient *smx509.Certificate) ([]byte, error) {
|
||||
if pub, ok := recipient.PublicKey.(*rsa.PublicKey); ok {
|
||||
return rsa.EncryptPKCS1v15(rand.Reader, pub, key)
|
||||
}
|
||||
if pub, ok := recipient.PublicKey.(*ecdsa.PublicKey); ok && pub.Curve == sm2.P256() {
|
||||
return sm2.EncryptASN1(rand.Reader, pub, key)
|
||||
}
|
||||
return nil, ErrUnsupportedAlgorithm
|
||||
}
|
158
pkcs7/encrypt_test.go
Normal file
158
pkcs7/encrypt_test.go
Normal file
@ -0,0 +1,158 @@
|
||||
package pkcs7
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/emmansun/gmsm/pkcs"
|
||||
"github.com/emmansun/gmsm/smx509"
|
||||
)
|
||||
|
||||
func TestEncrypt(t *testing.T) {
|
||||
ciphers := []pkcs.Cipher{
|
||||
pkcs.DESCBC,
|
||||
pkcs.TripleDESCBC,
|
||||
pkcs.SM4CBC,
|
||||
pkcs.SM4GCM,
|
||||
pkcs.AES128CBC,
|
||||
pkcs.AES192CBC,
|
||||
pkcs.AES256CBC,
|
||||
pkcs.AES128GCM,
|
||||
pkcs.AES192GCM,
|
||||
pkcs.AES256GCM,
|
||||
}
|
||||
sigalgs := []x509.SignatureAlgorithm{
|
||||
x509.SHA1WithRSA,
|
||||
x509.SHA256WithRSA,
|
||||
x509.SHA512WithRSA,
|
||||
smx509.SM2WithSM3,
|
||||
}
|
||||
for _, cipher := range ciphers {
|
||||
for _, sigalg := range sigalgs {
|
||||
plaintext := []byte("Hello Secret World!")
|
||||
cert, err := createTestCertificate(sigalg)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
encrypted, err := Encrypt(cipher, plaintext, []*smx509.Certificate{cert.Certificate})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
p7, err := Parse(encrypted)
|
||||
if err != nil {
|
||||
t.Fatalf("cannot Parse encrypted result: %s", err)
|
||||
}
|
||||
result, err := p7.Decrypt(cert.Certificate, *cert.PrivateKey)
|
||||
if err != nil {
|
||||
t.Fatalf("cannot Decrypt encrypted result: %s", err)
|
||||
}
|
||||
if !bytes.Equal(plaintext, result) {
|
||||
t.Errorf("encrypted data does not match plaintext:\n\tExpected: %s\n\tActual: %s", plaintext, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncryptSM(t *testing.T) {
|
||||
ciphers := []pkcs.Cipher{
|
||||
pkcs.SM4CBC,
|
||||
pkcs.SM4GCM,
|
||||
}
|
||||
sigalgs := []x509.SignatureAlgorithm{
|
||||
smx509.SM2WithSM3,
|
||||
}
|
||||
for _, cipher := range ciphers {
|
||||
for _, sigalg := range sigalgs {
|
||||
plaintext := []byte("Hello Secret World!")
|
||||
cert, err := createTestCertificate(sigalg)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
encrypted, err := EncryptSM(cipher, plaintext, []*smx509.Certificate{cert.Certificate})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: encrypted})
|
||||
p7, err := Parse(encrypted)
|
||||
if err != nil {
|
||||
t.Fatalf("cannot Parse encrypted result: %s", err)
|
||||
}
|
||||
result, err := p7.Decrypt(cert.Certificate, *cert.PrivateKey)
|
||||
if err != nil {
|
||||
t.Fatalf("cannot Decrypt encrypted result: %s", err)
|
||||
}
|
||||
if !bytes.Equal(plaintext, result) {
|
||||
t.Errorf("encrypted data does not match plaintext:\n\tExpected: %s\n\tActual: %s", plaintext, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncryptUsingPSK(t *testing.T) {
|
||||
ciphers := []pkcs.Cipher{
|
||||
pkcs.DESCBC,
|
||||
pkcs.SM4GCM,
|
||||
pkcs.AES128GCM,
|
||||
}
|
||||
|
||||
for _, cipher := range ciphers {
|
||||
plaintext := []byte("Hello Secret World!")
|
||||
var key []byte
|
||||
|
||||
switch cipher.KeySize() {
|
||||
case 8:
|
||||
key = []byte("64BitKey")
|
||||
case 16:
|
||||
key = []byte("128BitKey4AESGCM")
|
||||
}
|
||||
ciphertext, err := EncryptUsingPSK(cipher, plaintext, key)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
p7, _ := Parse(ciphertext)
|
||||
result, err := p7.DecryptUsingPSK(key)
|
||||
if err != nil {
|
||||
t.Fatalf("cannot Decrypt encrypted result: %s", err)
|
||||
}
|
||||
if !bytes.Equal(plaintext, result) {
|
||||
t.Errorf("encrypted data does not match plaintext:\n\tExpected: %s\n\tActual: %s", plaintext, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncryptSMUsingPSK(t *testing.T) {
|
||||
ciphers := []pkcs.Cipher{
|
||||
pkcs.DESCBC,
|
||||
pkcs.SM4GCM,
|
||||
pkcs.AES128GCM,
|
||||
}
|
||||
|
||||
for _, cipher := range ciphers {
|
||||
plaintext := []byte("Hello Secret World!")
|
||||
var key []byte
|
||||
|
||||
switch cipher.KeySize() {
|
||||
case 8:
|
||||
key = []byte("64BitKey")
|
||||
case 16:
|
||||
key = []byte("128BitKey4AESGCM")
|
||||
}
|
||||
ciphertext, err := EncryptSMUsingPSK(cipher, plaintext, key)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
p7, _ := Parse(ciphertext)
|
||||
result, err := p7.DecryptUsingPSK(key)
|
||||
if err != nil {
|
||||
t.Fatalf("cannot Decrypt encrypted result: %s", err)
|
||||
}
|
||||
if !bytes.Equal(plaintext, result) {
|
||||
t.Errorf("encrypted data does not match plaintext:\n\tExpected: %s\n\tActual: %s", plaintext, result)
|
||||
}
|
||||
}
|
||||
}
|
45
pkcs7/gm_test.go
Normal file
45
pkcs7/gm_test.go
Normal file
@ -0,0 +1,45 @@
|
||||
package pkcs7
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// from https://www.gmssl.cn/gmssl/index.jsp, just have certificates in SignedData content
|
||||
var certificateChainGMSSL = `-----BEGIN PKCS7-----
|
||||
MIID6wYJKoZIhvcNAQcCoIID3DCCA9gCAQExADALBgkqhkiG9w0BBwGgggPAMIIB
|
||||
zTCCAXCgAwIBAgIGAXKnMKNyMAwGCCqBHM9VAYN1BQAwSTELMAkGA1UEBhMCQ04x
|
||||
DjAMBgNVBAoTBUdNU1NMMRAwDgYDVQQLEwdQS0kvU00yMRgwFgYDVQQDEw9Sb290
|
||||
Q0EgZm9yIFRlc3QwIhgPMjAxNTEyMzExNjAwMDBaGA8yMDM1MTIzMDE2MDAwMFow
|
||||
STELMAkGA1UEBhMCQ04xDjAMBgNVBAoTBUdNU1NMMRAwDgYDVQQLEwdQS0kvU00y
|
||||
MRgwFgYDVQQDEw9Sb290Q0EgZm9yIFRlc3QwWTATBgcqhkjOPQIBBggqgRzPVQGC
|
||||
LQNCAATj+apYlL+ddWXZ7+mFZXZJGbcJFXUN+Fszz6humeyWZP4qEEr2N0+aZdwo
|
||||
/21ft232yo0jPLzdscKB261zSQXSoz4wPDAZBgNVHQ4EEgQQnGnsD7oaOcWv6CTr
|
||||
spwSBDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIAxjAMBggqgRzPVQGD
|
||||
dQUAA0kAMEYCIQCEnW5BlQh0vmsOLxSoXYc/7zs++wWyFc1tnBHENR4ElwIhAI1L
|
||||
wu6in1ruflZhzseWulXwcITf3bm/Y5X1g1XFWQUHMIIB6zCCAY+gAwIBAgIGAXKn
|
||||
MMauMAwGCCqBHM9VAYN1BQAwSTELMAkGA1UEBhMCQ04xDjAMBgNVBAoTBUdNU1NM
|
||||
MRAwDgYDVQQLEwdQS0kvU00yMRgwFgYDVQQDEw9Sb290Q0EgZm9yIFRlc3QwIhgP
|
||||
MjAxNTEyMzExNjAwMDBaGA8yMDM1MTIzMDE2MDAwMFowSzELMAkGA1UEBhMCQ04x
|
||||
DjAMBgNVBAoTBUdNU1NMMRAwDgYDVQQLEwdQS0kvU00yMRowGAYDVQQDExFNaWRk
|
||||
bGVDQSBmb3IgVGVzdDBZMBMGByqGSM49AgEGCCqBHM9VAYItA0IABA4uB1fiqJjs
|
||||
1uR6bFIrtxvLFuoU0x+uPPxrslzodyTG1Mj9dJpm4AUjT9q2bL4cj7H73qWJNpwA
|
||||
rnZr7fCc3A2jWzBZMBsGA1UdIwQUMBKAEJxp7A+6GjnFr+gk67KcEgQwGQYDVR0O
|
||||
BBIEEPl/VbQnlDNiplbKb8xdGv8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E
|
||||
BAMCAMYwDAYIKoEcz1UBg3UFAANIADBFAiA31tn0qKz6G0YgGjWd6/ULMyqfTzoL
|
||||
82Y7EkvxbOpX/AIhAKCJYkDp62cvbKvj/Njc2dIe5BN+DGhO5JOhIyo4oWE3MQA=
|
||||
-----END PKCS7-----`
|
||||
|
||||
func TestParseSM2CertificateChain(t *testing.T) {
|
||||
fixture := UnmarshalTestFixture(certificateChainGMSSL)
|
||||
p7, err := Parse(fixture.Input)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(p7.Certificates) != 2 {
|
||||
t.Errorf("expected 2, but got %d", len(p7.Certificates))
|
||||
}
|
||||
err = p7.Certificates[1].CheckSignatureFrom(p7.Certificates[0])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
337
pkcs7/pkcs7.go
Normal file
337
pkcs7/pkcs7.go
Normal file
@ -0,0 +1,337 @@
|
||||
// Package pkcs7 implements parsing and generation of some PKCS#7 structures.
|
||||
package pkcs7
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/dsa"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
_ "crypto/sha1" // for crypto.SHA1
|
||||
|
||||
"github.com/emmansun/gmsm/sm2"
|
||||
"github.com/emmansun/gmsm/smx509"
|
||||
)
|
||||
|
||||
// PKCS7 Represents a PKCS7 structure
|
||||
type PKCS7 struct {
|
||||
Content []byte
|
||||
Certificates []*smx509.Certificate
|
||||
CRLs []pkix.CertificateList
|
||||
Signers []signerInfo
|
||||
raw interface{}
|
||||
}
|
||||
|
||||
type contentInfo struct {
|
||||
ContentType asn1.ObjectIdentifier
|
||||
Content asn1.RawValue `asn1:"explicit,optional,tag:0"`
|
||||
}
|
||||
|
||||
// ErrUnsupportedContentType is returned when a PKCS7 content is not supported.
|
||||
// Currently only Data (1.2.840.113549.1.7.1), Signed Data (1.2.840.113549.1.7.2),
|
||||
// and Enveloped Data are supported (1.2.840.113549.1.7.3)
|
||||
var ErrUnsupportedContentType = errors.New("pkcs7: cannot parse data: unimplemented content type")
|
||||
|
||||
type unsignedData []byte
|
||||
|
||||
var (
|
||||
// Signed Data OIDs
|
||||
OIDData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 1}
|
||||
OIDSignedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 2}
|
||||
OIDEnvelopedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 3}
|
||||
OIDSignedEnvelopedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 4}
|
||||
OIDDigestData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 5}
|
||||
OIDEncryptedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 6}
|
||||
OIDAttributeContentType = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 3}
|
||||
OIDAttributeMessageDigest = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 4}
|
||||
OIDAttributeSigningTime = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 5}
|
||||
|
||||
// Digest Algorithms
|
||||
OIDDigestAlgorithmSHA1 = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 26}
|
||||
OIDDigestAlgorithmSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 1}
|
||||
OIDDigestAlgorithmSHA384 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 2}
|
||||
OIDDigestAlgorithmSHA512 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 3}
|
||||
|
||||
OIDDigestAlgorithmDSA = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1}
|
||||
OIDDigestAlgorithmDSASHA1 = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3}
|
||||
|
||||
OIDDigestAlgorithmECDSASHA1 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1}
|
||||
OIDDigestAlgorithmECDSASHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2}
|
||||
OIDDigestAlgorithmECDSASHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3}
|
||||
OIDDigestAlgorithmECDSASHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4}
|
||||
|
||||
// Signature Algorithms
|
||||
OIDEncryptionAlgorithmRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
|
||||
OIDEncryptionAlgorithmRSASHA1 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5}
|
||||
OIDEncryptionAlgorithmRSASHA256 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11}
|
||||
OIDEncryptionAlgorithmRSASHA384 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12}
|
||||
OIDEncryptionAlgorithmRSASHA512 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13}
|
||||
|
||||
OIDEncryptionAlgorithmECDSAP256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 3, 1, 7}
|
||||
OIDEncryptionAlgorithmECDSAP384 = asn1.ObjectIdentifier{1, 3, 132, 0, 34}
|
||||
OIDEncryptionAlgorithmECDSAP521 = asn1.ObjectIdentifier{1, 3, 132, 0, 35}
|
||||
)
|
||||
|
||||
var (
|
||||
//SM2 Signed Data OIDs
|
||||
SM2OIDData = asn1.ObjectIdentifier{1, 2, 156, 10197, 6, 1, 4, 2, 1}
|
||||
SM2OIDSignedData = asn1.ObjectIdentifier{1, 2, 156, 10197, 6, 1, 4, 2, 2}
|
||||
SM2OIDEnvelopedData = asn1.ObjectIdentifier{1, 2, 156, 10197, 6, 1, 4, 2, 3}
|
||||
SM2OIDSignedEnvelopedData = asn1.ObjectIdentifier{1, 2, 156, 10197, 6, 1, 4, 2, 4}
|
||||
SM2OIDEncryptedData = asn1.ObjectIdentifier{1, 2, 156, 10197, 6, 1, 4, 2, 5}
|
||||
|
||||
// Digest Algorithms
|
||||
OIDDigestAlgorithmSM3 = asn1.ObjectIdentifier{1, 2, 156, 10197, 1, 401}
|
||||
// SM2Sign-with-SM3
|
||||
OIDDigestAlgorithmSM2SM3 = asn1.ObjectIdentifier{1, 2, 156, 10197, 1, 501}
|
||||
// Signature Algorithms SM2-1
|
||||
OIDDigestEncryptionAlgorithmSM2 = asn1.ObjectIdentifier{1, 2, 156, 10197, 1, 301, 1}
|
||||
|
||||
// Encryption Algorithms SM2-3
|
||||
OIDKeyEncryptionAlgorithmSM2 = asn1.ObjectIdentifier{1, 2, 156, 10197, 1, 301, 3}
|
||||
|
||||
//SM9 Signed Data OIDs
|
||||
SM9OIDData = asn1.ObjectIdentifier{1, 2, 156, 10197, 6, 1, 4, 4, 1}
|
||||
SM9OIDSignedData = asn1.ObjectIdentifier{1, 2, 156, 10197, 6, 1, 4, 4, 2}
|
||||
SM9OIDEnvelopedData = asn1.ObjectIdentifier{1, 2, 156, 10197, 6, 1, 4, 4, 3}
|
||||
SM9OIDSignedEnvelopedData = asn1.ObjectIdentifier{1, 2, 156, 10197, 6, 1, 4, 4, 4}
|
||||
SM9OIDEncryptedData = asn1.ObjectIdentifier{1, 2, 156, 10197, 6, 1, 4, 4, 5}
|
||||
|
||||
// SM9Sign-with-SM3
|
||||
OIDDigestAlgorithmSM9SM3 = asn1.ObjectIdentifier{1, 2, 156, 10197, 1, 502}
|
||||
|
||||
// Signature Algorithms SM9-1
|
||||
OIDDigestEncryptionAlgorithmSM9 = asn1.ObjectIdentifier{1, 2, 156, 10197, 1, 302, 1}
|
||||
|
||||
// Encryption Algorithms SM9-3
|
||||
OIDKeyEncryptionAlgorithmSM9 = asn1.ObjectIdentifier{1, 2, 156, 10197, 1, 302, 3}
|
||||
)
|
||||
|
||||
func getHashForOID(oid asn1.ObjectIdentifier) (crypto.Hash, error) {
|
||||
switch {
|
||||
case oid.Equal(OIDDigestAlgorithmSHA1), oid.Equal(OIDDigestAlgorithmECDSASHA1),
|
||||
oid.Equal(OIDDigestAlgorithmDSA), oid.Equal(OIDDigestAlgorithmDSASHA1),
|
||||
oid.Equal(OIDEncryptionAlgorithmRSA):
|
||||
return crypto.SHA1, nil
|
||||
case oid.Equal(OIDDigestAlgorithmSHA256), oid.Equal(OIDDigestAlgorithmECDSASHA256):
|
||||
return crypto.SHA256, nil
|
||||
case oid.Equal(OIDDigestAlgorithmSHA384), oid.Equal(OIDDigestAlgorithmECDSASHA384):
|
||||
return crypto.SHA384, nil
|
||||
case oid.Equal(OIDDigestAlgorithmSHA512), oid.Equal(OIDDigestAlgorithmECDSASHA512):
|
||||
return crypto.SHA512, nil
|
||||
case oid.Equal(OIDDigestAlgorithmSM3), oid.Equal(OIDDigestAlgorithmSM2SM3):
|
||||
return crypto.Hash(0), nil
|
||||
}
|
||||
return crypto.Hash(0), fmt.Errorf("pkcs7: cannot get hash from oid %v", oid)
|
||||
}
|
||||
|
||||
// getDigestOIDForSignatureAlgorithm takes an x509.SignatureAlgorithm
|
||||
// and returns the corresponding OID digest algorithm
|
||||
func getDigestOIDForSignatureAlgorithm(digestAlg x509.SignatureAlgorithm) (asn1.ObjectIdentifier, error) {
|
||||
switch digestAlg {
|
||||
case x509.SHA1WithRSA, x509.ECDSAWithSHA1:
|
||||
return OIDDigestAlgorithmSHA1, nil
|
||||
case x509.SHA256WithRSA, x509.ECDSAWithSHA256:
|
||||
return OIDDigestAlgorithmSHA256, nil
|
||||
case x509.SHA384WithRSA, x509.ECDSAWithSHA384:
|
||||
return OIDDigestAlgorithmSHA384, nil
|
||||
case x509.SHA512WithRSA, x509.ECDSAWithSHA512:
|
||||
return OIDDigestAlgorithmSHA512, nil
|
||||
case smx509.SM2WithSM3:
|
||||
return OIDDigestAlgorithmSM3, nil
|
||||
}
|
||||
return nil, fmt.Errorf("pkcs7: cannot convert hash to oid, unknown hash algorithm")
|
||||
}
|
||||
|
||||
// getOIDForEncryptionAlgorithm takes the public or private key type of the signer and
|
||||
// the OID of a digest algorithm to return the appropriate signerInfo.DigestEncryptionAlgorithm
|
||||
func getOIDForEncryptionAlgorithm(pkey interface{}, OIDDigestAlg asn1.ObjectIdentifier) (asn1.ObjectIdentifier, error) {
|
||||
switch k := pkey.(type) {
|
||||
case *rsa.PrivateKey, *rsa.PublicKey:
|
||||
switch {
|
||||
default:
|
||||
return OIDEncryptionAlgorithmRSA, nil
|
||||
case OIDDigestAlg.Equal(OIDEncryptionAlgorithmRSA):
|
||||
return OIDEncryptionAlgorithmRSA, nil
|
||||
case OIDDigestAlg.Equal(OIDDigestAlgorithmSHA1):
|
||||
return OIDEncryptionAlgorithmRSASHA1, nil
|
||||
case OIDDigestAlg.Equal(OIDDigestAlgorithmSHA256):
|
||||
return OIDEncryptionAlgorithmRSASHA256, nil
|
||||
case OIDDigestAlg.Equal(OIDDigestAlgorithmSHA384):
|
||||
return OIDEncryptionAlgorithmRSASHA384, nil
|
||||
case OIDDigestAlg.Equal(OIDDigestAlgorithmSHA512):
|
||||
return OIDEncryptionAlgorithmRSASHA512, nil
|
||||
}
|
||||
case *ecdsa.PrivateKey, *ecdsa.PublicKey:
|
||||
switch {
|
||||
case OIDDigestAlg.Equal(OIDDigestAlgorithmSHA1):
|
||||
return OIDDigestAlgorithmECDSASHA1, nil
|
||||
case OIDDigestAlg.Equal(OIDDigestAlgorithmSHA256):
|
||||
return OIDDigestAlgorithmECDSASHA256, nil
|
||||
case OIDDigestAlg.Equal(OIDDigestAlgorithmSHA384):
|
||||
return OIDDigestAlgorithmECDSASHA384, nil
|
||||
case OIDDigestAlg.Equal(OIDDigestAlgorithmSHA512):
|
||||
return OIDDigestAlgorithmECDSASHA512, nil
|
||||
case OIDDigestAlg.Equal(OIDDigestAlgorithmSM3):
|
||||
// Do we need further checking?
|
||||
return OIDDigestEncryptionAlgorithmSM2, nil
|
||||
}
|
||||
case *sm2.PrivateKey:
|
||||
return OIDDigestEncryptionAlgorithmSM2, nil
|
||||
case *dsa.PrivateKey, *dsa.PublicKey:
|
||||
return OIDDigestAlgorithmDSA, nil
|
||||
case crypto.Signer:
|
||||
return getOIDForEncryptionAlgorithm(k.Public(), OIDDigestAlg)
|
||||
}
|
||||
return nil, fmt.Errorf("pkcs7: cannot convert encryption algorithm to oid, unknown private key type %T", pkey)
|
||||
|
||||
}
|
||||
|
||||
// Parse decodes a DER encoded PKCS7 package
|
||||
func Parse(data []byte) (p7 *PKCS7, err error) {
|
||||
if len(data) == 0 {
|
||||
return nil, errors.New("pkcs7: input data is empty")
|
||||
}
|
||||
var info contentInfo
|
||||
der, err := ber2der(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rest, err := asn1.Unmarshal(der, &info)
|
||||
if len(rest) > 0 {
|
||||
err = asn1.SyntaxError{Msg: "trailing data"}
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// fmt.Printf("--> Content Type: %s", info.ContentType)
|
||||
switch {
|
||||
case info.ContentType.Equal(OIDSignedData) || info.ContentType.Equal(SM2OIDSignedData):
|
||||
return parseSignedData(info.Content.Bytes)
|
||||
case info.ContentType.Equal(OIDEnvelopedData) || info.ContentType.Equal(SM2OIDEnvelopedData):
|
||||
return parseEnvelopedData(info.Content.Bytes)
|
||||
case info.ContentType.Equal(OIDEncryptedData) || info.ContentType.Equal(SM2OIDEncryptedData):
|
||||
return parseEncryptedData(info.Content.Bytes)
|
||||
case info.ContentType.Equal(OIDSignedEnvelopedData) || info.ContentType.Equal(SM2OIDSignedEnvelopedData):
|
||||
return parseSignedEnvelopedData(info.Content.Bytes)
|
||||
default:
|
||||
return nil, ErrUnsupportedContentType
|
||||
}
|
||||
}
|
||||
|
||||
func parseEnvelopedData(data []byte) (*PKCS7, error) {
|
||||
var ed envelopedData
|
||||
if _, err := asn1.Unmarshal(data, &ed); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &PKCS7{
|
||||
raw: ed,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseEncryptedData(data []byte) (*PKCS7, error) {
|
||||
var ed encryptedData
|
||||
if _, err := asn1.Unmarshal(data, &ed); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &PKCS7{
|
||||
raw: ed,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (raw rawCertificates) Parse() ([]*smx509.Certificate, error) {
|
||||
if len(raw.Raw) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var val asn1.RawValue
|
||||
if _, err := asn1.Unmarshal(raw.Raw, &val); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return smx509.ParseCertificates(val.Bytes)
|
||||
}
|
||||
|
||||
func isCertMatchForIssuerAndSerial(cert *smx509.Certificate, ias issuerAndSerial) bool {
|
||||
return cert.SerialNumber.Cmp(ias.SerialNumber) == 0 && bytes.Equal(cert.RawIssuer, ias.IssuerName.FullBytes)
|
||||
}
|
||||
|
||||
// Attribute represents a key value pair attribute. Value must be marshalable byte
|
||||
// `encoding/asn1`
|
||||
type Attribute struct {
|
||||
Type asn1.ObjectIdentifier
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
type attributes struct {
|
||||
types []asn1.ObjectIdentifier
|
||||
values []interface{}
|
||||
}
|
||||
|
||||
// Add adds the attribute, maintaining insertion order
|
||||
func (attrs *attributes) Add(attrType asn1.ObjectIdentifier, value interface{}) {
|
||||
attrs.types = append(attrs.types, attrType)
|
||||
attrs.values = append(attrs.values, value)
|
||||
}
|
||||
|
||||
type sortableAttribute struct {
|
||||
SortKey []byte
|
||||
Attribute attribute
|
||||
}
|
||||
|
||||
type attributeSet []sortableAttribute
|
||||
|
||||
func (sa attributeSet) Len() int {
|
||||
return len(sa)
|
||||
}
|
||||
|
||||
func (sa attributeSet) Less(i, j int) bool {
|
||||
return bytes.Compare(sa[i].SortKey, sa[j].SortKey) < 0
|
||||
}
|
||||
|
||||
func (sa attributeSet) Swap(i, j int) {
|
||||
sa[i], sa[j] = sa[j], sa[i]
|
||||
}
|
||||
|
||||
func (sa attributeSet) Attributes() []attribute {
|
||||
attrs := make([]attribute, len(sa))
|
||||
for i, attr := range sa {
|
||||
attrs[i] = attr.Attribute
|
||||
}
|
||||
return attrs
|
||||
}
|
||||
|
||||
func (attrs *attributes) ForMarshalling() ([]attribute, error) {
|
||||
sortables := make(attributeSet, len(attrs.types))
|
||||
for i := range sortables {
|
||||
attrType := attrs.types[i]
|
||||
attrValue := attrs.values[i]
|
||||
asn1Value, err := asn1.Marshal(attrValue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
attr := attribute{
|
||||
Type: attrType,
|
||||
Value: asn1.RawValue{Tag: 17, IsCompound: true, Bytes: asn1Value}, // 17 == SET tag
|
||||
}
|
||||
encoded, err := asn1.Marshal(attr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sortables[i] = sortableAttribute{
|
||||
SortKey: encoded,
|
||||
Attribute: attr,
|
||||
}
|
||||
}
|
||||
sort.Sort(sortables)
|
||||
return sortables.Attributes(), nil
|
||||
}
|
319
pkcs7/pkcs7_test.go
Normal file
319
pkcs7/pkcs7_test.go
Normal file
@ -0,0 +1,319 @@
|
||||
package pkcs7
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/dsa"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"log"
|
||||
"math/big"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/emmansun/gmsm/sm2"
|
||||
"github.com/emmansun/gmsm/smx509"
|
||||
)
|
||||
|
||||
var test1024Key, test2048Key, test3072Key, test4096Key *rsa.PrivateKey
|
||||
|
||||
func init() {
|
||||
test1024Key = &rsa.PrivateKey{
|
||||
PublicKey: rsa.PublicKey{
|
||||
N: fromBase10("123024078101403810516614073341068864574068590522569345017786163424062310013967742924377390210586226651760719671658568413826602264886073432535341149584680111145880576802262550990305759285883150470245429547886689754596541046564560506544976611114898883158121012232676781340602508151730773214407220733898059285561"),
|
||||
E: 65537,
|
||||
},
|
||||
D: fromBase10("118892427340746627750435157989073921703209000249285930635312944544706203626114423392257295670807166199489096863209592887347935991101581502404113203993092422730000157893515953622392722273095289787303943046491132467130346663160540744582438810535626328230098940583296878135092036661410664695896115177534496784545"),
|
||||
Primes: []*big.Int{
|
||||
fromBase10("12172745919282672373981903347443034348576729562395784527365032103134165674508405592530417723266847908118361582847315228810176708212888860333051929276459099"),
|
||||
fromBase10("10106518193772789699356660087736308350857919389391620140340519320928952625438936098550728858345355053201610649202713962702543058578827268756755006576249339"),
|
||||
},
|
||||
}
|
||||
test1024Key.Precompute()
|
||||
test2048Key = &rsa.PrivateKey{
|
||||
PublicKey: rsa.PublicKey{
|
||||
N: fromBase10("14314132931241006650998084889274020608918049032671858325988396851334124245188214251956198731333464217832226406088020736932173064754214329009979944037640912127943488972644697423190955557435910767690712778463524983667852819010259499695177313115447116110358524558307947613422897787329221478860907963827160223559690523660574329011927531289655711860504630573766609239332569210831325633840174683944553667352219670930408593321661375473885147973879086994006440025257225431977751512374815915392249179976902953721486040787792801849818254465486633791826766873076617116727073077821584676715609985777563958286637185868165868520557"),
|
||||
E: 3,
|
||||
},
|
||||
D: fromBase10("9542755287494004433998723259516013739278699355114572217325597900889416163458809501304132487555642811888150937392013824621448709836142886006653296025093941418628992648429798282127303704957273845127141852309016655778568546006839666463451542076964744073572349705538631742281931858219480985907271975884773482372966847639853897890615456605598071088189838676728836833012254065983259638538107719766738032720239892094196108713378822882383694456030043492571063441943847195939549773271694647657549658603365629458610273821292232646334717612674519997533901052790334279661754176490593041941863932308687197618671528035670452762731"),
|
||||
Primes: []*big.Int{
|
||||
fromBase10("130903255182996722426771613606077755295583329135067340152947172868415809027537376306193179624298874215608270802054347609836776473930072411958753044562214537013874103802006369634761074377213995983876788718033850153719421695468704276694983032644416930879093914927146648402139231293035971427838068945045019075433"),
|
||||
fromBase10("109348945610485453577574767652527472924289229538286649661240938988020367005475727988253438647560958573506159449538793540472829815903949343191091817779240101054552748665267574271163617694640513549693841337820602726596756351006149518830932261246698766355347898158548465400674856021497190430791824869615170301029"),
|
||||
},
|
||||
}
|
||||
test2048Key.Precompute()
|
||||
test3072Key = &rsa.PrivateKey{
|
||||
PublicKey: rsa.PublicKey{
|
||||
N: fromBase10("4799422180968749215324244710281712119910779465109490663934897082847293004098645365195947978124390029272750644394844443980065532911010718425428791498896288210928474905407341584968381379157418577471272697781778686372450913810019702928839200328075568223462554606149618941566459398862673532997592879359280754226882565483298027678735544377401276021471356093819491755877827249763065753555051973844057308627201762456191918852016986546071426986328720794061622370410645440235373576002278045257207695462423797272017386006110722769072206022723167102083033531426777518054025826800254337147514768377949097720074878744769255210076910190151785807232805749219196645305822228090875616900385866236956058984170647782567907618713309775105943700661530312800231153745705977436176908325539234432407050398510090070342851489496464612052853185583222422124535243967989533830816012180864309784486694786581956050902756173889941244024888811572094961378021"),
|
||||
E: 65537,
|
||||
},
|
||||
D: fromBase10("4068124900056380177006532461065648259352178312499768312132802353620854992915205894105621345694615110794369150964768050224096623567443679436821868510233726084582567244003894477723706516831312989564775159596496449435830457803384416702014837685962523313266832032687145914871879794104404800823188153886925022171560391765913739346955738372354826804228989767120353182641396181570533678315099748218734875742705419933837638038793286534641711407564379950728858267828581787483317040753987167237461567332386718574803231955771633274184646232632371006762852623964054645811527580417392163873708539175349637050049959954373319861427407953413018816604365474462455009323937599275324390953644555294418021286807661559165324810415569396577697316798600308544755741549699523972971375304826663847015905713096287495342701286542193782001358775773848824496321550110946106870685499577993864871847542645561943034990484973293461948058147956373115641615329"),
|
||||
Primes: []*big.Int{
|
||||
fromBase10("2378529069722721185825622840841310902793949682948530343491428052737890236476884657507685118578733560141370511507721598189068683665232991988491561624429938984370132428230072355214627085652359350722926394699707232921674771664421591347888367477300909202851476404132163673865768760147403525700174918450753162242834161458300343282159799476695001920226357456953682236859505243928716782707623075239350380352265954107362618991716602898266999700316937680986690964564264877"),
|
||||
fromBase10("2017811025336026464312837780072272578817919741496395062543647660689775637351085991504709917848745137013798005682591633910555599626950744674459976829106750083386168859581016361317479081273480343110649405858059581933773354781034946787147300862495438979895430001323443224335618577322449133208754541656374335100929456885995320929464029817626916719434010943205170760536768893924932021302887114400922813817969176636993508191950649313115712159241971065134077636674146073"),
|
||||
},
|
||||
}
|
||||
test3072Key.Precompute()
|
||||
test4096Key = &rsa.PrivateKey{
|
||||
PublicKey: rsa.PublicKey{
|
||||
N: fromBase10("633335480064287130853997429184971616419051348693342219741748040433588285601270210251206421401040394238592139790962887290698043839174341843721930134010306454716566698330215646704263665452264344664385995704186692432827662862845900348526672531755932642433662686500295989783595767573119607065791980381547677840410600100715146047382485989885183858757974681241303484641390718944520330953604501686666386926996348457928415093305041429178744778762826377713889019740060910363468343855830206640274442887621960581569183233822878661711798998132931623726434336448716605363514220760343097572198620479297583609779817750646169845195672483600293522186340560792255595411601450766002877850696008003794520089358819042318331840490155176019070646738739580486357084733208876620846449161909966690602374519398451042362690200166144326179405976024265116931974936425064291406950542193873313447617169603706868220189295654943247311295475722243471700112334609817776430552541319671117235957754556272646031356496763094955985615723596562217985372503002989591679252640940571608314743271809251568670314461039035793703429977801961867815257832671786542212589906513979094156334941265621017752516999186481477500481433634914622735206243841674973785078408289183000133399026553"),
|
||||
E: 65537,
|
||||
},
|
||||
D: fromBase10("439373650557744155078930178606343279553665694488479749802070836418412881168612407941793966086633543867614175621952769177088930851151267623886678906158545451731745754402575409204816390946376103491325109185445659065122640946673660760274557781540431107937331701243915001777636528502669576801704352961341634812275635811512806966908648671988644114352046582195051714797831307925775689566757438907578527366568747104508496278929566712224252103563340770696548181508180254674236716995730292431858611476396845443056967589437890065663497768422598977743046882539288481002449571403783500529740184608873520856954837631427724158592309018382711485601884461168736465751756282510065053161144027097169985941910909130083273691945578478173708396726266170473745329617793866669307716920992380350270584929908460462802627239204245339385636926433446418108504614031393494119344916828744888432279343816084433424594432427362258172264834429525166677273382617457205387388293888430391895615438030066428745187333897518037597413369705720436392869403948934993623418405908467147848576977008003556716087129242155836114780890054057743164411952731290520995017097151300091841286806603044227906213832083363876549637037625314539090155417589796428888619937329669464810549362433"),
|
||||
Primes: []*big.Int{
|
||||
fromBase10("25745433817240673759910623230144796182285844101796353869339294232644316274580053211056707671663014355388701931204078502829809738396303142990312095225333440050808647355535878394534263839500592870406002873182360027755750148248672968563366185348499498613479490545488025779331426515670185366021612402246813511722553210128074701620113404560399242413747318161403908617342170447610792422053460359960010544593668037305465806912471260799852789913123044326555978680190904164976511331681163576833618899773550873682147782263100803907156362439021929408298804955194748640633152519828940133338948391986823456836070708197320166146761"),
|
||||
fromBase10("24599914864909676687852658457515103765368967514652318497893275892114442089314173678877914038802355565271545910572804267918959612739009937926962653912943833939518967731764560204997062096919833970670512726396663920955497151415639902788974842698619579886297871162402643104696160155894685518587660015182381685605752989716946154299190561137541792784125356553411300817844325739404126956793095254412123887617931225840421856505925283322918693259047428656823141903489964287619982295891439430302405252447010728112098326033634688757933930065610737780413018498561434074501822951716586796047404555397992425143397497639322075233073"),
|
||||
},
|
||||
}
|
||||
test4096Key.Precompute()
|
||||
}
|
||||
|
||||
func fromBase10(base10 string) *big.Int {
|
||||
i, ok := new(big.Int).SetString(base10, 10)
|
||||
if !ok {
|
||||
panic("bad number: " + base10)
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
type certKeyPair struct {
|
||||
Certificate *smx509.Certificate
|
||||
PrivateKey *crypto.PrivateKey
|
||||
}
|
||||
|
||||
func createTestCertificate(sigAlg x509.SignatureAlgorithm) (certKeyPair, error) {
|
||||
signer, err := createTestCertificateByIssuer("Eddard Stark", nil, sigAlg, true)
|
||||
if err != nil {
|
||||
return certKeyPair{}, err
|
||||
}
|
||||
pair, err := createTestCertificateByIssuer("Jon Snow", signer, sigAlg, false)
|
||||
if err != nil {
|
||||
return certKeyPair{}, err
|
||||
}
|
||||
return *pair, nil
|
||||
}
|
||||
|
||||
func createTestCertificateByIssuer(name string, issuer *certKeyPair, sigAlg x509.SignatureAlgorithm, isCA bool) (*certKeyPair, error) {
|
||||
var (
|
||||
err error
|
||||
priv crypto.PrivateKey
|
||||
derCert []byte
|
||||
issuerCert *smx509.Certificate
|
||||
issuerKey crypto.PrivateKey
|
||||
)
|
||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 32)
|
||||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
template := x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
Subject: pkix.Name{
|
||||
CommonName: name,
|
||||
Organization: []string{"Acme Co"},
|
||||
},
|
||||
NotBefore: time.Now().Add(-1 * time.Second),
|
||||
NotAfter: time.Now().AddDate(1, 0, 0),
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageEmailProtection},
|
||||
}
|
||||
if issuer != nil {
|
||||
issuerCert = issuer.Certificate
|
||||
issuerKey = *issuer.PrivateKey
|
||||
}
|
||||
|
||||
switch sigAlg {
|
||||
case x509.SHA1WithRSA:
|
||||
priv = test1024Key
|
||||
switch issuerKey.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
template.SignatureAlgorithm = x509.SHA1WithRSA
|
||||
case *ecdsa.PrivateKey:
|
||||
template.SignatureAlgorithm = x509.ECDSAWithSHA1
|
||||
case *dsa.PrivateKey:
|
||||
template.SignatureAlgorithm = x509.DSAWithSHA1
|
||||
}
|
||||
case x509.SHA256WithRSA:
|
||||
priv = test2048Key
|
||||
switch issuerKey.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
template.SignatureAlgorithm = x509.SHA256WithRSA
|
||||
case *ecdsa.PrivateKey:
|
||||
template.SignatureAlgorithm = x509.ECDSAWithSHA256
|
||||
case *dsa.PrivateKey:
|
||||
template.SignatureAlgorithm = x509.DSAWithSHA256
|
||||
}
|
||||
case x509.SHA384WithRSA:
|
||||
priv = test3072Key
|
||||
|
||||
switch issuerKey.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
template.SignatureAlgorithm = x509.SHA384WithRSA
|
||||
case *ecdsa.PrivateKey:
|
||||
template.SignatureAlgorithm = x509.ECDSAWithSHA384
|
||||
case *dsa.PrivateKey:
|
||||
template.SignatureAlgorithm = x509.DSAWithSHA256
|
||||
}
|
||||
case x509.SHA512WithRSA:
|
||||
priv = test4096Key
|
||||
switch issuerKey.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
template.SignatureAlgorithm = x509.SHA512WithRSA
|
||||
case *ecdsa.PrivateKey:
|
||||
template.SignatureAlgorithm = x509.ECDSAWithSHA512
|
||||
case *dsa.PrivateKey:
|
||||
template.SignatureAlgorithm = x509.DSAWithSHA256
|
||||
}
|
||||
case x509.ECDSAWithSHA1:
|
||||
priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch issuerKey.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
template.SignatureAlgorithm = x509.SHA1WithRSA
|
||||
case *ecdsa.PrivateKey:
|
||||
template.SignatureAlgorithm = x509.ECDSAWithSHA1
|
||||
case *dsa.PrivateKey:
|
||||
template.SignatureAlgorithm = x509.DSAWithSHA1
|
||||
}
|
||||
case x509.ECDSAWithSHA256:
|
||||
priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch issuerKey.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
template.SignatureAlgorithm = x509.SHA256WithRSA
|
||||
case *ecdsa.PrivateKey:
|
||||
template.SignatureAlgorithm = x509.ECDSAWithSHA256
|
||||
case *dsa.PrivateKey:
|
||||
template.SignatureAlgorithm = x509.DSAWithSHA256
|
||||
}
|
||||
case x509.ECDSAWithSHA384:
|
||||
priv, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch issuerKey.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
template.SignatureAlgorithm = x509.SHA384WithRSA
|
||||
case *ecdsa.PrivateKey:
|
||||
template.SignatureAlgorithm = x509.ECDSAWithSHA384
|
||||
case *dsa.PrivateKey:
|
||||
template.SignatureAlgorithm = x509.DSAWithSHA256
|
||||
}
|
||||
case x509.ECDSAWithSHA512:
|
||||
priv, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch issuerKey.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
template.SignatureAlgorithm = x509.SHA512WithRSA
|
||||
case *ecdsa.PrivateKey:
|
||||
template.SignatureAlgorithm = x509.ECDSAWithSHA512
|
||||
case *dsa.PrivateKey:
|
||||
template.SignatureAlgorithm = x509.DSAWithSHA256
|
||||
}
|
||||
case smx509.SM2WithSM3:
|
||||
priv, err = sm2.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
case x509.DSAWithSHA1:
|
||||
var dsaPriv dsa.PrivateKey
|
||||
params := &dsaPriv.Parameters
|
||||
err = dsa.GenerateParameters(params, rand.Reader, dsa.L1024N160)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = dsa.GenerateKey(&dsaPriv, rand.Reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch issuerKey.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
template.SignatureAlgorithm = x509.SHA1WithRSA
|
||||
case *ecdsa.PrivateKey:
|
||||
template.SignatureAlgorithm = x509.ECDSAWithSHA1
|
||||
case *dsa.PrivateKey:
|
||||
template.SignatureAlgorithm = x509.DSAWithSHA1
|
||||
}
|
||||
priv = &dsaPriv
|
||||
}
|
||||
if isCA {
|
||||
template.IsCA = true
|
||||
template.KeyUsage |= x509.KeyUsageCertSign
|
||||
template.BasicConstraintsValid = true
|
||||
}
|
||||
if issuer == nil {
|
||||
// no issuer given,make this a self-signed root cert
|
||||
issuerCert = (*smx509.Certificate)(&template)
|
||||
issuerKey = priv
|
||||
}
|
||||
|
||||
log.Println("creating cert", name, "issued by", issuerCert.Subject.CommonName, "with sigalg", sigAlg)
|
||||
switch pkey := priv.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
derCert, err = smx509.CreateCertificate(rand.Reader, &template, (*x509.Certificate)(issuerCert), pkey.Public(), issuerKey)
|
||||
case *ecdsa.PrivateKey:
|
||||
derCert, err = smx509.CreateCertificate(rand.Reader, &template, (*x509.Certificate)(issuerCert), pkey.Public(), issuerKey)
|
||||
case *sm2.PrivateKey:
|
||||
derCert, err = smx509.CreateCertificate(rand.Reader, &template, (*x509.Certificate)(issuerCert), pkey.Public(), issuerKey)
|
||||
case *dsa.PrivateKey:
|
||||
derCert, err = smx509.CreateCertificate(rand.Reader, &template, (*x509.Certificate)(issuerCert), priv.(*dsa.PublicKey), issuerKey)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(derCert) == 0 {
|
||||
return nil, fmt.Errorf("no certificate created, probably due to wrong keys. types were %T and %T", priv, issuerKey)
|
||||
}
|
||||
cert, err := smx509.ParseCertificate(derCert)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pem.Encode(os.Stdout, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw})
|
||||
return &certKeyPair{
|
||||
Certificate: cert,
|
||||
PrivateKey: &priv,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type TestFixture struct {
|
||||
Input []byte
|
||||
Certificate *smx509.Certificate
|
||||
PrivateKey *rsa.PrivateKey
|
||||
}
|
||||
|
||||
func UnmarshalTestFixture(testPEMBlock string) TestFixture {
|
||||
var result TestFixture
|
||||
var derBlock *pem.Block
|
||||
var pemBlock = []byte(testPEMBlock)
|
||||
for {
|
||||
derBlock, pemBlock = pem.Decode(pemBlock)
|
||||
if derBlock == nil {
|
||||
break
|
||||
}
|
||||
switch derBlock.Type {
|
||||
case "PKCS7":
|
||||
result.Input = derBlock.Bytes
|
||||
case "CERTIFICATE":
|
||||
result.Certificate, _ = smx509.ParseCertificate(derBlock.Bytes)
|
||||
case "PRIVATE KEY":
|
||||
result.PrivateKey, _ = x509.ParsePKCS1PrivateKey(derBlock.Bytes)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
474
pkcs7/sign.go
Normal file
474
pkcs7/sign.go
Normal file
@ -0,0 +1,474 @@
|
||||
package pkcs7
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/dsa"
|
||||
"crypto/rand"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/emmansun/gmsm/sm2"
|
||||
"github.com/emmansun/gmsm/sm3"
|
||||
"github.com/emmansun/gmsm/smx509"
|
||||
)
|
||||
|
||||
// SignedData is an opaque data structure for creating signed data payloads
|
||||
type SignedData struct {
|
||||
sd signedData
|
||||
certs []*smx509.Certificate
|
||||
data, messageDigest []byte
|
||||
digestOid asn1.ObjectIdentifier
|
||||
encryptionOid asn1.ObjectIdentifier
|
||||
isSM bool
|
||||
}
|
||||
|
||||
// NewSignedData takes data and initializes a PKCS7 SignedData struct that is
|
||||
// ready to be signed via AddSigner. The digest algorithm is set to SHA1 by default
|
||||
// and can be changed by calling SetDigestAlgorithm.
|
||||
func NewSignedData(data []byte) (*SignedData, error) {
|
||||
content, err := asn1.Marshal(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ci := contentInfo{
|
||||
ContentType: OIDData,
|
||||
Content: asn1.RawValue{Class: 2, Tag: 0, Bytes: content, IsCompound: true},
|
||||
}
|
||||
sd := signedData{
|
||||
ContentInfo: ci,
|
||||
Version: 1,
|
||||
}
|
||||
return &SignedData{sd: sd, data: data, digestOid: OIDDigestAlgorithmSHA1, isSM: false}, nil
|
||||
}
|
||||
|
||||
// NewSMSignedData takes data and initializes a PKCS7 SignedData struct that is
|
||||
// ready to be signed via AddSigner. The digest algorithm is set to SM3 by default
|
||||
// and can be changed by calling SetDigestAlgorithm.
|
||||
func NewSMSignedData(data []byte) (*SignedData, error) {
|
||||
sd, err := NewSignedData(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sd.sd.ContentInfo.ContentType = SM2OIDData
|
||||
sd.digestOid = OIDDigestAlgorithmSM3
|
||||
sd.isSM = true
|
||||
return sd, nil
|
||||
}
|
||||
|
||||
// SignerInfoConfig are optional values to include when adding a signer
|
||||
type SignerInfoConfig struct {
|
||||
ExtraSignedAttributes []Attribute
|
||||
ExtraUnsignedAttributes []Attribute
|
||||
SkipCertificates bool
|
||||
}
|
||||
|
||||
type signedData struct {
|
||||
Version int `asn1:"default:1"`
|
||||
DigestAlgorithmIdentifiers []pkix.AlgorithmIdentifier `asn1:"set"`
|
||||
ContentInfo contentInfo
|
||||
Certificates rawCertificates `asn1:"optional,tag:0"`
|
||||
CRLs []pkix.CertificateList `asn1:"optional,tag:1"`
|
||||
SignerInfos []signerInfo `asn1:"set"`
|
||||
}
|
||||
|
||||
type signerInfo struct {
|
||||
Version int `asn1:"default:1"`
|
||||
IssuerAndSerialNumber issuerAndSerial
|
||||
DigestAlgorithm pkix.AlgorithmIdentifier
|
||||
AuthenticatedAttributes []attribute `asn1:"optional,omitempty,tag:0"`
|
||||
DigestEncryptionAlgorithm pkix.AlgorithmIdentifier
|
||||
EncryptedDigest []byte
|
||||
UnauthenticatedAttributes []attribute `asn1:"optional,omitempty,tag:1"`
|
||||
}
|
||||
|
||||
type attribute struct {
|
||||
Type asn1.ObjectIdentifier
|
||||
Value asn1.RawValue `asn1:"set"`
|
||||
}
|
||||
|
||||
func marshalAttributes(attrs []attribute) ([]byte, error) {
|
||||
encodedAttributes, err := asn1.Marshal(struct {
|
||||
A []attribute `asn1:"set"`
|
||||
}{A: attrs})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Remove the leading sequence octets
|
||||
var raw asn1.RawValue
|
||||
asn1.Unmarshal(encodedAttributes, &raw)
|
||||
return raw.Bytes, nil
|
||||
}
|
||||
|
||||
type rawCertificates struct {
|
||||
Raw asn1.RawContent
|
||||
}
|
||||
|
||||
type issuerAndSerial struct {
|
||||
IssuerName asn1.RawValue
|
||||
SerialNumber *big.Int
|
||||
}
|
||||
|
||||
// SetDigestAlgorithm sets the digest algorithm to be used in the signing process.
|
||||
//
|
||||
// This should be called before adding signers
|
||||
func (sd *SignedData) SetDigestAlgorithm(d asn1.ObjectIdentifier) {
|
||||
sd.digestOid = d
|
||||
}
|
||||
|
||||
// SetEncryptionAlgorithm sets the encryption algorithm to be used in the signing process.
|
||||
//
|
||||
// This should be called before adding signers
|
||||
func (sd *SignedData) SetEncryptionAlgorithm(d asn1.ObjectIdentifier) {
|
||||
sd.encryptionOid = d
|
||||
}
|
||||
|
||||
// AddSigner is a wrapper around AddSignerChain() that adds a signer without any parent.
|
||||
func (sd *SignedData) AddSigner(ee *smx509.Certificate, pkey crypto.PrivateKey, config SignerInfoConfig) error {
|
||||
var parents []*smx509.Certificate
|
||||
return sd.AddSignerChain(ee, pkey, parents, config)
|
||||
}
|
||||
|
||||
// AddSignerChain signs attributes about the content and adds certificates
|
||||
// and signers infos to the Signed Data. The certificate and private key
|
||||
// of the end-entity signer are used to issue the signature, and any
|
||||
// parent of that end-entity that need to be added to the list of
|
||||
// certifications can be specified in the parents slice.
|
||||
//
|
||||
// The signature algorithm used to hash the data is the one of the end-entity
|
||||
// certificate.
|
||||
func (sd *SignedData) AddSignerChain(ee *smx509.Certificate, pkey crypto.PrivateKey, parents []*smx509.Certificate, config SignerInfoConfig) error {
|
||||
// Following RFC 2315, 9.2 SignerInfo type, the distinguished name of
|
||||
// the issuer of the end-entity signer is stored in the issuerAndSerialNumber
|
||||
// section of the SignedData.SignerInfo, alongside the serial number of
|
||||
// the end-entity.
|
||||
var ias issuerAndSerial
|
||||
ias.SerialNumber = ee.SerialNumber
|
||||
if len(parents) == 0 {
|
||||
// no parent, the issuer is the end-entity cert itself
|
||||
ias.IssuerName = asn1.RawValue{FullBytes: ee.RawIssuer}
|
||||
} else {
|
||||
err := verifyPartialChain(ee, parents)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// the first parent is the issuer
|
||||
ias.IssuerName = asn1.RawValue{FullBytes: parents[0].RawSubject}
|
||||
}
|
||||
sd.sd.DigestAlgorithmIdentifiers = append(sd.sd.DigestAlgorithmIdentifiers,
|
||||
pkix.AlgorithmIdentifier{Algorithm: sd.digestOid},
|
||||
)
|
||||
hasher, err := getHashForOID(sd.digestOid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h := newHash(hasher, sd.digestOid)
|
||||
h.Write(sd.data)
|
||||
sd.messageDigest = h.Sum(nil)
|
||||
encryptionOid, err := getOIDForEncryptionAlgorithm(pkey, sd.digestOid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
attrs := &attributes{}
|
||||
attrs.Add(OIDAttributeContentType, sd.sd.ContentInfo.ContentType)
|
||||
attrs.Add(OIDAttributeMessageDigest, sd.messageDigest)
|
||||
attrs.Add(OIDAttributeSigningTime, time.Now().UTC())
|
||||
for _, attr := range config.ExtraSignedAttributes {
|
||||
attrs.Add(attr.Type, attr.Value)
|
||||
}
|
||||
finalAttrs, err := attrs.ForMarshalling()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
unsignedAttrs := &attributes{}
|
||||
for _, attr := range config.ExtraUnsignedAttributes {
|
||||
unsignedAttrs.Add(attr.Type, attr.Value)
|
||||
}
|
||||
finalUnsignedAttrs, err := unsignedAttrs.ForMarshalling()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// create signature of signed attributes
|
||||
signature, err := signAttributes(finalAttrs, pkey, hasher)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
signer := signerInfo{
|
||||
AuthenticatedAttributes: finalAttrs,
|
||||
UnauthenticatedAttributes: finalUnsignedAttrs,
|
||||
DigestAlgorithm: pkix.AlgorithmIdentifier{Algorithm: sd.digestOid},
|
||||
DigestEncryptionAlgorithm: pkix.AlgorithmIdentifier{Algorithm: encryptionOid},
|
||||
IssuerAndSerialNumber: ias,
|
||||
EncryptedDigest: signature,
|
||||
Version: 1,
|
||||
}
|
||||
if !config.SkipCertificates {
|
||||
sd.certs = append(sd.certs, ee)
|
||||
if len(parents) > 0 {
|
||||
sd.certs = append(sd.certs, parents...)
|
||||
}
|
||||
}
|
||||
sd.sd.SignerInfos = append(sd.sd.SignerInfos, signer)
|
||||
return nil
|
||||
}
|
||||
|
||||
func newHash(hasher crypto.Hash, hashOid asn1.ObjectIdentifier) hash.Hash {
|
||||
var h hash.Hash
|
||||
if hashOid.Equal(OIDDigestAlgorithmSM3) || hashOid.Equal(OIDDigestAlgorithmSM2SM3) {
|
||||
h = sm3.New()
|
||||
} else {
|
||||
h = hasher.New()
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
// SignWithoutAttr issues a signature on the content of the pkcs7 SignedData.
|
||||
// Unlike AddSigner/AddSignerChain, it calculates the digest on the data alone
|
||||
// and does not include any signed attributes like timestamp and so on.
|
||||
//
|
||||
// This function is needed to sign old Android APKs, something you probably
|
||||
// shouldn't do unless you're maintaining backward compatibility for old
|
||||
// applications.
|
||||
func (sd *SignedData) SignWithoutAttr(ee *smx509.Certificate, pkey crypto.PrivateKey, config SignerInfoConfig) error {
|
||||
var signature []byte
|
||||
sd.sd.DigestAlgorithmIdentifiers = append(sd.sd.DigestAlgorithmIdentifiers, pkix.AlgorithmIdentifier{Algorithm: sd.digestOid})
|
||||
hasher, err := getHashForOID(sd.digestOid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h := newHash(hasher, sd.digestOid)
|
||||
h.Write(sd.data)
|
||||
sd.messageDigest = h.Sum(nil)
|
||||
switch pkey := pkey.(type) {
|
||||
case *dsa.PrivateKey:
|
||||
// dsa doesn't implement crypto.Signer so we make a special case
|
||||
// https://github.com/golang/go/issues/27889
|
||||
r, s, err := dsa.Sign(rand.Reader, pkey, sd.messageDigest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
signature, err = asn1.Marshal(dsaSignature{r, s})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
key, ok := pkey.(crypto.Signer)
|
||||
if !ok {
|
||||
return errors.New("pkcs7: private key does not implement crypto.Signer")
|
||||
}
|
||||
signature, err = key.Sign(rand.Reader, sd.messageDigest, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
var ias issuerAndSerial
|
||||
ias.SerialNumber = ee.SerialNumber
|
||||
// no parent, the issue is the end-entity cert itself
|
||||
ias.IssuerName = asn1.RawValue{FullBytes: ee.RawIssuer}
|
||||
if sd.encryptionOid == nil {
|
||||
// if the encryption algorithm wasn't set by SetEncryptionAlgorithm,
|
||||
// infer it from the digest algorithm
|
||||
sd.encryptionOid, err = getOIDForEncryptionAlgorithm(pkey, sd.digestOid)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
signer := signerInfo{
|
||||
DigestAlgorithm: pkix.AlgorithmIdentifier{Algorithm: sd.digestOid},
|
||||
DigestEncryptionAlgorithm: pkix.AlgorithmIdentifier{Algorithm: sd.encryptionOid},
|
||||
IssuerAndSerialNumber: ias,
|
||||
EncryptedDigest: signature,
|
||||
Version: 1,
|
||||
}
|
||||
// create signature of signed attributes
|
||||
sd.certs = append(sd.certs, ee)
|
||||
sd.sd.SignerInfos = append(sd.sd.SignerInfos, signer)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (si *signerInfo) SetUnauthenticatedAttributes(extraUnsignedAttrs []Attribute) error {
|
||||
unsignedAttrs := &attributes{}
|
||||
for _, attr := range extraUnsignedAttrs {
|
||||
unsignedAttrs.Add(attr.Type, attr.Value)
|
||||
}
|
||||
finalUnsignedAttrs, err := unsignedAttrs.ForMarshalling()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
si.UnauthenticatedAttributes = finalUnsignedAttrs
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddCertificate adds the certificate to the payload. Useful for parent certificates
|
||||
func (sd *SignedData) AddCertificate(cert *smx509.Certificate) {
|
||||
sd.certs = append(sd.certs, cert)
|
||||
}
|
||||
|
||||
// Detach removes content from the signed data struct to make it a detached signature.
|
||||
// This must be called right before Finish()
|
||||
func (sd *SignedData) Detach() {
|
||||
sd.sd.ContentInfo = contentInfo{ContentType: OIDData}
|
||||
if sd.isSM {
|
||||
sd.sd.ContentInfo.ContentType = SM2OIDData
|
||||
}
|
||||
}
|
||||
|
||||
// GetSignedData returns the private Signed Data
|
||||
func (sd *SignedData) GetSignedData() *signedData {
|
||||
return &sd.sd
|
||||
}
|
||||
|
||||
// Finish marshals the content and its signers
|
||||
func (sd *SignedData) Finish() ([]byte, error) {
|
||||
if len(sd.certs) > 0 {
|
||||
sd.sd.Certificates = marshalCertificates(sd.certs)
|
||||
}
|
||||
inner, err := asn1.Marshal(sd.sd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outer := contentInfo{
|
||||
ContentType: OIDSignedData,
|
||||
Content: asn1.RawValue{Class: 2, Tag: 0, Bytes: inner, IsCompound: true},
|
||||
}
|
||||
if sd.isSM {
|
||||
outer.ContentType = SM2OIDSignedData
|
||||
}
|
||||
return asn1.Marshal(outer)
|
||||
}
|
||||
|
||||
// RemoveAuthenticatedAttributes removes authenticated attributes from signedData
|
||||
// similar to OpenSSL's PKCS7_NOATTR or -noattr flags
|
||||
func (sd *SignedData) RemoveAuthenticatedAttributes() {
|
||||
for i := range sd.sd.SignerInfos {
|
||||
sd.sd.SignerInfos[i].AuthenticatedAttributes = nil
|
||||
}
|
||||
}
|
||||
|
||||
// RemoveUnauthenticatedAttributes removes unauthenticated attributes from signedData
|
||||
func (sd *SignedData) RemoveUnauthenticatedAttributes() {
|
||||
for i := range sd.sd.SignerInfos {
|
||||
sd.sd.SignerInfos[i].UnauthenticatedAttributes = nil
|
||||
}
|
||||
}
|
||||
|
||||
// verifyPartialChain checks that a given cert is issued by the first parent in the list,
|
||||
// then continue down the path. It doesn't require the last parent to be a root CA,
|
||||
// or to be trusted in any truststore. It simply verifies that the chain provided, albeit
|
||||
// partial, makes sense.
|
||||
func verifyPartialChain(cert *smx509.Certificate, parents []*smx509.Certificate) error {
|
||||
if len(parents) == 0 {
|
||||
return fmt.Errorf("pkcs7: zero parents provided to verify the signature of certificate %q", cert.Subject.CommonName)
|
||||
}
|
||||
err := cert.CheckSignatureFrom(parents[0])
|
||||
if err != nil {
|
||||
return fmt.Errorf("pkcs7: certificate signature from parent is invalid: %v", err)
|
||||
}
|
||||
if len(parents) == 1 {
|
||||
// there is no more parent to check, return
|
||||
return nil
|
||||
}
|
||||
return verifyPartialChain(parents[0], parents[1:])
|
||||
}
|
||||
|
||||
func cert2issuerAndSerial(cert *smx509.Certificate) (issuerAndSerial, error) {
|
||||
var ias issuerAndSerial
|
||||
// The issuer RDNSequence has to match exactly the sequence in the certificate
|
||||
// We cannot use cert.Issuer.ToRDNSequence() here since it mangles the sequence
|
||||
ias.IssuerName = asn1.RawValue{FullBytes: cert.RawIssuer}
|
||||
ias.SerialNumber = cert.SerialNumber
|
||||
|
||||
return ias, nil
|
||||
}
|
||||
|
||||
// signs the DER encoded form of the attributes with the private key
|
||||
func signAttributes(attrs []attribute, pkey crypto.PrivateKey, hasher crypto.Hash) ([]byte, error) {
|
||||
attrBytes, err := marshalAttributes(attrs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if key, ok := pkey.(sm2.Signer); ok {
|
||||
return key.SignWithSM2(rand.Reader, nil, attrBytes)
|
||||
}
|
||||
|
||||
h := hasher.New()
|
||||
h.Write(attrBytes)
|
||||
hash := h.Sum(nil)
|
||||
|
||||
// dsa doesn't implement crypto.Signer so we make a special case
|
||||
// https://github.com/golang/go/issues/27889
|
||||
switch pkey := pkey.(type) {
|
||||
case *dsa.PrivateKey:
|
||||
r, s, err := dsa.Sign(rand.Reader, pkey, hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return asn1.Marshal(dsaSignature{r, s})
|
||||
}
|
||||
|
||||
key, ok := pkey.(crypto.Signer)
|
||||
if !ok {
|
||||
return nil, errors.New("pkcs7: private key does not implement crypto.Signer")
|
||||
}
|
||||
return key.Sign(rand.Reader, hash, hasher)
|
||||
}
|
||||
|
||||
type dsaSignature struct {
|
||||
R, S *big.Int
|
||||
}
|
||||
|
||||
// concats and wraps the certificates in the RawValue structure
|
||||
func marshalCertificates(certs []*smx509.Certificate) rawCertificates {
|
||||
var buf bytes.Buffer
|
||||
for _, cert := range certs {
|
||||
buf.Write(cert.Raw)
|
||||
}
|
||||
rawCerts, _ := marshalCertificateBytes(buf.Bytes())
|
||||
return rawCerts
|
||||
}
|
||||
|
||||
// Even though, the tag & length are stripped out during marshalling the
|
||||
// RawContent, we have to encode it into the RawContent. If its missing,
|
||||
// then `asn1.Marshal()` will strip out the certificate wrapper instead.
|
||||
func marshalCertificateBytes(certs []byte) (rawCertificates, error) {
|
||||
var val = asn1.RawValue{Bytes: certs, Class: 2, Tag: 0, IsCompound: true}
|
||||
b, err := asn1.Marshal(val)
|
||||
if err != nil {
|
||||
return rawCertificates{}, err
|
||||
}
|
||||
return rawCertificates{Raw: b}, nil
|
||||
}
|
||||
|
||||
// DegenerateCertificate creates a signed data structure containing only the
|
||||
// provided certificate or certificate chain.
|
||||
func DegenerateCertificate(cert []byte) ([]byte, error) {
|
||||
rawCert, err := marshalCertificateBytes(cert)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
emptyContent := contentInfo{ContentType: OIDData}
|
||||
sd := signedData{
|
||||
Version: 1,
|
||||
ContentInfo: emptyContent,
|
||||
Certificates: rawCert,
|
||||
CRLs: []pkix.CertificateList{},
|
||||
}
|
||||
content, err := asn1.Marshal(sd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
signedContent := contentInfo{
|
||||
ContentType: OIDSignedData,
|
||||
Content: asn1.RawValue{Class: 2, Tag: 0, Bytes: content, IsCompound: true},
|
||||
}
|
||||
return asn1.Marshal(signedContent)
|
||||
}
|
301
pkcs7/sign_enveloped.go
Normal file
301
pkcs7/sign_enveloped.go
Normal file
@ -0,0 +1,301 @@
|
||||
package pkcs7
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"errors"
|
||||
|
||||
"github.com/emmansun/gmsm/pkcs"
|
||||
"github.com/emmansun/gmsm/sm2"
|
||||
"github.com/emmansun/gmsm/smx509"
|
||||
)
|
||||
|
||||
// It is recommended to use a sequential combination of the signed-data and the enveloped-data content types instead of using the signed-and-enveloped-data content type,
|
||||
// since the signed-and-enveloped-data content type does not have authenticated or unauthenticated attributes,
|
||||
// and does not provide enveloping of signer information other than the signature.
|
||||
type signedEnvelopedData struct {
|
||||
Version int
|
||||
RecipientInfos []recipientInfo `asn1:"set"`
|
||||
DigestAlgorithmIdentifiers []pkix.AlgorithmIdentifier `asn1:"set"`
|
||||
EncryptedContentInfo encryptedContentInfo
|
||||
Certificates rawCertificates `asn1:"optional,tag:0"`
|
||||
CRLs []pkix.CertificateList `asn1:"optional,tag:1"`
|
||||
SignerInfos []signerInfo `asn1:"set"`
|
||||
}
|
||||
|
||||
func (data signedEnvelopedData) GetRecipient(cert *smx509.Certificate) *recipientInfo {
|
||||
for _, recp := range data.RecipientInfos {
|
||||
if isCertMatchForIssuerAndSerial(cert, recp.IssuerAndSerialNumber) {
|
||||
return &recp
|
||||
}
|
||||
}
|
||||
if len(data.RecipientInfos) == 1 {
|
||||
return &data.RecipientInfos[0]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (data signedEnvelopedData) GetEncryptedContentInfo() *encryptedContentInfo {
|
||||
return &data.EncryptedContentInfo
|
||||
}
|
||||
|
||||
func parseSignedEnvelopedData(data []byte) (*PKCS7, error) {
|
||||
var sed signedEnvelopedData
|
||||
if _, err := asn1.Unmarshal(data, &sed); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
certs, err := sed.Certificates.Parse()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &PKCS7{
|
||||
Certificates: certs,
|
||||
CRLs: sed.CRLs,
|
||||
Signers: sed.SignerInfos,
|
||||
raw: sed}, nil
|
||||
}
|
||||
|
||||
type VerifyFunc func() error
|
||||
|
||||
// DecryptAndVerifyOnlyOne decrypts encrypted content info for the only recipient private key
|
||||
// and verifies the signature.
|
||||
func (p7 *PKCS7) DecryptAndVerifyOnlyOne(pkey crypto.PrivateKey, verifyFunc VerifyFunc) ([]byte, error) {
|
||||
sed, ok := p7.raw.(signedEnvelopedData)
|
||||
if !ok {
|
||||
return nil, errors.New("pkcs7: it's NOT SignedAndEvelopedData")
|
||||
}
|
||||
if len(sed.RecipientInfos) != 1 {
|
||||
return nil, errors.New("pkcs7: more than one recipients or no receipient")
|
||||
}
|
||||
defer func() {
|
||||
p7.Content = nil
|
||||
}()
|
||||
plaintext, err := decryptSED(p7, &sed, &sed.RecipientInfos[0], pkey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if verifyFunc != nil {
|
||||
p7.Content = plaintext
|
||||
if err = verifyFunc(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return plaintext, nil
|
||||
}
|
||||
|
||||
// DecryptAndVerify decrypts encrypted content info for recipient cert and private key
|
||||
// and verifies the signature.
|
||||
func (p7 *PKCS7) DecryptAndVerify(cert *smx509.Certificate, pkey crypto.PrivateKey, verifyFunc VerifyFunc) ([]byte, error) {
|
||||
sed, ok := p7.raw.(signedEnvelopedData)
|
||||
if !ok {
|
||||
return nil, errors.New("pkcs7: it's NOT SignedAndEvelopedData")
|
||||
}
|
||||
recipient := sed.GetRecipient(cert)
|
||||
if recipient == nil {
|
||||
return nil, errors.New("pkcs7: no enveloped recipient for provided certificate")
|
||||
}
|
||||
defer func() {
|
||||
p7.Content = nil
|
||||
}()
|
||||
plaintext, err := decryptSED(p7, &sed, recipient, pkey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if verifyFunc != nil {
|
||||
p7.Content = plaintext
|
||||
if err = verifyFunc(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return plaintext, nil
|
||||
}
|
||||
|
||||
func decryptSED(p7 *PKCS7, sed *signedEnvelopedData, recipient *recipientInfo, pkey crypto.PrivateKey) ([]byte, error) {
|
||||
switch pkey := pkey.(type) {
|
||||
case crypto.Decrypter:
|
||||
// Generic case to handle anything that provides the crypto.Decrypter interface.
|
||||
contentKey, err := pkey.Decrypt(rand.Reader, recipient.EncryptedKey, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sed.GetEncryptedContentInfo().decrypt(contentKey)
|
||||
default:
|
||||
return nil, ErrUnsupportedAlgorithm
|
||||
}
|
||||
}
|
||||
|
||||
type SignedAndEnvelopedData struct {
|
||||
sed signedEnvelopedData
|
||||
certs []*smx509.Certificate
|
||||
data, cek []byte
|
||||
digestOid asn1.ObjectIdentifier
|
||||
isSM bool
|
||||
}
|
||||
|
||||
func NewSignedAndEnvelopedData(data []byte, cipher pkcs.Cipher) (*SignedAndEnvelopedData, error) {
|
||||
var key []byte
|
||||
var err error
|
||||
|
||||
// Create key
|
||||
key = make([]byte, cipher.KeySize())
|
||||
_, err = rand.Read(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
id, ciphertext, err := cipher.Encrypt(key, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sed := signedEnvelopedData{
|
||||
Version: 1, // 0 or 1?
|
||||
EncryptedContentInfo: encryptedContentInfo{
|
||||
ContentType: OIDData,
|
||||
ContentEncryptionAlgorithm: *id,
|
||||
EncryptedContent: marshalEncryptedContent(ciphertext),
|
||||
},
|
||||
}
|
||||
return &SignedAndEnvelopedData{sed: sed, data: data, cek: key, digestOid: OIDDigestAlgorithmSHA1, isSM: false}, nil
|
||||
}
|
||||
|
||||
func NewSMSignedAndEnvelopedData(data []byte, cipher pkcs.Cipher) (*SignedAndEnvelopedData, error) {
|
||||
sd, err := NewSignedAndEnvelopedData(data, cipher)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sd.digestOid = OIDDigestAlgorithmSM3
|
||||
sd.isSM = true
|
||||
sd.sed.EncryptedContentInfo.ContentType = SM2OIDData
|
||||
return sd, nil
|
||||
}
|
||||
|
||||
// SetDigestAlgorithm sets the digest algorithm to be used in the signing process.
|
||||
//
|
||||
// This should be called before adding signers
|
||||
func (saed *SignedAndEnvelopedData) SetDigestAlgorithm(d asn1.ObjectIdentifier) {
|
||||
saed.digestOid = d
|
||||
}
|
||||
|
||||
// AddSigner is a wrapper around AddSignerChain() that adds a signer without any parent.
|
||||
func (saed *SignedAndEnvelopedData) AddSigner(ee *smx509.Certificate, pkey crypto.PrivateKey) error {
|
||||
var parents []*smx509.Certificate
|
||||
return saed.AddSignerChain(ee, pkey, parents)
|
||||
}
|
||||
|
||||
func (saed *SignedAndEnvelopedData) AddSignerChain(ee *smx509.Certificate, pkey crypto.PrivateKey, parents []*smx509.Certificate) error {
|
||||
// Following RFC 2315, 9.2 SignerInfo type, the distinguished name of
|
||||
// the issuer of the end-entity signer is stored in the issuerAndSerialNumber
|
||||
// section of the SignedData.SignerInfo, alongside the serial number of
|
||||
// the end-entity.
|
||||
var ias issuerAndSerial
|
||||
ias.SerialNumber = ee.SerialNumber
|
||||
if len(parents) == 0 {
|
||||
// no parent, the issuer is the end-entity cert itself
|
||||
ias.IssuerName = asn1.RawValue{FullBytes: ee.RawIssuer}
|
||||
} else {
|
||||
err := verifyPartialChain(ee, parents)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// the first parent is the issuer
|
||||
ias.IssuerName = asn1.RawValue{FullBytes: parents[0].RawSubject}
|
||||
}
|
||||
saed.sed.DigestAlgorithmIdentifiers = append(saed.sed.DigestAlgorithmIdentifiers,
|
||||
pkix.AlgorithmIdentifier{Algorithm: saed.digestOid},
|
||||
)
|
||||
hasher, err := getHashForOID(saed.digestOid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
signatureOid, err := getOIDForEncryptionAlgorithm(pkey, saed.digestOid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key, ok := pkey.(crypto.Signer)
|
||||
if !ok {
|
||||
return errors.New("pkcs7: private key does not implement crypto.Signer")
|
||||
}
|
||||
var signOpt crypto.SignerOpts
|
||||
var tobeSigned []byte
|
||||
|
||||
if saed.isSM {
|
||||
signOpt = sm2.NewSM2SignerOption(true, nil)
|
||||
tobeSigned = saed.data
|
||||
} else {
|
||||
signOpt = hasher
|
||||
h := newHash(hasher, saed.digestOid)
|
||||
h.Write(saed.data)
|
||||
tobeSigned = h.Sum(nil)
|
||||
}
|
||||
signature, err := key.Sign(rand.Reader, tobeSigned, signOpt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
signer := signerInfo{
|
||||
DigestAlgorithm: pkix.AlgorithmIdentifier{Algorithm: saed.digestOid},
|
||||
DigestEncryptionAlgorithm: pkix.AlgorithmIdentifier{Algorithm: signatureOid},
|
||||
IssuerAndSerialNumber: ias,
|
||||
EncryptedDigest: signature,
|
||||
Version: 1,
|
||||
}
|
||||
saed.certs = append(saed.certs, ee)
|
||||
if len(parents) > 0 {
|
||||
saed.certs = append(saed.certs, parents...)
|
||||
}
|
||||
saed.sed.SignerInfos = append(saed.sed.SignerInfos, signer)
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddCertificate adds the certificate to the payload. Useful for parent certificates
|
||||
func (saed *SignedAndEnvelopedData) AddCertificate(cert *smx509.Certificate) {
|
||||
saed.certs = append(saed.certs, cert)
|
||||
}
|
||||
|
||||
func (saed *SignedAndEnvelopedData) AddRecipient(recipient *smx509.Certificate) error {
|
||||
encryptedKey, err := encryptKey(saed.cek, recipient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ias, err := cert2issuerAndSerial(recipient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var keyEncryptionAlgorithm asn1.ObjectIdentifier = OIDEncryptionAlgorithmRSA
|
||||
if recipient.SignatureAlgorithm == smx509.SM2WithSM3 {
|
||||
keyEncryptionAlgorithm = OIDKeyEncryptionAlgorithmSM2
|
||||
} else if saed.isSM {
|
||||
return errors.New("pkcs7: Shangmi does not support RSA")
|
||||
}
|
||||
info := recipientInfo{
|
||||
Version: 1,
|
||||
IssuerAndSerialNumber: ias,
|
||||
KeyEncryptionAlgorithm: pkix.AlgorithmIdentifier{
|
||||
Algorithm: keyEncryptionAlgorithm,
|
||||
},
|
||||
EncryptedKey: encryptedKey,
|
||||
}
|
||||
saed.sed.RecipientInfos = append(saed.sed.RecipientInfos, info)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Finish marshals the content and its signers
|
||||
func (saed *SignedAndEnvelopedData) Finish() ([]byte, error) {
|
||||
saed.sed.Certificates = marshalCertificates(saed.certs)
|
||||
inner, err := asn1.Marshal(saed.sed)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outer := contentInfo{
|
||||
ContentType: OIDSignedEnvelopedData,
|
||||
Content: asn1.RawValue{Class: 2, Tag: 0, Bytes: inner, IsCompound: true},
|
||||
}
|
||||
if saed.isSM {
|
||||
outer.ContentType = SM2OIDSignedEnvelopedData
|
||||
}
|
||||
return asn1.Marshal(outer)
|
||||
}
|
208
pkcs7/sign_enveloped_test.go
Normal file
208
pkcs7/sign_enveloped_test.go
Normal file
@ -0,0 +1,208 @@
|
||||
package pkcs7
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/emmansun/gmsm/pkcs"
|
||||
"github.com/emmansun/gmsm/sm2"
|
||||
"github.com/emmansun/gmsm/smx509"
|
||||
)
|
||||
|
||||
// from https://www.gmcert.org/
|
||||
var smSignedEvelopedTestData = `-----BEGIN PKCS7-----
|
||||
MIIDwwYKKoEcz1UGAQQCBKCCA7MwggOvAgEBMYGfMIGcAgEBMAwAAAIIAs64zJDL
|
||||
T8UwCwYJKoEcz1UBgi0DBHwwegIhAPbXLhqtkA/HeYKgPeZNPP4kT2/PqS7K8NiB
|
||||
vAFCBsf+AiEA4m9ZyghfFUaE1K4kre9T/R7Td4hVQPij9GOloRykKJ8EIMJ/zBGe
|
||||
WaqgtCUFu99S3Wovtd6+jN1tDkTJPWgZ6uu1BBCobCvaWMr0Of+Z686i/wVrMQww
|
||||
CgYIKoEcz1UBgxEwWQYKKoEcz1UGAQQCATAJBgcqgRzPVQFogEDM1pUC/MDTCRCQ
|
||||
uZiIxZYZzNaVAvzA0wkQkLmYiMWWGUnT7MvXe2M2khckxgU+ZMVBNDpf4EFl6+C2
|
||||
PRPcy8ROoIIB4jCCAd4wggGDoAMCAQICCALODAD8KSAXMAoGCCqBHM9VAYN1MEIx
|
||||
CzAJBgNVBAYTAkNOMQ8wDQYDVQQIDAbmtZnmsZ8xDzANBgNVBAcMBuadreW3njER
|
||||
MA8GA1UECgwI5rWL6K+VQ0EwHhcNMjExMjIzMDg0ODMzWhcNMzExMjIzMDg0ODMz
|
||||
WjBCMQswCQYDVQQGEwJDTjEPMA0GA1UECAwG5rWZ5rGfMQ8wDQYDVQQHDAbmna3l
|
||||
t54xETAPBgNVBAoMCOa1i+ivlUNBMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE
|
||||
SrOgeWQcu+dzrGUniH7/M0nG4ol5C4wfj5cPmFr6HrEZKmBnvzKo6/K65k4auohF
|
||||
rm2CumYkEFeeJCpXL2tx7aNjMGEwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF
|
||||
MAMBAf8wHQYDVR0OBBYEFDaT4xTnRQn61e/qLxIt06GWPMKkMB8GA1UdIwQYMBaA
|
||||
FDaT4xTnRQn61e/qLxIt06GWPMKkMAoGCCqBHM9VAYN1A0kAMEYCIQCw4bSylc4l
|
||||
IV203nQ6L0QDUgnbugidDAMO1m5d7wFhjgIhAMwly3Bd9gzOQM3vTKqVH0H2D2kU
|
||||
y2JDcEl5cPy1GBOhMYG4MIG1AgEBME4wQjELMAkGA1UEBhMCQ04xDzANBgNVBAgM
|
||||
Bua1meaxnzEPMA0GA1UEBwwG5p2t5beeMREwDwYDVQQKDAjmtYvor5VDQQIIAs4M
|
||||
APwpIBcwCgYIKoEcz1UBgxEwCwYJKoEcz1UBgi0BBEcwRQIgR7STVlgH/yy4k93+
|
||||
h3KRFN+dWEVeOJ7G1lRRSNXihnkCIQCHxZvmdUcv38SBCgZp+qxnpm2a+C1/tWKV
|
||||
d/A8tW8dnw==
|
||||
-----END PKCS7-----
|
||||
`
|
||||
|
||||
var encCert = `-----BEGIN CERTIFICATE-----
|
||||
MIICPTCCAeOgAwIBAgIIAs64zJDLT8UwCgYIKoEcz1UBg3UwQjELMAkGA1UEBhMC
|
||||
Q04xDzANBgNVBAgMBua1meaxnzEPMA0GA1UEBwwG5p2t5beeMREwDwYDVQQKDAjm
|
||||
tYvor5VDQTAeFw0yMzAyMjIxMjIwMzNaFw0yNDAyMjIxMjIwMzNaMH0xCzAJBgNV
|
||||
BAYTAkNOMQ8wDQYDVQQIDAbmtZnmsZ8xDzANBgNVBAcMBuadreW3njEVMBMGA1UE
|
||||
CgwM5rWL6K+V5py65p6EMRUwEwYDVQQLDAzmtYvor5Xnu4Tnu4cxHjAcBgNVBAMM
|
||||
Fea1i+ivleacjeWKoeWZqOWQjeensDBZMBMGByqGSM49AgEGCCqBHM9VAYItA0IA
|
||||
BGqelO/A74LrAZvxFopkSz9lpjygTF1ffslhB0BzwxQ5jMx1D4912Swb6foMe+0k
|
||||
bq9V2i3Kn2HrzSTAcj+G+9ujgYcwgYQwDgYDVR0PAQH/BAQDAgM4MBMGA1UdJQQM
|
||||
MAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFDd41c6+e9aahmQD
|
||||
PdC8YSXfwYgUMB8GA1UdIwQYMBaAFDaT4xTnRQn61e/qLxIt06GWPMKkMA8GA1Ud
|
||||
EQQIMAaHBH8AAAEwCgYIKoEcz1UBg3UDSAAwRQIgMZBhweovXaHVNSlLv0rTEYnT
|
||||
GRSsTKmrkCDrxQdaWVUCIQCqeAiXqEnwcdOb6DTFxKF2E2htppt7H4y1K8UVmF7s
|
||||
eg==
|
||||
-----END CERTIFICATE-----
|
||||
`
|
||||
|
||||
var signCert = `-----BEGIN CERTIFICATE-----
|
||||
MIICPTCCAeOgAwIBAgIIAs64zJDLTNQwCgYIKoEcz1UBg3UwQjELMAkGA1UEBhMC
|
||||
Q04xDzANBgNVBAgMBua1meaxnzEPMA0GA1UEBwwG5p2t5beeMREwDwYDVQQKDAjm
|
||||
tYvor5VDQTAeFw0yMzAyMjIxMjIwMzNaFw0yNDAyMjIxMjIwMzNaMH0xCzAJBgNV
|
||||
BAYTAkNOMQ8wDQYDVQQIDAbmtZnmsZ8xDzANBgNVBAcMBuadreW3njEVMBMGA1UE
|
||||
CgwM5rWL6K+V5py65p6EMRUwEwYDVQQLDAzmtYvor5Xnu4Tnu4cxHjAcBgNVBAMM
|
||||
Fea1i+ivleacjeWKoeWZqOWQjeensDBZMBMGByqGSM49AgEGCCqBHM9VAYItA0IA
|
||||
BL4bEPQAKg3aEjsXsnEm4tSFOetUMYzUpLJyYKc0isNwiu8fNBZAihjDOVzQ3FQf
|
||||
BeZXJdxvbdC5s22m1E81mwSjgYcwgYQwDgYDVR0PAQH/BAQDAgbAMBMGA1UdJQQM
|
||||
MAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFBGRF+xJjaBurdse
|
||||
flfRaPUcBjFWMB8GA1UdIwQYMBaAFDaT4xTnRQn61e/qLxIt06GWPMKkMA8GA1Ud
|
||||
EQQIMAaHBH8AAAEwCgYIKoEcz1UBg3UDSAAwRQIhAKfa/H/f2OgTXhipfEPXPiHb
|
||||
nZFJyugnvKFkrijK8Qp5AiARlYEA2FR21H43/e/qu2lrp+ZUeYk3ve8nMd3yua9L
|
||||
Ag==
|
||||
-----END CERTIFICATE-----
|
||||
`
|
||||
|
||||
var signKey = `-----BEGIN PRIVATE KEY-----
|
||||
MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQg1FlRx/WjmIFZ5dV4
|
||||
ghl1JwHIfMdGKLvYdPd1akXUCQSgCgYIKoEcz1UBgi2hRANCAAS+GxD0ACoN2hI7
|
||||
F7JxJuLUhTnrVDGM1KSycmCnNIrDcIrvHzQWQIoYwzlc0NxUHwXmVyXcb23QubNt
|
||||
ptRPNZsE
|
||||
-----END PRIVATE KEY-----
|
||||
`
|
||||
|
||||
var expectedEncKey = `-----BEGIN PRIVATE KEY-----
|
||||
MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgyhwdf0K3AnMCLEbG
|
||||
B1yMjJLLlfQkGE53dvCPttt1BkCgCgYIKoEcz1UBgi2hRANCAARqnpTvwO+C6wGb
|
||||
8RaKZEs/ZaY8oExdX37JYQdAc8MUOYzMdQ+PddksG+n6DHvtJG6vVdotyp9h680k
|
||||
wHI/hvvb
|
||||
-----END PRIVATE KEY-----
|
||||
`
|
||||
|
||||
func TestParseSignedEvnvelopedData(t *testing.T) {
|
||||
var block *pem.Block
|
||||
block, rest := pem.Decode([]byte(smSignedEvelopedTestData))
|
||||
if len(rest) != 0 {
|
||||
t.Fatal("unexpected remaining PEM block during decode")
|
||||
}
|
||||
p7Data, err := Parse(block.Bytes)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(p7Data.Certificates) != 1 {
|
||||
t.Fatal("should only one certificate")
|
||||
}
|
||||
|
||||
block, rest = pem.Decode([]byte(signKey))
|
||||
if len(rest) != 0 {
|
||||
t.Fatal("unexpected remaining PEM block during decode")
|
||||
}
|
||||
signPriv, err := smx509.ParsePKCS8PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
sm2SignPriv, ok := signPriv.(*sm2.PrivateKey)
|
||||
if !ok {
|
||||
t.Fatal("not expected key type")
|
||||
}
|
||||
|
||||
signCertificate, err := smx509.ParseCertificatePEM([]byte(signCert))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !sm2SignPriv.PublicKey.Equal(signCertificate.PublicKey) {
|
||||
t.Fatal("not one key pair")
|
||||
}
|
||||
|
||||
encKeyBytes, err := p7Data.DecryptAndVerifyOnlyOne(signPriv, func() error {
|
||||
return p7Data.Verify()
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
block, rest = pem.Decode([]byte(expectedEncKey))
|
||||
if len(rest) != 0 {
|
||||
t.Fatal("unexpected remaining PEM block during decode")
|
||||
}
|
||||
encPriv, err := smx509.ParsePKCS8PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
sm2EncPriv, ok := encPriv.(*sm2.PrivateKey)
|
||||
if !ok {
|
||||
t.Fatal("not expected key type")
|
||||
}
|
||||
if new(big.Int).SetBytes(encKeyBytes).Cmp(sm2EncPriv.D) != 0 {
|
||||
t.Fatalf("the priv key is not same, got %x, expected %x", encKeyBytes, sm2EncPriv.D.Bytes())
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateSignedEvnvelopedData(t *testing.T) {
|
||||
rootCert, err := createTestCertificateByIssuer("PKCS7 Test Root CA", nil, smx509.SM2WithSM3, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
recipient, err := createTestCertificateByIssuer("PKCS7 Test Recipient", rootCert, smx509.SM2WithSM3, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
encryptKey, err := createTestCertificateByIssuer("PKCS7 Test Encrypt Key", rootCert, smx509.SM2WithSM3, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
privKey := make([]byte, 32)
|
||||
sm2Key, ok := (*encryptKey.PrivateKey).(*sm2.PrivateKey)
|
||||
if !ok {
|
||||
t.Fatal("should be sm2 private key")
|
||||
}
|
||||
sm2Key.D.FillBytes(privKey)
|
||||
|
||||
testCipers := []pkcs.Cipher{pkcs.SM4, pkcs.SM4ECB, pkcs.SM4CBC, pkcs.SM4GCM}
|
||||
for _, cipher := range testCipers {
|
||||
saed, err := NewSMSignedAndEnvelopedData(privKey, cipher)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = saed.AddSigner(rootCert.Certificate, *rootCert.PrivateKey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = saed.AddRecipient(recipient.Certificate)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
result, err := saed.Finish()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Printf("%x\n", result)
|
||||
|
||||
// parse, decrypt, verify
|
||||
p7Data, err := Parse(result)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
encKeyBytes, err := p7Data.DecryptAndVerifyOnlyOne(*recipient.PrivateKey, func() error {
|
||||
return p7Data.Verify()
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(encKeyBytes, privKey) {
|
||||
t.Fatal("not same private key")
|
||||
}
|
||||
}
|
||||
}
|
335
pkcs7/sign_test.go
Normal file
335
pkcs7/sign_test.go
Normal file
@ -0,0 +1,335 @@
|
||||
package pkcs7
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/dsa"
|
||||
"crypto/x509"
|
||||
"encoding/asn1"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math/big"
|
||||
"os"
|
||||
"os/exec"
|
||||
"testing"
|
||||
|
||||
"github.com/emmansun/gmsm/smx509"
|
||||
)
|
||||
|
||||
func testSign(t *testing.T, isSM bool, content []byte, sigalgs []x509.SignatureAlgorithm) {
|
||||
for _, sigalgroot := range sigalgs {
|
||||
rootCert, err := createTestCertificateByIssuer("PKCS7 Test Root CA", nil, sigalgroot, true)
|
||||
if err != nil {
|
||||
t.Fatalf("test %s: cannot generate root cert: %s", sigalgroot, err)
|
||||
}
|
||||
truststore := smx509.NewCertPool()
|
||||
truststore.AddCert(rootCert.Certificate)
|
||||
for _, sigalginter := range sigalgs {
|
||||
interCert, err := createTestCertificateByIssuer("PKCS7 Test Intermediate Cert", rootCert, sigalginter, true)
|
||||
if err != nil {
|
||||
t.Fatalf("test %s/%s: cannot generate intermediate cert: %s", sigalgroot, sigalginter, err)
|
||||
}
|
||||
var parents []*smx509.Certificate
|
||||
parents = append(parents, interCert.Certificate)
|
||||
for _, sigalgsigner := range sigalgs {
|
||||
signerCert, err := createTestCertificateByIssuer("PKCS7 Test Signer Cert", interCert, sigalgsigner, false)
|
||||
if err != nil {
|
||||
t.Fatalf("test %s/%s/%s: cannot generate signer cert: %s", sigalgroot, sigalginter, sigalgsigner, err)
|
||||
}
|
||||
for _, testDetach := range []bool{false, true} {
|
||||
log.Printf("test %s/%s/%s detached %t\n", sigalgroot, sigalginter, sigalgsigner, testDetach)
|
||||
var toBeSigned *SignedData
|
||||
if isSM {
|
||||
toBeSigned, err = NewSMSignedData(content)
|
||||
} else {
|
||||
toBeSigned, err = NewSignedData(content)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("test %s/%s/%s: cannot initialize signed data: %s", sigalgroot, sigalginter, sigalgsigner, err)
|
||||
}
|
||||
|
||||
// Set the digest to match the end entity cert
|
||||
signerDigest, _ := getDigestOIDForSignatureAlgorithm(sigalgsigner)
|
||||
toBeSigned.SetDigestAlgorithm(signerDigest)
|
||||
|
||||
if err := toBeSigned.AddSignerChain(signerCert.Certificate, *signerCert.PrivateKey, parents, SignerInfoConfig{}); err != nil {
|
||||
t.Fatalf("test %s/%s/%s: cannot add signer: %s", sigalgroot, sigalginter, sigalgsigner, err)
|
||||
}
|
||||
if testDetach {
|
||||
toBeSigned.Detach()
|
||||
}
|
||||
signed, err := toBeSigned.Finish()
|
||||
if err != nil {
|
||||
t.Fatalf("test %s/%s/%s: cannot finish signing data: %s", sigalgroot, sigalginter, sigalgsigner, err)
|
||||
}
|
||||
pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: signed})
|
||||
p7, err := Parse(signed)
|
||||
if err != nil {
|
||||
t.Fatalf("test %s/%s/%s: cannot parse signed data: %s", sigalgroot, sigalginter, sigalgsigner, err)
|
||||
}
|
||||
if testDetach {
|
||||
p7.Content = content
|
||||
}
|
||||
if !bytes.Equal(content, p7.Content) {
|
||||
t.Errorf("test %s/%s/%s: content was not found in the parsed data:\n\tExpected: %s\n\tActual: %s", sigalgroot, sigalginter, sigalgsigner, content, p7.Content)
|
||||
}
|
||||
if err := p7.VerifyWithChain(truststore); err != nil {
|
||||
t.Errorf("test %s/%s/%s: cannot verify signed data: %s", sigalgroot, sigalginter, sigalgsigner, err)
|
||||
}
|
||||
if !signerDigest.Equal(p7.Signers[0].DigestAlgorithm.Algorithm) {
|
||||
t.Errorf("test %s/%s/%s: expected digest algorithm %q but got %q",
|
||||
sigalgroot, sigalginter, sigalgsigner, signerDigest, p7.Signers[0].DigestAlgorithm.Algorithm)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
func TestSign(t *testing.T) {
|
||||
content := []byte("Hello World")
|
||||
sigalgs := []x509.SignatureAlgorithm{
|
||||
x509.SHA1WithRSA,
|
||||
x509.SHA256WithRSA,
|
||||
x509.SHA512WithRSA,
|
||||
x509.ECDSAWithSHA1,
|
||||
x509.ECDSAWithSHA256,
|
||||
x509.ECDSAWithSHA384,
|
||||
x509.ECDSAWithSHA512,
|
||||
smx509.SM2WithSM3,
|
||||
}
|
||||
testSign(t, false, content, sigalgs)
|
||||
}
|
||||
|
||||
func TestSignSM(t *testing.T) {
|
||||
content := []byte("Hello World")
|
||||
sigalgs := []x509.SignatureAlgorithm{
|
||||
smx509.SM2WithSM3,
|
||||
}
|
||||
testSign(t, true, content, sigalgs)
|
||||
}
|
||||
|
||||
func TestDSASignAndVerifyWithOpenSSL(t *testing.T) {
|
||||
content := []byte("Hello World")
|
||||
// write the content to a temp file
|
||||
tmpContentFile, err := ioutil.TempFile("", "TestDSASignAndVerifyWithOpenSSL_content")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ioutil.WriteFile(tmpContentFile.Name(), content, 0755)
|
||||
|
||||
block, _ := pem.Decode([]byte(dsaPublicCert))
|
||||
if block == nil {
|
||||
t.Fatal("failed to parse certificate PEM")
|
||||
}
|
||||
signerCert, err := smx509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
t.Fatal("failed to parse certificate: " + err.Error())
|
||||
}
|
||||
|
||||
// write the signer cert to a temp file
|
||||
tmpSignerCertFile, err := ioutil.TempFile("", "TestDSASignAndVerifyWithOpenSSL_signer")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ioutil.WriteFile(tmpSignerCertFile.Name(), dsaPublicCert, 0755)
|
||||
|
||||
priv := dsa.PrivateKey{
|
||||
PublicKey: dsa.PublicKey{Parameters: dsa.Parameters{P: fromHex("fd7f53811d75122952df4a9c2eece4e7f611b7523cef4400c31e3f80b6512669455d402251fb593d8d58fabfc5f5ba30f6cb9b556cd7813b801d346ff26660b76b9950a5a49f9fe8047b1022c24fbba9d7feb7c61bf83b57e7c6a8a6150f04fb83f6d3c51ec3023554135a169132f675f3ae2b61d72aeff22203199dd14801c7"),
|
||||
Q: fromHex("9760508F15230BCCB292B982A2EB840BF0581CF5"),
|
||||
G: fromHex("F7E1A085D69B3DDECBBCAB5C36B857B97994AFBBFA3AEA82F9574C0B3D0782675159578EBAD4594FE67107108180B449167123E84C281613B7CF09328CC8A6E13C167A8B547C8D28E0A3AE1E2BB3A675916EA37F0BFA213562F1FB627A01243BCCA4F1BEA8519089A883DFE15AE59F06928B665E807B552564014C3BFECF492A"),
|
||||
},
|
||||
},
|
||||
X: fromHex("7D6E1A3DD4019FD809669D8AB8DA73807CEF7EC1"),
|
||||
}
|
||||
toBeSigned, err := NewSignedData(content)
|
||||
if err != nil {
|
||||
t.Fatalf("test case: cannot initialize signed data: %s", err)
|
||||
}
|
||||
if err := toBeSigned.SignWithoutAttr(signerCert, &priv, SignerInfoConfig{}); err != nil {
|
||||
t.Fatalf("Cannot add signer: %s", err)
|
||||
}
|
||||
toBeSigned.Detach()
|
||||
signed, err := toBeSigned.Finish()
|
||||
if err != nil {
|
||||
t.Fatalf("test case: cannot finish signing data: %s", err)
|
||||
}
|
||||
|
||||
// write the signature to a temp file
|
||||
tmpSignatureFile, err := ioutil.TempFile("", "TestDSASignAndVerifyWithOpenSSL_signature")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ioutil.WriteFile(tmpSignatureFile.Name(), pem.EncodeToMemory(&pem.Block{Type: "PKCS7", Bytes: signed}), 0755)
|
||||
|
||||
// call openssl to verify the signature on the content using the root
|
||||
opensslCMD := exec.Command("openssl", "smime", "-verify", "-noverify",
|
||||
"-in", tmpSignatureFile.Name(), "-inform", "PEM",
|
||||
"-content", tmpContentFile.Name())
|
||||
out, err := opensslCMD.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("test case: openssl command failed with %s: %s", err, out)
|
||||
}
|
||||
os.Remove(tmpSignatureFile.Name()) // clean up
|
||||
os.Remove(tmpContentFile.Name()) // clean up
|
||||
os.Remove(tmpSignerCertFile.Name()) // clean up
|
||||
}
|
||||
|
||||
func ExampleSignedData() {
|
||||
// generate a signing cert or load a key pair
|
||||
cert, err := createTestCertificate(x509.SHA256WithRSA)
|
||||
if err != nil {
|
||||
fmt.Printf("Cannot create test certificates: %s", err)
|
||||
}
|
||||
|
||||
// Initialize a SignedData struct with content to be signed
|
||||
signedData, err := NewSignedData([]byte("Example data to be signed"))
|
||||
if err != nil {
|
||||
fmt.Printf("Cannot initialize signed data: %s", err)
|
||||
}
|
||||
|
||||
// Add the signing cert and private key
|
||||
if err := signedData.AddSigner(cert.Certificate, cert.PrivateKey, SignerInfoConfig{}); err != nil {
|
||||
fmt.Printf("Cannot add signer: %s", err)
|
||||
}
|
||||
|
||||
// Call Detach() is you want to remove content from the signature
|
||||
// and generate an S/MIME detached signature
|
||||
signedData.Detach()
|
||||
|
||||
// Finish() to obtain the signature bytes
|
||||
detachedSignature, err := signedData.Finish()
|
||||
if err != nil {
|
||||
fmt.Printf("Cannot finish signing data: %s", err)
|
||||
}
|
||||
pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: detachedSignature})
|
||||
}
|
||||
|
||||
func TestUnmarshalSignedAttribute(t *testing.T) {
|
||||
cert, err := createTestCertificate(x509.SHA512WithRSA)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
content := []byte("Hello World")
|
||||
toBeSigned, err := NewSignedData(content)
|
||||
if err != nil {
|
||||
t.Fatalf("Cannot initialize signed data: %s", err)
|
||||
}
|
||||
oidTest := asn1.ObjectIdentifier{2, 3, 4, 5, 6, 7}
|
||||
testValue := "TestValue"
|
||||
if err := toBeSigned.AddSigner(cert.Certificate, *cert.PrivateKey, SignerInfoConfig{
|
||||
ExtraSignedAttributes: []Attribute{Attribute{Type: oidTest, Value: testValue}},
|
||||
}); err != nil {
|
||||
t.Fatalf("Cannot add signer: %s", err)
|
||||
}
|
||||
signed, err := toBeSigned.Finish()
|
||||
if err != nil {
|
||||
t.Fatalf("Cannot finish signing data: %s", err)
|
||||
}
|
||||
p7, err := Parse(signed)
|
||||
if err != nil {
|
||||
t.Fatalf("Cannot parse signed data: %v", err)
|
||||
}
|
||||
var actual string
|
||||
err = p7.UnmarshalSignedAttribute(oidTest, &actual)
|
||||
if err != nil {
|
||||
t.Fatalf("Cannot unmarshal test value: %s", err)
|
||||
}
|
||||
if testValue != actual {
|
||||
t.Errorf("Attribute does not match test value\n\tExpected: %s\n\tActual: %s", testValue, actual)
|
||||
}
|
||||
err = p7.Verify()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSkipCertificates(t *testing.T) {
|
||||
cert, err := createTestCertificate(x509.SHA512WithRSA)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
content := []byte("Hello World")
|
||||
toBeSigned, err := NewSignedData(content)
|
||||
if err != nil {
|
||||
t.Fatalf("Cannot initialize signed data: %s", err)
|
||||
}
|
||||
|
||||
if err := toBeSigned.AddSigner(cert.Certificate, *cert.PrivateKey, SignerInfoConfig{}); err != nil {
|
||||
t.Fatalf("Cannot add signer: %s", err)
|
||||
}
|
||||
signed, err := toBeSigned.Finish()
|
||||
if err != nil {
|
||||
t.Fatalf("Cannot finish signing data: %s", err)
|
||||
}
|
||||
p7, err := Parse(signed)
|
||||
if err != nil {
|
||||
t.Fatalf("Cannot parse signed data: %v", err)
|
||||
}
|
||||
if len(p7.Certificates) == 0 {
|
||||
t.Errorf("No certificates")
|
||||
}
|
||||
|
||||
toBeSigned2, err := NewSignedData(content)
|
||||
if err != nil {
|
||||
t.Fatalf("Cannot initialize signed data: %s", err)
|
||||
}
|
||||
if err := toBeSigned2.AddSigner(cert.Certificate, *cert.PrivateKey, SignerInfoConfig{SkipCertificates: true}); err != nil {
|
||||
t.Fatalf("Cannot add signer: %s", err)
|
||||
}
|
||||
signed, err = toBeSigned2.Finish()
|
||||
if err != nil {
|
||||
t.Fatalf("Cannot finish signing data: %s", err)
|
||||
}
|
||||
p7, err = Parse(signed)
|
||||
if err != nil {
|
||||
t.Fatalf("Cannot parse signed data: %v", err)
|
||||
}
|
||||
if len(p7.Certificates) > 0 {
|
||||
t.Errorf("Have certificates: %v", p7.Certificates)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDegenerateCertificate(t *testing.T) {
|
||||
cert, err := createTestCertificate(x509.SHA1WithRSA)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
deg, err := DegenerateCertificate(cert.Certificate.Raw)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
testOpenSSLParse(t, deg)
|
||||
pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: deg})
|
||||
}
|
||||
|
||||
// writes the cert to a temporary file and tests that openssl can read it.
|
||||
func testOpenSSLParse(t *testing.T, certBytes []byte) {
|
||||
tmpCertFile, err := ioutil.TempFile("", "testCertificate")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.Remove(tmpCertFile.Name()) // clean up
|
||||
|
||||
if _, err := tmpCertFile.Write(certBytes); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
opensslCMD := exec.Command("openssl", "pkcs7", "-inform", "der", "-in", tmpCertFile.Name())
|
||||
_, err = opensslCMD.Output()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := tmpCertFile.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
}
|
||||
func fromHex(s string) *big.Int {
|
||||
result, ok := new(big.Int).SetString(s, 16)
|
||||
if !ok {
|
||||
panic(s)
|
||||
}
|
||||
return result
|
||||
}
|
347
pkcs7/verify.go
Normal file
347
pkcs7/verify.go
Normal file
@ -0,0 +1,347 @@
|
||||
package pkcs7
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/emmansun/gmsm/smx509"
|
||||
)
|
||||
|
||||
// Verify is a wrapper around VerifyWithChain() that initializes an empty
|
||||
// trust store, effectively disabling certificate verification when validating
|
||||
// a signature.
|
||||
func (p7 *PKCS7) Verify() (err error) {
|
||||
return p7.VerifyWithChain(nil)
|
||||
}
|
||||
|
||||
// VerifyWithChain checks the signatures of a PKCS7 object.
|
||||
//
|
||||
// If truststore is not nil, it also verifies the chain of trust of
|
||||
// the end-entity signer cert to one of the roots in the
|
||||
// truststore. When the PKCS7 object includes the signing time
|
||||
// authenticated attr verifies the chain at that time and UTC now
|
||||
// otherwise.
|
||||
func (p7 *PKCS7) VerifyWithChain(truststore *smx509.CertPool) (err error) {
|
||||
if len(p7.Signers) == 0 {
|
||||
return errors.New("pkcs7: Message has no signers")
|
||||
}
|
||||
for _, signer := range p7.Signers {
|
||||
if err := verifySignature(p7, signer, truststore); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// VerifyWithChainAtTime checks the signatures of a PKCS7 object.
|
||||
//
|
||||
// If truststore is not nil, it also verifies the chain of trust of
|
||||
// the end-entity signer cert to a root in the truststore at
|
||||
// currentTime. It does not use the signing time authenticated
|
||||
// attribute.
|
||||
func (p7 *PKCS7) VerifyWithChainAtTime(truststore *smx509.CertPool, currentTime time.Time) (err error) {
|
||||
if len(p7.Signers) == 0 {
|
||||
return errors.New("pkcs7: Message has no signers")
|
||||
}
|
||||
for _, signer := range p7.Signers {
|
||||
if err := verifySignatureAtTime(p7, signer, truststore, currentTime); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func verifySignatureAtTime(p7 *PKCS7, signer signerInfo, truststore *smx509.CertPool, currentTime time.Time) (err error) {
|
||||
signedData := p7.Content
|
||||
ee := getCertFromCertsByIssuerAndSerial(p7.Certificates, signer.IssuerAndSerialNumber)
|
||||
if ee == nil {
|
||||
return errors.New("pkcs7: No certificate for signer")
|
||||
}
|
||||
if len(signer.AuthenticatedAttributes) > 0 {
|
||||
// TODO(fullsailor): First check the content type match
|
||||
var (
|
||||
digest []byte
|
||||
signingTime time.Time
|
||||
)
|
||||
err := unmarshalAttribute(signer.AuthenticatedAttributes, OIDAttributeMessageDigest, &digest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hasher, err := getHashForOID(signer.DigestAlgorithm.Algorithm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h := newHash(hasher, signer.DigestAlgorithm.Algorithm)
|
||||
h.Write(p7.Content)
|
||||
computed := h.Sum(nil)
|
||||
if subtle.ConstantTimeCompare(digest, computed) != 1 {
|
||||
return &MessageDigestMismatchError{
|
||||
ExpectedDigest: digest,
|
||||
ActualDigest: computed,
|
||||
}
|
||||
}
|
||||
signedData, err = marshalAttributes(signer.AuthenticatedAttributes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = unmarshalAttribute(signer.AuthenticatedAttributes, OIDAttributeSigningTime, &signingTime)
|
||||
if err == nil {
|
||||
// signing time found, performing validity check
|
||||
if signingTime.After(ee.NotAfter) || signingTime.Before(ee.NotBefore) {
|
||||
return fmt.Errorf("pkcs7: signing time %q is outside of certificate validity %q to %q",
|
||||
signingTime.Format(time.RFC3339),
|
||||
ee.NotBefore.Format(time.RFC3339),
|
||||
ee.NotAfter.Format(time.RFC3339))
|
||||
}
|
||||
}
|
||||
}
|
||||
if truststore != nil {
|
||||
_, err = verifyCertChain(ee, p7.Certificates, truststore, currentTime)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
sigalg, err := getSignatureAlgorithm(signer.DigestEncryptionAlgorithm, signer.DigestAlgorithm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ee.CheckSignature(sigalg, signedData, signer.EncryptedDigest)
|
||||
}
|
||||
|
||||
func verifySignature(p7 *PKCS7, signer signerInfo, truststore *smx509.CertPool) (err error) {
|
||||
signedData := p7.Content
|
||||
ee := getCertFromCertsByIssuerAndSerial(p7.Certificates, signer.IssuerAndSerialNumber)
|
||||
if ee == nil {
|
||||
return errors.New("pkcs7: No certificate for signer")
|
||||
}
|
||||
signingTime := time.Now().UTC()
|
||||
if len(signer.AuthenticatedAttributes) > 0 {
|
||||
// TODO(fullsailor): First check the content type match
|
||||
var digest []byte
|
||||
err := unmarshalAttribute(signer.AuthenticatedAttributes, OIDAttributeMessageDigest, &digest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hasher, err := getHashForOID(signer.DigestAlgorithm.Algorithm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h := newHash(hasher, signer.DigestAlgorithm.Algorithm)
|
||||
h.Write(p7.Content)
|
||||
computed := h.Sum(nil)
|
||||
if subtle.ConstantTimeCompare(digest, computed) != 1 {
|
||||
return &MessageDigestMismatchError{
|
||||
ExpectedDigest: digest,
|
||||
ActualDigest: computed,
|
||||
}
|
||||
}
|
||||
signedData, err = marshalAttributes(signer.AuthenticatedAttributes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = unmarshalAttribute(signer.AuthenticatedAttributes, OIDAttributeSigningTime, &signingTime)
|
||||
if err == nil {
|
||||
// signing time found, performing validity check
|
||||
if signingTime.After(ee.NotAfter) || signingTime.Before(ee.NotBefore) {
|
||||
return fmt.Errorf("pkcs7: signing time %q is outside of certificate validity %q to %q",
|
||||
signingTime.Format(time.RFC3339),
|
||||
ee.NotBefore.Format(time.RFC3339),
|
||||
ee.NotAfter.Format(time.RFC3339))
|
||||
}
|
||||
}
|
||||
}
|
||||
if truststore != nil {
|
||||
_, err = verifyCertChain(ee, p7.Certificates, truststore, signingTime)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
sigalg, err := getSignatureAlgorithm(signer.DigestEncryptionAlgorithm, signer.DigestAlgorithm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ee.CheckSignature(sigalg, signedData, signer.EncryptedDigest)
|
||||
}
|
||||
|
||||
// GetOnlySigner returns an x509.Certificate for the first signer of the signed
|
||||
// data payload. If there are more or less than one signer, nil is returned
|
||||
func (p7 *PKCS7) GetOnlySigner() *smx509.Certificate {
|
||||
if len(p7.Signers) != 1 {
|
||||
return nil
|
||||
}
|
||||
signer := p7.Signers[0]
|
||||
return getCertFromCertsByIssuerAndSerial(p7.Certificates, signer.IssuerAndSerialNumber)
|
||||
}
|
||||
|
||||
// UnmarshalSignedAttribute decodes a single attribute from the signer info
|
||||
func (p7 *PKCS7) UnmarshalSignedAttribute(attributeType asn1.ObjectIdentifier, out interface{}) error {
|
||||
sd, ok := p7.raw.(signedData)
|
||||
if !ok {
|
||||
return errors.New("pkcs7: payload is not signedData content")
|
||||
}
|
||||
if len(sd.SignerInfos) < 1 {
|
||||
return errors.New("pkcs7: payload has no signers")
|
||||
}
|
||||
attributes := sd.SignerInfos[0].AuthenticatedAttributes
|
||||
return unmarshalAttribute(attributes, attributeType, out)
|
||||
}
|
||||
|
||||
func parseSignedData(data []byte) (*PKCS7, error) {
|
||||
var sd signedData
|
||||
asn1.Unmarshal(data, &sd)
|
||||
certs, err := sd.Certificates.Parse()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// fmt.Printf("--> Signed Data Version %d\n", sd.Version)
|
||||
|
||||
var compound asn1.RawValue
|
||||
var content unsignedData
|
||||
|
||||
// The Content.Bytes maybe empty on PKI responses.
|
||||
if len(sd.ContentInfo.Content.Bytes) > 0 {
|
||||
if _, err := asn1.Unmarshal(sd.ContentInfo.Content.Bytes, &compound); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// Compound octet string
|
||||
if compound.IsCompound {
|
||||
if compound.Tag == 4 {
|
||||
if _, err = asn1.Unmarshal(compound.Bytes, &content); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
content = compound.Bytes
|
||||
}
|
||||
} else {
|
||||
// assuming this is tag 04
|
||||
content = compound.Bytes
|
||||
}
|
||||
return &PKCS7{
|
||||
Content: content,
|
||||
Certificates: certs,
|
||||
CRLs: sd.CRLs,
|
||||
Signers: sd.SignerInfos,
|
||||
raw: sd}, nil
|
||||
}
|
||||
|
||||
// verifyCertChain takes an end-entity certs, a list of potential intermediates and a
|
||||
// truststore, and built all potential chains between the EE and a trusted root.
|
||||
//
|
||||
// When verifying chains that may have expired, currentTime can be set to a past date
|
||||
// to allow the verification to pass. If unset, currentTime is set to the current UTC time.
|
||||
func verifyCertChain(ee *smx509.Certificate, certs []*smx509.Certificate, truststore *smx509.CertPool, currentTime time.Time) (chains [][]*smx509.Certificate, err error) {
|
||||
intermediates := smx509.NewCertPool()
|
||||
for _, intermediate := range certs {
|
||||
intermediates.AddCert(intermediate)
|
||||
}
|
||||
verifyOptions := smx509.VerifyOptions{
|
||||
Roots: truststore,
|
||||
Intermediates: intermediates,
|
||||
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
|
||||
CurrentTime: currentTime,
|
||||
}
|
||||
chains, err = ee.Verify(verifyOptions)
|
||||
if err != nil {
|
||||
return chains, fmt.Errorf("pkcs7: failed to verify certificate chain: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MessageDigestMismatchError is returned when the signer data digest does not
|
||||
// match the computed digest for the contained content
|
||||
type MessageDigestMismatchError struct {
|
||||
ExpectedDigest []byte
|
||||
ActualDigest []byte
|
||||
}
|
||||
|
||||
func (err *MessageDigestMismatchError) Error() string {
|
||||
return fmt.Sprintf("pkcs7: Message digest mismatch\n\tExpected: %X\n\tActual : %X", err.ExpectedDigest, err.ActualDigest)
|
||||
}
|
||||
|
||||
func getSignatureAlgorithm(digestEncryption, digest pkix.AlgorithmIdentifier) (x509.SignatureAlgorithm, error) {
|
||||
switch {
|
||||
case digestEncryption.Algorithm.Equal(OIDDigestAlgorithmECDSASHA1):
|
||||
return x509.ECDSAWithSHA1, nil
|
||||
case digestEncryption.Algorithm.Equal(OIDDigestAlgorithmECDSASHA256):
|
||||
return x509.ECDSAWithSHA256, nil
|
||||
case digestEncryption.Algorithm.Equal(OIDDigestAlgorithmECDSASHA384):
|
||||
return x509.ECDSAWithSHA384, nil
|
||||
case digestEncryption.Algorithm.Equal(OIDDigestAlgorithmECDSASHA512):
|
||||
return x509.ECDSAWithSHA512, nil
|
||||
case digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmRSA),
|
||||
digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmRSASHA1),
|
||||
digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmRSASHA256),
|
||||
digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmRSASHA384),
|
||||
digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmRSASHA512):
|
||||
switch {
|
||||
case digest.Algorithm.Equal(OIDDigestAlgorithmSHA1):
|
||||
return x509.SHA1WithRSA, nil
|
||||
case digest.Algorithm.Equal(OIDDigestAlgorithmSHA256):
|
||||
return x509.SHA256WithRSA, nil
|
||||
case digest.Algorithm.Equal(OIDDigestAlgorithmSHA384):
|
||||
return x509.SHA384WithRSA, nil
|
||||
case digest.Algorithm.Equal(OIDDigestAlgorithmSHA512):
|
||||
return x509.SHA512WithRSA, nil
|
||||
default:
|
||||
return -1, fmt.Errorf("pkcs7: unsupported digest %q for encryption algorithm %q",
|
||||
digest.Algorithm.String(), digestEncryption.Algorithm.String())
|
||||
}
|
||||
case digestEncryption.Algorithm.Equal(OIDDigestAlgorithmDSA),
|
||||
digestEncryption.Algorithm.Equal(OIDDigestAlgorithmDSASHA1):
|
||||
switch {
|
||||
case digest.Algorithm.Equal(OIDDigestAlgorithmSHA1):
|
||||
return x509.DSAWithSHA1, nil
|
||||
case digest.Algorithm.Equal(OIDDigestAlgorithmSHA256):
|
||||
return x509.DSAWithSHA256, nil
|
||||
default:
|
||||
return -1, fmt.Errorf("pkcs7: unsupported digest %q for encryption algorithm %q",
|
||||
digest.Algorithm.String(), digestEncryption.Algorithm.String())
|
||||
}
|
||||
case digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmECDSAP256),
|
||||
digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmECDSAP384),
|
||||
digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmECDSAP521):
|
||||
switch {
|
||||
case digest.Algorithm.Equal(OIDDigestAlgorithmSHA1):
|
||||
return x509.ECDSAWithSHA1, nil
|
||||
case digest.Algorithm.Equal(OIDDigestAlgorithmSHA256):
|
||||
return x509.ECDSAWithSHA256, nil
|
||||
case digest.Algorithm.Equal(OIDDigestAlgorithmSHA384):
|
||||
return x509.ECDSAWithSHA384, nil
|
||||
case digest.Algorithm.Equal(OIDDigestAlgorithmSHA512):
|
||||
return x509.ECDSAWithSHA512, nil
|
||||
default:
|
||||
return -1, fmt.Errorf("pkcs7: unsupported digest %q for encryption algorithm %q",
|
||||
digest.Algorithm.String(), digestEncryption.Algorithm.String())
|
||||
}
|
||||
case digestEncryption.Algorithm.Equal(OIDDigestEncryptionAlgorithmSM2):
|
||||
return smx509.SM2WithSM3, nil
|
||||
default:
|
||||
return -1, fmt.Errorf("pkcs7: unsupported algorithm %q",
|
||||
digestEncryption.Algorithm.String())
|
||||
}
|
||||
}
|
||||
|
||||
func getCertFromCertsByIssuerAndSerial(certs []*smx509.Certificate, ias issuerAndSerial) *smx509.Certificate {
|
||||
for _, cert := range certs {
|
||||
if isCertMatchForIssuerAndSerial(cert, ias) {
|
||||
return cert
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func unmarshalAttribute(attrs []attribute, attributeType asn1.ObjectIdentifier, out interface{}) error {
|
||||
for _, attr := range attrs {
|
||||
if attr.Type.Equal(attributeType) {
|
||||
_, err := asn1.Unmarshal(attr.Value.Bytes, out)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return errors.New("pkcs7: attribute type not in attributes")
|
||||
}
|
599
pkcs7/verify_test.go
Normal file
599
pkcs7/verify_test.go
Normal file
@ -0,0 +1,599 @@
|
||||
package pkcs7
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/emmansun/gmsm/smx509"
|
||||
)
|
||||
|
||||
func TestVerify(t *testing.T) {
|
||||
fixture := UnmarshalTestFixture(SignedTestFixture)
|
||||
p7, err := Parse(fixture.Input)
|
||||
if err != nil {
|
||||
t.Errorf("Parse encountered unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if err := p7.Verify(); err != nil {
|
||||
t.Errorf("Verify failed with error: %v", err)
|
||||
}
|
||||
expected := []byte("We the People")
|
||||
if !bytes.Equal(p7.Content, expected) {
|
||||
t.Errorf("Signed content does not match.\n\tExpected:%s\n\tActual:%s", expected, p7.Content)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
var SignedTestFixture = `
|
||||
-----BEGIN PKCS7-----
|
||||
MIIDVgYJKoZIhvcNAQcCoIIDRzCCA0MCAQExCTAHBgUrDgMCGjAcBgkqhkiG9w0B
|
||||
BwGgDwQNV2UgdGhlIFBlb3BsZaCCAdkwggHVMIIBQKADAgECAgRpuDctMAsGCSqG
|
||||
SIb3DQEBCzApMRAwDgYDVQQKEwdBY21lIENvMRUwEwYDVQQDEwxFZGRhcmQgU3Rh
|
||||
cmswHhcNMTUwNTA2MDQyNDQ4WhcNMTYwNTA2MDQyNDQ4WjAlMRAwDgYDVQQKEwdB
|
||||
Y21lIENvMREwDwYDVQQDEwhKb24gU25vdzCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
|
||||
gYkCgYEAqr+tTF4mZP5rMwlXp1y+crRtFpuLXF1zvBZiYMfIvAHwo1ta8E1IcyEP
|
||||
J1jIiKMcwbzeo6kAmZzIJRCTezq9jwXUsKbQTvcfOH9HmjUmXBRWFXZYoQs/OaaF
|
||||
a45deHmwEeMQkuSWEtYiVKKZXtJOtflKIT3MryJEDiiItMkdybUCAwEAAaMSMBAw
|
||||
DgYDVR0PAQH/BAQDAgCgMAsGCSqGSIb3DQEBCwOBgQDK1EweZWRL+f7Z+J0kVzY8
|
||||
zXptcBaV4Lf5wGZJLJVUgp33bpLNpT3yadS++XQJ+cvtW3wADQzBSTMduyOF8Zf+
|
||||
L7TjjrQ2+F2HbNbKUhBQKudxTfv9dJHdKbD+ngCCdQJYkIy2YexsoNG0C8nQkggy
|
||||
axZd/J69xDVx6pui3Sj8sDGCATYwggEyAgEBMDEwKTEQMA4GA1UEChMHQWNtZSBD
|
||||
bzEVMBMGA1UEAxMMRWRkYXJkIFN0YXJrAgRpuDctMAcGBSsOAwIaoGEwGAYJKoZI
|
||||
hvcNAQkDMQsGCSqGSIb3DQEHATAgBgkqhkiG9w0BCQUxExcRMTUwNTA2MDAyNDQ4
|
||||
LTA0MDAwIwYJKoZIhvcNAQkEMRYEFG9D7gcTh9zfKiYNJ1lgB0yTh4sZMAsGCSqG
|
||||
SIb3DQEBAQSBgFF3sGDU9PtXty/QMtpcFa35vvIOqmWQAIZt93XAskQOnBq4OloX
|
||||
iL9Ct7t1m4pzjRm0o9nDkbaSLZe7HKASHdCqijroScGlI8M+alJ8drHSFv6ZIjnM
|
||||
FIwIf0B2Lko6nh9/6mUXq7tbbIHa3Gd1JUVire/QFFtmgRXMbXYk8SIS
|
||||
-----END PKCS7-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIB1TCCAUCgAwIBAgIEabg3LTALBgkqhkiG9w0BAQswKTEQMA4GA1UEChMHQWNt
|
||||
ZSBDbzEVMBMGA1UEAxMMRWRkYXJkIFN0YXJrMB4XDTE1MDUwNjA0MjQ0OFoXDTE2
|
||||
MDUwNjA0MjQ0OFowJTEQMA4GA1UEChMHQWNtZSBDbzERMA8GA1UEAxMISm9uIFNu
|
||||
b3cwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKq/rUxeJmT+azMJV6dcvnK0
|
||||
bRabi1xdc7wWYmDHyLwB8KNbWvBNSHMhDydYyIijHMG83qOpAJmcyCUQk3s6vY8F
|
||||
1LCm0E73Hzh/R5o1JlwUVhV2WKELPzmmhWuOXXh5sBHjEJLklhLWIlSimV7STrX5
|
||||
SiE9zK8iRA4oiLTJHcm1AgMBAAGjEjAQMA4GA1UdDwEB/wQEAwIAoDALBgkqhkiG
|
||||
9w0BAQsDgYEAytRMHmVkS/n+2fidJFc2PM16bXAWleC3+cBmSSyVVIKd926SzaU9
|
||||
8mnUvvl0CfnL7Vt8AA0MwUkzHbsjhfGX/i+04460Nvhdh2zWylIQUCrncU37/XSR
|
||||
3Smw/p4AgnUCWJCMtmHsbKDRtAvJ0JIIMmsWXfyevcQ1ceqbot0o/LA=
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIICXgIBAAKBgQCqv61MXiZk/mszCVenXL5ytG0Wm4tcXXO8FmJgx8i8AfCjW1rw
|
||||
TUhzIQ8nWMiIoxzBvN6jqQCZnMglEJN7Or2PBdSwptBO9x84f0eaNSZcFFYVdlih
|
||||
Cz85poVrjl14ebAR4xCS5JYS1iJUople0k61+UohPcyvIkQOKIi0yR3JtQIDAQAB
|
||||
AoGBAIPLCR9N+IKxodq11lNXEaUFwMHXc1zqwP8no+2hpz3+nVfplqqubEJ4/PJY
|
||||
5AgbJoIfnxVhyBXJXu7E+aD/OPneKZrgp58YvHKgGvvPyJg2gpC/1Fh0vQB0HNpI
|
||||
1ZzIZUl8ZTUtVgtnCBUOh5JGI4bFokAqrT//Uvcfd+idgxqBAkEA1ZbP/Kseld14
|
||||
qbWmgmU5GCVxsZRxgR1j4lG3UVjH36KXMtRTm1atAam1uw3OEGa6Y3ANjpU52FaB
|
||||
Hep5rkk4FQJBAMynMo1L1uiN5GP+KYLEF5kKRxK+FLjXR0ywnMh+gpGcZDcOae+J
|
||||
+t1gLoWBIESH/Xt639T7smuSfrZSA9V0EyECQA8cvZiWDvLxmaEAXkipmtGPjKzQ
|
||||
4PsOtkuEFqFl07aKDYKmLUg3aMROWrJidqsIabWxbvQgsNgSvs38EiH3wkUCQQCg
|
||||
ndxb7piVXb9RBwm3OoU2tE1BlXMX+sVXmAkEhd2dwDsaxrI3sHf1xGXem5AimQRF
|
||||
JBOFyaCnMotGNioSHY5hAkEAxyXcNixQ2RpLXJTQZtwnbk0XDcbgB+fBgXnv/4f3
|
||||
BCvcu85DqJeJyQv44Oe1qsXEX9BfcQIOVaoep35RPlKi9g==
|
||||
-----END PRIVATE KEY-----`
|
||||
|
||||
func TestVerifyAppStore(t *testing.T) {
|
||||
fixture := UnmarshalTestFixture(AppStoreReceiptFixture)
|
||||
p7, err := Parse(fixture.Input)
|
||||
if err != nil {
|
||||
t.Errorf("Parse encountered unexpected error: %v", err)
|
||||
}
|
||||
if err := p7.Verify(); err != nil {
|
||||
t.Errorf("Verify failed with error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
var AppStoreReceiptFixture = `
|
||||
-----BEGIN PKCS7-----
|
||||
MIITtgYJKoZIhvcNAQcCoIITpzCCE6MCAQExCzAJBgUrDgMCGgUAMIIDVwYJKoZI
|
||||
hvcNAQcBoIIDSASCA0QxggNAMAoCAQgCAQEEAhYAMAoCARQCAQEEAgwAMAsCAQEC
|
||||
AQEEAwIBADALAgEDAgEBBAMMATEwCwIBCwIBAQQDAgEAMAsCAQ8CAQEEAwIBADAL
|
||||
AgEQAgEBBAMCAQAwCwIBGQIBAQQDAgEDMAwCAQoCAQEEBBYCNCswDAIBDgIBAQQE
|
||||
AgIAjTANAgENAgEBBAUCAwFgvTANAgETAgEBBAUMAzEuMDAOAgEJAgEBBAYCBFAy
|
||||
NDcwGAIBAgIBAQQQDA5jb20uemhpaHUudGVzdDAYAgEEAgECBBCS+ZODNMHwT1Nz
|
||||
gWYDXyWZMBsCAQACAQEEEwwRUHJvZHVjdGlvblNhbmRib3gwHAIBBQIBAQQU4nRh
|
||||
YCEZx70Flzv7hvJRjJZckYIwHgIBDAIBAQQWFhQyMDE2LTA3LTIzVDA2OjIxOjEx
|
||||
WjAeAgESAgEBBBYWFDIwMTMtMDgtMDFUMDc6MDA6MDBaMD0CAQYCAQEENbR21I+a
|
||||
8+byMXo3NPRoDWQmSXQF2EcCeBoD4GaL//ZCRETp9rGFPSg1KekCP7Kr9HAqw09m
|
||||
MEICAQcCAQEEOlVJozYYBdugybShbiiMsejDMNeCbZq6CrzGBwW6GBy+DGWxJI91
|
||||
Y3ouXN4TZUhuVvLvN1b0m5T3ggQwggFaAgERAgEBBIIBUDGCAUwwCwICBqwCAQEE
|
||||
AhYAMAsCAgatAgEBBAIMADALAgIGsAIBAQQCFgAwCwICBrICAQEEAgwAMAsCAgaz
|
||||
AgEBBAIMADALAgIGtAIBAQQCDAAwCwICBrUCAQEEAgwAMAsCAga2AgEBBAIMADAM
|
||||
AgIGpQIBAQQDAgEBMAwCAgarAgEBBAMCAQEwDAICBq4CAQEEAwIBADAMAgIGrwIB
|
||||
AQQDAgEAMAwCAgaxAgEBBAMCAQAwGwICBqcCAQEEEgwQMTAwMDAwMDIyNTMyNTkw
|
||||
MTAbAgIGqQIBAQQSDBAxMDAwMDAwMjI1MzI1OTAxMB8CAgaoAgEBBBYWFDIwMTYt
|
||||
MDctMjNUMDY6MjE6MTFaMB8CAgaqAgEBBBYWFDIwMTYtMDctMjNUMDY6MjE6MTFa
|
||||
MCACAgamAgEBBBcMFWNvbS56aGlodS50ZXN0LnRlc3RfMaCCDmUwggV8MIIEZKAD
|
||||
AgECAggO61eH554JjTANBgkqhkiG9w0BAQUFADCBljELMAkGA1UEBhMCVVMxEzAR
|
||||
BgNVBAoMCkFwcGxlIEluYy4xLDAqBgNVBAsMI0FwcGxlIFdvcmxkd2lkZSBEZXZl
|
||||
bG9wZXIgUmVsYXRpb25zMUQwQgYDVQQDDDtBcHBsZSBXb3JsZHdpZGUgRGV2ZWxv
|
||||
cGVyIFJlbGF0aW9ucyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xNTExMTMw
|
||||
MjE1MDlaFw0yMzAyMDcyMTQ4NDdaMIGJMTcwNQYDVQQDDC5NYWMgQXBwIFN0b3Jl
|
||||
IGFuZCBpVHVuZXMgU3RvcmUgUmVjZWlwdCBTaWduaW5nMSwwKgYDVQQLDCNBcHBs
|
||||
ZSBXb3JsZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczETMBEGA1UECgwKQXBwbGUg
|
||||
SW5jLjELMAkGA1UEBhMCVVMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
|
||||
AQClz4H9JaKBW9aH7SPaMxyO4iPApcQmyz3Gn+xKDVWG/6QC15fKOVRtfX+yVBid
|
||||
xCxScY5ke4LOibpJ1gjltIhxzz9bRi7GxB24A6lYogQ+IXjV27fQjhKNg0xbKmg3
|
||||
k8LyvR7E0qEMSlhSqxLj7d0fmBWQNS3CzBLKjUiB91h4VGvojDE2H0oGDEdU8zeQ
|
||||
uLKSiX1fpIVK4cCc4Lqku4KXY/Qrk8H9Pm/KwfU8qY9SGsAlCnYO3v6Z/v/Ca/Vb
|
||||
XqxzUUkIVonMQ5DMjoEC0KCXtlyxoWlph5AQaCYmObgdEHOwCl3Fc9DfdjvYLdmI
|
||||
HuPsB8/ijtDT+iZVge/iA0kjAgMBAAGjggHXMIIB0zA/BggrBgEFBQcBAQQzMDEw
|
||||
LwYIKwYBBQUHMAGGI2h0dHA6Ly9vY3NwLmFwcGxlLmNvbS9vY3NwMDMtd3dkcjA0
|
||||
MB0GA1UdDgQWBBSRpJz8xHa3n6CK9E31jzZd7SsEhTAMBgNVHRMBAf8EAjAAMB8G
|
||||
A1UdIwQYMBaAFIgnFwmpthhgi+zruvZHWcVSVKO3MIIBHgYDVR0gBIIBFTCCAREw
|
||||
ggENBgoqhkiG92NkBQYBMIH+MIHDBggrBgEFBQcCAjCBtgyBs1JlbGlhbmNlIG9u
|
||||
IHRoaXMgY2VydGlmaWNhdGUgYnkgYW55IHBhcnR5IGFzc3VtZXMgYWNjZXB0YW5j
|
||||
ZSBvZiB0aGUgdGhlbiBhcHBsaWNhYmxlIHN0YW5kYXJkIHRlcm1zIGFuZCBjb25k
|
||||
aXRpb25zIG9mIHVzZSwgY2VydGlmaWNhdGUgcG9saWN5IGFuZCBjZXJ0aWZpY2F0
|
||||
aW9uIHByYWN0aWNlIHN0YXRlbWVudHMuMDYGCCsGAQUFBwIBFipodHRwOi8vd3d3
|
||||
LmFwcGxlLmNvbS9jZXJ0aWZpY2F0ZWF1dGhvcml0eS8wDgYDVR0PAQH/BAQDAgeA
|
||||
MBAGCiqGSIb3Y2QGCwEEAgUAMA0GCSqGSIb3DQEBBQUAA4IBAQANphvTLj3jWysH
|
||||
bkKWbNPojEMwgl/gXNGNvr0PvRr8JZLbjIXDgFnf4+LXLgUUrA3btrj+/DUufMut
|
||||
F2uOfx/kd7mxZ5W0E16mGYZ2+FogledjjA9z/Ojtxh+umfhlSFyg4Cg6wBA3Lbmg
|
||||
BDkfc7nIBf3y3n8aKipuKwH8oCBc2et9J6Yz+PWY4L5E27FMZ/xuCk/J4gao0pfz
|
||||
p45rUaJahHVl0RYEYuPBX/UIqc9o2ZIAycGMs/iNAGS6WGDAfK+PdcppuVsq1h1o
|
||||
bphC9UynNxmbzDscehlD86Ntv0hgBgw2kivs3hi1EdotI9CO/KBpnBcbnoB7OUdF
|
||||
MGEvxxOoMIIEIjCCAwqgAwIBAgIIAd68xDltoBAwDQYJKoZIhvcNAQEFBQAwYjEL
|
||||
MAkGA1UEBhMCVVMxEzARBgNVBAoTCkFwcGxlIEluYy4xJjAkBgNVBAsTHUFwcGxl
|
||||
IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRYwFAYDVQQDEw1BcHBsZSBSb290IENB
|
||||
MB4XDTEzMDIwNzIxNDg0N1oXDTIzMDIwNzIxNDg0N1owgZYxCzAJBgNVBAYTAlVT
|
||||
MRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3JsZHdpZGUg
|
||||
RGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3aWRlIERl
|
||||
dmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0G
|
||||
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDKOFSmy1aqyCQ5SOmM7uxfuH8mkbw0
|
||||
U3rOfGOAYXdkXqUHI7Y5/lAtFVZYcC1+xG7BSoU+L/DehBqhV8mvexj/avoVEkkV
|
||||
CBmsqtsqMu2WY2hSFT2Miuy/axiV4AOsAX2XBWfODoWVN2rtCbauZ81RZJ/GXNG8
|
||||
V25nNYB2NqSHgW44j9grFU57Jdhav06DwY3Sk9UacbVgnJ0zTlX5ElgMhrgWDcHl
|
||||
d0WNUEi6Ky3klIXh6MSdxmilsKP8Z35wugJZS3dCkTm59c3hTO/AO0iMpuUhXf1q
|
||||
arunFjVg0uat80YpyejDi+l5wGphZxWy8P3laLxiX27Pmd3vG2P+kmWrAgMBAAGj
|
||||
gaYwgaMwHQYDVR0OBBYEFIgnFwmpthhgi+zruvZHWcVSVKO3MA8GA1UdEwEB/wQF
|
||||
MAMBAf8wHwYDVR0jBBgwFoAUK9BpR5R2Cf70a40uQKb3R01/CF4wLgYDVR0fBCcw
|
||||
JTAjoCGgH4YdaHR0cDovL2NybC5hcHBsZS5jb20vcm9vdC5jcmwwDgYDVR0PAQH/
|
||||
BAQDAgGGMBAGCiqGSIb3Y2QGAgEEAgUAMA0GCSqGSIb3DQEBBQUAA4IBAQBPz+9Z
|
||||
viz1smwvj+4ThzLoBTWobot9yWkMudkXvHcs1Gfi/ZptOllc34MBvbKuKmFysa/N
|
||||
w0Uwj6ODDc4dR7Txk4qjdJukw5hyhzs+r0ULklS5MruQGFNrCk4QttkdUGwhgAqJ
|
||||
TleMa1s8Pab93vcNIx0LSiaHP7qRkkykGRIZbVf1eliHe2iK5IaMSuviSRSqpd1V
|
||||
AKmuu0swruGgsbwpgOYJd+W+NKIByn/c4grmO7i77LpilfMFY0GCzQ87HUyVpNur
|
||||
+cmV6U/kTecmmYHpvPm0KdIBembhLoz2IYrF+Hjhga6/05Cdqa3zr/04GpZnMBxR
|
||||
pVzscYqCtGwPDBUfMIIEuzCCA6OgAwIBAgIBAjANBgkqhkiG9w0BAQUFADBiMQsw
|
||||
CQYDVQQGEwJVUzETMBEGA1UEChMKQXBwbGUgSW5jLjEmMCQGA1UECxMdQXBwbGUg
|
||||
Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkxFjAUBgNVBAMTDUFwcGxlIFJvb3QgQ0Ew
|
||||
HhcNMDYwNDI1MjE0MDM2WhcNMzUwMjA5MjE0MDM2WjBiMQswCQYDVQQGEwJVUzET
|
||||
MBEGA1UEChMKQXBwbGUgSW5jLjEmMCQGA1UECxMdQXBwbGUgQ2VydGlmaWNhdGlv
|
||||
biBBdXRob3JpdHkxFjAUBgNVBAMTDUFwcGxlIFJvb3QgQ0EwggEiMA0GCSqGSIb3
|
||||
DQEBAQUAA4IBDwAwggEKAoIBAQDkkakJH5HbHkdQ6wXtXnmELes2oldMVeyLGYne
|
||||
+Uts9QerIjAC6Bg++FAJ039BqJj50cpmnCRrEdCju+QbKsMflZ56DKRHi1vUFjcz
|
||||
y8QPTc4UadHJGXL1XQ7Vf1+b8iUDulWPTV0N8WQ1IxVLFVkds5T39pyez1C6wVhQ
|
||||
Z48ItCD3y6wsIG9wtj8BMIy3Q88PnT3zK0koGsj+zrW5DtleHNbLPbU6rfQPDgCS
|
||||
C7EhFi501TwN22IWq6NxkkdTVcGvL0Gz+PvjcM3mo0xFfh9Ma1CWQYnEdGILEINB
|
||||
hzOKgbEwWOxaBDKMaLOPHd5lc/9nXmW8Sdh2nzMUZaF3lMktAgMBAAGjggF6MIIB
|
||||
djAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUK9Bp
|
||||
R5R2Cf70a40uQKb3R01/CF4wHwYDVR0jBBgwFoAUK9BpR5R2Cf70a40uQKb3R01/
|
||||
CF4wggERBgNVHSAEggEIMIIBBDCCAQAGCSqGSIb3Y2QFATCB8jAqBggrBgEFBQcC
|
||||
ARYeaHR0cHM6Ly93d3cuYXBwbGUuY29tL2FwcGxlY2EvMIHDBggrBgEFBQcCAjCB
|
||||
thqBs1JlbGlhbmNlIG9uIHRoaXMgY2VydGlmaWNhdGUgYnkgYW55IHBhcnR5IGFz
|
||||
c3VtZXMgYWNjZXB0YW5jZSBvZiB0aGUgdGhlbiBhcHBsaWNhYmxlIHN0YW5kYXJk
|
||||
IHRlcm1zIGFuZCBjb25kaXRpb25zIG9mIHVzZSwgY2VydGlmaWNhdGUgcG9saWN5
|
||||
IGFuZCBjZXJ0aWZpY2F0aW9uIHByYWN0aWNlIHN0YXRlbWVudHMuMA0GCSqGSIb3
|
||||
DQEBBQUAA4IBAQBcNplMLXi37Yyb3PN3m/J20ncwT8EfhYOFG5k9RzfyqZtAjizU
|
||||
sZAS2L70c5vu0mQPy3lPNNiiPvl4/2vIB+x9OYOLUyDTOMSxv5pPCmv/K/xZpwUJ
|
||||
fBdAVhEedNO3iyM7R6PVbyTi69G3cN8PReEnyvFteO3ntRcXqNx+IjXKJdXZD9Zr
|
||||
1KIkIxH3oayPc4FgxhtbCS+SsvhESPBgOJ4V9T0mZyCKM2r3DYLP3uujL/lTaltk
|
||||
wGMzd/c6ByxW69oPIQ7aunMZT7XZNn/Bh1XZp5m5MkL72NVxnn6hUrcbvZNCJBIq
|
||||
xw8dtk2cXmPIS4AXUKqK1drk/NAJBzewdXUhMYIByzCCAccCAQEwgaMwgZYxCzAJ
|
||||
BgNVBAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBX
|
||||
b3JsZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29y
|
||||
bGR3aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
|
||||
dHkCCA7rV4fnngmNMAkGBSsOAwIaBQAwDQYJKoZIhvcNAQEBBQAEggEAasPtnide
|
||||
NWyfUtewW9OSgcQA8pW+5tWMR0469cBPZR84uJa0gyfmPspySvbNOAwnrwzZHYLa
|
||||
ujOxZLip4DUw4F5s3QwUa3y4BXpF4J+NSn9XNvxNtnT/GcEQtCuFwgJ0o3F0ilhv
|
||||
MTHrwiwyx/vr+uNDqlORK8lfK+1qNp+A/kzh8eszMrn4JSeTh9ZYxLHE56WkTQGD
|
||||
VZXl0gKgxSOmDrcp1eQxdlymzrPv9U60wUJ0bkPfrU9qZj3mJrmrkQk61JTe3j6/
|
||||
QfjfFBG9JG2mUmYQP1KQ3SypGHzDW8vngvsGu//tNU0NFfOqQu4bYU4VpQl0nPtD
|
||||
4B85NkrgvQsWAQ==
|
||||
-----END PKCS7-----`
|
||||
|
||||
func TestVerifyApkEcdsa(t *testing.T) {
|
||||
fixture := UnmarshalTestFixture(ApkEcdsaFixture)
|
||||
p7, err := Parse(fixture.Input)
|
||||
if err != nil {
|
||||
t.Errorf("Parse encountered unexpected error: %v", err)
|
||||
}
|
||||
p7.Content, err = base64.StdEncoding.DecodeString(ApkEcdsaContent)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to decode base64 signature file: %v", err)
|
||||
}
|
||||
if err := p7.Verify(); err != nil {
|
||||
t.Errorf("Verify failed with error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
var ApkEcdsaFixture = `-----BEGIN PKCS7-----
|
||||
MIIDAgYJKoZIhvcNAQcCoIIC8zCCAu8CAQExDzANBglghkgBZQMEAgMFADALBgkq
|
||||
hkiG9w0BBwGgggH3MIIB8zCCAVSgAwIBAgIJAOxXdFsvm3YiMAoGCCqGSM49BAME
|
||||
MBIxEDAOBgNVBAMMB2VjLXA1MjEwHhcNMTYwMzMxMTUzMTIyWhcNNDMwODE3MTUz
|
||||
MTIyWjASMRAwDgYDVQQDDAdlYy1wNTIxMIGbMBAGByqGSM49AgEGBSuBBAAjA4GG
|
||||
AAQAYX95sSjPEQqgyLD04tNUyq9y/w8seblOpfqa/Amx6H4GFdrjGXX0YTfXKr9G
|
||||
hAyIyQSnNrIg0zDlWQUbBPRW4CYBLFOg1pUn1NBhKFD4NtO1KWvYtNOYDegFjRCP
|
||||
B0p+fEXDbq8QFDYvlh+NZUJ16+ih8XNIf1C29xuLEqN6oKOnAvajUDBOMB0GA1Ud
|
||||
DgQWBBT/Ra3kz60gQ7tYk3byZckcLabt8TAfBgNVHSMEGDAWgBT/Ra3kz60gQ7tY
|
||||
k3byZckcLabt8TAMBgNVHRMEBTADAQH/MAoGCCqGSM49BAMEA4GMADCBiAJCAP39
|
||||
hYLsWk2H84oEw+HJqGGjexhqeD3vSO1mWhopripE/81oy3yV10puYoJe11xDSfcD
|
||||
j2VfNCHazuXO3kSxGA/1AkIBLUJxp/WYbYzhBGKr6lcxczKI/wuMfkZ6vL+0EMJV
|
||||
A/2uEoeqvnl7BsdkicyaOBNEADijuVdaPPIWzKClt9OaVxExgdAwgc0CAQEwHzAS
|
||||
MRAwDgYDVQQDDAdlYy1wNTIxAgkA7Fd0Wy+bdiIwDQYJYIZIAWUDBAIDBQAwCgYI
|
||||
KoZIzj0EAwQEgYswgYgCQgD1pVSNo7qTm9A6tpt3SU2yRa+xpJAnUbpZ+Gu36B71
|
||||
JnQBUzRgTGevniqHpyagi7b2zjWh1uvfz9FfrITUwGMddgJCAPjiBRcl7rKpxmZn
|
||||
V1MvcJOX41xRSJu1wmBiYXqaJarL+gQ/Wl7RYsMtqLjmNColvLaHNxCaWOO/8nAE
|
||||
Hg0OMA60
|
||||
-----END PKCS7-----`
|
||||
|
||||
var ApkEcdsaContent = `U2lnbmF0dXJlLVZlcnNpb246IDEuMA0KU0hBLTUxMi1EaWdlc3QtTWFuaWZlc3Q6IFAvVDRqSWtTMjQvNzFxeFE2WW1MeEtNdkRPUUF0WjUxR090dFRzUU9yemhHRQ0KIEpaUGVpWUtyUzZYY090bStYaWlFVC9uS2tYdWVtUVBwZ2RBRzFKUzFnPT0NCkNyZWF0ZWQtQnk6IDEuMCAoQW5kcm9pZCBTaWduQXBrKQ0KDQpOYW1lOiBBbmRyb2lkTWFuaWZlc3QueG1sDQpTSEEtNTEyLURpZ2VzdDogcm9NbWVQZmllYUNQSjFJK2VzMVpsYis0anB2UXowNHZqRWVpL2U0dkN1ald0VVVWSHEzMkNXDQogMUxsOHZiZGMzMCtRc1FlN29ibld4dmhLdXN2K3c1a2c9PQ0KDQpOYW1lOiByZXNvdXJjZXMuYXJzYw0KU0hBLTUxMi1EaWdlc3Q6IG5aYW1aUzlPZTRBRW41cEZaaCtoQ1JFT3krb1N6a3hHdU5YZU0wUFF6WGVBVlVQV3hSVzFPYQ0KIGVLbThRbXdmTmhhaS9HOEcwRUhIbHZEQWdlcy9HUGtBPT0NCg0KTmFtZTogY2xhc3Nlcy5kZXgNClNIQS01MTItRGlnZXN0OiBlbWlDQld2bkVSb0g2N2lCa3EwcUgrdm5tMkpaZDlMWUNEV051N3RNYzJ3bTRtV0dYSUVpWmcNCiBWZkVPV083MFRlZnFjUVhldkNtN2hQMnRpT0U3Y0w5UT09DQoNCg==`
|
||||
|
||||
func TestVerifyFirefoxAddon(t *testing.T) {
|
||||
fixture := UnmarshalTestFixture(FirefoxAddonFixture)
|
||||
p7, err := Parse(fixture.Input)
|
||||
if err != nil {
|
||||
t.Errorf("Parse encountered unexpected error: %v", err)
|
||||
}
|
||||
p7.Content = FirefoxAddonContent
|
||||
certPool := smx509.NewCertPool()
|
||||
certPool.AppendCertsFromPEM(FirefoxAddonRootCert)
|
||||
// verifies at the signingTime authenticated attr
|
||||
if err := p7.VerifyWithChain(certPool); err != nil {
|
||||
t.Errorf("Verify failed with error: %v", err)
|
||||
}
|
||||
|
||||
// The chain has validity:
|
||||
//
|
||||
// EE: 2016-08-17 20:04:58 +0000 UTC 2021-08-16 20:04:58 +0000 UTC
|
||||
// Intermediate: 2015-03-17 23:52:42 +0000 UTC 2025-03-14 23:52:42 +0000 UTC
|
||||
// Root: 2015-03-17 22:53:57 +0000 UTC 2025-03-14 22:53:57 +0000 UTC
|
||||
validTime := time.Date(2021, 8, 16, 20, 0, 0, 0, time.UTC)
|
||||
if err = p7.VerifyWithChainAtTime(certPool, validTime); err != nil {
|
||||
t.Errorf("Verify at UTC now failed with error: %v", err)
|
||||
}
|
||||
|
||||
expiredTime := time.Date(2030, time.January, 1, 0, 0, 0, 0, time.UTC)
|
||||
if err = p7.VerifyWithChainAtTime(certPool, expiredTime); err == nil {
|
||||
t.Errorf("Verify at expired time %s did not error", expiredTime)
|
||||
}
|
||||
notYetValidTime := time.Date(1999, time.July, 5, 0, 13, 0, 0, time.UTC)
|
||||
if err = p7.VerifyWithChainAtTime(certPool, notYetValidTime); err == nil {
|
||||
t.Errorf("Verify at not yet valid time %s did not error", notYetValidTime)
|
||||
}
|
||||
|
||||
// Verify the certificate chain to make sure the identified root
|
||||
// is the one we expect
|
||||
ee := getCertFromCertsByIssuerAndSerial(p7.Certificates, p7.Signers[0].IssuerAndSerialNumber)
|
||||
if ee == nil {
|
||||
t.Errorf("No end-entity certificate found for signer")
|
||||
}
|
||||
signingTime := mustParseTime("2017-02-23T09:06:16-05:00")
|
||||
chains, err := verifyCertChain(ee, p7.Certificates, certPool, signingTime)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if len(chains) != 1 {
|
||||
t.Errorf("Expected to find one chain, but found %d", len(chains))
|
||||
}
|
||||
if len(chains[0]) != 3 {
|
||||
t.Errorf("Expected to find three certificates in chain, but found %d", len(chains[0]))
|
||||
}
|
||||
if chains[0][0].Subject.CommonName != "tabscope@xuldev.org" {
|
||||
t.Errorf("Expected to find EE certificate with subject 'tabscope@xuldev.org', but found '%s'", chains[0][0].Subject.CommonName)
|
||||
}
|
||||
if chains[0][1].Subject.CommonName != "production-signing-ca.addons.mozilla.org" {
|
||||
t.Errorf("Expected to find intermediate certificate with subject 'production-signing-ca.addons.mozilla.org', but found '%s'", chains[0][1].Subject.CommonName)
|
||||
}
|
||||
if chains[0][2].Subject.CommonName != "root-ca-production-amo" {
|
||||
t.Errorf("Expected to find root certificate with subject 'root-ca-production-amo', but found '%s'", chains[0][2].Subject.CommonName)
|
||||
}
|
||||
}
|
||||
|
||||
func mustParseTime(s string) time.Time {
|
||||
t, err := time.Parse(time.RFC3339, s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
var FirefoxAddonContent = []byte(`Signature-Version: 1.0
|
||||
MD5-Digest-Manifest: KjRavc6/KNpuT1QLcB/Gsg==
|
||||
SHA1-Digest-Manifest: 5Md5nUg+U7hQ/UfzV+xGKWOruVI=
|
||||
|
||||
`)
|
||||
|
||||
var FirefoxAddonFixture = `
|
||||
-----BEGIN PKCS7-----
|
||||
MIIQTAYJKoZIhvcNAQcCoIIQPTCCEDkCAQExCzAJBgUrDgMCGgUAMAsGCSqGSIb3
|
||||
DQEHAaCCDL0wggW6MIIDoqADAgECAgYBVpobWVwwDQYJKoZIhvcNAQELBQAwgcUx
|
||||
CzAJBgNVBAYTAlVTMRwwGgYDVQQKExNNb3ppbGxhIENvcnBvcmF0aW9uMS8wLQYD
|
||||
VQQLEyZNb3ppbGxhIEFNTyBQcm9kdWN0aW9uIFNpZ25pbmcgU2VydmljZTExMC8G
|
||||
A1UEAxMocHJvZHVjdGlvbi1zaWduaW5nLWNhLmFkZG9ucy5tb3ppbGxhLm9yZzE0
|
||||
MDIGCSqGSIb3DQEJARYlc2VydmljZXMtb3BzK2FkZG9uc2lnbmluZ0Btb3ppbGxh
|
||||
LmNvbTAeFw0xNjA4MTcyMDA0NThaFw0yMTA4MTYyMDA0NThaMHYxEzARBgNVBAsT
|
||||
ClByb2R1Y3Rpb24xCzAJBgNVBAYTAlVTMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3
|
||||
MQ8wDQYDVQQKEwZBZGRvbnMxCzAJBgNVBAgTAkNBMRwwGgYDVQQDFBN0YWJzY29w
|
||||
ZUB4dWxkZXYub3JnMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAv6e0
|
||||
mPD8dt4J8HTNNq4ODns2DV6Weh1hllCIFvOeu1u3UrR03st0BMY8OXYwr/NvRVjg
|
||||
bA8gRySWAL+XqLzbhtXNeNegAoxrF+3mYY5rJjsLj/FGI6P6OXjngqwgm9VTBl7m
|
||||
jh/KXBSwYoUcavJo6cmk8sCFwoblyQiv+tsWaUCOI6zMzubNtIS+GFvET9y/VZMP
|
||||
j6mk8O10wBgJF5MMtA19va3qXy7aCZ7DnZp1l3equd/L6t324TtXoqx6xWQKo6TM
|
||||
I0mcTlKvm6TKegTGBCyGn3JRARoIJv4AW1qqgyaHXf9EoY2pKT8Avkri5++NuSJ6
|
||||
jtO4k/diBA2MZU20U0KGffYZNTxKDqd6XtI6y1tJPd/OWRFyU+mHntkcm9sar7L3
|
||||
nPKujHRox2re10ec1WBnJE3PjlAoesNjxzp+xs2mGGc8DX9NuWn+1uK9xmgGIIMl
|
||||
OFfyQ4s0G6hKp5goFcrFZxmexu0ZahOs8vZf8xDBW7yR1zToQElOXHvrscM386os
|
||||
kOF9IxQZfcCoPuNQVg1haCONNkx0oau3RQQlOSAZtC79b+rBjQ5JYfjRLYAworf2
|
||||
xQaprCh33TD1dTBrvzEbCGszgkN53Vqh5TFBjbU/NyldOkGvK8Xf6WhT5u+aftnV
|
||||
lbuE2McAg6x1AlloUZq6PNTBpz7zypcIISnQ+y8CAwEAATANBgkqhkiG9w0BAQsF
|
||||
AAOCAgEAIBoo2+OEYNCgP/IbUj9azaf/lde1q4AK/uTMoUeS5WcrXd8aqA0Y1qV7
|
||||
xUALgDQAExXgqcOMGu4mPMaoZDgwGI4Tj7XPJQq5Z5zYxpRf/Wtzae33T9BF6QPW
|
||||
v5xiRYuol+FbEtqRHZqxDWtIrd1MWBy3wjO3pLPdzDM9jWh+HLxdGWThJszaZp3T
|
||||
CqsOx+l9W0Q7qM5ioZpHStgXDfhw38Lg++kLnzcX9MqsjYyezdwE4krqW6hK3+4S
|
||||
0LZE4dTgsy8JULkyAF3HrPWEXESnD7c4mx6owZe+BNDK5hsVM/obAqH7sJq/igbM
|
||||
5N1l832p/ws8l5xKOr3qBWSzWn6u7ExvqG6Ckh0foJOVXvzGqvrXcoiBGV8S9Z7c
|
||||
DghUvMt6b0pZ0ildRCHfTUz7eG3g4MhfbjupR7b+L9FWEJhcd/H0dxpw7SKYha/n
|
||||
ePuRL7MXmbW8WLMqO/ImxzL8TPOB3pUg3nITfubV6gpPBmn+0nwbqYUmggJuwgvK
|
||||
I2GpN2Ny6EErZy17EEgyhJygJZMj+UzQjC781xxsl3ljpYEqqwgRLIZBSBUD5dXj
|
||||
XBuU24w162SeSyHZzkBbuv6lr52pqoZyFrG29DCHECgO9ZmNWgSpiWSkh+vExAG7
|
||||
wNs0y61t2HUG+BCMGPQ9sOzouyTfrnLVAWwzswGftFYQfoIBeJIwggb7MIIE46AD
|
||||
AgECAgMQAAIwDQYJKoZIhvcNAQEMBQAwfTELMAkGA1UEBhMCVVMxHDAaBgNVBAoT
|
||||
E01vemlsbGEgQ29ycG9yYXRpb24xLzAtBgNVBAsTJk1vemlsbGEgQU1PIFByb2R1
|
||||
Y3Rpb24gU2lnbmluZyBTZXJ2aWNlMR8wHQYDVQQDExZyb290LWNhLXByb2R1Y3Rp
|
||||
b24tYW1vMB4XDTE1MDMxNzIzNTI0MloXDTI1MDMxNDIzNTI0MlowgcUxCzAJBgNV
|
||||
BAYTAlVTMRwwGgYDVQQKExNNb3ppbGxhIENvcnBvcmF0aW9uMS8wLQYDVQQLEyZN
|
||||
b3ppbGxhIEFNTyBQcm9kdWN0aW9uIFNpZ25pbmcgU2VydmljZTExMC8GA1UEAxMo
|
||||
cHJvZHVjdGlvbi1zaWduaW5nLWNhLmFkZG9ucy5tb3ppbGxhLm9yZzE0MDIGCSqG
|
||||
SIb3DQEJARYlc2VydmljZXMtb3BzK2FkZG9uc2lnbmluZ0Btb3ppbGxhLmNvbTCC
|
||||
AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMLMM9m2HBLhCiO9mhljpehT
|
||||
hxpzlCnxluzDZ51I/H7MvBbIvZBm9zSpHdffubSsak2qYE69d+ebTa/CK83WIosM
|
||||
24/2Qp7n/GGaPJcCC4Y3JkrCsgA8+wV2MbFlKSv+qMdvI/sE3BPYDMCjVPMhHmIP
|
||||
XaPWd42OoHpI8R3GGUtVnR3Hm76pa2+v6TwgeMiO8om+ogGufiyv6FNMZ5NuY1Z9
|
||||
aLNEvehnAzSfddQyki+6FJd7XkgZbP7pb1Kl8yYgiy4piBerJ9H09uPehffE3Ell
|
||||
3cApQL3+0kjaUX4scMjuNQDMKziRZkYgJAM+qA9WA5Jn77AjerQBWQeEev1PWHYh
|
||||
0IDlgS/a0bjKmVjNZYG6adrY/R5/whzWGFCIE1UfhPm6PdN0557qvF838C2RFHsI
|
||||
KzV6KQf0chMjpa02tPaIctjVhnDQZZNKm2ZfLOt9kQ57Is/e6KxH7pYMit46+s99
|
||||
lYM7ZquvWbK19b1Ili/6S1BxSzd3wztgfN5jGsc+jCCYLm+AcVtfNKc8cFZHXKrB
|
||||
CwhGmdbWDSBCicZNA7FKJpO3oIx26VPF2XUldA/T5Mh/POGLilK3t9m9qbjEyDp1
|
||||
EwoBToOR/aMrdnNYvSWp0g/GHMzSfJjjXyAqrZY2itam/IJd8r9FoRAzevPt/zTX
|
||||
BET3INoiCDGRH0XrxUYtAgMGVTejggE5MIIBNTAMBgNVHRMEBTADAQH/MA4GA1Ud
|
||||
DwEB/wQEAwIBBjAWBgNVHSUBAf8EDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUdHxf
|
||||
FKXipZLjs20GqIUdNkQXH4gwgagGA1UdIwSBoDCBnYAUs7zqWHSr4W54KrKrnCMe
|
||||
qGMsl7ehgYGkfzB9MQswCQYDVQQGEwJVUzEcMBoGA1UEChMTTW96aWxsYSBDb3Jw
|
||||
b3JhdGlvbjEvMC0GA1UECxMmTW96aWxsYSBBTU8gUHJvZHVjdGlvbiBTaWduaW5n
|
||||
IFNlcnZpY2UxHzAdBgNVBAMTFnJvb3QtY2EtcHJvZHVjdGlvbi1hbW+CAQEwMwYJ
|
||||
YIZIAYb4QgEEBCYWJGh0dHA6Ly9hZGRvbnMubW96aWxsYS5vcmcvY2EvY3JsLnBl
|
||||
bTANBgkqhkiG9w0BAQwFAAOCAgEArde/fdjb7TE0eH7Ij7xU4JbcSyhY3cQhVYCw
|
||||
Fg+Q/2pj+NAfazcjUuLWA0Y/YZs9HOx6j+ZAqO4C/xfMP4RDs9IypxvzHDU6SXgD
|
||||
RK6uOKtS07HXLcXgFUBvJEQhbT/h5+IQOA4/GcpCshfD6iyiBBi+IocR+tnKPCuZ
|
||||
T3m1t60Eja/MkPKG/Gx8vSodHvlTTsJ2GzjUEANveCZOnlAdp9fjTvFZny9qqnbg
|
||||
sfVbuTqKndbCFW5QLXfkna6jBqMrY0+CpMYY2oJ5gwpHbE/7hhukjxGCTcpv7r/O
|
||||
M53bb/DZnybDlLLepacljvz7DBA1O1FFtEhf9MR+vyvmBpniAyKQhqG2hsVGurE1
|
||||
nBcE+oteZWar2lMp6+etDAb9DRC+jZv0aEQs2o/qQwyD8AGquLgBsJq5Jz3gGxzn
|
||||
4r3vGu2lV8VdzIm0C8sOFSWTmTZxQmJbF8xSsQBojnsvEah4DPER+eAt6qKolaWe
|
||||
s4drJQjzFyC7HJn2VqalpCwbe9CdMB7eRqzeP6GujJBi80/gx0pAysUtuKKpH5IJ
|
||||
WbXAOszfrjb3CaHafYZDnwPoOfj74ogFzjt2f54jwnU+ET/byfjZ7J8SLH316C1V
|
||||
HrvFXcTzyMV4aRluVPjPg9x1G58hMIbeuT4GpwQUNdJ9uL8t65v0XwG2t6Y7jpRO
|
||||
sFVxBtgxggNXMIIDUwIBATCB0DCBxTELMAkGA1UEBhMCVVMxHDAaBgNVBAoTE01v
|
||||
emlsbGEgQ29ycG9yYXRpb24xLzAtBgNVBAsTJk1vemlsbGEgQU1PIFByb2R1Y3Rp
|
||||
b24gU2lnbmluZyBTZXJ2aWNlMTEwLwYDVQQDEyhwcm9kdWN0aW9uLXNpZ25pbmct
|
||||
Y2EuYWRkb25zLm1vemlsbGEub3JnMTQwMgYJKoZIhvcNAQkBFiVzZXJ2aWNlcy1v
|
||||
cHMrYWRkb25zaWduaW5nQG1vemlsbGEuY29tAgYBVpobWVwwCQYFKw4DAhoFAKBd
|
||||
MBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTE2MDgx
|
||||
NzIwMDQ1OFowIwYJKoZIhvcNAQkEMRYEFAxlGvNFSx+Jqj70haE8b7UZk+2GMA0G
|
||||
CSqGSIb3DQEBAQUABIICADsDlrucYRgwq9o2QSsO6X6cRa5Zu6w+1n07PTIyc1zn
|
||||
Pi1cgkkWZ0kZBHDrJ5CY33yRQPl6I1tHXaq7SkOSdOppKhpUmBiKZxQRAZR21QHk
|
||||
R3v1XS+st/o0N+0btv3YoplUifLIwtH89oolxqlStChELu7FuOBretdhx/z12ytA
|
||||
EhIIS53o/XjDL7XKJbQA02vzOtOC/Eq6p8BI7F3y6pvtmJIRkeGv+u6ssJa6g5q8
|
||||
74w8hHXaH94Z9+hDPqjNWlsXJHgPdAKiEjzDz9oLkvDyX4Pd8JMK5ILskirpG+hj
|
||||
Q8jkTc5oYwyuSlBAUTGxW6ZbuOrtfVZvOVtRL/ixuiFiVlJ+JOQOxrtK19ukamsI
|
||||
iacFlbLgiA7w0HCtm2DsT9aL67/1e4rJ0lv0MjnQYUMmKQy7g0Gd3+nQPU9pn+Lf
|
||||
Z/UmSNWiJ8Csc/seDMyzT6jrzcGPfoSVaUowH0wGrI9If1snwcr+mMg7dWRGf1fm
|
||||
y/dcVSzed0ax4LqDmike1EshU+51cKWWlnhyNHK4KH+0fNsBQ0c6clrFpGx9MPmV
|
||||
YXie6C+LWkh5x12RU0sJt/SmSZV6q9VliIkX+yY3jBrC/pKgRahtcIyq46Da1E6K
|
||||
lc15Euur3NfGow+nott0Z8XutpYdK/2vBKcIh9JOdkd+oe6pcIP6hnhHRp53wqmG
|
||||
-----END PKCS7-----`
|
||||
|
||||
var FirefoxAddonRootCert = []byte(`
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIGYTCCBEmgAwIBAgIBATANBgkqhkiG9w0BAQwFADB9MQswCQYDVQQGEwJVUzEc
|
||||
MBoGA1UEChMTTW96aWxsYSBDb3Jwb3JhdGlvbjEvMC0GA1UECxMmTW96aWxsYSBB
|
||||
TU8gUHJvZHVjdGlvbiBTaWduaW5nIFNlcnZpY2UxHzAdBgNVBAMTFnJvb3QtY2Et
|
||||
cHJvZHVjdGlvbi1hbW8wHhcNMTUwMzE3MjI1MzU3WhcNMjUwMzE0MjI1MzU3WjB9
|
||||
MQswCQYDVQQGEwJVUzEcMBoGA1UEChMTTW96aWxsYSBDb3Jwb3JhdGlvbjEvMC0G
|
||||
A1UECxMmTW96aWxsYSBBTU8gUHJvZHVjdGlvbiBTaWduaW5nIFNlcnZpY2UxHzAd
|
||||
BgNVBAMTFnJvb3QtY2EtcHJvZHVjdGlvbi1hbW8wggIgMA0GCSqGSIb3DQEBAQUA
|
||||
A4ICDQAwggIIAoICAQC0u2HXXbrwy36+MPeKf5jgoASMfMNz7mJWBecJgvlTf4hH
|
||||
JbLzMPsIUauzI9GEpLfHdZ6wzSyFOb4AM+D1mxAWhuZJ3MDAJOf3B1Rs6QorHrl8
|
||||
qqlNtPGqepnpNJcLo7JsSqqE3NUm72MgqIHRgTRsqUs+7LIPGe7262U+N/T0LPYV
|
||||
Le4rZ2RDHoaZhYY7a9+49mHOI/g2YFB+9yZjE+XdplT2kBgA4P8db7i7I0tIi4b0
|
||||
B0N6y9MhL+CRZJyxdFe2wBykJX14LsheKsM1azHjZO56SKNrW8VAJTLkpRxCmsiT
|
||||
r08fnPyDKmaeZ0BtsugicdipcZpXriIGmsZbI12q5yuwjSELdkDV6Uajo2n+2ws5
|
||||
uXrP342X71WiWhC/dF5dz1LKtjBdmUkxaQMOP/uhtXEKBrZo1ounDRQx1j7+SkQ4
|
||||
BEwjB3SEtr7XDWGOcOIkoJZWPACfBLC3PJCBWjTAyBlud0C5n3Cy9regAAnOIqI1
|
||||
t16GU2laRh7elJ7gPRNgQgwLXeZcFxw6wvyiEcmCjOEQ6PM8UQjthOsKlszMhlKw
|
||||
vjyOGDoztkqSBy/v+Asx7OW2Q7rlVfKarL0mREZdSMfoy3zTgtMVCM0vhNl6zcvf
|
||||
5HNNopoEdg5yuXo2chZ1p1J+q86b0G5yJRMeT2+iOVY2EQ37tHrqUURncCy4uwIB
|
||||
A6OB7TCB6jAMBgNVHRMEBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAWBgNVHSUBAf8E
|
||||
DDAKBggrBgEFBQcDAzCBkgYDVR0jBIGKMIGHoYGBpH8wfTELMAkGA1UEBhMCVVMx
|
||||
HDAaBgNVBAoTE01vemlsbGEgQ29ycG9yYXRpb24xLzAtBgNVBAsTJk1vemlsbGEg
|
||||
QU1PIFByb2R1Y3Rpb24gU2lnbmluZyBTZXJ2aWNlMR8wHQYDVQQDExZyb290LWNh
|
||||
LXByb2R1Y3Rpb24tYW1vggEBMB0GA1UdDgQWBBSzvOpYdKvhbngqsqucIx6oYyyX
|
||||
tzANBgkqhkiG9w0BAQwFAAOCAgEAaNSRYAaECAePQFyfk12kl8UPLh8hBNidP2H6
|
||||
KT6O0vCVBjxmMrwr8Aqz6NL+TgdPmGRPDDLPDpDJTdWzdj7khAjxqWYhutACTew5
|
||||
eWEaAzyErbKQl+duKvtThhV2p6F6YHJ2vutu4KIciOMKB8dslIqIQr90IX2Usljq
|
||||
8Ttdyf+GhUmazqLtoB0GOuESEqT4unX6X7vSGu1oLV20t7t5eCnMMYD67ZBn0YIU
|
||||
/cm/+pan66hHrja+NeDGF8wabJxdqKItCS3p3GN1zUGuJKrLykxqbOp/21byAGog
|
||||
Z1amhz6NHUcfE6jki7sM7LHjPostU5ZWs3PEfVVgha9fZUhOrIDsyXEpCWVa3481
|
||||
LlAq3GiUMKZ5DVRh9/Nvm4NwrTfB3QkQQJCwfXvO9pwnPKtISYkZUqhEqvXk5nBg
|
||||
QCkDSLDjXTx39naBBGIVIqBtKKuVTla9enngdq692xX/CgO6QJVrwpqdGjebj5P8
|
||||
5fNZPABzTezG3Uls5Vp+4iIWVAEDkK23cUj3c/HhE+Oo7kxfUeu5Y1ZV3qr61+6t
|
||||
ZARKjbu1TuYQHf0fs+GwID8zeLc2zJL7UzcHFwwQ6Nda9OJN4uPAuC/BKaIpxCLL
|
||||
26b24/tRam4SJjqpiq20lynhUrmTtt6hbG3E1Hpy3bmkt2DYnuMFwEx2gfXNcnbT
|
||||
wNuvFqc=
|
||||
-----END CERTIFICATE-----`)
|
||||
|
||||
// sign a document with openssl and verify the signature with pkcs7.
|
||||
// this uses a chain of root, intermediate and signer cert, where the
|
||||
// intermediate is added to the certs but the root isn't.
|
||||
func TestSignWithOpenSSLAndVerify(t *testing.T) {
|
||||
content := []byte(`
|
||||
A ship in port is safe,
|
||||
but that's not what ships are built for.
|
||||
-- Grace Hopper`)
|
||||
// write the content to a temp file
|
||||
tmpContentFile, err := ioutil.TempFile("", "TestSignWithOpenSSLAndVerify_content")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ioutil.WriteFile(tmpContentFile.Name(), content, 0755)
|
||||
sigalgs := []x509.SignatureAlgorithm{
|
||||
x509.SHA1WithRSA,
|
||||
x509.SHA256WithRSA,
|
||||
x509.SHA512WithRSA,
|
||||
x509.ECDSAWithSHA1,
|
||||
x509.ECDSAWithSHA256,
|
||||
x509.ECDSAWithSHA384,
|
||||
x509.ECDSAWithSHA512,
|
||||
}
|
||||
for _, sigalgroot := range sigalgs {
|
||||
rootCert, err := createTestCertificateByIssuer("PKCS7 Test Root CA", nil, sigalgroot, true)
|
||||
if err != nil {
|
||||
t.Fatalf("test %s: cannot generate root cert: %s", sigalgroot, err)
|
||||
}
|
||||
truststore := smx509.NewCertPool()
|
||||
truststore.AddCert(rootCert.Certificate)
|
||||
for _, sigalginter := range sigalgs {
|
||||
interCert, err := createTestCertificateByIssuer("PKCS7 Test Intermediate Cert", rootCert, sigalginter, true)
|
||||
if err != nil {
|
||||
t.Fatalf("test %s/%s: cannot generate intermediate cert: %s", sigalgroot, sigalginter, err)
|
||||
}
|
||||
// write the intermediate cert to a temp file
|
||||
tmpInterCertFile, err := ioutil.TempFile("", "TestSignWithOpenSSLAndVerify_intermediate")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fd, err := os.OpenFile(tmpInterCertFile.Name(), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
pem.Encode(fd, &pem.Block{Type: "CERTIFICATE", Bytes: interCert.Certificate.Raw})
|
||||
fd.Close()
|
||||
for _, sigalgsigner := range sigalgs {
|
||||
signerCert, err := createTestCertificateByIssuer("PKCS7 Test Signer Cert", interCert, sigalgsigner, false)
|
||||
if err != nil {
|
||||
t.Fatalf("test %s/%s/%s: cannot generate signer cert: %s", sigalgroot, sigalginter, sigalgsigner, err)
|
||||
}
|
||||
|
||||
// write the signer cert to a temp file
|
||||
tmpSignerCertFile, err := ioutil.TempFile("", "TestSignWithOpenSSLAndVerify_signer")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fd, err = os.OpenFile(tmpSignerCertFile.Name(), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
pem.Encode(fd, &pem.Block{Type: "CERTIFICATE", Bytes: signerCert.Certificate.Raw})
|
||||
fd.Close()
|
||||
|
||||
// write the signer key to a temp file
|
||||
tmpSignerKeyFile, err := ioutil.TempFile("", "TestSignWithOpenSSLAndVerify_key")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fd, err = os.OpenFile(tmpSignerKeyFile.Name(), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var derKey []byte
|
||||
priv := *signerCert.PrivateKey
|
||||
switch priv := priv.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
derKey = x509.MarshalPKCS1PrivateKey(priv)
|
||||
pem.Encode(fd, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: derKey})
|
||||
case *ecdsa.PrivateKey:
|
||||
derKey, err = x509.MarshalECPrivateKey(priv)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
pem.Encode(fd, &pem.Block{Type: "EC PRIVATE KEY", Bytes: derKey})
|
||||
}
|
||||
fd.Close()
|
||||
|
||||
// write the root cert to a temp file
|
||||
tmpSignedFile, err := ioutil.TempFile("", "TestSignWithOpenSSLAndVerify_signature")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// call openssl to sign the content
|
||||
opensslCMD := exec.Command("openssl", "smime", "-sign", "-nodetach",
|
||||
"-in", tmpContentFile.Name(), "-out", tmpSignedFile.Name(),
|
||||
"-signer", tmpSignerCertFile.Name(), "-inkey", tmpSignerKeyFile.Name(),
|
||||
"-certfile", tmpInterCertFile.Name(), "-outform", "PEM")
|
||||
out, err := opensslCMD.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("test %s/%s/%s: openssl command failed with %s: %s", sigalgroot, sigalginter, sigalgsigner, err, out)
|
||||
}
|
||||
|
||||
// verify the signed content
|
||||
pemSignature, err := ioutil.ReadFile(tmpSignedFile.Name())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
derBlock, _ := pem.Decode(pemSignature)
|
||||
if derBlock == nil {
|
||||
break
|
||||
}
|
||||
p7, err := Parse(derBlock.Bytes)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse encountered unexpected error: %v", err)
|
||||
}
|
||||
if err := p7.VerifyWithChain(truststore); err != nil {
|
||||
t.Fatalf("Verify failed with error: %v", err)
|
||||
}
|
||||
// Verify the certificate chain to make sure the identified root
|
||||
// is the one we expect
|
||||
ee := getCertFromCertsByIssuerAndSerial(p7.Certificates, p7.Signers[0].IssuerAndSerialNumber)
|
||||
if ee == nil {
|
||||
t.Fatalf("No end-entity certificate found for signer")
|
||||
}
|
||||
chains, err := verifyCertChain(ee, p7.Certificates, truststore, time.Now())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(chains) != 1 {
|
||||
t.Fatalf("Expected to find one chain, but found %d", len(chains))
|
||||
}
|
||||
if len(chains[0]) != 3 {
|
||||
t.Fatalf("Expected to find three certificates in chain, but found %d", len(chains[0]))
|
||||
}
|
||||
if chains[0][0].Subject.CommonName != "PKCS7 Test Signer Cert" {
|
||||
t.Fatalf("Expected to find EE certificate with subject 'PKCS7 Test Signer Cert', but found '%s'", chains[0][0].Subject.CommonName)
|
||||
}
|
||||
if chains[0][1].Subject.CommonName != "PKCS7 Test Intermediate Cert" {
|
||||
t.Fatalf("Expected to find intermediate certificate with subject 'PKCS7 Test Intermediate Cert', but found '%s'", chains[0][1].Subject.CommonName)
|
||||
}
|
||||
if chains[0][2].Subject.CommonName != "PKCS7 Test Root CA" {
|
||||
t.Fatalf("Expected to find root certificate with subject 'PKCS7 Test Root CA', but found '%s'", chains[0][2].Subject.CommonName)
|
||||
}
|
||||
os.Remove(tmpSignerCertFile.Name()) // clean up
|
||||
os.Remove(tmpSignerKeyFile.Name()) // clean up
|
||||
}
|
||||
os.Remove(tmpInterCertFile.Name()) // clean up
|
||||
}
|
||||
}
|
||||
os.Remove(tmpContentFile.Name()) // clean up
|
||||
}
|
183
pkcs7/verify_test_dsa.go
Normal file
183
pkcs7/verify_test_dsa.go
Normal file
@ -0,0 +1,183 @@
|
||||
// +build go1.11 go1.12 go1.13 go1.14 go1.15
|
||||
|
||||
package pkcs7
|
||||
|
||||
import (
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"testing"
|
||||
|
||||
"github.com/emmansun/gmsm/smx509"
|
||||
)
|
||||
|
||||
func TestVerifyEC2(t *testing.T) {
|
||||
fixture := UnmarshalDSATestFixture(EC2IdentityDocumentFixture)
|
||||
p7, err := Parse(fixture.Input)
|
||||
if err != nil {
|
||||
t.Errorf("Parse encountered unexpected error: %v", err)
|
||||
}
|
||||
p7.Certificates = []*smx509.Certificate{fixture.Certificate}
|
||||
if err := p7.Verify(); err != nil {
|
||||
t.Errorf("Verify failed with error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
var EC2IdentityDocumentFixture = `
|
||||
-----BEGIN PKCS7-----
|
||||
MIAGCSqGSIb3DQEHAqCAMIACAQExCzAJBgUrDgMCGgUAMIAGCSqGSIb3DQEHAaCA
|
||||
JIAEggGmewogICJwcml2YXRlSXAiIDogIjE3Mi4zMC4wLjI1MiIsCiAgImRldnBh
|
||||
eVByb2R1Y3RDb2RlcyIgOiBudWxsLAogICJhdmFpbGFiaWxpdHlab25lIiA6ICJ1
|
||||
cy1lYXN0LTFhIiwKICAidmVyc2lvbiIgOiAiMjAxMC0wOC0zMSIsCiAgImluc3Rh
|
||||
bmNlSWQiIDogImktZjc5ZmU1NmMiLAogICJiaWxsaW5nUHJvZHVjdHMiIDogbnVs
|
||||
bCwKICAiaW5zdGFuY2VUeXBlIiA6ICJ0Mi5taWNybyIsCiAgImFjY291bnRJZCIg
|
||||
OiAiMTIxNjU5MDE0MzM0IiwKICAiaW1hZ2VJZCIgOiAiYW1pLWZjZTNjNjk2IiwK
|
||||
ICAicGVuZGluZ1RpbWUiIDogIjIwMTYtMDQtMDhUMDM6MDE6MzhaIiwKICAiYXJj
|
||||
aGl0ZWN0dXJlIiA6ICJ4ODZfNjQiLAogICJrZXJuZWxJZCIgOiBudWxsLAogICJy
|
||||
YW1kaXNrSWQiIDogbnVsbCwKICAicmVnaW9uIiA6ICJ1cy1lYXN0LTEiCn0AAAAA
|
||||
AAAxggEYMIIBFAIBATBpMFwxCzAJBgNVBAYTAlVTMRkwFwYDVQQIExBXYXNoaW5n
|
||||
dG9uIFN0YXRlMRAwDgYDVQQHEwdTZWF0dGxlMSAwHgYDVQQKExdBbWF6b24gV2Vi
|
||||
IFNlcnZpY2VzIExMQwIJAJa6SNnlXhpnMAkGBSsOAwIaBQCgXTAYBgkqhkiG9w0B
|
||||
CQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0xNjA0MDgwMzAxNDRaMCMG
|
||||
CSqGSIb3DQEJBDEWBBTuUc28eBXmImAautC+wOjqcFCBVjAJBgcqhkjOOAQDBC8w
|
||||
LQIVAKA54NxGHWWCz5InboDmY/GHs33nAhQ6O/ZI86NwjA9Vz3RNMUJrUPU5tAAA
|
||||
AAAAAA==
|
||||
-----END PKCS7-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIC7TCCAq0CCQCWukjZ5V4aZzAJBgcqhkjOOAQDMFwxCzAJBgNVBAYTAlVTMRkw
|
||||
FwYDVQQIExBXYXNoaW5ndG9uIFN0YXRlMRAwDgYDVQQHEwdTZWF0dGxlMSAwHgYD
|
||||
VQQKExdBbWF6b24gV2ViIFNlcnZpY2VzIExMQzAeFw0xMjAxMDUxMjU2MTJaFw0z
|
||||
ODAxMDUxMjU2MTJaMFwxCzAJBgNVBAYTAlVTMRkwFwYDVQQIExBXYXNoaW5ndG9u
|
||||
IFN0YXRlMRAwDgYDVQQHEwdTZWF0dGxlMSAwHgYDVQQKExdBbWF6b24gV2ViIFNl
|
||||
cnZpY2VzIExMQzCCAbcwggEsBgcqhkjOOAQBMIIBHwKBgQCjkvcS2bb1VQ4yt/5e
|
||||
ih5OO6kK/n1Lzllr7D8ZwtQP8fOEpp5E2ng+D6Ud1Z1gYipr58Kj3nssSNpI6bX3
|
||||
VyIQzK7wLclnd/YozqNNmgIyZecN7EglK9ITHJLP+x8FtUpt3QbyYXJdmVMegN6P
|
||||
hviYt5JH/nYl4hh3Pa1HJdskgQIVALVJ3ER11+Ko4tP6nwvHwh6+ERYRAoGBAI1j
|
||||
k+tkqMVHuAFcvAGKocTgsjJem6/5qomzJuKDmbJNu9Qxw3rAotXau8Qe+MBcJl/U
|
||||
hhy1KHVpCGl9fueQ2s6IL0CaO/buycU1CiYQk40KNHCcHfNiZbdlx1E9rpUp7bnF
|
||||
lRa2v1ntMX3caRVDdbtPEWmdxSCYsYFDk4mZrOLBA4GEAAKBgEbmeve5f8LIE/Gf
|
||||
MNmP9CM5eovQOGx5ho8WqD+aTebs+k2tn92BBPqeZqpWRa5P/+jrdKml1qx4llHW
|
||||
MXrs3IgIb6+hUIB+S8dz8/mmO0bpr76RoZVCXYab2CZedFut7qc3WUH9+EUAH5mw
|
||||
vSeDCOUMYQR7R9LINYwouHIziqQYMAkGByqGSM44BAMDLwAwLAIUWXBlk40xTwSw
|
||||
7HX32MxXYruse9ACFBNGmdX2ZBrVNGrN9N2f6ROk0k9K
|
||||
-----END CERTIFICATE-----`
|
||||
|
||||
func TestDSASignWithOpenSSLAndVerify(t *testing.T) {
|
||||
content := []byte(`
|
||||
A ship in port is safe,
|
||||
but that's not what ships are built for.
|
||||
-- Grace Hopper`)
|
||||
// write the content to a temp file
|
||||
tmpContentFile, err := ioutil.TempFile("", "TestDSASignWithOpenSSLAndVerify_content")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ioutil.WriteFile(tmpContentFile.Name(), content, 0755)
|
||||
|
||||
// write the signer cert to a temp file
|
||||
tmpSignerCertFile, err := ioutil.TempFile("", "TestDSASignWithOpenSSLAndVerify_signer")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ioutil.WriteFile(tmpSignerCertFile.Name(), dsaPublicCert, 0755)
|
||||
|
||||
// write the signer key to a temp file
|
||||
tmpSignerKeyFile, err := ioutil.TempFile("", "TestDSASignWithOpenSSLAndVerify_key")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ioutil.WriteFile(tmpSignerKeyFile.Name(), dsaPrivateKey, 0755)
|
||||
|
||||
tmpSignedFile, err := ioutil.TempFile("", "TestDSASignWithOpenSSLAndVerify_signature")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// call openssl to sign the content
|
||||
opensslCMD := exec.Command("openssl", "smime", "-sign", "-nodetach", "-md", "sha1",
|
||||
"-in", tmpContentFile.Name(), "-out", tmpSignedFile.Name(),
|
||||
"-signer", tmpSignerCertFile.Name(), "-inkey", tmpSignerKeyFile.Name(),
|
||||
"-certfile", tmpSignerCertFile.Name(), "-outform", "PEM")
|
||||
out, err := opensslCMD.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("openssl command failed with %s: %s", err, out)
|
||||
}
|
||||
|
||||
// verify the signed content
|
||||
pemSignature, err := ioutil.ReadFile(tmpSignedFile.Name())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Printf("%s\n", pemSignature)
|
||||
derBlock, _ := pem.Decode(pemSignature)
|
||||
if derBlock == nil {
|
||||
t.Fatalf("failed to read DER block from signature PEM %s", tmpSignedFile.Name())
|
||||
}
|
||||
p7, err := Parse(derBlock.Bytes)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse encountered unexpected error: %v", err)
|
||||
}
|
||||
if err := p7.Verify(); err != nil {
|
||||
t.Fatalf("Verify failed with error: %v", err)
|
||||
}
|
||||
os.Remove(tmpSignerCertFile.Name()) // clean up
|
||||
os.Remove(tmpSignerKeyFile.Name()) // clean up
|
||||
os.Remove(tmpContentFile.Name()) // clean up
|
||||
}
|
||||
|
||||
var dsaPrivateKey = []byte(`-----BEGIN PRIVATE KEY-----
|
||||
MIIBSwIBADCCASwGByqGSM44BAEwggEfAoGBAP1/U4EddRIpUt9KnC7s5Of2EbdS
|
||||
PO9EAMMeP4C2USZpRV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVCl
|
||||
pJ+f6AR7ECLCT7up1/63xhv4O1fnxqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith
|
||||
1yrv8iIDGZ3RSAHHAhUAl2BQjxUjC8yykrmCouuEC/BYHPUCgYEA9+GghdabPd7L
|
||||
vKtcNrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCBgLRJFnEj6EwoFhO3
|
||||
zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhRkImo
|
||||
g9/hWuWfBpKLZl6Ae1UlZAFMO/7PSSoEFgIUfW4aPdQBn9gJZp2KuNpzgHzvfsE=
|
||||
-----END PRIVATE KEY-----`)
|
||||
|
||||
var dsaPublicCert = []byte(`-----BEGIN CERTIFICATE-----
|
||||
MIIDOjCCAvWgAwIBAgIEPCY/UDANBglghkgBZQMEAwIFADBsMRAwDgYDVQQGEwdV
|
||||
bmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYD
|
||||
VQQKEwdVbmtub3duMRAwDgYDVQQLEwdVbmtub3duMRAwDgYDVQQDEwdVbmtub3du
|
||||
MB4XDTE4MTAyMjEzNDMwN1oXDTQ2MDMwOTEzNDMwN1owbDEQMA4GA1UEBhMHVW5r
|
||||
bm93bjEQMA4GA1UECBMHVW5rbm93bjEQMA4GA1UEBxMHVW5rbm93bjEQMA4GA1UE
|
||||
ChMHVW5rbm93bjEQMA4GA1UECxMHVW5rbm93bjEQMA4GA1UEAxMHVW5rbm93bjCC
|
||||
AbgwggEsBgcqhkjOOAQBMIIBHwKBgQD9f1OBHXUSKVLfSpwu7OTn9hG3UjzvRADD
|
||||
Hj+AtlEmaUVdQCJR+1k9jVj6v8X1ujD2y5tVbNeBO4AdNG/yZmC3a5lQpaSfn+gE
|
||||
exAiwk+7qdf+t8Yb+DtX58aophUPBPuD9tPFHsMCNVQTWhaRMvZ1864rYdcq7/Ii
|
||||
Axmd0UgBxwIVAJdgUI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4
|
||||
V7l5lK+7+jrqgvlXTAs9B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozI
|
||||
puE8FnqLVHyNKOCjrh4rs6Z1kW6jfwv6ITVi8ftiegEkO8yk8b6oUZCJqIPf4Vrl
|
||||
nwaSi2ZegHtVJWQBTDv+z0kqA4GFAAKBgQDCriMPbEVBoRK4SOUeFwg7+VRf4TTp
|
||||
rcOQC9IVVoCjXzuWEGrp3ZI7YWJSpFnSch4lk29RH8O0HpI/NOzKnOBtnKr782pt
|
||||
1k/bJVMH9EaLd6MKnAVjrCDMYBB0MhebZ8QHY2elZZCWoqDYAcIDOsEx+m4NLErT
|
||||
ypPnjS5M0jm1PKMhMB8wHQYDVR0OBBYEFC0Yt5XdM0Kc95IX8NQ8XRssGPx7MA0G
|
||||
CWCGSAFlAwQDAgUAAzAAMC0CFQCIgQtrZZ9hdZG1ROhR5hc8nYEmbgIUAIlgC688
|
||||
qzy/7yePTlhlpj+ahMM=
|
||||
-----END CERTIFICATE-----`)
|
||||
|
||||
type DSATestFixture struct {
|
||||
Input []byte
|
||||
Certificate *smx509.Certificate
|
||||
}
|
||||
|
||||
func UnmarshalDSATestFixture(testPEMBlock string) DSATestFixture {
|
||||
var result DSATestFixture
|
||||
var derBlock *pem.Block
|
||||
var pemBlock = []byte(testPEMBlock)
|
||||
for {
|
||||
derBlock, pemBlock = pem.Decode(pemBlock)
|
||||
if derBlock == nil {
|
||||
break
|
||||
}
|
||||
switch derBlock.Type {
|
||||
case "PKCS7":
|
||||
result.Input = derBlock.Bytes
|
||||
case "CERTIFICATE":
|
||||
result.Certificate, _ = smx509.ParseCertificate(derBlock.Bytes)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
21
pkcs8/LICENSE
Normal file
21
pkcs8/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 youmark
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
@ -1,37 +0,0 @@
|
||||
package pkcs8
|
||||
|
||||
import (
|
||||
"encoding/asn1"
|
||||
|
||||
"github.com/emmansun/gmsm/sm4"
|
||||
)
|
||||
|
||||
var (
|
||||
oidSM4CBC = asn1.ObjectIdentifier{1, 2, 156, 10197, 1, 104, 2}
|
||||
oidSM4GCM = asn1.ObjectIdentifier{1, 2, 156, 10197, 1, 104, 8}
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegisterCipher(oidSM4CBC, func() Cipher {
|
||||
return &SM4CBC
|
||||
})
|
||||
RegisterCipher(oidSM4GCM, func() Cipher {
|
||||
return &SM4GCM
|
||||
})
|
||||
}
|
||||
|
||||
// SM4CBC is the 128-bit key SM4 cipher in CBC mode.
|
||||
var SM4CBC = cbcBlockCipher{
|
||||
ivSize: sm4.BlockSize,
|
||||
keySize: 16,
|
||||
newBlock: sm4.NewCipher,
|
||||
oid: oidSM4CBC,
|
||||
}
|
||||
|
||||
// SM4GCM is the 128-bit key SM4 cipher in GCM mode.
|
||||
var SM4GCM = gcmBlockCipher{
|
||||
nonceSize: 12,
|
||||
keySize: 16,
|
||||
newBlock: sm4.NewCipher,
|
||||
oid: oidSM4GCM,
|
||||
}
|
@ -7,6 +7,7 @@ import (
|
||||
"math/big"
|
||||
"os"
|
||||
|
||||
"github.com/emmansun/gmsm/pkcs"
|
||||
"github.com/emmansun/gmsm/pkcs8"
|
||||
"github.com/emmansun/gmsm/sm2"
|
||||
"github.com/emmansun/gmsm/sm9"
|
||||
@ -121,7 +122,7 @@ func ExampleMarshalPrivateKey() {
|
||||
|
||||
password := []byte("Password1")
|
||||
opts := &pkcs8.Opts{
|
||||
Cipher: pkcs8.SM4CBC,
|
||||
Cipher: pkcs.SM4CBC,
|
||||
KDFOpts: pkcs8.PBKDF2Opts{
|
||||
SaltSize: 16, IterationCount: 16, HMACHash: pkcs8.SM3,
|
||||
},
|
||||
|
@ -16,6 +16,7 @@ import (
|
||||
"hash"
|
||||
"strconv"
|
||||
|
||||
"github.com/emmansun/gmsm/pkcs"
|
||||
"github.com/emmansun/gmsm/sm2"
|
||||
"github.com/emmansun/gmsm/sm3"
|
||||
"github.com/emmansun/gmsm/sm9"
|
||||
@ -65,7 +66,7 @@ func (h Hash) New() hash.Hash {
|
||||
// DefaultOpts are the default options for encrypting a key if none are given.
|
||||
// The defaults can be changed by the library user.
|
||||
var DefaultOpts = &Opts{
|
||||
Cipher: AES256CBC,
|
||||
Cipher: pkcs.AES256CBC,
|
||||
KDFOpts: PBKDF2Opts{
|
||||
SaltSize: 8,
|
||||
IterationCount: 10000,
|
||||
@ -108,29 +109,9 @@ type encryptedPrivateKeyInfo struct {
|
||||
EncryptedData []byte
|
||||
}
|
||||
|
||||
// Cipher represents a cipher for encrypting the key material.
|
||||
type Cipher interface {
|
||||
// KeySize returns the key size of the cipher, in bytes.
|
||||
KeySize() int
|
||||
// Encrypt encrypts the key material.
|
||||
Encrypt(key, plaintext []byte) (*pkix.AlgorithmIdentifier, []byte, error)
|
||||
// Decrypt decrypts the key material.
|
||||
Decrypt(key []byte, parameters *asn1.RawValue, encryptedKey []byte) ([]byte, error)
|
||||
// OID returns the OID of the cipher specified.
|
||||
OID() asn1.ObjectIdentifier
|
||||
}
|
||||
|
||||
var ciphers = make(map[string]func() Cipher)
|
||||
|
||||
// RegisterCipher registers a function that returns a new instance of the given
|
||||
// cipher. This allows the library to support client-provided ciphers.
|
||||
func RegisterCipher(oid asn1.ObjectIdentifier, cipher func() Cipher) {
|
||||
ciphers[oid.String()] = cipher
|
||||
}
|
||||
|
||||
// Opts contains options for encrypting a PKCS#8 key.
|
||||
type Opts struct {
|
||||
Cipher Cipher
|
||||
Cipher pkcs.Cipher
|
||||
KDFOpts KDFOpts
|
||||
}
|
||||
|
||||
@ -158,16 +139,6 @@ func parseKeyDerivationFunc(keyDerivationFunc pkix.AlgorithmIdentifier) (KDFPara
|
||||
return params, nil
|
||||
}
|
||||
|
||||
func parseEncryptionScheme(encryptionScheme *pkix.AlgorithmIdentifier) (Cipher, error) {
|
||||
oid := encryptionScheme.Algorithm.String()
|
||||
newCipher, ok := ciphers[oid]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("pkcs8: unsupported cipher (OID: %s)", oid)
|
||||
}
|
||||
cipher := newCipher()
|
||||
return cipher, nil
|
||||
}
|
||||
|
||||
// ParsePrivateKey parses a DER-encoded PKCS#8 private key.
|
||||
// Password can be nil.
|
||||
// This is equivalent to ParsePKCS8PrivateKey.
|
||||
@ -196,7 +167,7 @@ func ParsePrivateKey(der []byte, password []byte) (interface{}, KDFParameters, e
|
||||
return nil, nil, errors.New("pkcs8: invalid PBES2 parameters")
|
||||
}
|
||||
|
||||
cipher, err := parseEncryptionScheme(¶ms.EncryptionScheme)
|
||||
cipher, err := pkcs.GetCipher(params.EncryptionScheme.Algorithm)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"encoding/pem"
|
||||
"testing"
|
||||
|
||||
"github.com/emmansun/gmsm/pkcs"
|
||||
"github.com/emmansun/gmsm/pkcs8"
|
||||
"github.com/emmansun/gmsm/sm2"
|
||||
"github.com/emmansun/gmsm/sm9"
|
||||
@ -391,7 +392,7 @@ func TestMarshalPrivateKey(t *testing.T) {
|
||||
{
|
||||
password: []byte("password"),
|
||||
opts: &pkcs8.Opts{
|
||||
Cipher: pkcs8.SM4CBC,
|
||||
Cipher: pkcs.SM4CBC,
|
||||
KDFOpts: pkcs8.PBKDF2Opts{
|
||||
SaltSize: 8, IterationCount: 2048, HMACHash: pkcs8.SM3,
|
||||
},
|
||||
@ -400,7 +401,7 @@ func TestMarshalPrivateKey(t *testing.T) {
|
||||
{
|
||||
password: []byte("password"),
|
||||
opts: &pkcs8.Opts{
|
||||
Cipher: pkcs8.SM4GCM,
|
||||
Cipher: pkcs.SM4GCM,
|
||||
KDFOpts: pkcs8.PBKDF2Opts{
|
||||
SaltSize: 8, IterationCount: 2048, HMACHash: pkcs8.SM3,
|
||||
},
|
||||
@ -409,7 +410,7 @@ func TestMarshalPrivateKey(t *testing.T) {
|
||||
{
|
||||
password: []byte("password"),
|
||||
opts: &pkcs8.Opts{
|
||||
Cipher: pkcs8.AES128CBC,
|
||||
Cipher: pkcs.AES128CBC,
|
||||
KDFOpts: pkcs8.PBKDF2Opts{
|
||||
SaltSize: 8, IterationCount: 2048, HMACHash: pkcs8.SHA224,
|
||||
},
|
||||
@ -418,7 +419,7 @@ func TestMarshalPrivateKey(t *testing.T) {
|
||||
{
|
||||
password: []byte("password"),
|
||||
opts: &pkcs8.Opts{
|
||||
Cipher: pkcs8.AES128CBC,
|
||||
Cipher: pkcs.AES128CBC,
|
||||
KDFOpts: pkcs8.PBKDF2Opts{
|
||||
SaltSize: 8, IterationCount: 2048, HMACHash: pkcs8.SHA256,
|
||||
},
|
||||
@ -427,7 +428,7 @@ func TestMarshalPrivateKey(t *testing.T) {
|
||||
{
|
||||
password: []byte("password"),
|
||||
opts: &pkcs8.Opts{
|
||||
Cipher: pkcs8.AES128CBC,
|
||||
Cipher: pkcs.AES128CBC,
|
||||
KDFOpts: pkcs8.PBKDF2Opts{
|
||||
SaltSize: 8, IterationCount: 2048, HMACHash: pkcs8.SHA512,
|
||||
},
|
||||
@ -436,7 +437,7 @@ func TestMarshalPrivateKey(t *testing.T) {
|
||||
{
|
||||
password: []byte("password"),
|
||||
opts: &pkcs8.Opts{
|
||||
Cipher: pkcs8.AES128CBC,
|
||||
Cipher: pkcs.AES128CBC,
|
||||
KDFOpts: pkcs8.PBKDF2Opts{
|
||||
SaltSize: 8, IterationCount: 2048, HMACHash: pkcs8.SHA384,
|
||||
},
|
||||
@ -445,7 +446,7 @@ func TestMarshalPrivateKey(t *testing.T) {
|
||||
{
|
||||
password: []byte("password"),
|
||||
opts: &pkcs8.Opts{
|
||||
Cipher: pkcs8.AES128CBC,
|
||||
Cipher: pkcs.AES128CBC,
|
||||
KDFOpts: pkcs8.PBKDF2Opts{
|
||||
SaltSize: 8, IterationCount: 2048, HMACHash: pkcs8.SHA512_224,
|
||||
},
|
||||
@ -454,7 +455,7 @@ func TestMarshalPrivateKey(t *testing.T) {
|
||||
{
|
||||
password: []byte("password"),
|
||||
opts: &pkcs8.Opts{
|
||||
Cipher: pkcs8.AES128CBC,
|
||||
Cipher: pkcs.AES128CBC,
|
||||
KDFOpts: pkcs8.PBKDF2Opts{
|
||||
SaltSize: 8, IterationCount: 2048, HMACHash: pkcs8.SHA512_256,
|
||||
},
|
||||
@ -463,7 +464,7 @@ func TestMarshalPrivateKey(t *testing.T) {
|
||||
{
|
||||
password: []byte("password"),
|
||||
opts: &pkcs8.Opts{
|
||||
Cipher: pkcs8.AES192CBC,
|
||||
Cipher: pkcs.AES192CBC,
|
||||
KDFOpts: pkcs8.PBKDF2Opts{
|
||||
SaltSize: 8, IterationCount: 1000, HMACHash: pkcs8.SHA256,
|
||||
},
|
||||
@ -472,7 +473,7 @@ func TestMarshalPrivateKey(t *testing.T) {
|
||||
{
|
||||
password: []byte("password"),
|
||||
opts: &pkcs8.Opts{
|
||||
Cipher: pkcs8.AES256CBC,
|
||||
Cipher: pkcs.AES256CBC,
|
||||
KDFOpts: pkcs8.PBKDF2Opts{
|
||||
SaltSize: 16, IterationCount: 2000, HMACHash: pkcs8.SHA256,
|
||||
},
|
||||
@ -481,7 +482,7 @@ func TestMarshalPrivateKey(t *testing.T) {
|
||||
{
|
||||
password: []byte("password"),
|
||||
opts: &pkcs8.Opts{
|
||||
Cipher: pkcs8.AES128GCM,
|
||||
Cipher: pkcs.AES128GCM,
|
||||
KDFOpts: pkcs8.PBKDF2Opts{
|
||||
SaltSize: 8, IterationCount: 2048, HMACHash: pkcs8.SHA256,
|
||||
},
|
||||
@ -490,7 +491,7 @@ func TestMarshalPrivateKey(t *testing.T) {
|
||||
{
|
||||
password: []byte("password"),
|
||||
opts: &pkcs8.Opts{
|
||||
Cipher: pkcs8.AES192GCM,
|
||||
Cipher: pkcs.AES192GCM,
|
||||
KDFOpts: pkcs8.PBKDF2Opts{
|
||||
SaltSize: 8, IterationCount: 10000, HMACHash: pkcs8.SHA256,
|
||||
},
|
||||
@ -499,7 +500,7 @@ func TestMarshalPrivateKey(t *testing.T) {
|
||||
{
|
||||
password: []byte("password"),
|
||||
opts: &pkcs8.Opts{
|
||||
Cipher: pkcs8.AES256GCM,
|
||||
Cipher: pkcs.AES256GCM,
|
||||
KDFOpts: pkcs8.PBKDF2Opts{
|
||||
SaltSize: 16, IterationCount: 16, HMACHash: pkcs8.SHA256,
|
||||
},
|
||||
@ -508,7 +509,7 @@ func TestMarshalPrivateKey(t *testing.T) {
|
||||
{
|
||||
password: []byte("password"),
|
||||
opts: &pkcs8.Opts{
|
||||
Cipher: pkcs8.DESCBC,
|
||||
Cipher: pkcs.DESCBC,
|
||||
KDFOpts: pkcs8.PBKDF2Opts{
|
||||
SaltSize: 16, IterationCount: 16, HMACHash: pkcs8.SHA1,
|
||||
},
|
||||
@ -517,7 +518,7 @@ func TestMarshalPrivateKey(t *testing.T) {
|
||||
{
|
||||
password: []byte("password"),
|
||||
opts: &pkcs8.Opts{
|
||||
Cipher: pkcs8.TripleDESCBC,
|
||||
Cipher: pkcs.TripleDESCBC,
|
||||
KDFOpts: pkcs8.PBKDF2Opts{
|
||||
SaltSize: 16, IterationCount: 16, HMACHash: pkcs8.SHA1,
|
||||
},
|
||||
@ -526,7 +527,7 @@ func TestMarshalPrivateKey(t *testing.T) {
|
||||
{
|
||||
password: []byte("password"),
|
||||
opts: &pkcs8.Opts{
|
||||
Cipher: pkcs8.AES256CBC,
|
||||
Cipher: pkcs.AES256CBC,
|
||||
KDFOpts: pkcs8.ScryptOpts{
|
||||
CostParameter: 1 << 2,
|
||||
BlockSize: 8,
|
||||
|
27
sm9/bn256/LICENSE
Normal file
27
sm9/bn256/LICENSE
Normal file
@ -0,0 +1,27 @@
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
Loading…
x
Reference in New Issue
Block a user