├── go.mod ├── Makefile ├── .gitignore ├── .github └── workflows │ └── ci.yml ├── LICENSE ├── README.md ├── decrypt_test.go ├── encrypt_test.go ├── decrypt.go ├── ber.go ├── verify_test_dsa.go ├── ber_test.go ├── sign_test.go ├── pkcs7.go ├── encrypt.go ├── verify.go ├── sign.go ├── pkcs7_test.go └── verify_test.go /go.mod: -------------------------------------------------------------------------------- 1 | module go.mozilla.org/pkcs7 2 | 3 | go 1.11 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: vet staticcheck test 2 | 3 | test: 4 | go test -race -covermode=atomic -count=1 -coverprofile=coverage.out . 5 | 6 | showcoverage: test 7 | go tool cover -html=coverage.out 8 | 9 | vet: 10 | go vet . 11 | 12 | lint: 13 | golint . 14 | 15 | staticcheck: 16 | staticcheck . 17 | 18 | gettools: 19 | go get -u honnef.co/go/tools/... 20 | go get -u golang.org/x/lint/golint 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | 26 | coverage.out 27 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | test: 6 | name: Test 7 | strategy: 8 | matrix: 9 | go: ['1.12', '1.13', '1.14', '1.15', '1.16'] 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | with: 14 | fetch-depth: 0 15 | - uses: actions/setup-go@v2 16 | with: 17 | go-version: ${{ matrix.go }} 18 | stable: false 19 | - name: Test 20 | run: go vet . && go build . && go test -race -covermode=atomic -count=1 -coverprofile=coverage.out . 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Andrew Smith 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pkcs7 2 | 3 | [![GoDoc](https://godoc.org/go.mozilla.org/pkcs7?status.svg)](https://godoc.org/go.mozilla.org/pkcs7) 4 | [![Build Status](https://github.com/mozilla-services/pkcs7/workflows/CI/badge.svg?branch=master&event=push)](https://github.com/mozilla-services/pkcs7/actions/workflows/ci.yml?query=branch%3Amaster+event%3Apush) 5 | 6 | pkcs7 implements parsing and creating signed and enveloped messages. 7 | 8 | ```go 9 | package main 10 | 11 | import ( 12 | "bytes" 13 | "crypto/rsa" 14 | "crypto/x509" 15 | "encoding/pem" 16 | "fmt" 17 | "os" 18 | 19 | "go.mozilla.org/pkcs7" 20 | ) 21 | 22 | func SignAndDetach(content []byte, cert *x509.Certificate, privkey *rsa.PrivateKey) (signed []byte, err error) { 23 | toBeSigned, err := NewSignedData(content) 24 | if err != nil { 25 | err = fmt.Errorf("Cannot initialize signed data: %s", err) 26 | return 27 | } 28 | if err = toBeSigned.AddSigner(cert, privkey, SignerInfoConfig{}); err != nil { 29 | err = fmt.Errorf("Cannot add signer: %s", err) 30 | return 31 | } 32 | 33 | // Detach signature, omit if you want an embedded signature 34 | toBeSigned.Detach() 35 | 36 | signed, err = toBeSigned.Finish() 37 | if err != nil { 38 | err = fmt.Errorf("Cannot finish signing data: %s", err) 39 | return 40 | } 41 | 42 | // Verify the signature 43 | pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: signed}) 44 | p7, err := pkcs7.Parse(signed) 45 | if err != nil { 46 | err = fmt.Errorf("Cannot parse our signed data: %s", err) 47 | return 48 | } 49 | 50 | // since the signature was detached, reattach the content here 51 | p7.Content = content 52 | 53 | if bytes.Compare(content, p7.Content) != 0 { 54 | err = fmt.Errorf("Our content was not in the parsed data:\n\tExpected: %s\n\tActual: %s", content, p7.Content) 55 | return 56 | } 57 | if err = p7.Verify(); err != nil { 58 | err = fmt.Errorf("Cannot verify our signed data: %s", err) 59 | return 60 | } 61 | 62 | return signed, nil 63 | } 64 | ``` 65 | 66 | 67 | 68 | ## Credits 69 | This is a fork of [fullsailor/pkcs7](https://github.com/fullsailor/pkcs7) 70 | -------------------------------------------------------------------------------- /decrypt_test.go: -------------------------------------------------------------------------------- 1 | package pkcs7 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | ) 7 | 8 | func TestDecrypt(t *testing.T) { 9 | fixture := UnmarshalTestFixture(EncryptedTestFixture) 10 | p7, err := Parse(fixture.Input) 11 | if err != nil { 12 | t.Fatal(err) 13 | } 14 | content, err := p7.Decrypt(fixture.Certificate, fixture.PrivateKey) 15 | if err != nil { 16 | t.Errorf("Cannot Decrypt with error: %v", err) 17 | } 18 | expected := []byte("This is a test") 19 | if !bytes.Equal(content, expected) { 20 | t.Errorf("Decrypted result does not match.\n\tExpected:%s\n\tActual:%s", expected, content) 21 | } 22 | } 23 | 24 | // echo -n "This is a test" > test.txt 25 | // openssl cms -encrypt -in test.txt cert.pem 26 | var EncryptedTestFixture = ` 27 | -----BEGIN PKCS7----- 28 | MIIBGgYJKoZIhvcNAQcDoIIBCzCCAQcCAQAxgcwwgckCAQAwMjApMRAwDgYDVQQK 29 | EwdBY21lIENvMRUwEwYDVQQDEwxFZGRhcmQgU3RhcmsCBQDL+CvWMA0GCSqGSIb3 30 | DQEBAQUABIGAyFz7bfI2noUs4FpmYfztm1pVjGyB00p9x0H3gGHEYNXdqlq8VG8d 31 | iq36poWtEkatnwsOlURWZYECSi0g5IAL0U9sj82EN0xssZNaK0S5FTGnB3DPvYgt 32 | HJvcKq7YvNLKMh4oqd17C6GB4oXyEBDj0vZnL7SUoCAOAWELPeC8CTUwMwYJKoZI 33 | hvcNAQcBMBQGCCqGSIb3DQMHBAhEowTkot3a7oAQFD//J/IhFnk+JbkH7HZQFA== 34 | -----END PKCS7----- 35 | -----BEGIN CERTIFICATE----- 36 | MIIB1jCCAUGgAwIBAgIFAMv4K9YwCwYJKoZIhvcNAQELMCkxEDAOBgNVBAoTB0Fj 37 | bWUgQ28xFTATBgNVBAMTDEVkZGFyZCBTdGFyazAeFw0xNTA1MDYwMzU2NDBaFw0x 38 | NjA1MDYwMzU2NDBaMCUxEDAOBgNVBAoTB0FjbWUgQ28xETAPBgNVBAMTCEpvbiBT 39 | bm93MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDK6NU0R0eiCYVquU4RcjKc 40 | LzGfx0aa1lMr2TnLQUSeLFZHFxsyyMXXuMPig3HK4A7SGFHupO+/1H/sL4xpH5zg 41 | 8+Zg2r8xnnney7abxcuv0uATWSIeKlNnb1ZO1BAxFnESc3GtyOCr2dUwZHX5mRVP 42 | +Zxp2ni5qHNraf3wE2VPIQIDAQABoxIwEDAOBgNVHQ8BAf8EBAMCAKAwCwYJKoZI 43 | hvcNAQELA4GBAIr2F7wsqmEU/J/kLyrCgEVXgaV/sKZq4pPNnzS0tBYk8fkV3V18 44 | sBJyHKRLL/wFZASvzDcVGCplXyMdAOCyfd8jO3F9Ac/xdlz10RrHJT75hNu3a7/n 45 | 9KNwKhfN4A1CQv2x372oGjRhCW5bHNCWx4PIVeNzCyq/KZhyY9sxHE6f 46 | -----END CERTIFICATE----- 47 | -----BEGIN PRIVATE KEY----- 48 | MIICXgIBAAKBgQDK6NU0R0eiCYVquU4RcjKcLzGfx0aa1lMr2TnLQUSeLFZHFxsy 49 | yMXXuMPig3HK4A7SGFHupO+/1H/sL4xpH5zg8+Zg2r8xnnney7abxcuv0uATWSIe 50 | KlNnb1ZO1BAxFnESc3GtyOCr2dUwZHX5mRVP+Zxp2ni5qHNraf3wE2VPIQIDAQAB 51 | AoGBALyvnSt7KUquDen7nXQtvJBudnf9KFPt//OjkdHHxNZNpoF/JCSqfQeoYkeu 52 | MdAVYNLQGMiRifzZz4dDhA9xfUAuy7lcGQcMCxEQ1dwwuFaYkawbS0Tvy2PFlq2d 53 | H5/HeDXU4EDJ3BZg0eYj2Bnkt1sJI35UKQSxblQ0MY2q0uFBAkEA5MMOogkgUx1C 54 | 67S1tFqMUSM8D0mZB0O5vOJZC5Gtt2Urju6vywge2ArExWRXlM2qGl8afFy2SgSv 55 | Xk5eybcEiQJBAOMRwwbEoW5NYHuFFbSJyWll4n71CYuWuQOCzehDPyTb80WFZGLV 56 | i91kFIjeERyq88eDE5xVB3ZuRiXqaShO/9kCQQCKOEkpInaDgZSjskZvuJ47kByD 57 | 6CYsO4GIXQMMeHML8ncFH7bb6AYq5ybJVb2NTU7QLFJmfeYuhvIm+xdOreRxAkEA 58 | o5FC5Jg2FUfFzZSDmyZ6IONUsdF/i78KDV5nRv1R+hI6/oRlWNCtTNBv/lvBBd6b 59 | dseUE9QoaQZsn5lpILEvmQJAZ0B+Or1rAYjnbjnUhdVZoy9kC4Zov+4UH3N/BtSy 60 | KJRWUR0wTWfZBPZ5hAYZjTBEAFULaYCXlQKsODSp0M1aQA== 61 | -----END PRIVATE KEY-----` 62 | -------------------------------------------------------------------------------- /encrypt_test.go: -------------------------------------------------------------------------------- 1 | package pkcs7 2 | 3 | import ( 4 | "bytes" 5 | "crypto/x509" 6 | "testing" 7 | ) 8 | 9 | func TestEncrypt(t *testing.T) { 10 | modes := []int{ 11 | EncryptionAlgorithmDESCBC, 12 | EncryptionAlgorithmAES128CBC, 13 | EncryptionAlgorithmAES256CBC, 14 | EncryptionAlgorithmAES128GCM, 15 | EncryptionAlgorithmAES256GCM, 16 | } 17 | sigalgs := []x509.SignatureAlgorithm{ 18 | x509.SHA1WithRSA, 19 | x509.SHA256WithRSA, 20 | x509.SHA512WithRSA, 21 | } 22 | for _, mode := range modes { 23 | for _, sigalg := range sigalgs { 24 | ContentEncryptionAlgorithm = mode 25 | 26 | plaintext := []byte("Hello Secret World!") 27 | cert, err := createTestCertificate(sigalg) 28 | if err != nil { 29 | t.Fatal(err) 30 | } 31 | encrypted, err := Encrypt(plaintext, []*x509.Certificate{cert.Certificate}) 32 | if err != nil { 33 | t.Fatal(err) 34 | } 35 | p7, err := Parse(encrypted) 36 | if err != nil { 37 | t.Fatalf("cannot Parse encrypted result: %s", err) 38 | } 39 | result, err := p7.Decrypt(cert.Certificate, *cert.PrivateKey) 40 | if err != nil { 41 | t.Fatalf("cannot Decrypt encrypted result: %s", err) 42 | } 43 | if !bytes.Equal(plaintext, result) { 44 | t.Errorf("encrypted data does not match plaintext:\n\tExpected: %s\n\tActual: %s", plaintext, result) 45 | } 46 | } 47 | } 48 | } 49 | 50 | func TestEncryptUsingPSK(t *testing.T) { 51 | modes := []int{ 52 | EncryptionAlgorithmDESCBC, 53 | EncryptionAlgorithmAES128GCM, 54 | } 55 | 56 | for _, mode := range modes { 57 | ContentEncryptionAlgorithm = mode 58 | plaintext := []byte("Hello Secret World!") 59 | var key []byte 60 | 61 | switch mode { 62 | case EncryptionAlgorithmDESCBC: 63 | key = []byte("64BitKey") 64 | case EncryptionAlgorithmAES128GCM: 65 | key = []byte("128BitKey4AESGCM") 66 | } 67 | ciphertext, err := EncryptUsingPSK(plaintext, key) 68 | if err != nil { 69 | t.Fatal(err) 70 | } 71 | 72 | p7, _ := Parse(ciphertext) 73 | result, err := p7.DecryptUsingPSK(key) 74 | if err != nil { 75 | t.Fatalf("cannot Decrypt encrypted result: %s", err) 76 | } 77 | if !bytes.Equal(plaintext, result) { 78 | t.Errorf("encrypted data does not match plaintext:\n\tExpected: %s\n\tActual: %s", plaintext, result) 79 | } 80 | } 81 | } 82 | 83 | func TestPad(t *testing.T) { 84 | tests := []struct { 85 | Original []byte 86 | Expected []byte 87 | BlockSize int 88 | }{ 89 | {[]byte{0x1, 0x2, 0x3, 0x10}, []byte{0x1, 0x2, 0x3, 0x10, 0x4, 0x4, 0x4, 0x4}, 8}, 90 | {[]byte{0x1, 0x2, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0}, []byte{0x1, 0x2, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8}, 8}, 91 | } 92 | for _, test := range tests { 93 | padded, err := pad(test.Original, test.BlockSize) 94 | if err != nil { 95 | t.Errorf("pad encountered error: %s", err) 96 | continue 97 | } 98 | if !bytes.Equal(test.Expected, padded) { 99 | t.Errorf("pad results mismatch:\n\tExpected: %X\n\tActual: %X", test.Expected, padded) 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /decrypt.go: -------------------------------------------------------------------------------- 1 | package pkcs7 2 | 3 | import ( 4 | "bytes" 5 | "crypto" 6 | "crypto/aes" 7 | "crypto/cipher" 8 | "crypto/des" 9 | "crypto/rand" 10 | "crypto/rsa" 11 | "crypto/x509" 12 | "encoding/asn1" 13 | "errors" 14 | "fmt" 15 | ) 16 | 17 | // ErrUnsupportedAlgorithm tells you when our quick dev assumptions have failed 18 | var ErrUnsupportedAlgorithm = errors.New("pkcs7: cannot decrypt data: only RSA, DES, DES-EDE3, AES-256-CBC and AES-128-GCM supported") 19 | 20 | // ErrNotEncryptedContent is returned when attempting to Decrypt data that is not encrypted data 21 | var ErrNotEncryptedContent = errors.New("pkcs7: content data is a decryptable data type") 22 | 23 | // Decrypt decrypts encrypted content info for recipient cert and private key 24 | func (p7 *PKCS7) Decrypt(cert *x509.Certificate, pkey crypto.PrivateKey) ([]byte, error) { 25 | data, ok := p7.raw.(envelopedData) 26 | if !ok { 27 | return nil, ErrNotEncryptedContent 28 | } 29 | recipient := selectRecipientForCertificate(data.RecipientInfos, cert) 30 | if recipient.EncryptedKey == nil { 31 | return nil, errors.New("pkcs7: no enveloped recipient for provided certificate") 32 | } 33 | switch pkey := pkey.(type) { 34 | case *rsa.PrivateKey: 35 | var contentKey []byte 36 | contentKey, err := rsa.DecryptPKCS1v15(rand.Reader, pkey, recipient.EncryptedKey) 37 | if err != nil { 38 | return nil, err 39 | } 40 | return data.EncryptedContentInfo.decrypt(contentKey) 41 | } 42 | return nil, ErrUnsupportedAlgorithm 43 | } 44 | 45 | // DecryptUsingPSK decrypts encrypted data using caller provided 46 | // pre-shared secret 47 | func (p7 *PKCS7) DecryptUsingPSK(key []byte) ([]byte, error) { 48 | data, ok := p7.raw.(encryptedData) 49 | if !ok { 50 | return nil, ErrNotEncryptedContent 51 | } 52 | return data.EncryptedContentInfo.decrypt(key) 53 | } 54 | 55 | func (eci encryptedContentInfo) decrypt(key []byte) ([]byte, error) { 56 | alg := eci.ContentEncryptionAlgorithm.Algorithm 57 | if !alg.Equal(OIDEncryptionAlgorithmDESCBC) && 58 | !alg.Equal(OIDEncryptionAlgorithmDESEDE3CBC) && 59 | !alg.Equal(OIDEncryptionAlgorithmAES256CBC) && 60 | !alg.Equal(OIDEncryptionAlgorithmAES128CBC) && 61 | !alg.Equal(OIDEncryptionAlgorithmAES128GCM) && 62 | !alg.Equal(OIDEncryptionAlgorithmAES256GCM) { 63 | fmt.Printf("Unsupported Content Encryption Algorithm: %s\n", alg) 64 | return nil, ErrUnsupportedAlgorithm 65 | } 66 | 67 | // EncryptedContent can either be constructed of multple OCTET STRINGs 68 | // or _be_ a tagged OCTET STRING 69 | var cyphertext []byte 70 | if eci.EncryptedContent.IsCompound { 71 | // Complex case to concat all of the children OCTET STRINGs 72 | var buf bytes.Buffer 73 | cypherbytes := eci.EncryptedContent.Bytes 74 | for { 75 | var part []byte 76 | cypherbytes, _ = asn1.Unmarshal(cypherbytes, &part) 77 | buf.Write(part) 78 | if cypherbytes == nil { 79 | break 80 | } 81 | } 82 | cyphertext = buf.Bytes() 83 | } else { 84 | // Simple case, the bytes _are_ the cyphertext 85 | cyphertext = eci.EncryptedContent.Bytes 86 | } 87 | 88 | var block cipher.Block 89 | var err error 90 | 91 | switch { 92 | case alg.Equal(OIDEncryptionAlgorithmDESCBC): 93 | block, err = des.NewCipher(key) 94 | case alg.Equal(OIDEncryptionAlgorithmDESEDE3CBC): 95 | block, err = des.NewTripleDESCipher(key) 96 | case alg.Equal(OIDEncryptionAlgorithmAES256CBC), alg.Equal(OIDEncryptionAlgorithmAES256GCM): 97 | fallthrough 98 | case alg.Equal(OIDEncryptionAlgorithmAES128GCM), alg.Equal(OIDEncryptionAlgorithmAES128CBC): 99 | block, err = aes.NewCipher(key) 100 | } 101 | 102 | if err != nil { 103 | return nil, err 104 | } 105 | 106 | if alg.Equal(OIDEncryptionAlgorithmAES128GCM) || alg.Equal(OIDEncryptionAlgorithmAES256GCM) { 107 | params := aesGCMParameters{} 108 | paramBytes := eci.ContentEncryptionAlgorithm.Parameters.Bytes 109 | 110 | _, err := asn1.Unmarshal(paramBytes, ¶ms) 111 | if err != nil { 112 | return nil, err 113 | } 114 | 115 | gcm, err := cipher.NewGCM(block) 116 | if err != nil { 117 | return nil, err 118 | } 119 | 120 | if len(params.Nonce) != gcm.NonceSize() { 121 | return nil, errors.New("pkcs7: encryption algorithm parameters are incorrect") 122 | } 123 | if params.ICVLen != gcm.Overhead() { 124 | return nil, errors.New("pkcs7: encryption algorithm parameters are incorrect") 125 | } 126 | 127 | plaintext, err := gcm.Open(nil, params.Nonce, cyphertext, nil) 128 | if err != nil { 129 | return nil, err 130 | } 131 | 132 | return plaintext, nil 133 | } 134 | 135 | iv := eci.ContentEncryptionAlgorithm.Parameters.Bytes 136 | if len(iv) != block.BlockSize() { 137 | return nil, errors.New("pkcs7: encryption algorithm parameters are malformed") 138 | } 139 | mode := cipher.NewCBCDecrypter(block, iv) 140 | plaintext := make([]byte, len(cyphertext)) 141 | mode.CryptBlocks(plaintext, cyphertext) 142 | if plaintext, err = unpad(plaintext, mode.BlockSize()); err != nil { 143 | return nil, err 144 | } 145 | return plaintext, nil 146 | } 147 | 148 | func unpad(data []byte, blocklen int) ([]byte, error) { 149 | if blocklen < 1 { 150 | return nil, fmt.Errorf("invalid blocklen %d", blocklen) 151 | } 152 | if len(data)%blocklen != 0 || len(data) == 0 { 153 | return nil, fmt.Errorf("invalid data len %d", len(data)) 154 | } 155 | 156 | // the last byte is the length of padding 157 | padlen := int(data[len(data)-1]) 158 | 159 | // check padding integrity, all bytes should be the same 160 | pad := data[len(data)-padlen:] 161 | for _, padbyte := range pad { 162 | if padbyte != byte(padlen) { 163 | return nil, errors.New("invalid padding") 164 | } 165 | } 166 | 167 | return data[:len(data)-padlen], nil 168 | } 169 | 170 | func selectRecipientForCertificate(recipients []recipientInfo, cert *x509.Certificate) recipientInfo { 171 | for _, recp := range recipients { 172 | if isCertMatchForIssuerAndSerial(cert, recp.IssuerAndSerialNumber) { 173 | return recp 174 | } 175 | } 176 | return recipientInfo{} 177 | } 178 | -------------------------------------------------------------------------------- /ber.go: -------------------------------------------------------------------------------- 1 | package pkcs7 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | ) 7 | 8 | type asn1Object interface { 9 | EncodeTo(writer *bytes.Buffer) error 10 | } 11 | 12 | type asn1Structured struct { 13 | tagBytes []byte 14 | content []asn1Object 15 | } 16 | 17 | func (s asn1Structured) EncodeTo(out *bytes.Buffer) error { 18 | inner := new(bytes.Buffer) 19 | for _, obj := range s.content { 20 | err := obj.EncodeTo(inner) 21 | if err != nil { 22 | return err 23 | } 24 | } 25 | out.Write(s.tagBytes) 26 | encodeLength(out, inner.Len()) 27 | out.Write(inner.Bytes()) 28 | return nil 29 | } 30 | 31 | type asn1Primitive struct { 32 | tagBytes []byte 33 | length int 34 | content []byte 35 | } 36 | 37 | func (p asn1Primitive) EncodeTo(out *bytes.Buffer) error { 38 | _, err := out.Write(p.tagBytes) 39 | if err != nil { 40 | return err 41 | } 42 | if err = encodeLength(out, p.length); err != nil { 43 | return err 44 | } 45 | out.Write(p.content) 46 | return nil 47 | } 48 | 49 | func ber2der(ber []byte) ([]byte, error) { 50 | if len(ber) == 0 { 51 | return nil, errors.New("ber2der: input ber is empty") 52 | } 53 | out := new(bytes.Buffer) 54 | 55 | obj, _, err := readObject(ber, 0) 56 | if err != nil { 57 | return nil, err 58 | } 59 | obj.EncodeTo(out) 60 | 61 | // if offset < len(ber) { 62 | // return nil, fmt.Errorf("ber2der: Content longer than expected. Got %d, expected %d", offset, len(ber)) 63 | //} 64 | 65 | return out.Bytes(), nil 66 | } 67 | 68 | // encodes lengths that are longer than 127 into string of bytes 69 | func marshalLongLength(out *bytes.Buffer, i int) (err error) { 70 | n := lengthLength(i) 71 | 72 | for ; n > 0; n-- { 73 | err = out.WriteByte(byte(i >> uint((n-1)*8))) 74 | if err != nil { 75 | return 76 | } 77 | } 78 | 79 | return nil 80 | } 81 | 82 | // computes the byte length of an encoded length value 83 | func lengthLength(i int) (numBytes int) { 84 | numBytes = 1 85 | for i > 255 { 86 | numBytes++ 87 | i >>= 8 88 | } 89 | return 90 | } 91 | 92 | // encodes the length in DER format 93 | // If the length fits in 7 bits, the value is encoded directly. 94 | // 95 | // Otherwise, the number of bytes to encode the length is first determined. 96 | // This number is likely to be 4 or less for a 32bit length. This number is 97 | // added to 0x80. The length is encoded in big endian encoding follow after 98 | // 99 | // Examples: 100 | // length | byte 1 | bytes n 101 | // 0 | 0x00 | - 102 | // 120 | 0x78 | - 103 | // 200 | 0x81 | 0xC8 104 | // 500 | 0x82 | 0x01 0xF4 105 | // 106 | func encodeLength(out *bytes.Buffer, length int) (err error) { 107 | if length >= 128 { 108 | l := lengthLength(length) 109 | err = out.WriteByte(0x80 | byte(l)) 110 | if err != nil { 111 | return 112 | } 113 | err = marshalLongLength(out, length) 114 | if err != nil { 115 | return 116 | } 117 | } else { 118 | err = out.WriteByte(byte(length)) 119 | if err != nil { 120 | return 121 | } 122 | } 123 | return 124 | } 125 | 126 | func readObject(ber []byte, offset int) (asn1Object, int, error) { 127 | berLen := len(ber) 128 | if offset >= berLen { 129 | return nil, 0, errors.New("ber2der: offset is after end of ber data") 130 | } 131 | tagStart := offset 132 | b := ber[offset] 133 | offset++ 134 | if offset >= berLen { 135 | return nil, 0, errors.New("ber2der: cannot move offset forward, end of ber data reached") 136 | } 137 | tag := b & 0x1F // last 5 bits 138 | if tag == 0x1F { 139 | tag = 0 140 | for ber[offset] >= 0x80 { 141 | tag = tag*128 + ber[offset] - 0x80 142 | offset++ 143 | if offset > berLen { 144 | return nil, 0, errors.New("ber2der: cannot move offset forward, end of ber data reached") 145 | } 146 | } 147 | // jvehent 20170227: this doesn't appear to be used anywhere... 148 | //tag = tag*128 + ber[offset] - 0x80 149 | offset++ 150 | if offset > berLen { 151 | return nil, 0, errors.New("ber2der: cannot move offset forward, end of ber data reached") 152 | } 153 | } 154 | tagEnd := offset 155 | 156 | kind := b & 0x20 157 | if kind == 0 { 158 | debugprint("--> Primitive\n") 159 | } else { 160 | debugprint("--> Constructed\n") 161 | } 162 | // read length 163 | var length int 164 | l := ber[offset] 165 | offset++ 166 | if offset > berLen { 167 | return nil, 0, errors.New("ber2der: cannot move offset forward, end of ber data reached") 168 | } 169 | indefinite := false 170 | if l > 0x80 { 171 | numberOfBytes := (int)(l & 0x7F) 172 | if numberOfBytes > 4 { // int is only guaranteed to be 32bit 173 | return nil, 0, errors.New("ber2der: BER tag length too long") 174 | } 175 | if numberOfBytes == 4 && (int)(ber[offset]) > 0x7F { 176 | return nil, 0, errors.New("ber2der: BER tag length is negative") 177 | } 178 | if (int)(ber[offset]) == 0x0 { 179 | return nil, 0, errors.New("ber2der: BER tag length has leading zero") 180 | } 181 | debugprint("--> (compute length) indicator byte: %x\n", l) 182 | debugprint("--> (compute length) length bytes: % X\n", ber[offset:offset+numberOfBytes]) 183 | for i := 0; i < numberOfBytes; i++ { 184 | length = length*256 + (int)(ber[offset]) 185 | offset++ 186 | if offset > berLen { 187 | return nil, 0, errors.New("ber2der: cannot move offset forward, end of ber data reached") 188 | } 189 | } 190 | } else if l == 0x80 { 191 | indefinite = true 192 | } else { 193 | length = (int)(l) 194 | } 195 | if length < 0 { 196 | return nil, 0, errors.New("ber2der: invalid negative value found in BER tag length") 197 | } 198 | //fmt.Printf("--> length : %d\n", length) 199 | contentEnd := offset + length 200 | if contentEnd > len(ber) { 201 | return nil, 0, errors.New("ber2der: BER tag length is more than available data") 202 | } 203 | debugprint("--> content start : %d\n", offset) 204 | debugprint("--> content end : %d\n", contentEnd) 205 | debugprint("--> content : % X\n", ber[offset:contentEnd]) 206 | var obj asn1Object 207 | if indefinite && kind == 0 { 208 | return nil, 0, errors.New("ber2der: Indefinite form tag must have constructed encoding") 209 | } 210 | if kind == 0 { 211 | obj = asn1Primitive{ 212 | tagBytes: ber[tagStart:tagEnd], 213 | length: length, 214 | content: ber[offset:contentEnd], 215 | } 216 | } else { 217 | var subObjects []asn1Object 218 | for (offset < contentEnd) || indefinite { 219 | var subObj asn1Object 220 | var err error 221 | subObj, offset, err = readObject(ber, offset) 222 | if err != nil { 223 | return nil, 0, err 224 | } 225 | subObjects = append(subObjects, subObj) 226 | 227 | if indefinite { 228 | terminated, err := isIndefiniteTermination(ber, offset) 229 | if err != nil { 230 | return nil, 0, err 231 | } 232 | 233 | if terminated { 234 | break 235 | } 236 | } 237 | } 238 | obj = asn1Structured{ 239 | tagBytes: ber[tagStart:tagEnd], 240 | content: subObjects, 241 | } 242 | } 243 | 244 | // Apply indefinite form length with 0x0000 terminator. 245 | if indefinite { 246 | contentEnd = offset + 2 247 | } 248 | 249 | return obj, contentEnd, nil 250 | } 251 | 252 | func isIndefiniteTermination(ber []byte, offset int) (bool, error) { 253 | if len(ber) - offset < 2 { 254 | return false, errors.New("ber2der: Invalid BER format") 255 | } 256 | 257 | return bytes.Index(ber[offset:], []byte{0x0, 0x0}) == 0, nil 258 | } 259 | 260 | func debugprint(format string, a ...interface{}) { 261 | //fmt.Printf(format, a) 262 | } 263 | -------------------------------------------------------------------------------- /verify_test_dsa.go: -------------------------------------------------------------------------------- 1 | // +build go1.11 go1.12 go1.13 go1.14 go1.15 2 | 3 | package pkcs7 4 | 5 | import ( 6 | "crypto/x509" 7 | "encoding/pem" 8 | "fmt" 9 | "io/ioutil" 10 | "os" 11 | "os/exec" 12 | "testing" 13 | ) 14 | 15 | func TestVerifyEC2(t *testing.T) { 16 | fixture := UnmarshalDSATestFixture(EC2IdentityDocumentFixture) 17 | p7, err := Parse(fixture.Input) 18 | if err != nil { 19 | t.Errorf("Parse encountered unexpected error: %v", err) 20 | } 21 | p7.Certificates = []*x509.Certificate{fixture.Certificate} 22 | if err := p7.Verify(); err != nil { 23 | t.Errorf("Verify failed with error: %v", err) 24 | } 25 | } 26 | 27 | var EC2IdentityDocumentFixture = ` 28 | -----BEGIN PKCS7----- 29 | MIAGCSqGSIb3DQEHAqCAMIACAQExCzAJBgUrDgMCGgUAMIAGCSqGSIb3DQEHAaCA 30 | JIAEggGmewogICJwcml2YXRlSXAiIDogIjE3Mi4zMC4wLjI1MiIsCiAgImRldnBh 31 | eVByb2R1Y3RDb2RlcyIgOiBudWxsLAogICJhdmFpbGFiaWxpdHlab25lIiA6ICJ1 32 | cy1lYXN0LTFhIiwKICAidmVyc2lvbiIgOiAiMjAxMC0wOC0zMSIsCiAgImluc3Rh 33 | bmNlSWQiIDogImktZjc5ZmU1NmMiLAogICJiaWxsaW5nUHJvZHVjdHMiIDogbnVs 34 | bCwKICAiaW5zdGFuY2VUeXBlIiA6ICJ0Mi5taWNybyIsCiAgImFjY291bnRJZCIg 35 | OiAiMTIxNjU5MDE0MzM0IiwKICAiaW1hZ2VJZCIgOiAiYW1pLWZjZTNjNjk2IiwK 36 | ICAicGVuZGluZ1RpbWUiIDogIjIwMTYtMDQtMDhUMDM6MDE6MzhaIiwKICAiYXJj 37 | aGl0ZWN0dXJlIiA6ICJ4ODZfNjQiLAogICJrZXJuZWxJZCIgOiBudWxsLAogICJy 38 | YW1kaXNrSWQiIDogbnVsbCwKICAicmVnaW9uIiA6ICJ1cy1lYXN0LTEiCn0AAAAA 39 | AAAxggEYMIIBFAIBATBpMFwxCzAJBgNVBAYTAlVTMRkwFwYDVQQIExBXYXNoaW5n 40 | dG9uIFN0YXRlMRAwDgYDVQQHEwdTZWF0dGxlMSAwHgYDVQQKExdBbWF6b24gV2Vi 41 | IFNlcnZpY2VzIExMQwIJAJa6SNnlXhpnMAkGBSsOAwIaBQCgXTAYBgkqhkiG9w0B 42 | CQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0xNjA0MDgwMzAxNDRaMCMG 43 | CSqGSIb3DQEJBDEWBBTuUc28eBXmImAautC+wOjqcFCBVjAJBgcqhkjOOAQDBC8w 44 | LQIVAKA54NxGHWWCz5InboDmY/GHs33nAhQ6O/ZI86NwjA9Vz3RNMUJrUPU5tAAA 45 | AAAAAA== 46 | -----END PKCS7----- 47 | -----BEGIN CERTIFICATE----- 48 | MIIC7TCCAq0CCQCWukjZ5V4aZzAJBgcqhkjOOAQDMFwxCzAJBgNVBAYTAlVTMRkw 49 | FwYDVQQIExBXYXNoaW5ndG9uIFN0YXRlMRAwDgYDVQQHEwdTZWF0dGxlMSAwHgYD 50 | VQQKExdBbWF6b24gV2ViIFNlcnZpY2VzIExMQzAeFw0xMjAxMDUxMjU2MTJaFw0z 51 | ODAxMDUxMjU2MTJaMFwxCzAJBgNVBAYTAlVTMRkwFwYDVQQIExBXYXNoaW5ndG9u 52 | IFN0YXRlMRAwDgYDVQQHEwdTZWF0dGxlMSAwHgYDVQQKExdBbWF6b24gV2ViIFNl 53 | cnZpY2VzIExMQzCCAbcwggEsBgcqhkjOOAQBMIIBHwKBgQCjkvcS2bb1VQ4yt/5e 54 | ih5OO6kK/n1Lzllr7D8ZwtQP8fOEpp5E2ng+D6Ud1Z1gYipr58Kj3nssSNpI6bX3 55 | VyIQzK7wLclnd/YozqNNmgIyZecN7EglK9ITHJLP+x8FtUpt3QbyYXJdmVMegN6P 56 | hviYt5JH/nYl4hh3Pa1HJdskgQIVALVJ3ER11+Ko4tP6nwvHwh6+ERYRAoGBAI1j 57 | k+tkqMVHuAFcvAGKocTgsjJem6/5qomzJuKDmbJNu9Qxw3rAotXau8Qe+MBcJl/U 58 | hhy1KHVpCGl9fueQ2s6IL0CaO/buycU1CiYQk40KNHCcHfNiZbdlx1E9rpUp7bnF 59 | lRa2v1ntMX3caRVDdbtPEWmdxSCYsYFDk4mZrOLBA4GEAAKBgEbmeve5f8LIE/Gf 60 | MNmP9CM5eovQOGx5ho8WqD+aTebs+k2tn92BBPqeZqpWRa5P/+jrdKml1qx4llHW 61 | MXrs3IgIb6+hUIB+S8dz8/mmO0bpr76RoZVCXYab2CZedFut7qc3WUH9+EUAH5mw 62 | vSeDCOUMYQR7R9LINYwouHIziqQYMAkGByqGSM44BAMDLwAwLAIUWXBlk40xTwSw 63 | 7HX32MxXYruse9ACFBNGmdX2ZBrVNGrN9N2f6ROk0k9K 64 | -----END CERTIFICATE-----` 65 | 66 | func TestDSASignWithOpenSSLAndVerify(t *testing.T) { 67 | content := []byte(` 68 | A ship in port is safe, 69 | but that's not what ships are built for. 70 | -- Grace Hopper`) 71 | // write the content to a temp file 72 | tmpContentFile, err := ioutil.TempFile("", "TestDSASignWithOpenSSLAndVerify_content") 73 | if err != nil { 74 | t.Fatal(err) 75 | } 76 | ioutil.WriteFile(tmpContentFile.Name(), content, 0755) 77 | 78 | // write the signer cert to a temp file 79 | tmpSignerCertFile, err := ioutil.TempFile("", "TestDSASignWithOpenSSLAndVerify_signer") 80 | if err != nil { 81 | t.Fatal(err) 82 | } 83 | ioutil.WriteFile(tmpSignerCertFile.Name(), dsaPublicCert, 0755) 84 | 85 | // write the signer key to a temp file 86 | tmpSignerKeyFile, err := ioutil.TempFile("", "TestDSASignWithOpenSSLAndVerify_key") 87 | if err != nil { 88 | t.Fatal(err) 89 | } 90 | ioutil.WriteFile(tmpSignerKeyFile.Name(), dsaPrivateKey, 0755) 91 | 92 | tmpSignedFile, err := ioutil.TempFile("", "TestDSASignWithOpenSSLAndVerify_signature") 93 | if err != nil { 94 | t.Fatal(err) 95 | } 96 | // call openssl to sign the content 97 | opensslCMD := exec.Command("openssl", "smime", "-sign", "-nodetach", "-md", "sha1", 98 | "-in", tmpContentFile.Name(), "-out", tmpSignedFile.Name(), 99 | "-signer", tmpSignerCertFile.Name(), "-inkey", tmpSignerKeyFile.Name(), 100 | "-certfile", tmpSignerCertFile.Name(), "-outform", "PEM") 101 | out, err := opensslCMD.CombinedOutput() 102 | if err != nil { 103 | t.Fatalf("openssl command failed with %s: %s", err, out) 104 | } 105 | 106 | // verify the signed content 107 | pemSignature, err := ioutil.ReadFile(tmpSignedFile.Name()) 108 | if err != nil { 109 | t.Fatal(err) 110 | } 111 | fmt.Printf("%s\n", pemSignature) 112 | derBlock, _ := pem.Decode(pemSignature) 113 | if derBlock == nil { 114 | t.Fatalf("failed to read DER block from signature PEM %s", tmpSignedFile.Name()) 115 | } 116 | p7, err := Parse(derBlock.Bytes) 117 | if err != nil { 118 | t.Fatalf("Parse encountered unexpected error: %v", err) 119 | } 120 | if err := p7.Verify(); err != nil { 121 | t.Fatalf("Verify failed with error: %v", err) 122 | } 123 | os.Remove(tmpSignerCertFile.Name()) // clean up 124 | os.Remove(tmpSignerKeyFile.Name()) // clean up 125 | os.Remove(tmpContentFile.Name()) // clean up 126 | } 127 | 128 | var dsaPrivateKey = []byte(`-----BEGIN PRIVATE KEY----- 129 | MIIBSwIBADCCASwGByqGSM44BAEwggEfAoGBAP1/U4EddRIpUt9KnC7s5Of2EbdS 130 | PO9EAMMeP4C2USZpRV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVCl 131 | pJ+f6AR7ECLCT7up1/63xhv4O1fnxqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith 132 | 1yrv8iIDGZ3RSAHHAhUAl2BQjxUjC8yykrmCouuEC/BYHPUCgYEA9+GghdabPd7L 133 | vKtcNrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCBgLRJFnEj6EwoFhO3 134 | zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhRkImo 135 | g9/hWuWfBpKLZl6Ae1UlZAFMO/7PSSoEFgIUfW4aPdQBn9gJZp2KuNpzgHzvfsE= 136 | -----END PRIVATE KEY-----`) 137 | 138 | var dsaPublicCert = []byte(`-----BEGIN CERTIFICATE----- 139 | MIIDOjCCAvWgAwIBAgIEPCY/UDANBglghkgBZQMEAwIFADBsMRAwDgYDVQQGEwdV 140 | bmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYD 141 | VQQKEwdVbmtub3duMRAwDgYDVQQLEwdVbmtub3duMRAwDgYDVQQDEwdVbmtub3du 142 | MB4XDTE4MTAyMjEzNDMwN1oXDTQ2MDMwOTEzNDMwN1owbDEQMA4GA1UEBhMHVW5r 143 | bm93bjEQMA4GA1UECBMHVW5rbm93bjEQMA4GA1UEBxMHVW5rbm93bjEQMA4GA1UE 144 | ChMHVW5rbm93bjEQMA4GA1UECxMHVW5rbm93bjEQMA4GA1UEAxMHVW5rbm93bjCC 145 | AbgwggEsBgcqhkjOOAQBMIIBHwKBgQD9f1OBHXUSKVLfSpwu7OTn9hG3UjzvRADD 146 | Hj+AtlEmaUVdQCJR+1k9jVj6v8X1ujD2y5tVbNeBO4AdNG/yZmC3a5lQpaSfn+gE 147 | exAiwk+7qdf+t8Yb+DtX58aophUPBPuD9tPFHsMCNVQTWhaRMvZ1864rYdcq7/Ii 148 | Axmd0UgBxwIVAJdgUI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4 149 | V7l5lK+7+jrqgvlXTAs9B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozI 150 | puE8FnqLVHyNKOCjrh4rs6Z1kW6jfwv6ITVi8ftiegEkO8yk8b6oUZCJqIPf4Vrl 151 | nwaSi2ZegHtVJWQBTDv+z0kqA4GFAAKBgQDCriMPbEVBoRK4SOUeFwg7+VRf4TTp 152 | rcOQC9IVVoCjXzuWEGrp3ZI7YWJSpFnSch4lk29RH8O0HpI/NOzKnOBtnKr782pt 153 | 1k/bJVMH9EaLd6MKnAVjrCDMYBB0MhebZ8QHY2elZZCWoqDYAcIDOsEx+m4NLErT 154 | ypPnjS5M0jm1PKMhMB8wHQYDVR0OBBYEFC0Yt5XdM0Kc95IX8NQ8XRssGPx7MA0G 155 | CWCGSAFlAwQDAgUAAzAAMC0CFQCIgQtrZZ9hdZG1ROhR5hc8nYEmbgIUAIlgC688 156 | qzy/7yePTlhlpj+ahMM= 157 | -----END CERTIFICATE-----`) 158 | 159 | type DSATestFixture struct { 160 | Input []byte 161 | Certificate *x509.Certificate 162 | } 163 | 164 | func UnmarshalDSATestFixture(testPEMBlock string) DSATestFixture { 165 | var result DSATestFixture 166 | var derBlock *pem.Block 167 | var pemBlock = []byte(testPEMBlock) 168 | for { 169 | derBlock, pemBlock = pem.Decode(pemBlock) 170 | if derBlock == nil { 171 | break 172 | } 173 | switch derBlock.Type { 174 | case "PKCS7": 175 | result.Input = derBlock.Bytes 176 | case "CERTIFICATE": 177 | result.Certificate, _ = x509.ParseCertificate(derBlock.Bytes) 178 | } 179 | } 180 | 181 | return result 182 | } 183 | -------------------------------------------------------------------------------- /ber_test.go: -------------------------------------------------------------------------------- 1 | package pkcs7 2 | 3 | import ( 4 | "bytes" 5 | "encoding/asn1" 6 | "encoding/pem" 7 | "fmt" 8 | "strings" 9 | "testing" 10 | ) 11 | 12 | func TestBer2Der(t *testing.T) { 13 | t.Parallel() 14 | // indefinite length fixture 15 | ber := []byte{0x30, 0x80, 0x02, 0x01, 0x01, 0x00, 0x00} 16 | expected := []byte{0x30, 0x03, 0x02, 0x01, 0x01} 17 | der, err := ber2der(ber) 18 | if err != nil { 19 | t.Fatalf("ber2der failed with error: %v", err) 20 | } 21 | if !bytes.Equal(der, expected) { 22 | t.Errorf("ber2der result did not match.\n\tExpected: % X\n\tActual: % X", expected, der) 23 | } 24 | 25 | if der2, err := ber2der(der); err != nil { 26 | t.Errorf("ber2der on DER bytes failed with error: %v", err) 27 | } else { 28 | if !bytes.Equal(der, der2) { 29 | t.Error("ber2der is not idempotent") 30 | } 31 | } 32 | var thing struct { 33 | Number int 34 | } 35 | rest, err := asn1.Unmarshal(der, &thing) 36 | if err != nil { 37 | t.Errorf("Cannot parse resulting DER because: %v", err) 38 | } else if len(rest) > 0 { 39 | t.Errorf("Resulting DER has trailing data: % X", rest) 40 | } 41 | } 42 | 43 | func TestBer2Der_Negatives(t *testing.T) { 44 | t.Parallel() 45 | fixtures := []struct { 46 | Input []byte 47 | ErrorContains string 48 | }{ 49 | {[]byte{0x30, 0x85}, "tag length too long"}, 50 | {[]byte{0x30, 0x84, 0x80, 0x0, 0x0, 0x0}, "length is negative"}, 51 | {[]byte{0x30, 0x82, 0x0, 0x1}, "length has leading zero"}, 52 | {[]byte{0x30, 0x80, 0x1, 0x2, 0x1, 0x2}, "Invalid BER format"}, 53 | {[]byte{0x30, 0x80, 0x1, 0x2}, "BER tag length is more than available data"}, 54 | {[]byte{0x30, 0x03, 0x01, 0x02}, "length is more than available data"}, 55 | {[]byte{0x30}, "end of ber data reached"}, 56 | } 57 | 58 | for _, fixture := range fixtures { 59 | _, err := ber2der(fixture.Input) 60 | if err == nil { 61 | t.Errorf("No error thrown. Expected: %s", fixture.ErrorContains) 62 | } 63 | if !strings.Contains(err.Error(), fixture.ErrorContains) { 64 | t.Errorf("Unexpected error thrown.\n\tExpected: /%s/\n\tActual: %s", fixture.ErrorContains, err.Error()) 65 | } 66 | } 67 | } 68 | 69 | func TestBer2Der_NestedMultipleIndefinite(t *testing.T) { 70 | t.Parallel() 71 | // indefinite length fixture 72 | ber := []byte{0x30, 0x80, 0x30, 0x80, 0x02, 0x01, 0x01, 0x00, 0x00, 0x30, 0x80, 0x02, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00} 73 | expected := []byte{0x30, 0x0A, 0x30, 0x03, 0x02, 0x01, 0x01, 0x30, 0x03, 0x02, 0x01, 0x02} 74 | 75 | der, err := ber2der(ber) 76 | if err != nil { 77 | t.Fatalf("ber2der failed with error: %v", err) 78 | } 79 | if bytes.Compare(der, expected) != 0 { 80 | t.Errorf("ber2der result did not match.\n\tExpected: % X\n\tActual: % X", expected, der) 81 | } 82 | 83 | if der2, err := ber2der(der); err != nil { 84 | t.Errorf("ber2der on DER bytes failed with error: %v", err) 85 | } else { 86 | if !bytes.Equal(der, der2) { 87 | t.Error("ber2der is not idempotent") 88 | } 89 | } 90 | var thing struct { 91 | Nest1 struct { 92 | Number int 93 | } 94 | Nest2 struct { 95 | Number int 96 | } 97 | } 98 | rest, err := asn1.Unmarshal(der, &thing) 99 | if err != nil { 100 | t.Errorf("Cannot parse resulting DER because: %v", err) 101 | } else if len(rest) > 0 { 102 | t.Errorf("Resulting DER has trailing data: % X", rest) 103 | } 104 | } 105 | 106 | func TestVerifyIndefiniteLengthBer(t *testing.T) { 107 | t.Parallel() 108 | decoded := mustDecodePEM([]byte(testPKCS7)) 109 | 110 | _, err := ber2der(decoded) 111 | if err != nil { 112 | t.Errorf("cannot parse indefinite length ber: %v", err) 113 | } 114 | } 115 | 116 | func mustDecodePEM(data []byte) []byte { 117 | var block *pem.Block 118 | block, rest := pem.Decode(data) 119 | if len(rest) != 0 { 120 | panic(fmt.Errorf("unexpected remaining PEM block during decode")) 121 | } 122 | return block.Bytes 123 | } 124 | 125 | const testPKCS7 = ` 126 | -----BEGIN PKCS7----- 127 | MIAGCSqGSIb3DQEHAqCAMIACAQExDzANBglghkgBZQMEAgEFADCABgkqhkiG9w0B 128 | BwGggCSABIIDfXsiQWdlbnRBY3Rpb25PdmVycmlkZXMiOnsiQWdlbnRPdmVycmlk 129 | ZXMiOnsiRmlsZUV4aXN0c0JlaGF2aW9yIjoiT1ZFUldSSVRFIn19LCJBcHBsaWNh 130 | dGlvbklkIjoiZTA0NDIzZTQtN2E2Ny00ZjljLWIyOTEtOTllNjNjMWMyMTU4Iiwi 131 | QXBwbGljYXRpb25OYW1lIjoibWthbmlhLXhyZF9zYW0uY2R3c19lY2hvc2VydmVy 132 | IiwiRGVwbG95bWVudENyZWF0b3IiOiJ1c2VyIiwiRGVwbG95bWVudEdyb3VwSWQi 133 | OiJmYWI5MjEwZi1mNmM3LTQyODUtYWEyZC03Mzc2MGQ4ODE3NmEiLCJEZXBsb3lt 134 | ZW50R3JvdXBOYW1lIjoibWthbmlhLXhyZF9zYW0uY2R3c19lY2hvc2VydmVyX2Rn 135 | IiwiRGVwbG95bWVudElkIjoiZC1UREUxVTNXREEiLCJEZXBsb3ltZW50VHlwZSI6 136 | IklOX1BMQUNFIiwiR2l0SHViQWNjZXNzVG9rZW4iOm51bGwsIkluc3RhbmNlR3Jv 137 | dXBJZCI6ImZhYjkyMTBmLWY2YzctNDI4NS1hYTJkLTczNzYwZDg4MTc2YSIsIlJl 138 | dmlzaW9uIjp7IkFwcFNwZWNDb250ZW50IjpudWxsLCJDb2RlQ29tbWl0UmV2aXNp 139 | b24iOm51bGwsIkdpdEh1YlJldmlzaW9uIjpudWxsLCJHaXRSZXZpc2lvbiI6bnVs 140 | bCwiUmV2aXNpb25UeXBlIjoiUzMiLCJTM1JldmlzaW9uIjp7IkJ1Y2tldCI6Im1r 141 | YW5pYS1jZHdzLWRlcGxveS1idWNrZXQiLCJCdW5kbGVUeXBlIjoiemlwIiwiRVRh 142 | ZyI6bnVsbCwiS2V5IjoieHJkOjpzYW0uY2R3czo6ZWNob3NlcnZlcjo6MTo6Lnpp 143 | cCIsIlZlcnNpb24iOm51bGx9fSwiUzNSZXZpc2lvbiI6eyJCdWNrZXQiOiJta2Fu 144 | aWEtY2R3cy1kZXBsb3ktYnVja2V0IiwiQnVuZGxlVHlwZSI6InppcCIsIkVUYWci 145 | Om51bGwsIktleSI6InhyZDo6c2FtLmNkd3M6OmVjaG9zZXJ2ZXI6OjE6Oi56aXAi 146 | LCJWZXJzaW9uIjpudWxsfSwiVGFyZ2V0UmV2aXNpb24iOm51bGx9AAAAAAAAoIAw 147 | ggWbMIIEg6ADAgECAhAGrjFMK45t2jcNHtjY1DjEMA0GCSqGSIb3DQEBCwUAMEYx 148 | CzAJBgNVBAYTAlVTMQ8wDQYDVQQKEwZBbWF6b24xFTATBgNVBAsTDFNlcnZlciBD 149 | QSAxQjEPMA0GA1UEAxMGQW1hem9uMB4XDTIwMTExMjAwMDAwMFoXDTIxMTAxNTIz 150 | NTk1OVowNDEyMDAGA1UEAxMpY29kZWRlcGxveS1zaWduZXItdXMtZWFzdC0yLmFt 151 | YXpvbmF3cy5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDit4f+ 152 | I4BSv4rBV/8bJ+f4KqBwTCt9iJeau/r9liQfMgj/C1M2E+aa++u8BtY/LQstB44v 153 | v6KqcaiOyWpkD9OsUty9qb4eNTPF2Y4jpNsi/Hfw0phsd9gLun2foppILmL4lZIG 154 | lBhTeEwv6qV4KbyXOG9abHOX32+jVFtM1rbzHNFvz90ysfZp16TBAi7IRKEZeXvd 155 | MvlJJMAJtAoblxiDIS3A1csY1G4XHYET8xIoCop3mqEZEtAxUUP2epdXXdhD2U0G 156 | 7alSRS54o91QW1Dp3A13lu1A1nds9CkWlPkDTpKSUG/qN5y5+6dCCGaydgL5krMs 157 | R79bCrR1sEKm5hi1AgMBAAGjggKVMIICkTAfBgNVHSMEGDAWgBRZpGYGUqB7lZI8 158 | o5QHJ5Z0W/k90DAdBgNVHQ4EFgQUPF5qTbnTDYhmp7tGmmL/jTmLoHMwNAYDVR0R 159 | BC0wK4IpY29kZWRlcGxveS1zaWduZXItdXMtZWFzdC0yLmFtYXpvbmF3cy5jb20w 160 | DgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjA7 161 | BgNVHR8ENDAyMDCgLqAshipodHRwOi8vY3JsLnNjYTFiLmFtYXpvbnRydXN0LmNv 162 | bS9zY2ExYi5jcmwwIAYDVR0gBBkwFzALBglghkgBhv1sAQIwCAYGZ4EMAQIBMHUG 163 | CCsGAQUFBwEBBGkwZzAtBggrBgEFBQcwAYYhaHR0cDovL29jc3Auc2NhMWIuYW1h 164 | em9udHJ1c3QuY29tMDYGCCsGAQUFBzAChipodHRwOi8vY3J0LnNjYTFiLmFtYXpv 165 | bnRydXN0LmNvbS9zY2ExYi5jcnQwDAYDVR0TAQH/BAIwADCCAQQGCisGAQQB1nkC 166 | BAIEgfUEgfIA8AB2APZclC/RdzAiFFQYCDCUVo7jTRMZM7/fDC8gC8xO8WTjAAAB 167 | dboejIcAAAQDAEcwRQIgeqoKXbST17TCEzM1BMWx/jjyVQVBIN3LG17U4OaV364C 168 | IQDPUSJZhJm7uqGea6+VwqeDe/vGuGSuJzkDwTIOeIXPaAB2AFzcQ5L+5qtFRLFe 169 | mtRW5hA3+9X6R9yhc5SyXub2xw7KAAABdboejNQAAAQDAEcwRQIgEKIAwwhjUcq2 170 | iwzBAagdy+fTiKnBY1Yjf6wOeRpwXfMCIQC8wM3nxiWrGgIpdzzgDvFhZZTV3N81 171 | JWcYAu+srIVOhTANBgkqhkiG9w0BAQsFAAOCAQEAer9kml53XFy4ZSVzCbdsIFYP 172 | Ohu7LDf5iffHBVZFnGOEVOmiPYYkNwi9R6EHIYaAs7G7GGLCp/6tdc+G4eF1j6wB 173 | IkmXZcxMTxk/87R+S+36yDLg1GBZvqttLfexj0TRVAfVLJc7FjLXAW2+wi7YyNe8 174 | X17lWBwHxa1r5KgweJshGzYVUsgMTSx0aJ+93ZnqplBp9x+9DSQNqqNlBgxFANxs 175 | ux+dfpduyLd8VLqtlECGC07tYE4mBaAjMiNjCZRWMp8ya/Z6J/bJZ27IDGA4dXzm 176 | l9NNnlbuUDAenAByUqE+0b78J6EmmdAVf+N8siriMg02FdP3lAXJLE8tDeZp8AAA 177 | MYICIDCCAhwCAQEwWjBGMQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRUw 178 | EwYDVQQLEwxTZXJ2ZXIgQ0EgMUIxDzANBgNVBAMTBkFtYXpvbgIQBq4xTCuObdo3 179 | DR7Y2NQ4xDANBglghkgBZQMEAgEFAKCBmDAYBgkqhkiG9w0BCQMxCwYJKoZIhvcN 180 | AQcBMBwGCSqGSIb3DQEJBTEPFw0yMTA2MjQxOTU1MzFaMC0GCSqGSIb3DQEJNDEg 181 | MB4wDQYJYIZIAWUDBAIBBQChDQYJKoZIhvcNAQELBQAwLwYJKoZIhvcNAQkEMSIE 182 | IP7gMuT2H0/AhgPgj3Eo0NWCIdQOBjJO18coNKIaOnJYMA0GCSqGSIb3DQEBCwUA 183 | BIIBAJX+e87q0YvRon9/ENTvE0FoYMzYblID2Reek6L217ZlZ6pUuRsc4ghhJ5Yh 184 | WZeOCaLwi4mrnQ5/+DGKkJ4a/w5sqFTwtJIGIIAuDCn/uDm8kIDUVkbeznSOLoPA 185 | 67cxiqgIdqZ5pqUoid2YsDj20owrGDG4wUF6ZvhM9g/5va3CAhxqvTE2HwjhHTfz 186 | Cgl8Nlvalz7YxXEf2clFEiEVa1fVaGMl9pCyedAmTfd6hoivcpAsopvXfVaaaR2y 187 | iuZidpUfFhSk+Ls7TU/kB74ckfUGj5q/5HcKJgb/S+FYUV7eu0ewzTyW1uRl/d0U 188 | Tb7e7EjgDGJsjOTMdTrMfv8ho8kAAAAAAAA= 189 | -----END PKCS7----- 190 | ` 191 | -------------------------------------------------------------------------------- /sign_test.go: -------------------------------------------------------------------------------- 1 | package pkcs7 2 | 3 | import ( 4 | "bytes" 5 | "crypto/dsa" 6 | "crypto/x509" 7 | "encoding/asn1" 8 | "encoding/pem" 9 | "fmt" 10 | "io/ioutil" 11 | "log" 12 | "math/big" 13 | "os" 14 | "os/exec" 15 | "testing" 16 | ) 17 | 18 | func TestSign(t *testing.T) { 19 | content := []byte("Hello World") 20 | sigalgs := []x509.SignatureAlgorithm{ 21 | x509.SHA1WithRSA, 22 | x509.SHA256WithRSA, 23 | x509.SHA512WithRSA, 24 | x509.ECDSAWithSHA1, 25 | x509.ECDSAWithSHA256, 26 | x509.ECDSAWithSHA384, 27 | x509.ECDSAWithSHA512, 28 | } 29 | for _, sigalgroot := range sigalgs { 30 | rootCert, err := createTestCertificateByIssuer("PKCS7 Test Root CA", nil, sigalgroot, true) 31 | if err != nil { 32 | t.Fatalf("test %s: cannot generate root cert: %s", sigalgroot, err) 33 | } 34 | truststore := x509.NewCertPool() 35 | truststore.AddCert(rootCert.Certificate) 36 | for _, sigalginter := range sigalgs { 37 | interCert, err := createTestCertificateByIssuer("PKCS7 Test Intermediate Cert", rootCert, sigalginter, true) 38 | if err != nil { 39 | t.Fatalf("test %s/%s: cannot generate intermediate cert: %s", sigalgroot, sigalginter, err) 40 | } 41 | var parents []*x509.Certificate 42 | parents = append(parents, interCert.Certificate) 43 | for _, sigalgsigner := range sigalgs { 44 | signerCert, err := createTestCertificateByIssuer("PKCS7 Test Signer Cert", interCert, sigalgsigner, false) 45 | if err != nil { 46 | t.Fatalf("test %s/%s/%s: cannot generate signer cert: %s", sigalgroot, sigalginter, sigalgsigner, err) 47 | } 48 | for _, testDetach := range []bool{false, true} { 49 | log.Printf("test %s/%s/%s detached %t\n", sigalgroot, sigalginter, sigalgsigner, testDetach) 50 | toBeSigned, err := NewSignedData(content) 51 | if err != nil { 52 | t.Fatalf("test %s/%s/%s: cannot initialize signed data: %s", sigalgroot, sigalginter, sigalgsigner, err) 53 | } 54 | 55 | // Set the digest to match the end entity cert 56 | signerDigest, _ := getDigestOIDForSignatureAlgorithm(signerCert.Certificate.SignatureAlgorithm) 57 | toBeSigned.SetDigestAlgorithm(signerDigest) 58 | 59 | if err := toBeSigned.AddSignerChain(signerCert.Certificate, *signerCert.PrivateKey, parents, SignerInfoConfig{}); err != nil { 60 | t.Fatalf("test %s/%s/%s: cannot add signer: %s", sigalgroot, sigalginter, sigalgsigner, err) 61 | } 62 | if testDetach { 63 | toBeSigned.Detach() 64 | } 65 | signed, err := toBeSigned.Finish() 66 | if err != nil { 67 | t.Fatalf("test %s/%s/%s: cannot finish signing data: %s", sigalgroot, sigalginter, sigalgsigner, err) 68 | } 69 | pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: signed}) 70 | p7, err := Parse(signed) 71 | if err != nil { 72 | t.Fatalf("test %s/%s/%s: cannot parse signed data: %s", sigalgroot, sigalginter, sigalgsigner, err) 73 | } 74 | if testDetach { 75 | p7.Content = content 76 | } 77 | if !bytes.Equal(content, p7.Content) { 78 | 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) 79 | } 80 | if err := p7.VerifyWithChain(truststore); err != nil { 81 | t.Errorf("test %s/%s/%s: cannot verify signed data: %s", sigalgroot, sigalginter, sigalgsigner, err) 82 | } 83 | if !signerDigest.Equal(p7.Signers[0].DigestAlgorithm.Algorithm) { 84 | t.Errorf("test %s/%s/%s: expected digest algorithm %q but got %q", 85 | sigalgroot, sigalginter, sigalgsigner, signerDigest, p7.Signers[0].DigestAlgorithm.Algorithm) 86 | } 87 | } 88 | } 89 | } 90 | } 91 | } 92 | 93 | func TestDSASignAndVerifyWithOpenSSL(t *testing.T) { 94 | content := []byte("Hello World") 95 | // write the content to a temp file 96 | tmpContentFile, err := ioutil.TempFile("", "TestDSASignAndVerifyWithOpenSSL_content") 97 | if err != nil { 98 | t.Fatal(err) 99 | } 100 | ioutil.WriteFile(tmpContentFile.Name(), content, 0755) 101 | 102 | block, _ := pem.Decode([]byte(dsaPublicCert)) 103 | if block == nil { 104 | t.Fatal("failed to parse certificate PEM") 105 | } 106 | signerCert, err := x509.ParseCertificate(block.Bytes) 107 | if err != nil { 108 | t.Fatal("failed to parse certificate: " + err.Error()) 109 | } 110 | 111 | // write the signer cert to a temp file 112 | tmpSignerCertFile, err := ioutil.TempFile("", "TestDSASignAndVerifyWithOpenSSL_signer") 113 | if err != nil { 114 | t.Fatal(err) 115 | } 116 | ioutil.WriteFile(tmpSignerCertFile.Name(), dsaPublicCert, 0755) 117 | 118 | priv := dsa.PrivateKey{ 119 | PublicKey: dsa.PublicKey{Parameters: dsa.Parameters{P: fromHex("fd7f53811d75122952df4a9c2eece4e7f611b7523cef4400c31e3f80b6512669455d402251fb593d8d58fabfc5f5ba30f6cb9b556cd7813b801d346ff26660b76b9950a5a49f9fe8047b1022c24fbba9d7feb7c61bf83b57e7c6a8a6150f04fb83f6d3c51ec3023554135a169132f675f3ae2b61d72aeff22203199dd14801c7"), 120 | Q: fromHex("9760508F15230BCCB292B982A2EB840BF0581CF5"), 121 | G: fromHex("F7E1A085D69B3DDECBBCAB5C36B857B97994AFBBFA3AEA82F9574C0B3D0782675159578EBAD4594FE67107108180B449167123E84C281613B7CF09328CC8A6E13C167A8B547C8D28E0A3AE1E2BB3A675916EA37F0BFA213562F1FB627A01243BCCA4F1BEA8519089A883DFE15AE59F06928B665E807B552564014C3BFECF492A"), 122 | }, 123 | }, 124 | X: fromHex("7D6E1A3DD4019FD809669D8AB8DA73807CEF7EC1"), 125 | } 126 | toBeSigned, err := NewSignedData(content) 127 | if err != nil { 128 | t.Fatalf("test case: cannot initialize signed data: %s", err) 129 | } 130 | if err := toBeSigned.SignWithoutAttr(signerCert, &priv, SignerInfoConfig{}); err != nil { 131 | t.Fatalf("Cannot add signer: %s", err) 132 | } 133 | toBeSigned.Detach() 134 | signed, err := toBeSigned.Finish() 135 | if err != nil { 136 | t.Fatalf("test case: cannot finish signing data: %s", err) 137 | } 138 | 139 | // write the signature to a temp file 140 | tmpSignatureFile, err := ioutil.TempFile("", "TestDSASignAndVerifyWithOpenSSL_signature") 141 | if err != nil { 142 | t.Fatal(err) 143 | } 144 | ioutil.WriteFile(tmpSignatureFile.Name(), pem.EncodeToMemory(&pem.Block{Type: "PKCS7", Bytes: signed}), 0755) 145 | 146 | // call openssl to verify the signature on the content using the root 147 | opensslCMD := exec.Command("openssl", "smime", "-verify", "-noverify", 148 | "-in", tmpSignatureFile.Name(), "-inform", "PEM", 149 | "-content", tmpContentFile.Name()) 150 | out, err := opensslCMD.CombinedOutput() 151 | if err != nil { 152 | t.Fatalf("test case: openssl command failed with %s: %s", err, out) 153 | } 154 | os.Remove(tmpSignatureFile.Name()) // clean up 155 | os.Remove(tmpContentFile.Name()) // clean up 156 | os.Remove(tmpSignerCertFile.Name()) // clean up 157 | } 158 | 159 | func ExampleSignedData() { 160 | // generate a signing cert or load a key pair 161 | cert, err := createTestCertificate(x509.SHA256WithRSA) 162 | if err != nil { 163 | fmt.Printf("Cannot create test certificates: %s", err) 164 | } 165 | 166 | // Initialize a SignedData struct with content to be signed 167 | signedData, err := NewSignedData([]byte("Example data to be signed")) 168 | if err != nil { 169 | fmt.Printf("Cannot initialize signed data: %s", err) 170 | } 171 | 172 | // Add the signing cert and private key 173 | if err := signedData.AddSigner(cert.Certificate, cert.PrivateKey, SignerInfoConfig{}); err != nil { 174 | fmt.Printf("Cannot add signer: %s", err) 175 | } 176 | 177 | // Call Detach() is you want to remove content from the signature 178 | // and generate an S/MIME detached signature 179 | signedData.Detach() 180 | 181 | // Finish() to obtain the signature bytes 182 | detachedSignature, err := signedData.Finish() 183 | if err != nil { 184 | fmt.Printf("Cannot finish signing data: %s", err) 185 | } 186 | pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: detachedSignature}) 187 | } 188 | 189 | func TestUnmarshalSignedAttribute(t *testing.T) { 190 | cert, err := createTestCertificate(x509.SHA512WithRSA) 191 | if err != nil { 192 | t.Fatal(err) 193 | } 194 | content := []byte("Hello World") 195 | toBeSigned, err := NewSignedData(content) 196 | if err != nil { 197 | t.Fatalf("Cannot initialize signed data: %s", err) 198 | } 199 | oidTest := asn1.ObjectIdentifier{2, 3, 4, 5, 6, 7} 200 | testValue := "TestValue" 201 | if err := toBeSigned.AddSigner(cert.Certificate, *cert.PrivateKey, SignerInfoConfig{ 202 | ExtraSignedAttributes: []Attribute{Attribute{Type: oidTest, Value: testValue}}, 203 | }); err != nil { 204 | t.Fatalf("Cannot add signer: %s", err) 205 | } 206 | signed, err := toBeSigned.Finish() 207 | if err != nil { 208 | t.Fatalf("Cannot finish signing data: %s", err) 209 | } 210 | p7, err := Parse(signed) 211 | if err != nil { 212 | t.Fatalf("Cannot parse signed data: %v", err) 213 | } 214 | var actual string 215 | err = p7.UnmarshalSignedAttribute(oidTest, &actual) 216 | if err != nil { 217 | t.Fatalf("Cannot unmarshal test value: %s", err) 218 | } 219 | if testValue != actual { 220 | t.Errorf("Attribute does not match test value\n\tExpected: %s\n\tActual: %s", testValue, actual) 221 | } 222 | } 223 | 224 | func TestDegenerateCertificate(t *testing.T) { 225 | cert, err := createTestCertificate(x509.SHA1WithRSA) 226 | if err != nil { 227 | t.Fatal(err) 228 | } 229 | deg, err := DegenerateCertificate(cert.Certificate.Raw) 230 | if err != nil { 231 | t.Fatal(err) 232 | } 233 | testOpenSSLParse(t, deg) 234 | pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: deg}) 235 | } 236 | 237 | // writes the cert to a temporary file and tests that openssl can read it. 238 | func testOpenSSLParse(t *testing.T, certBytes []byte) { 239 | tmpCertFile, err := ioutil.TempFile("", "testCertificate") 240 | if err != nil { 241 | t.Fatal(err) 242 | } 243 | defer os.Remove(tmpCertFile.Name()) // clean up 244 | 245 | if _, err := tmpCertFile.Write(certBytes); err != nil { 246 | t.Fatal(err) 247 | } 248 | 249 | opensslCMD := exec.Command("openssl", "pkcs7", "-inform", "der", "-in", tmpCertFile.Name()) 250 | _, err = opensslCMD.Output() 251 | if err != nil { 252 | t.Fatal(err) 253 | } 254 | 255 | if err := tmpCertFile.Close(); err != nil { 256 | t.Fatal(err) 257 | } 258 | 259 | } 260 | func fromHex(s string) *big.Int { 261 | result, ok := new(big.Int).SetString(s, 16) 262 | if !ok { 263 | panic(s) 264 | } 265 | return result 266 | } 267 | -------------------------------------------------------------------------------- /pkcs7.go: -------------------------------------------------------------------------------- 1 | // Package pkcs7 implements parsing and generation of some PKCS#7 structures. 2 | package pkcs7 3 | 4 | import ( 5 | "bytes" 6 | "crypto" 7 | "crypto/dsa" 8 | "crypto/ecdsa" 9 | "crypto/rsa" 10 | "crypto/x509" 11 | "crypto/x509/pkix" 12 | "encoding/asn1" 13 | "errors" 14 | "fmt" 15 | "sort" 16 | 17 | _ "crypto/sha1" // for crypto.SHA1 18 | ) 19 | 20 | // PKCS7 Represents a PKCS7 structure 21 | type PKCS7 struct { 22 | Content []byte 23 | Certificates []*x509.Certificate 24 | CRLs []pkix.CertificateList 25 | Signers []signerInfo 26 | raw interface{} 27 | } 28 | 29 | type contentInfo struct { 30 | ContentType asn1.ObjectIdentifier 31 | Content asn1.RawValue `asn1:"explicit,optional,tag:0"` 32 | } 33 | 34 | // ErrUnsupportedContentType is returned when a PKCS7 content is not supported. 35 | // Currently only Data (1.2.840.113549.1.7.1), Signed Data (1.2.840.113549.1.7.2), 36 | // and Enveloped Data are supported (1.2.840.113549.1.7.3) 37 | var ErrUnsupportedContentType = errors.New("pkcs7: cannot parse data: unimplemented content type") 38 | 39 | type unsignedData []byte 40 | 41 | var ( 42 | // Signed Data OIDs 43 | OIDData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 1} 44 | OIDSignedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 2} 45 | OIDEnvelopedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 3} 46 | OIDEncryptedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 6} 47 | OIDAttributeContentType = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 3} 48 | OIDAttributeMessageDigest = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 4} 49 | OIDAttributeSigningTime = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 5} 50 | 51 | // Digest Algorithms 52 | OIDDigestAlgorithmSHA1 = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 26} 53 | OIDDigestAlgorithmSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 1} 54 | OIDDigestAlgorithmSHA384 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 2} 55 | OIDDigestAlgorithmSHA512 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 3} 56 | 57 | OIDDigestAlgorithmDSA = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1} 58 | OIDDigestAlgorithmDSASHA1 = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3} 59 | 60 | OIDDigestAlgorithmECDSASHA1 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1} 61 | OIDDigestAlgorithmECDSASHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2} 62 | OIDDigestAlgorithmECDSASHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3} 63 | OIDDigestAlgorithmECDSASHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4} 64 | 65 | // Signature Algorithms 66 | OIDEncryptionAlgorithmRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1} 67 | OIDEncryptionAlgorithmRSASHA1 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5} 68 | OIDEncryptionAlgorithmRSASHA256 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11} 69 | OIDEncryptionAlgorithmRSASHA384 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12} 70 | OIDEncryptionAlgorithmRSASHA512 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13} 71 | 72 | OIDEncryptionAlgorithmECDSAP256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 3, 1, 7} 73 | OIDEncryptionAlgorithmECDSAP384 = asn1.ObjectIdentifier{1, 3, 132, 0, 34} 74 | OIDEncryptionAlgorithmECDSAP521 = asn1.ObjectIdentifier{1, 3, 132, 0, 35} 75 | 76 | // Encryption Algorithms 77 | OIDEncryptionAlgorithmDESCBC = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 7} 78 | OIDEncryptionAlgorithmDESEDE3CBC = asn1.ObjectIdentifier{1, 2, 840, 113549, 3, 7} 79 | OIDEncryptionAlgorithmAES256CBC = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 42} 80 | OIDEncryptionAlgorithmAES128GCM = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 6} 81 | OIDEncryptionAlgorithmAES128CBC = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 2} 82 | OIDEncryptionAlgorithmAES256GCM = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 46} 83 | ) 84 | 85 | func getHashForOID(oid asn1.ObjectIdentifier) (crypto.Hash, error) { 86 | switch { 87 | case oid.Equal(OIDDigestAlgorithmSHA1), oid.Equal(OIDDigestAlgorithmECDSASHA1), 88 | oid.Equal(OIDDigestAlgorithmDSA), oid.Equal(OIDDigestAlgorithmDSASHA1), 89 | oid.Equal(OIDEncryptionAlgorithmRSA): 90 | return crypto.SHA1, nil 91 | case oid.Equal(OIDDigestAlgorithmSHA256), oid.Equal(OIDDigestAlgorithmECDSASHA256): 92 | return crypto.SHA256, nil 93 | case oid.Equal(OIDDigestAlgorithmSHA384), oid.Equal(OIDDigestAlgorithmECDSASHA384): 94 | return crypto.SHA384, nil 95 | case oid.Equal(OIDDigestAlgorithmSHA512), oid.Equal(OIDDigestAlgorithmECDSASHA512): 96 | return crypto.SHA512, nil 97 | } 98 | return crypto.Hash(0), ErrUnsupportedAlgorithm 99 | } 100 | 101 | // getDigestOIDForSignatureAlgorithm takes an x509.SignatureAlgorithm 102 | // and returns the corresponding OID digest algorithm 103 | func getDigestOIDForSignatureAlgorithm(digestAlg x509.SignatureAlgorithm) (asn1.ObjectIdentifier, error) { 104 | switch digestAlg { 105 | case x509.SHA1WithRSA, x509.ECDSAWithSHA1: 106 | return OIDDigestAlgorithmSHA1, nil 107 | case x509.SHA256WithRSA, x509.ECDSAWithSHA256: 108 | return OIDDigestAlgorithmSHA256, nil 109 | case x509.SHA384WithRSA, x509.ECDSAWithSHA384: 110 | return OIDDigestAlgorithmSHA384, nil 111 | case x509.SHA512WithRSA, x509.ECDSAWithSHA512: 112 | return OIDDigestAlgorithmSHA512, nil 113 | } 114 | return nil, fmt.Errorf("pkcs7: cannot convert hash to oid, unknown hash algorithm") 115 | } 116 | 117 | // getOIDForEncryptionAlgorithm takes the private key type of the signer and 118 | // the OID of a digest algorithm to return the appropriate signerInfo.DigestEncryptionAlgorithm 119 | func getOIDForEncryptionAlgorithm(pkey crypto.PrivateKey, OIDDigestAlg asn1.ObjectIdentifier) (asn1.ObjectIdentifier, error) { 120 | switch pkey.(type) { 121 | case *rsa.PrivateKey: 122 | switch { 123 | default: 124 | return OIDEncryptionAlgorithmRSA, nil 125 | case OIDDigestAlg.Equal(OIDEncryptionAlgorithmRSA): 126 | return OIDEncryptionAlgorithmRSA, nil 127 | case OIDDigestAlg.Equal(OIDDigestAlgorithmSHA1): 128 | return OIDEncryptionAlgorithmRSASHA1, nil 129 | case OIDDigestAlg.Equal(OIDDigestAlgorithmSHA256): 130 | return OIDEncryptionAlgorithmRSASHA256, nil 131 | case OIDDigestAlg.Equal(OIDDigestAlgorithmSHA384): 132 | return OIDEncryptionAlgorithmRSASHA384, nil 133 | case OIDDigestAlg.Equal(OIDDigestAlgorithmSHA512): 134 | return OIDEncryptionAlgorithmRSASHA512, nil 135 | } 136 | case *ecdsa.PrivateKey: 137 | switch { 138 | case OIDDigestAlg.Equal(OIDDigestAlgorithmSHA1): 139 | return OIDDigestAlgorithmECDSASHA1, nil 140 | case OIDDigestAlg.Equal(OIDDigestAlgorithmSHA256): 141 | return OIDDigestAlgorithmECDSASHA256, nil 142 | case OIDDigestAlg.Equal(OIDDigestAlgorithmSHA384): 143 | return OIDDigestAlgorithmECDSASHA384, nil 144 | case OIDDigestAlg.Equal(OIDDigestAlgorithmSHA512): 145 | return OIDDigestAlgorithmECDSASHA512, nil 146 | } 147 | case *dsa.PrivateKey: 148 | return OIDDigestAlgorithmDSA, nil 149 | } 150 | return nil, fmt.Errorf("pkcs7: cannot convert encryption algorithm to oid, unknown private key type %T", pkey) 151 | 152 | } 153 | 154 | // Parse decodes a DER encoded PKCS7 package 155 | func Parse(data []byte) (p7 *PKCS7, err error) { 156 | if len(data) == 0 { 157 | return nil, errors.New("pkcs7: input data is empty") 158 | } 159 | var info contentInfo 160 | der, err := ber2der(data) 161 | if err != nil { 162 | return nil, err 163 | } 164 | rest, err := asn1.Unmarshal(der, &info) 165 | if len(rest) > 0 { 166 | err = asn1.SyntaxError{Msg: "trailing data"} 167 | return 168 | } 169 | if err != nil { 170 | return 171 | } 172 | 173 | // fmt.Printf("--> Content Type: %s", info.ContentType) 174 | switch { 175 | case info.ContentType.Equal(OIDSignedData): 176 | return parseSignedData(info.Content.Bytes) 177 | case info.ContentType.Equal(OIDEnvelopedData): 178 | return parseEnvelopedData(info.Content.Bytes) 179 | case info.ContentType.Equal(OIDEncryptedData): 180 | return parseEncryptedData(info.Content.Bytes) 181 | } 182 | return nil, ErrUnsupportedContentType 183 | } 184 | 185 | func parseEnvelopedData(data []byte) (*PKCS7, error) { 186 | var ed envelopedData 187 | if _, err := asn1.Unmarshal(data, &ed); err != nil { 188 | return nil, err 189 | } 190 | return &PKCS7{ 191 | raw: ed, 192 | }, nil 193 | } 194 | 195 | func parseEncryptedData(data []byte) (*PKCS7, error) { 196 | var ed encryptedData 197 | if _, err := asn1.Unmarshal(data, &ed); err != nil { 198 | return nil, err 199 | } 200 | return &PKCS7{ 201 | raw: ed, 202 | }, nil 203 | } 204 | 205 | func (raw rawCertificates) Parse() ([]*x509.Certificate, error) { 206 | if len(raw.Raw) == 0 { 207 | return nil, nil 208 | } 209 | 210 | var val asn1.RawValue 211 | if _, err := asn1.Unmarshal(raw.Raw, &val); err != nil { 212 | return nil, err 213 | } 214 | 215 | return x509.ParseCertificates(val.Bytes) 216 | } 217 | 218 | func isCertMatchForIssuerAndSerial(cert *x509.Certificate, ias issuerAndSerial) bool { 219 | return cert.SerialNumber.Cmp(ias.SerialNumber) == 0 && bytes.Equal(cert.RawIssuer, ias.IssuerName.FullBytes) 220 | } 221 | 222 | // Attribute represents a key value pair attribute. Value must be marshalable byte 223 | // `encoding/asn1` 224 | type Attribute struct { 225 | Type asn1.ObjectIdentifier 226 | Value interface{} 227 | } 228 | 229 | type attributes struct { 230 | types []asn1.ObjectIdentifier 231 | values []interface{} 232 | } 233 | 234 | // Add adds the attribute, maintaining insertion order 235 | func (attrs *attributes) Add(attrType asn1.ObjectIdentifier, value interface{}) { 236 | attrs.types = append(attrs.types, attrType) 237 | attrs.values = append(attrs.values, value) 238 | } 239 | 240 | type sortableAttribute struct { 241 | SortKey []byte 242 | Attribute attribute 243 | } 244 | 245 | type attributeSet []sortableAttribute 246 | 247 | func (sa attributeSet) Len() int { 248 | return len(sa) 249 | } 250 | 251 | func (sa attributeSet) Less(i, j int) bool { 252 | return bytes.Compare(sa[i].SortKey, sa[j].SortKey) < 0 253 | } 254 | 255 | func (sa attributeSet) Swap(i, j int) { 256 | sa[i], sa[j] = sa[j], sa[i] 257 | } 258 | 259 | func (sa attributeSet) Attributes() []attribute { 260 | attrs := make([]attribute, len(sa)) 261 | for i, attr := range sa { 262 | attrs[i] = attr.Attribute 263 | } 264 | return attrs 265 | } 266 | 267 | func (attrs *attributes) ForMarshalling() ([]attribute, error) { 268 | sortables := make(attributeSet, len(attrs.types)) 269 | for i := range sortables { 270 | attrType := attrs.types[i] 271 | attrValue := attrs.values[i] 272 | asn1Value, err := asn1.Marshal(attrValue) 273 | if err != nil { 274 | return nil, err 275 | } 276 | attr := attribute{ 277 | Type: attrType, 278 | Value: asn1.RawValue{Tag: 17, IsCompound: true, Bytes: asn1Value}, // 17 == SET tag 279 | } 280 | encoded, err := asn1.Marshal(attr) 281 | if err != nil { 282 | return nil, err 283 | } 284 | sortables[i] = sortableAttribute{ 285 | SortKey: encoded, 286 | Attribute: attr, 287 | } 288 | } 289 | sort.Sort(sortables) 290 | return sortables.Attributes(), nil 291 | } 292 | -------------------------------------------------------------------------------- /encrypt.go: -------------------------------------------------------------------------------- 1 | package pkcs7 2 | 3 | import ( 4 | "bytes" 5 | "crypto/aes" 6 | "crypto/cipher" 7 | "crypto/des" 8 | "crypto/rand" 9 | "crypto/rsa" 10 | "crypto/x509" 11 | "crypto/x509/pkix" 12 | "encoding/asn1" 13 | "errors" 14 | "fmt" 15 | ) 16 | 17 | type envelopedData struct { 18 | Version int 19 | RecipientInfos []recipientInfo `asn1:"set"` 20 | EncryptedContentInfo encryptedContentInfo 21 | } 22 | 23 | type encryptedData struct { 24 | Version int 25 | EncryptedContentInfo encryptedContentInfo 26 | } 27 | 28 | type recipientInfo struct { 29 | Version int 30 | IssuerAndSerialNumber issuerAndSerial 31 | KeyEncryptionAlgorithm pkix.AlgorithmIdentifier 32 | EncryptedKey []byte 33 | } 34 | 35 | type encryptedContentInfo struct { 36 | ContentType asn1.ObjectIdentifier 37 | ContentEncryptionAlgorithm pkix.AlgorithmIdentifier 38 | EncryptedContent asn1.RawValue `asn1:"tag:0,optional"` 39 | } 40 | 41 | const ( 42 | // EncryptionAlgorithmDESCBC is the DES CBC encryption algorithm 43 | EncryptionAlgorithmDESCBC = iota 44 | 45 | // EncryptionAlgorithmAES128CBC is the AES 128 bits with CBC encryption algorithm 46 | // Avoid this algorithm unless required for interoperability; use AES GCM instead. 47 | EncryptionAlgorithmAES128CBC 48 | 49 | // EncryptionAlgorithmAES256CBC is the AES 256 bits with CBC encryption algorithm 50 | // Avoid this algorithm unless required for interoperability; use AES GCM instead. 51 | EncryptionAlgorithmAES256CBC 52 | 53 | // EncryptionAlgorithmAES128GCM is the AES 128 bits with GCM encryption algorithm 54 | EncryptionAlgorithmAES128GCM 55 | 56 | // EncryptionAlgorithmAES256GCM is the AES 256 bits with GCM encryption algorithm 57 | EncryptionAlgorithmAES256GCM 58 | ) 59 | 60 | // ContentEncryptionAlgorithm determines the algorithm used to encrypt the 61 | // plaintext message. Change the value of this variable to change which 62 | // algorithm is used in the Encrypt() function. 63 | var ContentEncryptionAlgorithm = EncryptionAlgorithmDESCBC 64 | 65 | // ErrUnsupportedEncryptionAlgorithm is returned when attempting to encrypt 66 | // content with an unsupported algorithm. 67 | var ErrUnsupportedEncryptionAlgorithm = errors.New("pkcs7: cannot encrypt content: only DES-CBC, AES-CBC, and AES-GCM supported") 68 | 69 | // ErrPSKNotProvided is returned when attempting to encrypt 70 | // using a PSK without actually providing the PSK. 71 | var ErrPSKNotProvided = errors.New("pkcs7: cannot encrypt content: PSK not provided") 72 | 73 | const nonceSize = 12 74 | 75 | type aesGCMParameters struct { 76 | Nonce []byte `asn1:"tag:4"` 77 | ICVLen int 78 | } 79 | 80 | func encryptAESGCM(content []byte, key []byte) ([]byte, *encryptedContentInfo, error) { 81 | var keyLen int 82 | var algID asn1.ObjectIdentifier 83 | switch ContentEncryptionAlgorithm { 84 | case EncryptionAlgorithmAES128GCM: 85 | keyLen = 16 86 | algID = OIDEncryptionAlgorithmAES128GCM 87 | case EncryptionAlgorithmAES256GCM: 88 | keyLen = 32 89 | algID = OIDEncryptionAlgorithmAES256GCM 90 | default: 91 | return nil, nil, fmt.Errorf("invalid ContentEncryptionAlgorithm in encryptAESGCM: %d", ContentEncryptionAlgorithm) 92 | } 93 | if key == nil { 94 | // Create AES key 95 | key = make([]byte, keyLen) 96 | 97 | _, err := rand.Read(key) 98 | if err != nil { 99 | return nil, nil, err 100 | } 101 | } 102 | 103 | // Create nonce 104 | nonce := make([]byte, nonceSize) 105 | 106 | _, err := rand.Read(nonce) 107 | if err != nil { 108 | return nil, nil, err 109 | } 110 | 111 | // Encrypt content 112 | block, err := aes.NewCipher(key) 113 | if err != nil { 114 | return nil, nil, err 115 | } 116 | 117 | gcm, err := cipher.NewGCM(block) 118 | if err != nil { 119 | return nil, nil, err 120 | } 121 | 122 | ciphertext := gcm.Seal(nil, nonce, content, nil) 123 | 124 | // Prepare ASN.1 Encrypted Content Info 125 | paramSeq := aesGCMParameters{ 126 | Nonce: nonce, 127 | ICVLen: gcm.Overhead(), 128 | } 129 | 130 | paramBytes, err := asn1.Marshal(paramSeq) 131 | if err != nil { 132 | return nil, nil, err 133 | } 134 | 135 | eci := encryptedContentInfo{ 136 | ContentType: OIDData, 137 | ContentEncryptionAlgorithm: pkix.AlgorithmIdentifier{ 138 | Algorithm: algID, 139 | Parameters: asn1.RawValue{ 140 | Tag: asn1.TagSequence, 141 | Bytes: paramBytes, 142 | }, 143 | }, 144 | EncryptedContent: marshalEncryptedContent(ciphertext), 145 | } 146 | 147 | return key, &eci, nil 148 | } 149 | 150 | func encryptDESCBC(content []byte, key []byte) ([]byte, *encryptedContentInfo, error) { 151 | if key == nil { 152 | // Create DES key 153 | key = make([]byte, 8) 154 | 155 | _, err := rand.Read(key) 156 | if err != nil { 157 | return nil, nil, err 158 | } 159 | } 160 | 161 | // Create CBC IV 162 | iv := make([]byte, des.BlockSize) 163 | _, err := rand.Read(iv) 164 | if err != nil { 165 | return nil, nil, err 166 | } 167 | 168 | // Encrypt padded content 169 | block, err := des.NewCipher(key) 170 | if err != nil { 171 | return nil, nil, err 172 | } 173 | mode := cipher.NewCBCEncrypter(block, iv) 174 | plaintext, err := pad(content, mode.BlockSize()) 175 | if err != nil { 176 | return nil, nil, err 177 | } 178 | cyphertext := make([]byte, len(plaintext)) 179 | mode.CryptBlocks(cyphertext, plaintext) 180 | 181 | // Prepare ASN.1 Encrypted Content Info 182 | eci := encryptedContentInfo{ 183 | ContentType: OIDData, 184 | ContentEncryptionAlgorithm: pkix.AlgorithmIdentifier{ 185 | Algorithm: OIDEncryptionAlgorithmDESCBC, 186 | Parameters: asn1.RawValue{Tag: 4, Bytes: iv}, 187 | }, 188 | EncryptedContent: marshalEncryptedContent(cyphertext), 189 | } 190 | 191 | return key, &eci, nil 192 | } 193 | 194 | func encryptAESCBC(content []byte, key []byte) ([]byte, *encryptedContentInfo, error) { 195 | var keyLen int 196 | var algID asn1.ObjectIdentifier 197 | switch ContentEncryptionAlgorithm { 198 | case EncryptionAlgorithmAES128CBC: 199 | keyLen = 16 200 | algID = OIDEncryptionAlgorithmAES128CBC 201 | case EncryptionAlgorithmAES256CBC: 202 | keyLen = 32 203 | algID = OIDEncryptionAlgorithmAES256CBC 204 | default: 205 | return nil, nil, fmt.Errorf("invalid ContentEncryptionAlgorithm in encryptAESCBC: %d", ContentEncryptionAlgorithm) 206 | } 207 | 208 | if key == nil { 209 | // Create AES key 210 | key = make([]byte, keyLen) 211 | 212 | _, err := rand.Read(key) 213 | if err != nil { 214 | return nil, nil, err 215 | } 216 | } 217 | 218 | // Create CBC IV 219 | iv := make([]byte, aes.BlockSize) 220 | _, err := rand.Read(iv) 221 | if err != nil { 222 | return nil, nil, err 223 | } 224 | 225 | // Encrypt padded content 226 | block, err := aes.NewCipher(key) 227 | if err != nil { 228 | return nil, nil, err 229 | } 230 | mode := cipher.NewCBCEncrypter(block, iv) 231 | plaintext, err := pad(content, mode.BlockSize()) 232 | if err != nil { 233 | return nil, nil, err 234 | } 235 | cyphertext := make([]byte, len(plaintext)) 236 | mode.CryptBlocks(cyphertext, plaintext) 237 | 238 | // Prepare ASN.1 Encrypted Content Info 239 | eci := encryptedContentInfo{ 240 | ContentType: OIDData, 241 | ContentEncryptionAlgorithm: pkix.AlgorithmIdentifier{ 242 | Algorithm: algID, 243 | Parameters: asn1.RawValue{Tag: 4, Bytes: iv}, 244 | }, 245 | EncryptedContent: marshalEncryptedContent(cyphertext), 246 | } 247 | 248 | return key, &eci, nil 249 | } 250 | 251 | // Encrypt creates and returns an envelope data PKCS7 structure with encrypted 252 | // recipient keys for each recipient public key. 253 | // 254 | // The algorithm used to perform encryption is determined by the current value 255 | // of the global ContentEncryptionAlgorithm package variable. By default, the 256 | // value is EncryptionAlgorithmDESCBC. To use a different algorithm, change the 257 | // value before calling Encrypt(). For example: 258 | // 259 | // ContentEncryptionAlgorithm = EncryptionAlgorithmAES128GCM 260 | // 261 | // TODO(fullsailor): Add support for encrypting content with other algorithms 262 | func Encrypt(content []byte, recipients []*x509.Certificate) ([]byte, error) { 263 | var eci *encryptedContentInfo 264 | var key []byte 265 | var err error 266 | 267 | // Apply chosen symmetric encryption method 268 | switch ContentEncryptionAlgorithm { 269 | case EncryptionAlgorithmDESCBC: 270 | key, eci, err = encryptDESCBC(content, nil) 271 | case EncryptionAlgorithmAES128CBC: 272 | fallthrough 273 | case EncryptionAlgorithmAES256CBC: 274 | key, eci, err = encryptAESCBC(content, nil) 275 | case EncryptionAlgorithmAES128GCM: 276 | fallthrough 277 | case EncryptionAlgorithmAES256GCM: 278 | key, eci, err = encryptAESGCM(content, nil) 279 | 280 | default: 281 | return nil, ErrUnsupportedEncryptionAlgorithm 282 | } 283 | 284 | if err != nil { 285 | return nil, err 286 | } 287 | 288 | // Prepare each recipient's encrypted cipher key 289 | recipientInfos := make([]recipientInfo, len(recipients)) 290 | for i, recipient := range recipients { 291 | encrypted, err := encryptKey(key, recipient) 292 | if err != nil { 293 | return nil, err 294 | } 295 | ias, err := cert2issuerAndSerial(recipient) 296 | if err != nil { 297 | return nil, err 298 | } 299 | info := recipientInfo{ 300 | Version: 0, 301 | IssuerAndSerialNumber: ias, 302 | KeyEncryptionAlgorithm: pkix.AlgorithmIdentifier{ 303 | Algorithm: OIDEncryptionAlgorithmRSA, 304 | }, 305 | EncryptedKey: encrypted, 306 | } 307 | recipientInfos[i] = info 308 | } 309 | 310 | // Prepare envelope content 311 | envelope := envelopedData{ 312 | EncryptedContentInfo: *eci, 313 | Version: 0, 314 | RecipientInfos: recipientInfos, 315 | } 316 | innerContent, err := asn1.Marshal(envelope) 317 | if err != nil { 318 | return nil, err 319 | } 320 | 321 | // Prepare outer payload structure 322 | wrapper := contentInfo{ 323 | ContentType: OIDEnvelopedData, 324 | Content: asn1.RawValue{Class: 2, Tag: 0, IsCompound: true, Bytes: innerContent}, 325 | } 326 | 327 | return asn1.Marshal(wrapper) 328 | } 329 | 330 | // EncryptUsingPSK creates and returns an encrypted data PKCS7 structure, 331 | // encrypted using caller provided pre-shared secret. 332 | func EncryptUsingPSK(content []byte, key []byte) ([]byte, error) { 333 | var eci *encryptedContentInfo 334 | var err error 335 | 336 | if key == nil { 337 | return nil, ErrPSKNotProvided 338 | } 339 | 340 | // Apply chosen symmetric encryption method 341 | switch ContentEncryptionAlgorithm { 342 | case EncryptionAlgorithmDESCBC: 343 | _, eci, err = encryptDESCBC(content, key) 344 | 345 | case EncryptionAlgorithmAES128GCM: 346 | fallthrough 347 | case EncryptionAlgorithmAES256GCM: 348 | _, eci, err = encryptAESGCM(content, key) 349 | 350 | default: 351 | return nil, ErrUnsupportedEncryptionAlgorithm 352 | } 353 | 354 | if err != nil { 355 | return nil, err 356 | } 357 | 358 | // Prepare encrypted-data content 359 | ed := encryptedData{ 360 | Version: 0, 361 | EncryptedContentInfo: *eci, 362 | } 363 | innerContent, err := asn1.Marshal(ed) 364 | if err != nil { 365 | return nil, err 366 | } 367 | 368 | // Prepare outer payload structure 369 | wrapper := contentInfo{ 370 | ContentType: OIDEncryptedData, 371 | Content: asn1.RawValue{Class: 2, Tag: 0, IsCompound: true, Bytes: innerContent}, 372 | } 373 | 374 | return asn1.Marshal(wrapper) 375 | } 376 | 377 | func marshalEncryptedContent(content []byte) asn1.RawValue { 378 | asn1Content, _ := asn1.Marshal(content) 379 | return asn1.RawValue{Tag: 0, Class: 2, Bytes: asn1Content, IsCompound: true} 380 | } 381 | 382 | func encryptKey(key []byte, recipient *x509.Certificate) ([]byte, error) { 383 | if pub := recipient.PublicKey.(*rsa.PublicKey); pub != nil { 384 | return rsa.EncryptPKCS1v15(rand.Reader, pub, key) 385 | } 386 | return nil, ErrUnsupportedAlgorithm 387 | } 388 | 389 | func pad(data []byte, blocklen int) ([]byte, error) { 390 | if blocklen < 1 { 391 | return nil, fmt.Errorf("invalid blocklen %d", blocklen) 392 | } 393 | padlen := blocklen - (len(data) % blocklen) 394 | if padlen == 0 { 395 | padlen = blocklen 396 | } 397 | pad := bytes.Repeat([]byte{byte(padlen)}, padlen) 398 | return append(data, pad...), nil 399 | } 400 | -------------------------------------------------------------------------------- /verify.go: -------------------------------------------------------------------------------- 1 | package pkcs7 2 | 3 | import ( 4 | "crypto/subtle" 5 | "crypto/x509" 6 | "crypto/x509/pkix" 7 | "encoding/asn1" 8 | "errors" 9 | "fmt" 10 | "time" 11 | ) 12 | 13 | // Verify is a wrapper around VerifyWithChain() that initializes an empty 14 | // trust store, effectively disabling certificate verification when validating 15 | // a signature. 16 | func (p7 *PKCS7) Verify() (err error) { 17 | return p7.VerifyWithChain(nil) 18 | } 19 | 20 | // VerifyWithChain checks the signatures of a PKCS7 object. 21 | // 22 | // If truststore is not nil, it also verifies the chain of trust of 23 | // the end-entity signer cert to one of the roots in the 24 | // truststore. When the PKCS7 object includes the signing time 25 | // authenticated attr verifies the chain at that time and UTC now 26 | // otherwise. 27 | func (p7 *PKCS7) VerifyWithChain(truststore *x509.CertPool) (err error) { 28 | if len(p7.Signers) == 0 { 29 | return errors.New("pkcs7: Message has no signers") 30 | } 31 | for _, signer := range p7.Signers { 32 | if err := verifySignature(p7, signer, truststore); err != nil { 33 | return err 34 | } 35 | } 36 | return nil 37 | } 38 | 39 | // VerifyWithChainAtTime checks the signatures of a PKCS7 object. 40 | // 41 | // If truststore is not nil, it also verifies the chain of trust of 42 | // the end-entity signer cert to a root in the truststore at 43 | // currentTime. It does not use the signing time authenticated 44 | // attribute. 45 | func (p7 *PKCS7) VerifyWithChainAtTime(truststore *x509.CertPool, currentTime time.Time) (err error) { 46 | if len(p7.Signers) == 0 { 47 | return errors.New("pkcs7: Message has no signers") 48 | } 49 | for _, signer := range p7.Signers { 50 | if err := verifySignatureAtTime(p7, signer, truststore, currentTime); err != nil { 51 | return err 52 | } 53 | } 54 | return nil 55 | } 56 | 57 | func verifySignatureAtTime(p7 *PKCS7, signer signerInfo, truststore *x509.CertPool, currentTime time.Time) (err error) { 58 | signedData := p7.Content 59 | ee := getCertFromCertsByIssuerAndSerial(p7.Certificates, signer.IssuerAndSerialNumber) 60 | if ee == nil { 61 | return errors.New("pkcs7: No certificate for signer") 62 | } 63 | if len(signer.AuthenticatedAttributes) > 0 { 64 | // TODO(fullsailor): First check the content type match 65 | var ( 66 | digest []byte 67 | signingTime time.Time 68 | ) 69 | err := unmarshalAttribute(signer.AuthenticatedAttributes, OIDAttributeMessageDigest, &digest) 70 | if err != nil { 71 | return err 72 | } 73 | hash, err := getHashForOID(signer.DigestAlgorithm.Algorithm) 74 | if err != nil { 75 | return err 76 | } 77 | h := hash.New() 78 | h.Write(p7.Content) 79 | computed := h.Sum(nil) 80 | if subtle.ConstantTimeCompare(digest, computed) != 1 { 81 | return &MessageDigestMismatchError{ 82 | ExpectedDigest: digest, 83 | ActualDigest: computed, 84 | } 85 | } 86 | signedData, err = marshalAttributes(signer.AuthenticatedAttributes) 87 | if err != nil { 88 | return err 89 | } 90 | err = unmarshalAttribute(signer.AuthenticatedAttributes, OIDAttributeSigningTime, &signingTime) 91 | if err == nil { 92 | // signing time found, performing validity check 93 | if signingTime.After(ee.NotAfter) || signingTime.Before(ee.NotBefore) { 94 | return fmt.Errorf("pkcs7: signing time %q is outside of certificate validity %q to %q", 95 | signingTime.Format(time.RFC3339), 96 | ee.NotBefore.Format(time.RFC3339), 97 | ee.NotAfter.Format(time.RFC3339)) 98 | } 99 | } 100 | } 101 | if truststore != nil { 102 | _, err = verifyCertChain(ee, p7.Certificates, truststore, currentTime) 103 | if err != nil { 104 | return err 105 | } 106 | } 107 | sigalg, err := getSignatureAlgorithm(signer.DigestEncryptionAlgorithm, signer.DigestAlgorithm) 108 | if err != nil { 109 | return err 110 | } 111 | return ee.CheckSignature(sigalg, signedData, signer.EncryptedDigest) 112 | } 113 | 114 | func verifySignature(p7 *PKCS7, signer signerInfo, truststore *x509.CertPool) (err error) { 115 | signedData := p7.Content 116 | ee := getCertFromCertsByIssuerAndSerial(p7.Certificates, signer.IssuerAndSerialNumber) 117 | if ee == nil { 118 | return errors.New("pkcs7: No certificate for signer") 119 | } 120 | signingTime := time.Now().UTC() 121 | if len(signer.AuthenticatedAttributes) > 0 { 122 | // TODO(fullsailor): First check the content type match 123 | var digest []byte 124 | err := unmarshalAttribute(signer.AuthenticatedAttributes, OIDAttributeMessageDigest, &digest) 125 | if err != nil { 126 | return err 127 | } 128 | hash, err := getHashForOID(signer.DigestAlgorithm.Algorithm) 129 | if err != nil { 130 | return err 131 | } 132 | h := hash.New() 133 | h.Write(p7.Content) 134 | computed := h.Sum(nil) 135 | if subtle.ConstantTimeCompare(digest, computed) != 1 { 136 | return &MessageDigestMismatchError{ 137 | ExpectedDigest: digest, 138 | ActualDigest: computed, 139 | } 140 | } 141 | signedData, err = marshalAttributes(signer.AuthenticatedAttributes) 142 | if err != nil { 143 | return err 144 | } 145 | err = unmarshalAttribute(signer.AuthenticatedAttributes, OIDAttributeSigningTime, &signingTime) 146 | if err == nil { 147 | // signing time found, performing validity check 148 | if signingTime.After(ee.NotAfter) || signingTime.Before(ee.NotBefore) { 149 | return fmt.Errorf("pkcs7: signing time %q is outside of certificate validity %q to %q", 150 | signingTime.Format(time.RFC3339), 151 | ee.NotBefore.Format(time.RFC3339), 152 | ee.NotAfter.Format(time.RFC3339)) 153 | } 154 | } 155 | } 156 | if truststore != nil { 157 | _, err = verifyCertChain(ee, p7.Certificates, truststore, signingTime) 158 | if err != nil { 159 | return err 160 | } 161 | } 162 | sigalg, err := getSignatureAlgorithm(signer.DigestEncryptionAlgorithm, signer.DigestAlgorithm) 163 | if err != nil { 164 | return err 165 | } 166 | return ee.CheckSignature(sigalg, signedData, signer.EncryptedDigest) 167 | } 168 | 169 | // GetOnlySigner returns an x509.Certificate for the first signer of the signed 170 | // data payload. If there are more or less than one signer, nil is returned 171 | func (p7 *PKCS7) GetOnlySigner() *x509.Certificate { 172 | if len(p7.Signers) != 1 { 173 | return nil 174 | } 175 | signer := p7.Signers[0] 176 | return getCertFromCertsByIssuerAndSerial(p7.Certificates, signer.IssuerAndSerialNumber) 177 | } 178 | 179 | // UnmarshalSignedAttribute decodes a single attribute from the signer info 180 | func (p7 *PKCS7) UnmarshalSignedAttribute(attributeType asn1.ObjectIdentifier, out interface{}) error { 181 | sd, ok := p7.raw.(signedData) 182 | if !ok { 183 | return errors.New("pkcs7: payload is not signedData content") 184 | } 185 | if len(sd.SignerInfos) < 1 { 186 | return errors.New("pkcs7: payload has no signers") 187 | } 188 | attributes := sd.SignerInfos[0].AuthenticatedAttributes 189 | return unmarshalAttribute(attributes, attributeType, out) 190 | } 191 | 192 | func parseSignedData(data []byte) (*PKCS7, error) { 193 | var sd signedData 194 | asn1.Unmarshal(data, &sd) 195 | certs, err := sd.Certificates.Parse() 196 | if err != nil { 197 | return nil, err 198 | } 199 | // fmt.Printf("--> Signed Data Version %d\n", sd.Version) 200 | 201 | var compound asn1.RawValue 202 | var content unsignedData 203 | 204 | // The Content.Bytes maybe empty on PKI responses. 205 | if len(sd.ContentInfo.Content.Bytes) > 0 { 206 | if _, err := asn1.Unmarshal(sd.ContentInfo.Content.Bytes, &compound); err != nil { 207 | return nil, err 208 | } 209 | } 210 | // Compound octet string 211 | if compound.IsCompound { 212 | if compound.Tag == 4 { 213 | if _, err = asn1.Unmarshal(compound.Bytes, &content); err != nil { 214 | return nil, err 215 | } 216 | } else { 217 | content = compound.Bytes 218 | } 219 | } else { 220 | // assuming this is tag 04 221 | content = compound.Bytes 222 | } 223 | return &PKCS7{ 224 | Content: content, 225 | Certificates: certs, 226 | CRLs: sd.CRLs, 227 | Signers: sd.SignerInfos, 228 | raw: sd}, nil 229 | } 230 | 231 | // verifyCertChain takes an end-entity certs, a list of potential intermediates and a 232 | // truststore, and built all potential chains between the EE and a trusted root. 233 | // 234 | // When verifying chains that may have expired, currentTime can be set to a past date 235 | // to allow the verification to pass. If unset, currentTime is set to the current UTC time. 236 | func verifyCertChain(ee *x509.Certificate, certs []*x509.Certificate, truststore *x509.CertPool, currentTime time.Time) (chains [][]*x509.Certificate, err error) { 237 | intermediates := x509.NewCertPool() 238 | for _, intermediate := range certs { 239 | intermediates.AddCert(intermediate) 240 | } 241 | verifyOptions := x509.VerifyOptions{ 242 | Roots: truststore, 243 | Intermediates: intermediates, 244 | KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny}, 245 | CurrentTime: currentTime, 246 | } 247 | chains, err = ee.Verify(verifyOptions) 248 | if err != nil { 249 | return chains, fmt.Errorf("pkcs7: failed to verify certificate chain: %v", err) 250 | } 251 | return 252 | } 253 | 254 | // MessageDigestMismatchError is returned when the signer data digest does not 255 | // match the computed digest for the contained content 256 | type MessageDigestMismatchError struct { 257 | ExpectedDigest []byte 258 | ActualDigest []byte 259 | } 260 | 261 | func (err *MessageDigestMismatchError) Error() string { 262 | return fmt.Sprintf("pkcs7: Message digest mismatch\n\tExpected: %X\n\tActual : %X", err.ExpectedDigest, err.ActualDigest) 263 | } 264 | 265 | func getSignatureAlgorithm(digestEncryption, digest pkix.AlgorithmIdentifier) (x509.SignatureAlgorithm, error) { 266 | switch { 267 | case digestEncryption.Algorithm.Equal(OIDDigestAlgorithmECDSASHA1): 268 | return x509.ECDSAWithSHA1, nil 269 | case digestEncryption.Algorithm.Equal(OIDDigestAlgorithmECDSASHA256): 270 | return x509.ECDSAWithSHA256, nil 271 | case digestEncryption.Algorithm.Equal(OIDDigestAlgorithmECDSASHA384): 272 | return x509.ECDSAWithSHA384, nil 273 | case digestEncryption.Algorithm.Equal(OIDDigestAlgorithmECDSASHA512): 274 | return x509.ECDSAWithSHA512, nil 275 | case digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmRSA), 276 | digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmRSASHA1), 277 | digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmRSASHA256), 278 | digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmRSASHA384), 279 | digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmRSASHA512): 280 | switch { 281 | case digest.Algorithm.Equal(OIDDigestAlgorithmSHA1): 282 | return x509.SHA1WithRSA, nil 283 | case digest.Algorithm.Equal(OIDDigestAlgorithmSHA256): 284 | return x509.SHA256WithRSA, nil 285 | case digest.Algorithm.Equal(OIDDigestAlgorithmSHA384): 286 | return x509.SHA384WithRSA, nil 287 | case digest.Algorithm.Equal(OIDDigestAlgorithmSHA512): 288 | return x509.SHA512WithRSA, nil 289 | default: 290 | return -1, fmt.Errorf("pkcs7: unsupported digest %q for encryption algorithm %q", 291 | digest.Algorithm.String(), digestEncryption.Algorithm.String()) 292 | } 293 | case digestEncryption.Algorithm.Equal(OIDDigestAlgorithmDSA), 294 | digestEncryption.Algorithm.Equal(OIDDigestAlgorithmDSASHA1): 295 | switch { 296 | case digest.Algorithm.Equal(OIDDigestAlgorithmSHA1): 297 | return x509.DSAWithSHA1, nil 298 | case digest.Algorithm.Equal(OIDDigestAlgorithmSHA256): 299 | return x509.DSAWithSHA256, nil 300 | default: 301 | return -1, fmt.Errorf("pkcs7: unsupported digest %q for encryption algorithm %q", 302 | digest.Algorithm.String(), digestEncryption.Algorithm.String()) 303 | } 304 | case digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmECDSAP256), 305 | digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmECDSAP384), 306 | digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmECDSAP521): 307 | switch { 308 | case digest.Algorithm.Equal(OIDDigestAlgorithmSHA1): 309 | return x509.ECDSAWithSHA1, nil 310 | case digest.Algorithm.Equal(OIDDigestAlgorithmSHA256): 311 | return x509.ECDSAWithSHA256, nil 312 | case digest.Algorithm.Equal(OIDDigestAlgorithmSHA384): 313 | return x509.ECDSAWithSHA384, nil 314 | case digest.Algorithm.Equal(OIDDigestAlgorithmSHA512): 315 | return x509.ECDSAWithSHA512, nil 316 | default: 317 | return -1, fmt.Errorf("pkcs7: unsupported digest %q for encryption algorithm %q", 318 | digest.Algorithm.String(), digestEncryption.Algorithm.String()) 319 | } 320 | default: 321 | return -1, fmt.Errorf("pkcs7: unsupported algorithm %q", 322 | digestEncryption.Algorithm.String()) 323 | } 324 | } 325 | 326 | func getCertFromCertsByIssuerAndSerial(certs []*x509.Certificate, ias issuerAndSerial) *x509.Certificate { 327 | for _, cert := range certs { 328 | if isCertMatchForIssuerAndSerial(cert, ias) { 329 | return cert 330 | } 331 | } 332 | return nil 333 | } 334 | 335 | func unmarshalAttribute(attrs []attribute, attributeType asn1.ObjectIdentifier, out interface{}) error { 336 | for _, attr := range attrs { 337 | if attr.Type.Equal(attributeType) { 338 | _, err := asn1.Unmarshal(attr.Value.Bytes, out) 339 | return err 340 | } 341 | } 342 | return errors.New("pkcs7: attribute type not in attributes") 343 | } 344 | -------------------------------------------------------------------------------- /sign.go: -------------------------------------------------------------------------------- 1 | package pkcs7 2 | 3 | import ( 4 | "bytes" 5 | "crypto" 6 | "crypto/dsa" 7 | "crypto/rand" 8 | "crypto/x509" 9 | "crypto/x509/pkix" 10 | "encoding/asn1" 11 | "errors" 12 | "fmt" 13 | "math/big" 14 | "time" 15 | ) 16 | 17 | // SignedData is an opaque data structure for creating signed data payloads 18 | type SignedData struct { 19 | sd signedData 20 | certs []*x509.Certificate 21 | data, messageDigest []byte 22 | digestOid asn1.ObjectIdentifier 23 | encryptionOid asn1.ObjectIdentifier 24 | } 25 | 26 | // NewSignedData takes data and initializes a PKCS7 SignedData struct that is 27 | // ready to be signed via AddSigner. The digest algorithm is set to SHA1 by default 28 | // and can be changed by calling SetDigestAlgorithm. 29 | func NewSignedData(data []byte) (*SignedData, error) { 30 | content, err := asn1.Marshal(data) 31 | if err != nil { 32 | return nil, err 33 | } 34 | ci := contentInfo{ 35 | ContentType: OIDData, 36 | Content: asn1.RawValue{Class: 2, Tag: 0, Bytes: content, IsCompound: true}, 37 | } 38 | sd := signedData{ 39 | ContentInfo: ci, 40 | Version: 1, 41 | } 42 | return &SignedData{sd: sd, data: data, digestOid: OIDDigestAlgorithmSHA1}, nil 43 | } 44 | 45 | // SignerInfoConfig are optional values to include when adding a signer 46 | type SignerInfoConfig struct { 47 | ExtraSignedAttributes []Attribute 48 | ExtraUnsignedAttributes []Attribute 49 | } 50 | 51 | type signedData struct { 52 | Version int `asn1:"default:1"` 53 | DigestAlgorithmIdentifiers []pkix.AlgorithmIdentifier `asn1:"set"` 54 | ContentInfo contentInfo 55 | Certificates rawCertificates `asn1:"optional,tag:0"` 56 | CRLs []pkix.CertificateList `asn1:"optional,tag:1"` 57 | SignerInfos []signerInfo `asn1:"set"` 58 | } 59 | 60 | type signerInfo struct { 61 | Version int `asn1:"default:1"` 62 | IssuerAndSerialNumber issuerAndSerial 63 | DigestAlgorithm pkix.AlgorithmIdentifier 64 | AuthenticatedAttributes []attribute `asn1:"optional,omitempty,tag:0"` 65 | DigestEncryptionAlgorithm pkix.AlgorithmIdentifier 66 | EncryptedDigest []byte 67 | UnauthenticatedAttributes []attribute `asn1:"optional,omitempty,tag:1"` 68 | } 69 | 70 | type attribute struct { 71 | Type asn1.ObjectIdentifier 72 | Value asn1.RawValue `asn1:"set"` 73 | } 74 | 75 | func marshalAttributes(attrs []attribute) ([]byte, error) { 76 | encodedAttributes, err := asn1.Marshal(struct { 77 | A []attribute `asn1:"set"` 78 | }{A: attrs}) 79 | if err != nil { 80 | return nil, err 81 | } 82 | 83 | // Remove the leading sequence octets 84 | var raw asn1.RawValue 85 | asn1.Unmarshal(encodedAttributes, &raw) 86 | return raw.Bytes, nil 87 | } 88 | 89 | type rawCertificates struct { 90 | Raw asn1.RawContent 91 | } 92 | 93 | type issuerAndSerial struct { 94 | IssuerName asn1.RawValue 95 | SerialNumber *big.Int 96 | } 97 | 98 | // SetDigestAlgorithm sets the digest algorithm to be used in the signing process. 99 | // 100 | // This should be called before adding signers 101 | func (sd *SignedData) SetDigestAlgorithm(d asn1.ObjectIdentifier) { 102 | sd.digestOid = d 103 | } 104 | 105 | // SetEncryptionAlgorithm sets the encryption algorithm to be used in the signing process. 106 | // 107 | // This should be called before adding signers 108 | func (sd *SignedData) SetEncryptionAlgorithm(d asn1.ObjectIdentifier) { 109 | sd.encryptionOid = d 110 | } 111 | 112 | // AddSigner is a wrapper around AddSignerChain() that adds a signer without any parent. 113 | func (sd *SignedData) AddSigner(ee *x509.Certificate, pkey crypto.PrivateKey, config SignerInfoConfig) error { 114 | var parents []*x509.Certificate 115 | return sd.AddSignerChain(ee, pkey, parents, config) 116 | } 117 | 118 | // AddSignerChain signs attributes about the content and adds certificates 119 | // and signers infos to the Signed Data. The certificate and private key 120 | // of the end-entity signer are used to issue the signature, and any 121 | // parent of that end-entity that need to be added to the list of 122 | // certifications can be specified in the parents slice. 123 | // 124 | // The signature algorithm used to hash the data is the one of the end-entity 125 | // certificate. 126 | func (sd *SignedData) AddSignerChain(ee *x509.Certificate, pkey crypto.PrivateKey, parents []*x509.Certificate, config SignerInfoConfig) error { 127 | // Following RFC 2315, 9.2 SignerInfo type, the distinguished name of 128 | // the issuer of the end-entity signer is stored in the issuerAndSerialNumber 129 | // section of the SignedData.SignerInfo, alongside the serial number of 130 | // the end-entity. 131 | var ias issuerAndSerial 132 | ias.SerialNumber = ee.SerialNumber 133 | if len(parents) == 0 { 134 | // no parent, the issuer is the end-entity cert itself 135 | ias.IssuerName = asn1.RawValue{FullBytes: ee.RawIssuer} 136 | } else { 137 | err := verifyPartialChain(ee, parents) 138 | if err != nil { 139 | return err 140 | } 141 | // the first parent is the issuer 142 | ias.IssuerName = asn1.RawValue{FullBytes: parents[0].RawSubject} 143 | } 144 | sd.sd.DigestAlgorithmIdentifiers = append(sd.sd.DigestAlgorithmIdentifiers, 145 | pkix.AlgorithmIdentifier{Algorithm: sd.digestOid}, 146 | ) 147 | hash, err := getHashForOID(sd.digestOid) 148 | if err != nil { 149 | return err 150 | } 151 | h := hash.New() 152 | h.Write(sd.data) 153 | sd.messageDigest = h.Sum(nil) 154 | encryptionOid, err := getOIDForEncryptionAlgorithm(pkey, sd.digestOid) 155 | if err != nil { 156 | return err 157 | } 158 | attrs := &attributes{} 159 | attrs.Add(OIDAttributeContentType, sd.sd.ContentInfo.ContentType) 160 | attrs.Add(OIDAttributeMessageDigest, sd.messageDigest) 161 | attrs.Add(OIDAttributeSigningTime, time.Now().UTC()) 162 | for _, attr := range config.ExtraSignedAttributes { 163 | attrs.Add(attr.Type, attr.Value) 164 | } 165 | finalAttrs, err := attrs.ForMarshalling() 166 | if err != nil { 167 | return err 168 | } 169 | unsignedAttrs := &attributes{} 170 | for _, attr := range config.ExtraUnsignedAttributes { 171 | unsignedAttrs.Add(attr.Type, attr.Value) 172 | } 173 | finalUnsignedAttrs, err := unsignedAttrs.ForMarshalling() 174 | if err != nil { 175 | return err 176 | } 177 | // create signature of signed attributes 178 | signature, err := signAttributes(finalAttrs, pkey, hash) 179 | if err != nil { 180 | return err 181 | } 182 | signer := signerInfo{ 183 | AuthenticatedAttributes: finalAttrs, 184 | UnauthenticatedAttributes: finalUnsignedAttrs, 185 | DigestAlgorithm: pkix.AlgorithmIdentifier{Algorithm: sd.digestOid}, 186 | DigestEncryptionAlgorithm: pkix.AlgorithmIdentifier{Algorithm: encryptionOid}, 187 | IssuerAndSerialNumber: ias, 188 | EncryptedDigest: signature, 189 | Version: 1, 190 | } 191 | sd.certs = append(sd.certs, ee) 192 | if len(parents) > 0 { 193 | sd.certs = append(sd.certs, parents...) 194 | } 195 | sd.sd.SignerInfos = append(sd.sd.SignerInfos, signer) 196 | return nil 197 | } 198 | 199 | // SignWithoutAttr issues a signature on the content of the pkcs7 SignedData. 200 | // Unlike AddSigner/AddSignerChain, it calculates the digest on the data alone 201 | // and does not include any signed attributes like timestamp and so on. 202 | // 203 | // This function is needed to sign old Android APKs, something you probably 204 | // shouldn't do unless you're maintaining backward compatibility for old 205 | // applications. 206 | func (sd *SignedData) SignWithoutAttr(ee *x509.Certificate, pkey crypto.PrivateKey, config SignerInfoConfig) error { 207 | var signature []byte 208 | sd.sd.DigestAlgorithmIdentifiers = append(sd.sd.DigestAlgorithmIdentifiers, pkix.AlgorithmIdentifier{Algorithm: sd.digestOid}) 209 | hash, err := getHashForOID(sd.digestOid) 210 | if err != nil { 211 | return err 212 | } 213 | h := hash.New() 214 | h.Write(sd.data) 215 | sd.messageDigest = h.Sum(nil) 216 | switch pkey := pkey.(type) { 217 | case *dsa.PrivateKey: 218 | // dsa doesn't implement crypto.Signer so we make a special case 219 | // https://github.com/golang/go/issues/27889 220 | r, s, err := dsa.Sign(rand.Reader, pkey, sd.messageDigest) 221 | if err != nil { 222 | return err 223 | } 224 | signature, err = asn1.Marshal(dsaSignature{r, s}) 225 | if err != nil { 226 | return err 227 | } 228 | default: 229 | key, ok := pkey.(crypto.Signer) 230 | if !ok { 231 | return errors.New("pkcs7: private key does not implement crypto.Signer") 232 | } 233 | signature, err = key.Sign(rand.Reader, sd.messageDigest, hash) 234 | if err != nil { 235 | return err 236 | } 237 | } 238 | var ias issuerAndSerial 239 | ias.SerialNumber = ee.SerialNumber 240 | // no parent, the issue is the end-entity cert itself 241 | ias.IssuerName = asn1.RawValue{FullBytes: ee.RawIssuer} 242 | if sd.encryptionOid == nil { 243 | // if the encryption algorithm wasn't set by SetEncryptionAlgorithm, 244 | // infer it from the digest algorithm 245 | sd.encryptionOid, err = getOIDForEncryptionAlgorithm(pkey, sd.digestOid) 246 | } 247 | if err != nil { 248 | return err 249 | } 250 | signer := signerInfo{ 251 | DigestAlgorithm: pkix.AlgorithmIdentifier{Algorithm: sd.digestOid}, 252 | DigestEncryptionAlgorithm: pkix.AlgorithmIdentifier{Algorithm: sd.encryptionOid}, 253 | IssuerAndSerialNumber: ias, 254 | EncryptedDigest: signature, 255 | Version: 1, 256 | } 257 | // create signature of signed attributes 258 | sd.certs = append(sd.certs, ee) 259 | sd.sd.SignerInfos = append(sd.sd.SignerInfos, signer) 260 | return nil 261 | } 262 | 263 | func (si *signerInfo) SetUnauthenticatedAttributes(extraUnsignedAttrs []Attribute) error { 264 | unsignedAttrs := &attributes{} 265 | for _, attr := range extraUnsignedAttrs { 266 | unsignedAttrs.Add(attr.Type, attr.Value) 267 | } 268 | finalUnsignedAttrs, err := unsignedAttrs.ForMarshalling() 269 | if err != nil { 270 | return err 271 | } 272 | 273 | si.UnauthenticatedAttributes = finalUnsignedAttrs 274 | 275 | return nil 276 | } 277 | 278 | // AddCertificate adds the certificate to the payload. Useful for parent certificates 279 | func (sd *SignedData) AddCertificate(cert *x509.Certificate) { 280 | sd.certs = append(sd.certs, cert) 281 | } 282 | 283 | // Detach removes content from the signed data struct to make it a detached signature. 284 | // This must be called right before Finish() 285 | func (sd *SignedData) Detach() { 286 | sd.sd.ContentInfo = contentInfo{ContentType: OIDData} 287 | } 288 | 289 | // GetSignedData returns the private Signed Data 290 | func (sd *SignedData) GetSignedData() *signedData { 291 | return &sd.sd 292 | } 293 | 294 | // Finish marshals the content and its signers 295 | func (sd *SignedData) Finish() ([]byte, error) { 296 | sd.sd.Certificates = marshalCertificates(sd.certs) 297 | inner, err := asn1.Marshal(sd.sd) 298 | if err != nil { 299 | return nil, err 300 | } 301 | outer := contentInfo{ 302 | ContentType: OIDSignedData, 303 | Content: asn1.RawValue{Class: 2, Tag: 0, Bytes: inner, IsCompound: true}, 304 | } 305 | return asn1.Marshal(outer) 306 | } 307 | 308 | // RemoveAuthenticatedAttributes removes authenticated attributes from signedData 309 | // similar to OpenSSL's PKCS7_NOATTR or -noattr flags 310 | func (sd *SignedData) RemoveAuthenticatedAttributes() { 311 | for i := range sd.sd.SignerInfos { 312 | sd.sd.SignerInfos[i].AuthenticatedAttributes = nil 313 | } 314 | } 315 | 316 | // RemoveUnauthenticatedAttributes removes unauthenticated attributes from signedData 317 | func (sd *SignedData) RemoveUnauthenticatedAttributes() { 318 | for i := range sd.sd.SignerInfos { 319 | sd.sd.SignerInfos[i].UnauthenticatedAttributes = nil 320 | } 321 | } 322 | 323 | // verifyPartialChain checks that a given cert is issued by the first parent in the list, 324 | // then continue down the path. It doesn't require the last parent to be a root CA, 325 | // or to be trusted in any truststore. It simply verifies that the chain provided, albeit 326 | // partial, makes sense. 327 | func verifyPartialChain(cert *x509.Certificate, parents []*x509.Certificate) error { 328 | if len(parents) == 0 { 329 | return fmt.Errorf("pkcs7: zero parents provided to verify the signature of certificate %q", cert.Subject.CommonName) 330 | } 331 | err := cert.CheckSignatureFrom(parents[0]) 332 | if err != nil { 333 | return fmt.Errorf("pkcs7: certificate signature from parent is invalid: %v", err) 334 | } 335 | if len(parents) == 1 { 336 | // there is no more parent to check, return 337 | return nil 338 | } 339 | return verifyPartialChain(parents[0], parents[1:]) 340 | } 341 | 342 | func cert2issuerAndSerial(cert *x509.Certificate) (issuerAndSerial, error) { 343 | var ias issuerAndSerial 344 | // The issuer RDNSequence has to match exactly the sequence in the certificate 345 | // We cannot use cert.Issuer.ToRDNSequence() here since it mangles the sequence 346 | ias.IssuerName = asn1.RawValue{FullBytes: cert.RawIssuer} 347 | ias.SerialNumber = cert.SerialNumber 348 | 349 | return ias, nil 350 | } 351 | 352 | // signs the DER encoded form of the attributes with the private key 353 | func signAttributes(attrs []attribute, pkey crypto.PrivateKey, digestAlg crypto.Hash) ([]byte, error) { 354 | attrBytes, err := marshalAttributes(attrs) 355 | if err != nil { 356 | return nil, err 357 | } 358 | h := digestAlg.New() 359 | h.Write(attrBytes) 360 | hash := h.Sum(nil) 361 | 362 | // dsa doesn't implement crypto.Signer so we make a special case 363 | // https://github.com/golang/go/issues/27889 364 | switch pkey := pkey.(type) { 365 | case *dsa.PrivateKey: 366 | r, s, err := dsa.Sign(rand.Reader, pkey, hash) 367 | if err != nil { 368 | return nil, err 369 | } 370 | return asn1.Marshal(dsaSignature{r, s}) 371 | } 372 | 373 | key, ok := pkey.(crypto.Signer) 374 | if !ok { 375 | return nil, errors.New("pkcs7: private key does not implement crypto.Signer") 376 | } 377 | return key.Sign(rand.Reader, hash, digestAlg) 378 | } 379 | 380 | type dsaSignature struct { 381 | R, S *big.Int 382 | } 383 | 384 | // concats and wraps the certificates in the RawValue structure 385 | func marshalCertificates(certs []*x509.Certificate) rawCertificates { 386 | var buf bytes.Buffer 387 | for _, cert := range certs { 388 | buf.Write(cert.Raw) 389 | } 390 | rawCerts, _ := marshalCertificateBytes(buf.Bytes()) 391 | return rawCerts 392 | } 393 | 394 | // Even though, the tag & length are stripped out during marshalling the 395 | // RawContent, we have to encode it into the RawContent. If its missing, 396 | // then `asn1.Marshal()` will strip out the certificate wrapper instead. 397 | func marshalCertificateBytes(certs []byte) (rawCertificates, error) { 398 | var val = asn1.RawValue{Bytes: certs, Class: 2, Tag: 0, IsCompound: true} 399 | b, err := asn1.Marshal(val) 400 | if err != nil { 401 | return rawCertificates{}, err 402 | } 403 | return rawCertificates{Raw: b}, nil 404 | } 405 | 406 | // DegenerateCertificate creates a signed data structure containing only the 407 | // provided certificate or certificate chain. 408 | func DegenerateCertificate(cert []byte) ([]byte, error) { 409 | rawCert, err := marshalCertificateBytes(cert) 410 | if err != nil { 411 | return nil, err 412 | } 413 | emptyContent := contentInfo{ContentType: OIDData} 414 | sd := signedData{ 415 | Version: 1, 416 | ContentInfo: emptyContent, 417 | Certificates: rawCert, 418 | CRLs: []pkix.CertificateList{}, 419 | } 420 | content, err := asn1.Marshal(sd) 421 | if err != nil { 422 | return nil, err 423 | } 424 | signedContent := contentInfo{ 425 | ContentType: OIDSignedData, 426 | Content: asn1.RawValue{Class: 2, Tag: 0, Bytes: content, IsCompound: true}, 427 | } 428 | return asn1.Marshal(signedContent) 429 | } 430 | -------------------------------------------------------------------------------- /pkcs7_test.go: -------------------------------------------------------------------------------- 1 | package pkcs7 2 | 3 | import ( 4 | "crypto" 5 | "crypto/dsa" 6 | "crypto/ecdsa" 7 | "crypto/elliptic" 8 | "crypto/rand" 9 | "crypto/rsa" 10 | "crypto/x509" 11 | "crypto/x509/pkix" 12 | "encoding/pem" 13 | "fmt" 14 | "log" 15 | "math/big" 16 | "os" 17 | "time" 18 | ) 19 | 20 | var test1024Key, test2048Key, test3072Key, test4096Key *rsa.PrivateKey 21 | 22 | func init() { 23 | test1024Key = &rsa.PrivateKey{ 24 | PublicKey: rsa.PublicKey{ 25 | N: fromBase10("123024078101403810516614073341068864574068590522569345017786163424062310013967742924377390210586226651760719671658568413826602264886073432535341149584680111145880576802262550990305759285883150470245429547886689754596541046564560506544976611114898883158121012232676781340602508151730773214407220733898059285561"), 26 | E: 65537, 27 | }, 28 | D: fromBase10("118892427340746627750435157989073921703209000249285930635312944544706203626114423392257295670807166199489096863209592887347935991101581502404113203993092422730000157893515953622392722273095289787303943046491132467130346663160540744582438810535626328230098940583296878135092036661410664695896115177534496784545"), 29 | Primes: []*big.Int{ 30 | fromBase10("12172745919282672373981903347443034348576729562395784527365032103134165674508405592530417723266847908118361582847315228810176708212888860333051929276459099"), 31 | fromBase10("10106518193772789699356660087736308350857919389391620140340519320928952625438936098550728858345355053201610649202713962702543058578827268756755006576249339"), 32 | }, 33 | } 34 | test1024Key.Precompute() 35 | test2048Key = &rsa.PrivateKey{ 36 | PublicKey: rsa.PublicKey{ 37 | N: fromBase10("14314132931241006650998084889274020608918049032671858325988396851334124245188214251956198731333464217832226406088020736932173064754214329009979944037640912127943488972644697423190955557435910767690712778463524983667852819010259499695177313115447116110358524558307947613422897787329221478860907963827160223559690523660574329011927531289655711860504630573766609239332569210831325633840174683944553667352219670930408593321661375473885147973879086994006440025257225431977751512374815915392249179976902953721486040787792801849818254465486633791826766873076617116727073077821584676715609985777563958286637185868165868520557"), 38 | E: 3, 39 | }, 40 | D: fromBase10("9542755287494004433998723259516013739278699355114572217325597900889416163458809501304132487555642811888150937392013824621448709836142886006653296025093941418628992648429798282127303704957273845127141852309016655778568546006839666463451542076964744073572349705538631742281931858219480985907271975884773482372966847639853897890615456605598071088189838676728836833012254065983259638538107719766738032720239892094196108713378822882383694456030043492571063441943847195939549773271694647657549658603365629458610273821292232646334717612674519997533901052790334279661754176490593041941863932308687197618671528035670452762731"), 41 | Primes: []*big.Int{ 42 | fromBase10("130903255182996722426771613606077755295583329135067340152947172868415809027537376306193179624298874215608270802054347609836776473930072411958753044562214537013874103802006369634761074377213995983876788718033850153719421695468704276694983032644416930879093914927146648402139231293035971427838068945045019075433"), 43 | fromBase10("109348945610485453577574767652527472924289229538286649661240938988020367005475727988253438647560958573506159449538793540472829815903949343191091817779240101054552748665267574271163617694640513549693841337820602726596756351006149518830932261246698766355347898158548465400674856021497190430791824869615170301029"), 44 | }, 45 | } 46 | test2048Key.Precompute() 47 | test3072Key = &rsa.PrivateKey{ 48 | PublicKey: rsa.PublicKey{ 49 | N: fromBase10("4799422180968749215324244710281712119910779465109490663934897082847293004098645365195947978124390029272750644394844443980065532911010718425428791498896288210928474905407341584968381379157418577471272697781778686372450913810019702928839200328075568223462554606149618941566459398862673532997592879359280754226882565483298027678735544377401276021471356093819491755877827249763065753555051973844057308627201762456191918852016986546071426986328720794061622370410645440235373576002278045257207695462423797272017386006110722769072206022723167102083033531426777518054025826800254337147514768377949097720074878744769255210076910190151785807232805749219196645305822228090875616900385866236956058984170647782567907618713309775105943700661530312800231153745705977436176908325539234432407050398510090070342851489496464612052853185583222422124535243967989533830816012180864309784486694786581956050902756173889941244024888811572094961378021"), 50 | E: 65537, 51 | }, 52 | D: fromBase10("4068124900056380177006532461065648259352178312499768312132802353620854992915205894105621345694615110794369150964768050224096623567443679436821868510233726084582567244003894477723706516831312989564775159596496449435830457803384416702014837685962523313266832032687145914871879794104404800823188153886925022171560391765913739346955738372354826804228989767120353182641396181570533678315099748218734875742705419933837638038793286534641711407564379950728858267828581787483317040753987167237461567332386718574803231955771633274184646232632371006762852623964054645811527580417392163873708539175349637050049959954373319861427407953413018816604365474462455009323937599275324390953644555294418021286807661559165324810415569396577697316798600308544755741549699523972971375304826663847015905713096287495342701286542193782001358775773848824496321550110946106870685499577993864871847542645561943034990484973293461948058147956373115641615329"), 53 | Primes: []*big.Int{ 54 | fromBase10("2378529069722721185825622840841310902793949682948530343491428052737890236476884657507685118578733560141370511507721598189068683665232991988491561624429938984370132428230072355214627085652359350722926394699707232921674771664421591347888367477300909202851476404132163673865768760147403525700174918450753162242834161458300343282159799476695001920226357456953682236859505243928716782707623075239350380352265954107362618991716602898266999700316937680986690964564264877"), 55 | fromBase10("2017811025336026464312837780072272578817919741496395062543647660689775637351085991504709917848745137013798005682591633910555599626950744674459976829106750083386168859581016361317479081273480343110649405858059581933773354781034946787147300862495438979895430001323443224335618577322449133208754541656374335100929456885995320929464029817626916719434010943205170760536768893924932021302887114400922813817969176636993508191950649313115712159241971065134077636674146073"), 56 | }, 57 | } 58 | test3072Key.Precompute() 59 | test4096Key = &rsa.PrivateKey{ 60 | PublicKey: rsa.PublicKey{ 61 | N: fromBase10("633335480064287130853997429184971616419051348693342219741748040433588285601270210251206421401040394238592139790962887290698043839174341843721930134010306454716566698330215646704263665452264344664385995704186692432827662862845900348526672531755932642433662686500295989783595767573119607065791980381547677840410600100715146047382485989885183858757974681241303484641390718944520330953604501686666386926996348457928415093305041429178744778762826377713889019740060910363468343855830206640274442887621960581569183233822878661711798998132931623726434336448716605363514220760343097572198620479297583609779817750646169845195672483600293522186340560792255595411601450766002877850696008003794520089358819042318331840490155176019070646738739580486357084733208876620846449161909966690602374519398451042362690200166144326179405976024265116931974936425064291406950542193873313447617169603706868220189295654943247311295475722243471700112334609817776430552541319671117235957754556272646031356496763094955985615723596562217985372503002989591679252640940571608314743271809251568670314461039035793703429977801961867815257832671786542212589906513979094156334941265621017752516999186481477500481433634914622735206243841674973785078408289183000133399026553"), 62 | E: 65537, 63 | }, 64 | D: fromBase10("439373650557744155078930178606343279553665694488479749802070836418412881168612407941793966086633543867614175621952769177088930851151267623886678906158545451731745754402575409204816390946376103491325109185445659065122640946673660760274557781540431107937331701243915001777636528502669576801704352961341634812275635811512806966908648671988644114352046582195051714797831307925775689566757438907578527366568747104508496278929566712224252103563340770696548181508180254674236716995730292431858611476396845443056967589437890065663497768422598977743046882539288481002449571403783500529740184608873520856954837631427724158592309018382711485601884461168736465751756282510065053161144027097169985941910909130083273691945578478173708396726266170473745329617793866669307716920992380350270584929908460462802627239204245339385636926433446418108504614031393494119344916828744888432279343816084433424594432427362258172264834429525166677273382617457205387388293888430391895615438030066428745187333897518037597413369705720436392869403948934993623418405908467147848576977008003556716087129242155836114780890054057743164411952731290520995017097151300091841286806603044227906213832083363876549637037625314539090155417589796428888619937329669464810549362433"), 65 | Primes: []*big.Int{ 66 | fromBase10("25745433817240673759910623230144796182285844101796353869339294232644316274580053211056707671663014355388701931204078502829809738396303142990312095225333440050808647355535878394534263839500592870406002873182360027755750148248672968563366185348499498613479490545488025779331426515670185366021612402246813511722553210128074701620113404560399242413747318161403908617342170447610792422053460359960010544593668037305465806912471260799852789913123044326555978680190904164976511331681163576833618899773550873682147782263100803907156362439021929408298804955194748640633152519828940133338948391986823456836070708197320166146761"), 67 | fromBase10("24599914864909676687852658457515103765368967514652318497893275892114442089314173678877914038802355565271545910572804267918959612739009937926962653912943833939518967731764560204997062096919833970670512726396663920955497151415639902788974842698619579886297871162402643104696160155894685518587660015182381685605752989716946154299190561137541792784125356553411300817844325739404126956793095254412123887617931225840421856505925283322918693259047428656823141903489964287619982295891439430302405252447010728112098326033634688757933930065610737780413018498561434074501822951716586796047404555397992425143397497639322075233073"), 68 | }, 69 | } 70 | test4096Key.Precompute() 71 | } 72 | 73 | func fromBase10(base10 string) *big.Int { 74 | i, ok := new(big.Int).SetString(base10, 10) 75 | if !ok { 76 | panic("bad number: " + base10) 77 | } 78 | return i 79 | } 80 | 81 | type certKeyPair struct { 82 | Certificate *x509.Certificate 83 | PrivateKey *crypto.PrivateKey 84 | } 85 | 86 | func createTestCertificate(sigAlg x509.SignatureAlgorithm) (certKeyPair, error) { 87 | signer, err := createTestCertificateByIssuer("Eddard Stark", nil, sigAlg, true) 88 | if err != nil { 89 | return certKeyPair{}, err 90 | } 91 | pair, err := createTestCertificateByIssuer("Jon Snow", signer, sigAlg, false) 92 | if err != nil { 93 | return certKeyPair{}, err 94 | } 95 | return *pair, nil 96 | } 97 | 98 | func createTestCertificateByIssuer(name string, issuer *certKeyPair, sigAlg x509.SignatureAlgorithm, isCA bool) (*certKeyPair, error) { 99 | var ( 100 | err error 101 | priv crypto.PrivateKey 102 | derCert []byte 103 | issuerCert *x509.Certificate 104 | issuerKey crypto.PrivateKey 105 | ) 106 | serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 32) 107 | serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) 108 | if err != nil { 109 | return nil, err 110 | } 111 | 112 | template := x509.Certificate{ 113 | SerialNumber: serialNumber, 114 | Subject: pkix.Name{ 115 | CommonName: name, 116 | Organization: []string{"Acme Co"}, 117 | }, 118 | NotBefore: time.Now().Add(-1 * time.Second), 119 | NotAfter: time.Now().AddDate(1, 0, 0), 120 | KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, 121 | ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageEmailProtection}, 122 | } 123 | if issuer != nil { 124 | issuerCert = issuer.Certificate 125 | issuerKey = *issuer.PrivateKey 126 | } 127 | switch sigAlg { 128 | case x509.SHA1WithRSA: 129 | priv = test1024Key 130 | switch issuerKey.(type) { 131 | case *rsa.PrivateKey: 132 | template.SignatureAlgorithm = x509.SHA1WithRSA 133 | case *ecdsa.PrivateKey: 134 | template.SignatureAlgorithm = x509.ECDSAWithSHA1 135 | case *dsa.PrivateKey: 136 | template.SignatureAlgorithm = x509.DSAWithSHA1 137 | } 138 | case x509.SHA256WithRSA: 139 | priv = test2048Key 140 | switch issuerKey.(type) { 141 | case *rsa.PrivateKey: 142 | template.SignatureAlgorithm = x509.SHA256WithRSA 143 | case *ecdsa.PrivateKey: 144 | template.SignatureAlgorithm = x509.ECDSAWithSHA256 145 | case *dsa.PrivateKey: 146 | template.SignatureAlgorithm = x509.DSAWithSHA256 147 | } 148 | case x509.SHA384WithRSA: 149 | priv = test3072Key 150 | switch issuerKey.(type) { 151 | case *rsa.PrivateKey: 152 | template.SignatureAlgorithm = x509.SHA384WithRSA 153 | case *ecdsa.PrivateKey: 154 | template.SignatureAlgorithm = x509.ECDSAWithSHA384 155 | case *dsa.PrivateKey: 156 | template.SignatureAlgorithm = x509.DSAWithSHA256 157 | } 158 | case x509.SHA512WithRSA: 159 | priv = test4096Key 160 | switch issuerKey.(type) { 161 | case *rsa.PrivateKey: 162 | template.SignatureAlgorithm = x509.SHA512WithRSA 163 | case *ecdsa.PrivateKey: 164 | template.SignatureAlgorithm = x509.ECDSAWithSHA512 165 | case *dsa.PrivateKey: 166 | template.SignatureAlgorithm = x509.DSAWithSHA256 167 | } 168 | case x509.ECDSAWithSHA1: 169 | priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) 170 | if err != nil { 171 | return nil, err 172 | } 173 | switch issuerKey.(type) { 174 | case *rsa.PrivateKey: 175 | template.SignatureAlgorithm = x509.SHA1WithRSA 176 | case *ecdsa.PrivateKey: 177 | template.SignatureAlgorithm = x509.ECDSAWithSHA1 178 | case *dsa.PrivateKey: 179 | template.SignatureAlgorithm = x509.DSAWithSHA1 180 | } 181 | case x509.ECDSAWithSHA256: 182 | priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) 183 | if err != nil { 184 | return nil, err 185 | } 186 | switch issuerKey.(type) { 187 | case *rsa.PrivateKey: 188 | template.SignatureAlgorithm = x509.SHA256WithRSA 189 | case *ecdsa.PrivateKey: 190 | template.SignatureAlgorithm = x509.ECDSAWithSHA256 191 | case *dsa.PrivateKey: 192 | template.SignatureAlgorithm = x509.DSAWithSHA256 193 | } 194 | case x509.ECDSAWithSHA384: 195 | priv, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader) 196 | if err != nil { 197 | return nil, err 198 | } 199 | switch issuerKey.(type) { 200 | case *rsa.PrivateKey: 201 | template.SignatureAlgorithm = x509.SHA384WithRSA 202 | case *ecdsa.PrivateKey: 203 | template.SignatureAlgorithm = x509.ECDSAWithSHA384 204 | case *dsa.PrivateKey: 205 | template.SignatureAlgorithm = x509.DSAWithSHA256 206 | } 207 | case x509.ECDSAWithSHA512: 208 | priv, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader) 209 | if err != nil { 210 | return nil, err 211 | } 212 | switch issuerKey.(type) { 213 | case *rsa.PrivateKey: 214 | template.SignatureAlgorithm = x509.SHA512WithRSA 215 | case *ecdsa.PrivateKey: 216 | template.SignatureAlgorithm = x509.ECDSAWithSHA512 217 | case *dsa.PrivateKey: 218 | template.SignatureAlgorithm = x509.DSAWithSHA256 219 | } 220 | case x509.DSAWithSHA1: 221 | var dsaPriv dsa.PrivateKey 222 | params := &dsaPriv.Parameters 223 | err = dsa.GenerateParameters(params, rand.Reader, dsa.L1024N160) 224 | if err != nil { 225 | return nil, err 226 | } 227 | err = dsa.GenerateKey(&dsaPriv, rand.Reader) 228 | if err != nil { 229 | return nil, err 230 | } 231 | switch issuerKey.(type) { 232 | case *rsa.PrivateKey: 233 | template.SignatureAlgorithm = x509.SHA1WithRSA 234 | case *ecdsa.PrivateKey: 235 | template.SignatureAlgorithm = x509.ECDSAWithSHA1 236 | case *dsa.PrivateKey: 237 | template.SignatureAlgorithm = x509.DSAWithSHA1 238 | } 239 | priv = &dsaPriv 240 | } 241 | if isCA { 242 | template.IsCA = true 243 | template.KeyUsage |= x509.KeyUsageCertSign 244 | template.BasicConstraintsValid = true 245 | } 246 | if issuer == nil { 247 | // no issuer given,make this a self-signed root cert 248 | issuerCert = &template 249 | issuerKey = priv 250 | } 251 | 252 | log.Println("creating cert", name, "issued by", issuerCert.Subject.CommonName, "with sigalg", sigAlg) 253 | switch priv.(type) { 254 | case *rsa.PrivateKey: 255 | switch issuerKey.(type) { 256 | case *rsa.PrivateKey: 257 | derCert, err = x509.CreateCertificate(rand.Reader, &template, issuerCert, priv.(*rsa.PrivateKey).Public(), issuerKey.(*rsa.PrivateKey)) 258 | case *ecdsa.PrivateKey: 259 | derCert, err = x509.CreateCertificate(rand.Reader, &template, issuerCert, priv.(*rsa.PrivateKey).Public(), issuerKey.(*ecdsa.PrivateKey)) 260 | case *dsa.PrivateKey: 261 | derCert, err = x509.CreateCertificate(rand.Reader, &template, issuerCert, priv.(*rsa.PrivateKey).Public(), issuerKey.(*dsa.PrivateKey)) 262 | } 263 | case *ecdsa.PrivateKey: 264 | switch issuerKey.(type) { 265 | case *rsa.PrivateKey: 266 | derCert, err = x509.CreateCertificate(rand.Reader, &template, issuerCert, priv.(*ecdsa.PrivateKey).Public(), issuerKey.(*rsa.PrivateKey)) 267 | case *ecdsa.PrivateKey: 268 | derCert, err = x509.CreateCertificate(rand.Reader, &template, issuerCert, priv.(*ecdsa.PrivateKey).Public(), issuerKey.(*ecdsa.PrivateKey)) 269 | case *dsa.PrivateKey: 270 | derCert, err = x509.CreateCertificate(rand.Reader, &template, issuerCert, priv.(*ecdsa.PrivateKey).Public(), issuerKey.(*dsa.PrivateKey)) 271 | } 272 | case *dsa.PrivateKey: 273 | pub := &priv.(*dsa.PrivateKey).PublicKey 274 | switch issuerKey := issuerKey.(type) { 275 | case *rsa.PrivateKey: 276 | derCert, err = x509.CreateCertificate(rand.Reader, &template, issuerCert, pub, issuerKey) 277 | case *ecdsa.PrivateKey: 278 | derCert, err = x509.CreateCertificate(rand.Reader, &template, issuerCert, priv.(*dsa.PublicKey), issuerKey) 279 | case *dsa.PrivateKey: 280 | derCert, err = x509.CreateCertificate(rand.Reader, &template, issuerCert, priv.(*dsa.PublicKey), issuerKey) 281 | } 282 | } 283 | if err != nil { 284 | return nil, err 285 | } 286 | if len(derCert) == 0 { 287 | return nil, fmt.Errorf("no certificate created, probably due to wrong keys. types were %T and %T", priv, issuerKey) 288 | } 289 | cert, err := x509.ParseCertificate(derCert) 290 | if err != nil { 291 | return nil, err 292 | } 293 | pem.Encode(os.Stdout, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}) 294 | return &certKeyPair{ 295 | Certificate: cert, 296 | PrivateKey: &priv, 297 | }, nil 298 | } 299 | 300 | type TestFixture struct { 301 | Input []byte 302 | Certificate *x509.Certificate 303 | PrivateKey *rsa.PrivateKey 304 | } 305 | 306 | func UnmarshalTestFixture(testPEMBlock string) TestFixture { 307 | var result TestFixture 308 | var derBlock *pem.Block 309 | var pemBlock = []byte(testPEMBlock) 310 | for { 311 | derBlock, pemBlock = pem.Decode(pemBlock) 312 | if derBlock == nil { 313 | break 314 | } 315 | switch derBlock.Type { 316 | case "PKCS7": 317 | result.Input = derBlock.Bytes 318 | case "CERTIFICATE": 319 | result.Certificate, _ = x509.ParseCertificate(derBlock.Bytes) 320 | case "PRIVATE KEY": 321 | result.PrivateKey, _ = x509.ParsePKCS1PrivateKey(derBlock.Bytes) 322 | } 323 | } 324 | 325 | return result 326 | } 327 | -------------------------------------------------------------------------------- /verify_test.go: -------------------------------------------------------------------------------- 1 | package pkcs7 2 | 3 | import ( 4 | "bytes" 5 | "crypto/ecdsa" 6 | "crypto/rsa" 7 | "crypto/x509" 8 | "encoding/base64" 9 | "encoding/pem" 10 | "io/ioutil" 11 | "os" 12 | "os/exec" 13 | "testing" 14 | "time" 15 | ) 16 | 17 | func TestVerify(t *testing.T) { 18 | fixture := UnmarshalTestFixture(SignedTestFixture) 19 | p7, err := Parse(fixture.Input) 20 | if err != nil { 21 | t.Errorf("Parse encountered unexpected error: %v", err) 22 | } 23 | 24 | if err := p7.Verify(); err != nil { 25 | t.Errorf("Verify failed with error: %v", err) 26 | } 27 | expected := []byte("We the People") 28 | if !bytes.Equal(p7.Content, expected) { 29 | t.Errorf("Signed content does not match.\n\tExpected:%s\n\tActual:%s", expected, p7.Content) 30 | 31 | } 32 | } 33 | 34 | var SignedTestFixture = ` 35 | -----BEGIN PKCS7----- 36 | MIIDVgYJKoZIhvcNAQcCoIIDRzCCA0MCAQExCTAHBgUrDgMCGjAcBgkqhkiG9w0B 37 | BwGgDwQNV2UgdGhlIFBlb3BsZaCCAdkwggHVMIIBQKADAgECAgRpuDctMAsGCSqG 38 | SIb3DQEBCzApMRAwDgYDVQQKEwdBY21lIENvMRUwEwYDVQQDEwxFZGRhcmQgU3Rh 39 | cmswHhcNMTUwNTA2MDQyNDQ4WhcNMTYwNTA2MDQyNDQ4WjAlMRAwDgYDVQQKEwdB 40 | Y21lIENvMREwDwYDVQQDEwhKb24gU25vdzCBnzANBgkqhkiG9w0BAQEFAAOBjQAw 41 | gYkCgYEAqr+tTF4mZP5rMwlXp1y+crRtFpuLXF1zvBZiYMfIvAHwo1ta8E1IcyEP 42 | J1jIiKMcwbzeo6kAmZzIJRCTezq9jwXUsKbQTvcfOH9HmjUmXBRWFXZYoQs/OaaF 43 | a45deHmwEeMQkuSWEtYiVKKZXtJOtflKIT3MryJEDiiItMkdybUCAwEAAaMSMBAw 44 | DgYDVR0PAQH/BAQDAgCgMAsGCSqGSIb3DQEBCwOBgQDK1EweZWRL+f7Z+J0kVzY8 45 | zXptcBaV4Lf5wGZJLJVUgp33bpLNpT3yadS++XQJ+cvtW3wADQzBSTMduyOF8Zf+ 46 | L7TjjrQ2+F2HbNbKUhBQKudxTfv9dJHdKbD+ngCCdQJYkIy2YexsoNG0C8nQkggy 47 | axZd/J69xDVx6pui3Sj8sDGCATYwggEyAgEBMDEwKTEQMA4GA1UEChMHQWNtZSBD 48 | bzEVMBMGA1UEAxMMRWRkYXJkIFN0YXJrAgRpuDctMAcGBSsOAwIaoGEwGAYJKoZI 49 | hvcNAQkDMQsGCSqGSIb3DQEHATAgBgkqhkiG9w0BCQUxExcRMTUwNTA2MDAyNDQ4 50 | LTA0MDAwIwYJKoZIhvcNAQkEMRYEFG9D7gcTh9zfKiYNJ1lgB0yTh4sZMAsGCSqG 51 | SIb3DQEBAQSBgFF3sGDU9PtXty/QMtpcFa35vvIOqmWQAIZt93XAskQOnBq4OloX 52 | iL9Ct7t1m4pzjRm0o9nDkbaSLZe7HKASHdCqijroScGlI8M+alJ8drHSFv6ZIjnM 53 | FIwIf0B2Lko6nh9/6mUXq7tbbIHa3Gd1JUVire/QFFtmgRXMbXYk8SIS 54 | -----END PKCS7----- 55 | -----BEGIN CERTIFICATE----- 56 | MIIB1TCCAUCgAwIBAgIEabg3LTALBgkqhkiG9w0BAQswKTEQMA4GA1UEChMHQWNt 57 | ZSBDbzEVMBMGA1UEAxMMRWRkYXJkIFN0YXJrMB4XDTE1MDUwNjA0MjQ0OFoXDTE2 58 | MDUwNjA0MjQ0OFowJTEQMA4GA1UEChMHQWNtZSBDbzERMA8GA1UEAxMISm9uIFNu 59 | b3cwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKq/rUxeJmT+azMJV6dcvnK0 60 | bRabi1xdc7wWYmDHyLwB8KNbWvBNSHMhDydYyIijHMG83qOpAJmcyCUQk3s6vY8F 61 | 1LCm0E73Hzh/R5o1JlwUVhV2WKELPzmmhWuOXXh5sBHjEJLklhLWIlSimV7STrX5 62 | SiE9zK8iRA4oiLTJHcm1AgMBAAGjEjAQMA4GA1UdDwEB/wQEAwIAoDALBgkqhkiG 63 | 9w0BAQsDgYEAytRMHmVkS/n+2fidJFc2PM16bXAWleC3+cBmSSyVVIKd926SzaU9 64 | 8mnUvvl0CfnL7Vt8AA0MwUkzHbsjhfGX/i+04460Nvhdh2zWylIQUCrncU37/XSR 65 | 3Smw/p4AgnUCWJCMtmHsbKDRtAvJ0JIIMmsWXfyevcQ1ceqbot0o/LA= 66 | -----END CERTIFICATE----- 67 | -----BEGIN PRIVATE KEY----- 68 | MIICXgIBAAKBgQCqv61MXiZk/mszCVenXL5ytG0Wm4tcXXO8FmJgx8i8AfCjW1rw 69 | TUhzIQ8nWMiIoxzBvN6jqQCZnMglEJN7Or2PBdSwptBO9x84f0eaNSZcFFYVdlih 70 | Cz85poVrjl14ebAR4xCS5JYS1iJUople0k61+UohPcyvIkQOKIi0yR3JtQIDAQAB 71 | AoGBAIPLCR9N+IKxodq11lNXEaUFwMHXc1zqwP8no+2hpz3+nVfplqqubEJ4/PJY 72 | 5AgbJoIfnxVhyBXJXu7E+aD/OPneKZrgp58YvHKgGvvPyJg2gpC/1Fh0vQB0HNpI 73 | 1ZzIZUl8ZTUtVgtnCBUOh5JGI4bFokAqrT//Uvcfd+idgxqBAkEA1ZbP/Kseld14 74 | qbWmgmU5GCVxsZRxgR1j4lG3UVjH36KXMtRTm1atAam1uw3OEGa6Y3ANjpU52FaB 75 | Hep5rkk4FQJBAMynMo1L1uiN5GP+KYLEF5kKRxK+FLjXR0ywnMh+gpGcZDcOae+J 76 | +t1gLoWBIESH/Xt639T7smuSfrZSA9V0EyECQA8cvZiWDvLxmaEAXkipmtGPjKzQ 77 | 4PsOtkuEFqFl07aKDYKmLUg3aMROWrJidqsIabWxbvQgsNgSvs38EiH3wkUCQQCg 78 | ndxb7piVXb9RBwm3OoU2tE1BlXMX+sVXmAkEhd2dwDsaxrI3sHf1xGXem5AimQRF 79 | JBOFyaCnMotGNioSHY5hAkEAxyXcNixQ2RpLXJTQZtwnbk0XDcbgB+fBgXnv/4f3 80 | BCvcu85DqJeJyQv44Oe1qsXEX9BfcQIOVaoep35RPlKi9g== 81 | -----END PRIVATE KEY-----` 82 | 83 | func TestVerifyAppStore(t *testing.T) { 84 | fixture := UnmarshalTestFixture(AppStoreReceiptFixture) 85 | p7, err := Parse(fixture.Input) 86 | if err != nil { 87 | t.Errorf("Parse encountered unexpected error: %v", err) 88 | } 89 | if err := p7.Verify(); err != nil { 90 | t.Errorf("Verify failed with error: %v", err) 91 | } 92 | } 93 | 94 | var AppStoreReceiptFixture = ` 95 | -----BEGIN PKCS7----- 96 | MIITtgYJKoZIhvcNAQcCoIITpzCCE6MCAQExCzAJBgUrDgMCGgUAMIIDVwYJKoZI 97 | hvcNAQcBoIIDSASCA0QxggNAMAoCAQgCAQEEAhYAMAoCARQCAQEEAgwAMAsCAQEC 98 | AQEEAwIBADALAgEDAgEBBAMMATEwCwIBCwIBAQQDAgEAMAsCAQ8CAQEEAwIBADAL 99 | AgEQAgEBBAMCAQAwCwIBGQIBAQQDAgEDMAwCAQoCAQEEBBYCNCswDAIBDgIBAQQE 100 | AgIAjTANAgENAgEBBAUCAwFgvTANAgETAgEBBAUMAzEuMDAOAgEJAgEBBAYCBFAy 101 | NDcwGAIBAgIBAQQQDA5jb20uemhpaHUudGVzdDAYAgEEAgECBBCS+ZODNMHwT1Nz 102 | gWYDXyWZMBsCAQACAQEEEwwRUHJvZHVjdGlvblNhbmRib3gwHAIBBQIBAQQU4nRh 103 | YCEZx70Flzv7hvJRjJZckYIwHgIBDAIBAQQWFhQyMDE2LTA3LTIzVDA2OjIxOjEx 104 | WjAeAgESAgEBBBYWFDIwMTMtMDgtMDFUMDc6MDA6MDBaMD0CAQYCAQEENbR21I+a 105 | 8+byMXo3NPRoDWQmSXQF2EcCeBoD4GaL//ZCRETp9rGFPSg1KekCP7Kr9HAqw09m 106 | MEICAQcCAQEEOlVJozYYBdugybShbiiMsejDMNeCbZq6CrzGBwW6GBy+DGWxJI91 107 | Y3ouXN4TZUhuVvLvN1b0m5T3ggQwggFaAgERAgEBBIIBUDGCAUwwCwICBqwCAQEE 108 | AhYAMAsCAgatAgEBBAIMADALAgIGsAIBAQQCFgAwCwICBrICAQEEAgwAMAsCAgaz 109 | AgEBBAIMADALAgIGtAIBAQQCDAAwCwICBrUCAQEEAgwAMAsCAga2AgEBBAIMADAM 110 | AgIGpQIBAQQDAgEBMAwCAgarAgEBBAMCAQEwDAICBq4CAQEEAwIBADAMAgIGrwIB 111 | AQQDAgEAMAwCAgaxAgEBBAMCAQAwGwICBqcCAQEEEgwQMTAwMDAwMDIyNTMyNTkw 112 | MTAbAgIGqQIBAQQSDBAxMDAwMDAwMjI1MzI1OTAxMB8CAgaoAgEBBBYWFDIwMTYt 113 | MDctMjNUMDY6MjE6MTFaMB8CAgaqAgEBBBYWFDIwMTYtMDctMjNUMDY6MjE6MTFa 114 | MCACAgamAgEBBBcMFWNvbS56aGlodS50ZXN0LnRlc3RfMaCCDmUwggV8MIIEZKAD 115 | AgECAggO61eH554JjTANBgkqhkiG9w0BAQUFADCBljELMAkGA1UEBhMCVVMxEzAR 116 | BgNVBAoMCkFwcGxlIEluYy4xLDAqBgNVBAsMI0FwcGxlIFdvcmxkd2lkZSBEZXZl 117 | bG9wZXIgUmVsYXRpb25zMUQwQgYDVQQDDDtBcHBsZSBXb3JsZHdpZGUgRGV2ZWxv 118 | cGVyIFJlbGF0aW9ucyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xNTExMTMw 119 | MjE1MDlaFw0yMzAyMDcyMTQ4NDdaMIGJMTcwNQYDVQQDDC5NYWMgQXBwIFN0b3Jl 120 | IGFuZCBpVHVuZXMgU3RvcmUgUmVjZWlwdCBTaWduaW5nMSwwKgYDVQQLDCNBcHBs 121 | ZSBXb3JsZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczETMBEGA1UECgwKQXBwbGUg 122 | SW5jLjELMAkGA1UEBhMCVVMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB 123 | AQClz4H9JaKBW9aH7SPaMxyO4iPApcQmyz3Gn+xKDVWG/6QC15fKOVRtfX+yVBid 124 | xCxScY5ke4LOibpJ1gjltIhxzz9bRi7GxB24A6lYogQ+IXjV27fQjhKNg0xbKmg3 125 | k8LyvR7E0qEMSlhSqxLj7d0fmBWQNS3CzBLKjUiB91h4VGvojDE2H0oGDEdU8zeQ 126 | uLKSiX1fpIVK4cCc4Lqku4KXY/Qrk8H9Pm/KwfU8qY9SGsAlCnYO3v6Z/v/Ca/Vb 127 | XqxzUUkIVonMQ5DMjoEC0KCXtlyxoWlph5AQaCYmObgdEHOwCl3Fc9DfdjvYLdmI 128 | HuPsB8/ijtDT+iZVge/iA0kjAgMBAAGjggHXMIIB0zA/BggrBgEFBQcBAQQzMDEw 129 | LwYIKwYBBQUHMAGGI2h0dHA6Ly9vY3NwLmFwcGxlLmNvbS9vY3NwMDMtd3dkcjA0 130 | MB0GA1UdDgQWBBSRpJz8xHa3n6CK9E31jzZd7SsEhTAMBgNVHRMBAf8EAjAAMB8G 131 | A1UdIwQYMBaAFIgnFwmpthhgi+zruvZHWcVSVKO3MIIBHgYDVR0gBIIBFTCCAREw 132 | ggENBgoqhkiG92NkBQYBMIH+MIHDBggrBgEFBQcCAjCBtgyBs1JlbGlhbmNlIG9u 133 | IHRoaXMgY2VydGlmaWNhdGUgYnkgYW55IHBhcnR5IGFzc3VtZXMgYWNjZXB0YW5j 134 | ZSBvZiB0aGUgdGhlbiBhcHBsaWNhYmxlIHN0YW5kYXJkIHRlcm1zIGFuZCBjb25k 135 | aXRpb25zIG9mIHVzZSwgY2VydGlmaWNhdGUgcG9saWN5IGFuZCBjZXJ0aWZpY2F0 136 | aW9uIHByYWN0aWNlIHN0YXRlbWVudHMuMDYGCCsGAQUFBwIBFipodHRwOi8vd3d3 137 | LmFwcGxlLmNvbS9jZXJ0aWZpY2F0ZWF1dGhvcml0eS8wDgYDVR0PAQH/BAQDAgeA 138 | MBAGCiqGSIb3Y2QGCwEEAgUAMA0GCSqGSIb3DQEBBQUAA4IBAQANphvTLj3jWysH 139 | bkKWbNPojEMwgl/gXNGNvr0PvRr8JZLbjIXDgFnf4+LXLgUUrA3btrj+/DUufMut 140 | F2uOfx/kd7mxZ5W0E16mGYZ2+FogledjjA9z/Ojtxh+umfhlSFyg4Cg6wBA3Lbmg 141 | BDkfc7nIBf3y3n8aKipuKwH8oCBc2et9J6Yz+PWY4L5E27FMZ/xuCk/J4gao0pfz 142 | p45rUaJahHVl0RYEYuPBX/UIqc9o2ZIAycGMs/iNAGS6WGDAfK+PdcppuVsq1h1o 143 | bphC9UynNxmbzDscehlD86Ntv0hgBgw2kivs3hi1EdotI9CO/KBpnBcbnoB7OUdF 144 | MGEvxxOoMIIEIjCCAwqgAwIBAgIIAd68xDltoBAwDQYJKoZIhvcNAQEFBQAwYjEL 145 | MAkGA1UEBhMCVVMxEzARBgNVBAoTCkFwcGxlIEluYy4xJjAkBgNVBAsTHUFwcGxl 146 | IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRYwFAYDVQQDEw1BcHBsZSBSb290IENB 147 | MB4XDTEzMDIwNzIxNDg0N1oXDTIzMDIwNzIxNDg0N1owgZYxCzAJBgNVBAYTAlVT 148 | MRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3JsZHdpZGUg 149 | RGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3aWRlIERl 150 | dmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0G 151 | CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDKOFSmy1aqyCQ5SOmM7uxfuH8mkbw0 152 | U3rOfGOAYXdkXqUHI7Y5/lAtFVZYcC1+xG7BSoU+L/DehBqhV8mvexj/avoVEkkV 153 | CBmsqtsqMu2WY2hSFT2Miuy/axiV4AOsAX2XBWfODoWVN2rtCbauZ81RZJ/GXNG8 154 | V25nNYB2NqSHgW44j9grFU57Jdhav06DwY3Sk9UacbVgnJ0zTlX5ElgMhrgWDcHl 155 | d0WNUEi6Ky3klIXh6MSdxmilsKP8Z35wugJZS3dCkTm59c3hTO/AO0iMpuUhXf1q 156 | arunFjVg0uat80YpyejDi+l5wGphZxWy8P3laLxiX27Pmd3vG2P+kmWrAgMBAAGj 157 | gaYwgaMwHQYDVR0OBBYEFIgnFwmpthhgi+zruvZHWcVSVKO3MA8GA1UdEwEB/wQF 158 | MAMBAf8wHwYDVR0jBBgwFoAUK9BpR5R2Cf70a40uQKb3R01/CF4wLgYDVR0fBCcw 159 | JTAjoCGgH4YdaHR0cDovL2NybC5hcHBsZS5jb20vcm9vdC5jcmwwDgYDVR0PAQH/ 160 | BAQDAgGGMBAGCiqGSIb3Y2QGAgEEAgUAMA0GCSqGSIb3DQEBBQUAA4IBAQBPz+9Z 161 | viz1smwvj+4ThzLoBTWobot9yWkMudkXvHcs1Gfi/ZptOllc34MBvbKuKmFysa/N 162 | w0Uwj6ODDc4dR7Txk4qjdJukw5hyhzs+r0ULklS5MruQGFNrCk4QttkdUGwhgAqJ 163 | TleMa1s8Pab93vcNIx0LSiaHP7qRkkykGRIZbVf1eliHe2iK5IaMSuviSRSqpd1V 164 | AKmuu0swruGgsbwpgOYJd+W+NKIByn/c4grmO7i77LpilfMFY0GCzQ87HUyVpNur 165 | +cmV6U/kTecmmYHpvPm0KdIBembhLoz2IYrF+Hjhga6/05Cdqa3zr/04GpZnMBxR 166 | pVzscYqCtGwPDBUfMIIEuzCCA6OgAwIBAgIBAjANBgkqhkiG9w0BAQUFADBiMQsw 167 | CQYDVQQGEwJVUzETMBEGA1UEChMKQXBwbGUgSW5jLjEmMCQGA1UECxMdQXBwbGUg 168 | Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkxFjAUBgNVBAMTDUFwcGxlIFJvb3QgQ0Ew 169 | HhcNMDYwNDI1MjE0MDM2WhcNMzUwMjA5MjE0MDM2WjBiMQswCQYDVQQGEwJVUzET 170 | MBEGA1UEChMKQXBwbGUgSW5jLjEmMCQGA1UECxMdQXBwbGUgQ2VydGlmaWNhdGlv 171 | biBBdXRob3JpdHkxFjAUBgNVBAMTDUFwcGxlIFJvb3QgQ0EwggEiMA0GCSqGSIb3 172 | DQEBAQUAA4IBDwAwggEKAoIBAQDkkakJH5HbHkdQ6wXtXnmELes2oldMVeyLGYne 173 | +Uts9QerIjAC6Bg++FAJ039BqJj50cpmnCRrEdCju+QbKsMflZ56DKRHi1vUFjcz 174 | y8QPTc4UadHJGXL1XQ7Vf1+b8iUDulWPTV0N8WQ1IxVLFVkds5T39pyez1C6wVhQ 175 | Z48ItCD3y6wsIG9wtj8BMIy3Q88PnT3zK0koGsj+zrW5DtleHNbLPbU6rfQPDgCS 176 | C7EhFi501TwN22IWq6NxkkdTVcGvL0Gz+PvjcM3mo0xFfh9Ma1CWQYnEdGILEINB 177 | hzOKgbEwWOxaBDKMaLOPHd5lc/9nXmW8Sdh2nzMUZaF3lMktAgMBAAGjggF6MIIB 178 | djAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUK9Bp 179 | R5R2Cf70a40uQKb3R01/CF4wHwYDVR0jBBgwFoAUK9BpR5R2Cf70a40uQKb3R01/ 180 | CF4wggERBgNVHSAEggEIMIIBBDCCAQAGCSqGSIb3Y2QFATCB8jAqBggrBgEFBQcC 181 | ARYeaHR0cHM6Ly93d3cuYXBwbGUuY29tL2FwcGxlY2EvMIHDBggrBgEFBQcCAjCB 182 | thqBs1JlbGlhbmNlIG9uIHRoaXMgY2VydGlmaWNhdGUgYnkgYW55IHBhcnR5IGFz 183 | c3VtZXMgYWNjZXB0YW5jZSBvZiB0aGUgdGhlbiBhcHBsaWNhYmxlIHN0YW5kYXJk 184 | IHRlcm1zIGFuZCBjb25kaXRpb25zIG9mIHVzZSwgY2VydGlmaWNhdGUgcG9saWN5 185 | IGFuZCBjZXJ0aWZpY2F0aW9uIHByYWN0aWNlIHN0YXRlbWVudHMuMA0GCSqGSIb3 186 | DQEBBQUAA4IBAQBcNplMLXi37Yyb3PN3m/J20ncwT8EfhYOFG5k9RzfyqZtAjizU 187 | sZAS2L70c5vu0mQPy3lPNNiiPvl4/2vIB+x9OYOLUyDTOMSxv5pPCmv/K/xZpwUJ 188 | fBdAVhEedNO3iyM7R6PVbyTi69G3cN8PReEnyvFteO3ntRcXqNx+IjXKJdXZD9Zr 189 | 1KIkIxH3oayPc4FgxhtbCS+SsvhESPBgOJ4V9T0mZyCKM2r3DYLP3uujL/lTaltk 190 | wGMzd/c6ByxW69oPIQ7aunMZT7XZNn/Bh1XZp5m5MkL72NVxnn6hUrcbvZNCJBIq 191 | xw8dtk2cXmPIS4AXUKqK1drk/NAJBzewdXUhMYIByzCCAccCAQEwgaMwgZYxCzAJ 192 | BgNVBAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBX 193 | b3JsZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29y 194 | bGR3aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3Jp 195 | dHkCCA7rV4fnngmNMAkGBSsOAwIaBQAwDQYJKoZIhvcNAQEBBQAEggEAasPtnide 196 | NWyfUtewW9OSgcQA8pW+5tWMR0469cBPZR84uJa0gyfmPspySvbNOAwnrwzZHYLa 197 | ujOxZLip4DUw4F5s3QwUa3y4BXpF4J+NSn9XNvxNtnT/GcEQtCuFwgJ0o3F0ilhv 198 | MTHrwiwyx/vr+uNDqlORK8lfK+1qNp+A/kzh8eszMrn4JSeTh9ZYxLHE56WkTQGD 199 | VZXl0gKgxSOmDrcp1eQxdlymzrPv9U60wUJ0bkPfrU9qZj3mJrmrkQk61JTe3j6/ 200 | QfjfFBG9JG2mUmYQP1KQ3SypGHzDW8vngvsGu//tNU0NFfOqQu4bYU4VpQl0nPtD 201 | 4B85NkrgvQsWAQ== 202 | -----END PKCS7-----` 203 | 204 | func TestVerifyApkEcdsa(t *testing.T) { 205 | fixture := UnmarshalTestFixture(ApkEcdsaFixture) 206 | p7, err := Parse(fixture.Input) 207 | if err != nil { 208 | t.Errorf("Parse encountered unexpected error: %v", err) 209 | } 210 | p7.Content, err = base64.StdEncoding.DecodeString(ApkEcdsaContent) 211 | if err != nil { 212 | t.Errorf("Failed to decode base64 signature file: %v", err) 213 | } 214 | if err := p7.Verify(); err != nil { 215 | t.Errorf("Verify failed with error: %v", err) 216 | } 217 | } 218 | 219 | var ApkEcdsaFixture = `-----BEGIN PKCS7----- 220 | MIIDAgYJKoZIhvcNAQcCoIIC8zCCAu8CAQExDzANBglghkgBZQMEAgMFADALBgkq 221 | hkiG9w0BBwGgggH3MIIB8zCCAVSgAwIBAgIJAOxXdFsvm3YiMAoGCCqGSM49BAME 222 | MBIxEDAOBgNVBAMMB2VjLXA1MjEwHhcNMTYwMzMxMTUzMTIyWhcNNDMwODE3MTUz 223 | MTIyWjASMRAwDgYDVQQDDAdlYy1wNTIxMIGbMBAGByqGSM49AgEGBSuBBAAjA4GG 224 | AAQAYX95sSjPEQqgyLD04tNUyq9y/w8seblOpfqa/Amx6H4GFdrjGXX0YTfXKr9G 225 | hAyIyQSnNrIg0zDlWQUbBPRW4CYBLFOg1pUn1NBhKFD4NtO1KWvYtNOYDegFjRCP 226 | B0p+fEXDbq8QFDYvlh+NZUJ16+ih8XNIf1C29xuLEqN6oKOnAvajUDBOMB0GA1Ud 227 | DgQWBBT/Ra3kz60gQ7tYk3byZckcLabt8TAfBgNVHSMEGDAWgBT/Ra3kz60gQ7tY 228 | k3byZckcLabt8TAMBgNVHRMEBTADAQH/MAoGCCqGSM49BAMEA4GMADCBiAJCAP39 229 | hYLsWk2H84oEw+HJqGGjexhqeD3vSO1mWhopripE/81oy3yV10puYoJe11xDSfcD 230 | j2VfNCHazuXO3kSxGA/1AkIBLUJxp/WYbYzhBGKr6lcxczKI/wuMfkZ6vL+0EMJV 231 | A/2uEoeqvnl7BsdkicyaOBNEADijuVdaPPIWzKClt9OaVxExgdAwgc0CAQEwHzAS 232 | MRAwDgYDVQQDDAdlYy1wNTIxAgkA7Fd0Wy+bdiIwDQYJYIZIAWUDBAIDBQAwCgYI 233 | KoZIzj0EAwQEgYswgYgCQgD1pVSNo7qTm9A6tpt3SU2yRa+xpJAnUbpZ+Gu36B71 234 | JnQBUzRgTGevniqHpyagi7b2zjWh1uvfz9FfrITUwGMddgJCAPjiBRcl7rKpxmZn 235 | V1MvcJOX41xRSJu1wmBiYXqaJarL+gQ/Wl7RYsMtqLjmNColvLaHNxCaWOO/8nAE 236 | Hg0OMA60 237 | -----END PKCS7-----` 238 | 239 | var ApkEcdsaContent = `U2lnbmF0dXJlLVZlcnNpb246IDEuMA0KU0hBLTUxMi1EaWdlc3QtTWFuaWZlc3Q6IFAvVDRqSWtTMjQvNzFxeFE2WW1MeEtNdkRPUUF0WjUxR090dFRzUU9yemhHRQ0KIEpaUGVpWUtyUzZYY090bStYaWlFVC9uS2tYdWVtUVBwZ2RBRzFKUzFnPT0NCkNyZWF0ZWQtQnk6IDEuMCAoQW5kcm9pZCBTaWduQXBrKQ0KDQpOYW1lOiBBbmRyb2lkTWFuaWZlc3QueG1sDQpTSEEtNTEyLURpZ2VzdDogcm9NbWVQZmllYUNQSjFJK2VzMVpsYis0anB2UXowNHZqRWVpL2U0dkN1ald0VVVWSHEzMkNXDQogMUxsOHZiZGMzMCtRc1FlN29ibld4dmhLdXN2K3c1a2c9PQ0KDQpOYW1lOiByZXNvdXJjZXMuYXJzYw0KU0hBLTUxMi1EaWdlc3Q6IG5aYW1aUzlPZTRBRW41cEZaaCtoQ1JFT3krb1N6a3hHdU5YZU0wUFF6WGVBVlVQV3hSVzFPYQ0KIGVLbThRbXdmTmhhaS9HOEcwRUhIbHZEQWdlcy9HUGtBPT0NCg0KTmFtZTogY2xhc3Nlcy5kZXgNClNIQS01MTItRGlnZXN0OiBlbWlDQld2bkVSb0g2N2lCa3EwcUgrdm5tMkpaZDlMWUNEV051N3RNYzJ3bTRtV0dYSUVpWmcNCiBWZkVPV083MFRlZnFjUVhldkNtN2hQMnRpT0U3Y0w5UT09DQoNCg==` 240 | 241 | func TestVerifyFirefoxAddon(t *testing.T) { 242 | fixture := UnmarshalTestFixture(FirefoxAddonFixture) 243 | p7, err := Parse(fixture.Input) 244 | if err != nil { 245 | t.Errorf("Parse encountered unexpected error: %v", err) 246 | } 247 | p7.Content = FirefoxAddonContent 248 | certPool := x509.NewCertPool() 249 | certPool.AppendCertsFromPEM(FirefoxAddonRootCert) 250 | // verifies at the signingTime authenticated attr 251 | if err := p7.VerifyWithChain(certPool); err != nil { 252 | t.Errorf("Verify failed with error: %v", err) 253 | } 254 | 255 | // The chain has validity: 256 | // 257 | // EE: 2016-08-17 20:04:58 +0000 UTC 2021-08-16 20:04:58 +0000 UTC 258 | // Intermediate: 2015-03-17 23:52:42 +0000 UTC 2025-03-14 23:52:42 +0000 UTC 259 | // Root: 2015-03-17 22:53:57 +0000 UTC 2025-03-14 22:53:57 +0000 UTC 260 | validTime := time.Date(2021, 8, 16, 20, 0, 0, 0, time.UTC) 261 | if err = p7.VerifyWithChainAtTime(certPool, validTime); err != nil { 262 | t.Errorf("Verify at UTC now failed with error: %v", err) 263 | } 264 | 265 | expiredTime := time.Date(2030, time.January, 1, 0, 0, 0, 0, time.UTC) 266 | if err = p7.VerifyWithChainAtTime(certPool, expiredTime); err == nil { 267 | t.Errorf("Verify at expired time %s did not error", expiredTime) 268 | } 269 | notYetValidTime := time.Date(1999, time.July, 5, 0, 13, 0, 0, time.UTC) 270 | if err = p7.VerifyWithChainAtTime(certPool, notYetValidTime); err == nil { 271 | t.Errorf("Verify at not yet valid time %s did not error", notYetValidTime) 272 | } 273 | 274 | // Verify the certificate chain to make sure the identified root 275 | // is the one we expect 276 | ee := getCertFromCertsByIssuerAndSerial(p7.Certificates, p7.Signers[0].IssuerAndSerialNumber) 277 | if ee == nil { 278 | t.Errorf("No end-entity certificate found for signer") 279 | } 280 | signingTime := mustParseTime("2017-02-23T09:06:16-05:00") 281 | chains, err := verifyCertChain(ee, p7.Certificates, certPool, signingTime) 282 | if err != nil { 283 | t.Error(err) 284 | } 285 | if len(chains) != 1 { 286 | t.Errorf("Expected to find one chain, but found %d", len(chains)) 287 | } 288 | if len(chains[0]) != 3 { 289 | t.Errorf("Expected to find three certificates in chain, but found %d", len(chains[0])) 290 | } 291 | if chains[0][0].Subject.CommonName != "tabscope@xuldev.org" { 292 | t.Errorf("Expected to find EE certificate with subject 'tabscope@xuldev.org', but found '%s'", chains[0][0].Subject.CommonName) 293 | } 294 | if chains[0][1].Subject.CommonName != "production-signing-ca.addons.mozilla.org" { 295 | t.Errorf("Expected to find intermediate certificate with subject 'production-signing-ca.addons.mozilla.org', but found '%s'", chains[0][1].Subject.CommonName) 296 | } 297 | if chains[0][2].Subject.CommonName != "root-ca-production-amo" { 298 | t.Errorf("Expected to find root certificate with subject 'root-ca-production-amo', but found '%s'", chains[0][2].Subject.CommonName) 299 | } 300 | } 301 | 302 | func mustParseTime(s string) time.Time { 303 | t, err := time.Parse(time.RFC3339, s) 304 | if err != nil { 305 | panic(err) 306 | } 307 | return t 308 | } 309 | 310 | var FirefoxAddonContent = []byte(`Signature-Version: 1.0 311 | MD5-Digest-Manifest: KjRavc6/KNpuT1QLcB/Gsg== 312 | SHA1-Digest-Manifest: 5Md5nUg+U7hQ/UfzV+xGKWOruVI= 313 | 314 | `) 315 | 316 | var FirefoxAddonFixture = ` 317 | -----BEGIN PKCS7----- 318 | MIIQTAYJKoZIhvcNAQcCoIIQPTCCEDkCAQExCzAJBgUrDgMCGgUAMAsGCSqGSIb3 319 | DQEHAaCCDL0wggW6MIIDoqADAgECAgYBVpobWVwwDQYJKoZIhvcNAQELBQAwgcUx 320 | CzAJBgNVBAYTAlVTMRwwGgYDVQQKExNNb3ppbGxhIENvcnBvcmF0aW9uMS8wLQYD 321 | VQQLEyZNb3ppbGxhIEFNTyBQcm9kdWN0aW9uIFNpZ25pbmcgU2VydmljZTExMC8G 322 | A1UEAxMocHJvZHVjdGlvbi1zaWduaW5nLWNhLmFkZG9ucy5tb3ppbGxhLm9yZzE0 323 | MDIGCSqGSIb3DQEJARYlc2VydmljZXMtb3BzK2FkZG9uc2lnbmluZ0Btb3ppbGxh 324 | LmNvbTAeFw0xNjA4MTcyMDA0NThaFw0yMTA4MTYyMDA0NThaMHYxEzARBgNVBAsT 325 | ClByb2R1Y3Rpb24xCzAJBgNVBAYTAlVTMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3 326 | MQ8wDQYDVQQKEwZBZGRvbnMxCzAJBgNVBAgTAkNBMRwwGgYDVQQDFBN0YWJzY29w 327 | ZUB4dWxkZXYub3JnMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAv6e0 328 | mPD8dt4J8HTNNq4ODns2DV6Weh1hllCIFvOeu1u3UrR03st0BMY8OXYwr/NvRVjg 329 | bA8gRySWAL+XqLzbhtXNeNegAoxrF+3mYY5rJjsLj/FGI6P6OXjngqwgm9VTBl7m 330 | jh/KXBSwYoUcavJo6cmk8sCFwoblyQiv+tsWaUCOI6zMzubNtIS+GFvET9y/VZMP 331 | j6mk8O10wBgJF5MMtA19va3qXy7aCZ7DnZp1l3equd/L6t324TtXoqx6xWQKo6TM 332 | I0mcTlKvm6TKegTGBCyGn3JRARoIJv4AW1qqgyaHXf9EoY2pKT8Avkri5++NuSJ6 333 | jtO4k/diBA2MZU20U0KGffYZNTxKDqd6XtI6y1tJPd/OWRFyU+mHntkcm9sar7L3 334 | nPKujHRox2re10ec1WBnJE3PjlAoesNjxzp+xs2mGGc8DX9NuWn+1uK9xmgGIIMl 335 | OFfyQ4s0G6hKp5goFcrFZxmexu0ZahOs8vZf8xDBW7yR1zToQElOXHvrscM386os 336 | kOF9IxQZfcCoPuNQVg1haCONNkx0oau3RQQlOSAZtC79b+rBjQ5JYfjRLYAworf2 337 | xQaprCh33TD1dTBrvzEbCGszgkN53Vqh5TFBjbU/NyldOkGvK8Xf6WhT5u+aftnV 338 | lbuE2McAg6x1AlloUZq6PNTBpz7zypcIISnQ+y8CAwEAATANBgkqhkiG9w0BAQsF 339 | AAOCAgEAIBoo2+OEYNCgP/IbUj9azaf/lde1q4AK/uTMoUeS5WcrXd8aqA0Y1qV7 340 | xUALgDQAExXgqcOMGu4mPMaoZDgwGI4Tj7XPJQq5Z5zYxpRf/Wtzae33T9BF6QPW 341 | v5xiRYuol+FbEtqRHZqxDWtIrd1MWBy3wjO3pLPdzDM9jWh+HLxdGWThJszaZp3T 342 | CqsOx+l9W0Q7qM5ioZpHStgXDfhw38Lg++kLnzcX9MqsjYyezdwE4krqW6hK3+4S 343 | 0LZE4dTgsy8JULkyAF3HrPWEXESnD7c4mx6owZe+BNDK5hsVM/obAqH7sJq/igbM 344 | 5N1l832p/ws8l5xKOr3qBWSzWn6u7ExvqG6Ckh0foJOVXvzGqvrXcoiBGV8S9Z7c 345 | DghUvMt6b0pZ0ildRCHfTUz7eG3g4MhfbjupR7b+L9FWEJhcd/H0dxpw7SKYha/n 346 | ePuRL7MXmbW8WLMqO/ImxzL8TPOB3pUg3nITfubV6gpPBmn+0nwbqYUmggJuwgvK 347 | I2GpN2Ny6EErZy17EEgyhJygJZMj+UzQjC781xxsl3ljpYEqqwgRLIZBSBUD5dXj 348 | XBuU24w162SeSyHZzkBbuv6lr52pqoZyFrG29DCHECgO9ZmNWgSpiWSkh+vExAG7 349 | wNs0y61t2HUG+BCMGPQ9sOzouyTfrnLVAWwzswGftFYQfoIBeJIwggb7MIIE46AD 350 | AgECAgMQAAIwDQYJKoZIhvcNAQEMBQAwfTELMAkGA1UEBhMCVVMxHDAaBgNVBAoT 351 | E01vemlsbGEgQ29ycG9yYXRpb24xLzAtBgNVBAsTJk1vemlsbGEgQU1PIFByb2R1 352 | Y3Rpb24gU2lnbmluZyBTZXJ2aWNlMR8wHQYDVQQDExZyb290LWNhLXByb2R1Y3Rp 353 | b24tYW1vMB4XDTE1MDMxNzIzNTI0MloXDTI1MDMxNDIzNTI0MlowgcUxCzAJBgNV 354 | BAYTAlVTMRwwGgYDVQQKExNNb3ppbGxhIENvcnBvcmF0aW9uMS8wLQYDVQQLEyZN 355 | b3ppbGxhIEFNTyBQcm9kdWN0aW9uIFNpZ25pbmcgU2VydmljZTExMC8GA1UEAxMo 356 | cHJvZHVjdGlvbi1zaWduaW5nLWNhLmFkZG9ucy5tb3ppbGxhLm9yZzE0MDIGCSqG 357 | SIb3DQEJARYlc2VydmljZXMtb3BzK2FkZG9uc2lnbmluZ0Btb3ppbGxhLmNvbTCC 358 | AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMLMM9m2HBLhCiO9mhljpehT 359 | hxpzlCnxluzDZ51I/H7MvBbIvZBm9zSpHdffubSsak2qYE69d+ebTa/CK83WIosM 360 | 24/2Qp7n/GGaPJcCC4Y3JkrCsgA8+wV2MbFlKSv+qMdvI/sE3BPYDMCjVPMhHmIP 361 | XaPWd42OoHpI8R3GGUtVnR3Hm76pa2+v6TwgeMiO8om+ogGufiyv6FNMZ5NuY1Z9 362 | aLNEvehnAzSfddQyki+6FJd7XkgZbP7pb1Kl8yYgiy4piBerJ9H09uPehffE3Ell 363 | 3cApQL3+0kjaUX4scMjuNQDMKziRZkYgJAM+qA9WA5Jn77AjerQBWQeEev1PWHYh 364 | 0IDlgS/a0bjKmVjNZYG6adrY/R5/whzWGFCIE1UfhPm6PdN0557qvF838C2RFHsI 365 | KzV6KQf0chMjpa02tPaIctjVhnDQZZNKm2ZfLOt9kQ57Is/e6KxH7pYMit46+s99 366 | lYM7ZquvWbK19b1Ili/6S1BxSzd3wztgfN5jGsc+jCCYLm+AcVtfNKc8cFZHXKrB 367 | CwhGmdbWDSBCicZNA7FKJpO3oIx26VPF2XUldA/T5Mh/POGLilK3t9m9qbjEyDp1 368 | EwoBToOR/aMrdnNYvSWp0g/GHMzSfJjjXyAqrZY2itam/IJd8r9FoRAzevPt/zTX 369 | BET3INoiCDGRH0XrxUYtAgMGVTejggE5MIIBNTAMBgNVHRMEBTADAQH/MA4GA1Ud 370 | DwEB/wQEAwIBBjAWBgNVHSUBAf8EDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUdHxf 371 | FKXipZLjs20GqIUdNkQXH4gwgagGA1UdIwSBoDCBnYAUs7zqWHSr4W54KrKrnCMe 372 | qGMsl7ehgYGkfzB9MQswCQYDVQQGEwJVUzEcMBoGA1UEChMTTW96aWxsYSBDb3Jw 373 | b3JhdGlvbjEvMC0GA1UECxMmTW96aWxsYSBBTU8gUHJvZHVjdGlvbiBTaWduaW5n 374 | IFNlcnZpY2UxHzAdBgNVBAMTFnJvb3QtY2EtcHJvZHVjdGlvbi1hbW+CAQEwMwYJ 375 | YIZIAYb4QgEEBCYWJGh0dHA6Ly9hZGRvbnMubW96aWxsYS5vcmcvY2EvY3JsLnBl 376 | bTANBgkqhkiG9w0BAQwFAAOCAgEArde/fdjb7TE0eH7Ij7xU4JbcSyhY3cQhVYCw 377 | Fg+Q/2pj+NAfazcjUuLWA0Y/YZs9HOx6j+ZAqO4C/xfMP4RDs9IypxvzHDU6SXgD 378 | RK6uOKtS07HXLcXgFUBvJEQhbT/h5+IQOA4/GcpCshfD6iyiBBi+IocR+tnKPCuZ 379 | T3m1t60Eja/MkPKG/Gx8vSodHvlTTsJ2GzjUEANveCZOnlAdp9fjTvFZny9qqnbg 380 | sfVbuTqKndbCFW5QLXfkna6jBqMrY0+CpMYY2oJ5gwpHbE/7hhukjxGCTcpv7r/O 381 | M53bb/DZnybDlLLepacljvz7DBA1O1FFtEhf9MR+vyvmBpniAyKQhqG2hsVGurE1 382 | nBcE+oteZWar2lMp6+etDAb9DRC+jZv0aEQs2o/qQwyD8AGquLgBsJq5Jz3gGxzn 383 | 4r3vGu2lV8VdzIm0C8sOFSWTmTZxQmJbF8xSsQBojnsvEah4DPER+eAt6qKolaWe 384 | s4drJQjzFyC7HJn2VqalpCwbe9CdMB7eRqzeP6GujJBi80/gx0pAysUtuKKpH5IJ 385 | WbXAOszfrjb3CaHafYZDnwPoOfj74ogFzjt2f54jwnU+ET/byfjZ7J8SLH316C1V 386 | HrvFXcTzyMV4aRluVPjPg9x1G58hMIbeuT4GpwQUNdJ9uL8t65v0XwG2t6Y7jpRO 387 | sFVxBtgxggNXMIIDUwIBATCB0DCBxTELMAkGA1UEBhMCVVMxHDAaBgNVBAoTE01v 388 | emlsbGEgQ29ycG9yYXRpb24xLzAtBgNVBAsTJk1vemlsbGEgQU1PIFByb2R1Y3Rp 389 | b24gU2lnbmluZyBTZXJ2aWNlMTEwLwYDVQQDEyhwcm9kdWN0aW9uLXNpZ25pbmct 390 | Y2EuYWRkb25zLm1vemlsbGEub3JnMTQwMgYJKoZIhvcNAQkBFiVzZXJ2aWNlcy1v 391 | cHMrYWRkb25zaWduaW5nQG1vemlsbGEuY29tAgYBVpobWVwwCQYFKw4DAhoFAKBd 392 | MBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTE2MDgx 393 | NzIwMDQ1OFowIwYJKoZIhvcNAQkEMRYEFAxlGvNFSx+Jqj70haE8b7UZk+2GMA0G 394 | CSqGSIb3DQEBAQUABIICADsDlrucYRgwq9o2QSsO6X6cRa5Zu6w+1n07PTIyc1zn 395 | Pi1cgkkWZ0kZBHDrJ5CY33yRQPl6I1tHXaq7SkOSdOppKhpUmBiKZxQRAZR21QHk 396 | R3v1XS+st/o0N+0btv3YoplUifLIwtH89oolxqlStChELu7FuOBretdhx/z12ytA 397 | EhIIS53o/XjDL7XKJbQA02vzOtOC/Eq6p8BI7F3y6pvtmJIRkeGv+u6ssJa6g5q8 398 | 74w8hHXaH94Z9+hDPqjNWlsXJHgPdAKiEjzDz9oLkvDyX4Pd8JMK5ILskirpG+hj 399 | Q8jkTc5oYwyuSlBAUTGxW6ZbuOrtfVZvOVtRL/ixuiFiVlJ+JOQOxrtK19ukamsI 400 | iacFlbLgiA7w0HCtm2DsT9aL67/1e4rJ0lv0MjnQYUMmKQy7g0Gd3+nQPU9pn+Lf 401 | Z/UmSNWiJ8Csc/seDMyzT6jrzcGPfoSVaUowH0wGrI9If1snwcr+mMg7dWRGf1fm 402 | y/dcVSzed0ax4LqDmike1EshU+51cKWWlnhyNHK4KH+0fNsBQ0c6clrFpGx9MPmV 403 | YXie6C+LWkh5x12RU0sJt/SmSZV6q9VliIkX+yY3jBrC/pKgRahtcIyq46Da1E6K 404 | lc15Euur3NfGow+nott0Z8XutpYdK/2vBKcIh9JOdkd+oe6pcIP6hnhHRp53wqmG 405 | -----END PKCS7-----` 406 | 407 | var FirefoxAddonRootCert = []byte(` 408 | -----BEGIN CERTIFICATE----- 409 | MIIGYTCCBEmgAwIBAgIBATANBgkqhkiG9w0BAQwFADB9MQswCQYDVQQGEwJVUzEc 410 | MBoGA1UEChMTTW96aWxsYSBDb3Jwb3JhdGlvbjEvMC0GA1UECxMmTW96aWxsYSBB 411 | TU8gUHJvZHVjdGlvbiBTaWduaW5nIFNlcnZpY2UxHzAdBgNVBAMTFnJvb3QtY2Et 412 | cHJvZHVjdGlvbi1hbW8wHhcNMTUwMzE3MjI1MzU3WhcNMjUwMzE0MjI1MzU3WjB9 413 | MQswCQYDVQQGEwJVUzEcMBoGA1UEChMTTW96aWxsYSBDb3Jwb3JhdGlvbjEvMC0G 414 | A1UECxMmTW96aWxsYSBBTU8gUHJvZHVjdGlvbiBTaWduaW5nIFNlcnZpY2UxHzAd 415 | BgNVBAMTFnJvb3QtY2EtcHJvZHVjdGlvbi1hbW8wggIgMA0GCSqGSIb3DQEBAQUA 416 | A4ICDQAwggIIAoICAQC0u2HXXbrwy36+MPeKf5jgoASMfMNz7mJWBecJgvlTf4hH 417 | JbLzMPsIUauzI9GEpLfHdZ6wzSyFOb4AM+D1mxAWhuZJ3MDAJOf3B1Rs6QorHrl8 418 | qqlNtPGqepnpNJcLo7JsSqqE3NUm72MgqIHRgTRsqUs+7LIPGe7262U+N/T0LPYV 419 | Le4rZ2RDHoaZhYY7a9+49mHOI/g2YFB+9yZjE+XdplT2kBgA4P8db7i7I0tIi4b0 420 | B0N6y9MhL+CRZJyxdFe2wBykJX14LsheKsM1azHjZO56SKNrW8VAJTLkpRxCmsiT 421 | r08fnPyDKmaeZ0BtsugicdipcZpXriIGmsZbI12q5yuwjSELdkDV6Uajo2n+2ws5 422 | uXrP342X71WiWhC/dF5dz1LKtjBdmUkxaQMOP/uhtXEKBrZo1ounDRQx1j7+SkQ4 423 | BEwjB3SEtr7XDWGOcOIkoJZWPACfBLC3PJCBWjTAyBlud0C5n3Cy9regAAnOIqI1 424 | t16GU2laRh7elJ7gPRNgQgwLXeZcFxw6wvyiEcmCjOEQ6PM8UQjthOsKlszMhlKw 425 | vjyOGDoztkqSBy/v+Asx7OW2Q7rlVfKarL0mREZdSMfoy3zTgtMVCM0vhNl6zcvf 426 | 5HNNopoEdg5yuXo2chZ1p1J+q86b0G5yJRMeT2+iOVY2EQ37tHrqUURncCy4uwIB 427 | A6OB7TCB6jAMBgNVHRMEBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAWBgNVHSUBAf8E 428 | DDAKBggrBgEFBQcDAzCBkgYDVR0jBIGKMIGHoYGBpH8wfTELMAkGA1UEBhMCVVMx 429 | HDAaBgNVBAoTE01vemlsbGEgQ29ycG9yYXRpb24xLzAtBgNVBAsTJk1vemlsbGEg 430 | QU1PIFByb2R1Y3Rpb24gU2lnbmluZyBTZXJ2aWNlMR8wHQYDVQQDExZyb290LWNh 431 | LXByb2R1Y3Rpb24tYW1vggEBMB0GA1UdDgQWBBSzvOpYdKvhbngqsqucIx6oYyyX 432 | tzANBgkqhkiG9w0BAQwFAAOCAgEAaNSRYAaECAePQFyfk12kl8UPLh8hBNidP2H6 433 | KT6O0vCVBjxmMrwr8Aqz6NL+TgdPmGRPDDLPDpDJTdWzdj7khAjxqWYhutACTew5 434 | eWEaAzyErbKQl+duKvtThhV2p6F6YHJ2vutu4KIciOMKB8dslIqIQr90IX2Usljq 435 | 8Ttdyf+GhUmazqLtoB0GOuESEqT4unX6X7vSGu1oLV20t7t5eCnMMYD67ZBn0YIU 436 | /cm/+pan66hHrja+NeDGF8wabJxdqKItCS3p3GN1zUGuJKrLykxqbOp/21byAGog 437 | Z1amhz6NHUcfE6jki7sM7LHjPostU5ZWs3PEfVVgha9fZUhOrIDsyXEpCWVa3481 438 | LlAq3GiUMKZ5DVRh9/Nvm4NwrTfB3QkQQJCwfXvO9pwnPKtISYkZUqhEqvXk5nBg 439 | QCkDSLDjXTx39naBBGIVIqBtKKuVTla9enngdq692xX/CgO6QJVrwpqdGjebj5P8 440 | 5fNZPABzTezG3Uls5Vp+4iIWVAEDkK23cUj3c/HhE+Oo7kxfUeu5Y1ZV3qr61+6t 441 | ZARKjbu1TuYQHf0fs+GwID8zeLc2zJL7UzcHFwwQ6Nda9OJN4uPAuC/BKaIpxCLL 442 | 26b24/tRam4SJjqpiq20lynhUrmTtt6hbG3E1Hpy3bmkt2DYnuMFwEx2gfXNcnbT 443 | wNuvFqc= 444 | -----END CERTIFICATE-----`) 445 | 446 | // sign a document with openssl and verify the signature with pkcs7. 447 | // this uses a chain of root, intermediate and signer cert, where the 448 | // intermediate is added to the certs but the root isn't. 449 | func TestSignWithOpenSSLAndVerify(t *testing.T) { 450 | content := []byte(` 451 | A ship in port is safe, 452 | but that's not what ships are built for. 453 | -- Grace Hopper`) 454 | // write the content to a temp file 455 | tmpContentFile, err := ioutil.TempFile("", "TestSignWithOpenSSLAndVerify_content") 456 | if err != nil { 457 | t.Fatal(err) 458 | } 459 | ioutil.WriteFile(tmpContentFile.Name(), content, 0755) 460 | sigalgs := []x509.SignatureAlgorithm{ 461 | x509.SHA1WithRSA, 462 | x509.SHA256WithRSA, 463 | x509.SHA512WithRSA, 464 | x509.ECDSAWithSHA1, 465 | x509.ECDSAWithSHA256, 466 | x509.ECDSAWithSHA384, 467 | x509.ECDSAWithSHA512, 468 | } 469 | for _, sigalgroot := range sigalgs { 470 | rootCert, err := createTestCertificateByIssuer("PKCS7 Test Root CA", nil, sigalgroot, true) 471 | if err != nil { 472 | t.Fatalf("test %s: cannot generate root cert: %s", sigalgroot, err) 473 | } 474 | truststore := x509.NewCertPool() 475 | truststore.AddCert(rootCert.Certificate) 476 | for _, sigalginter := range sigalgs { 477 | interCert, err := createTestCertificateByIssuer("PKCS7 Test Intermediate Cert", rootCert, sigalginter, true) 478 | if err != nil { 479 | t.Fatalf("test %s/%s: cannot generate intermediate cert: %s", sigalgroot, sigalginter, err) 480 | } 481 | // write the intermediate cert to a temp file 482 | tmpInterCertFile, err := ioutil.TempFile("", "TestSignWithOpenSSLAndVerify_intermediate") 483 | if err != nil { 484 | t.Fatal(err) 485 | } 486 | fd, err := os.OpenFile(tmpInterCertFile.Name(), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755) 487 | if err != nil { 488 | t.Fatal(err) 489 | } 490 | pem.Encode(fd, &pem.Block{Type: "CERTIFICATE", Bytes: interCert.Certificate.Raw}) 491 | fd.Close() 492 | for _, sigalgsigner := range sigalgs { 493 | signerCert, err := createTestCertificateByIssuer("PKCS7 Test Signer Cert", interCert, sigalgsigner, false) 494 | if err != nil { 495 | t.Fatalf("test %s/%s/%s: cannot generate signer cert: %s", sigalgroot, sigalginter, sigalgsigner, err) 496 | } 497 | 498 | // write the signer cert to a temp file 499 | tmpSignerCertFile, err := ioutil.TempFile("", "TestSignWithOpenSSLAndVerify_signer") 500 | if err != nil { 501 | t.Fatal(err) 502 | } 503 | fd, err = os.OpenFile(tmpSignerCertFile.Name(), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755) 504 | if err != nil { 505 | t.Fatal(err) 506 | } 507 | pem.Encode(fd, &pem.Block{Type: "CERTIFICATE", Bytes: signerCert.Certificate.Raw}) 508 | fd.Close() 509 | 510 | // write the signer key to a temp file 511 | tmpSignerKeyFile, err := ioutil.TempFile("", "TestSignWithOpenSSLAndVerify_key") 512 | if err != nil { 513 | t.Fatal(err) 514 | } 515 | fd, err = os.OpenFile(tmpSignerKeyFile.Name(), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755) 516 | if err != nil { 517 | t.Fatal(err) 518 | } 519 | var derKey []byte 520 | priv := *signerCert.PrivateKey 521 | switch priv := priv.(type) { 522 | case *rsa.PrivateKey: 523 | derKey = x509.MarshalPKCS1PrivateKey(priv) 524 | pem.Encode(fd, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: derKey}) 525 | case *ecdsa.PrivateKey: 526 | derKey, err = x509.MarshalECPrivateKey(priv) 527 | if err != nil { 528 | t.Fatal(err) 529 | } 530 | pem.Encode(fd, &pem.Block{Type: "EC PRIVATE KEY", Bytes: derKey}) 531 | } 532 | fd.Close() 533 | 534 | // write the root cert to a temp file 535 | tmpSignedFile, err := ioutil.TempFile("", "TestSignWithOpenSSLAndVerify_signature") 536 | if err != nil { 537 | t.Fatal(err) 538 | } 539 | // call openssl to sign the content 540 | opensslCMD := exec.Command("openssl", "smime", "-sign", "-nodetach", 541 | "-in", tmpContentFile.Name(), "-out", tmpSignedFile.Name(), 542 | "-signer", tmpSignerCertFile.Name(), "-inkey", tmpSignerKeyFile.Name(), 543 | "-certfile", tmpInterCertFile.Name(), "-outform", "PEM") 544 | out, err := opensslCMD.CombinedOutput() 545 | if err != nil { 546 | t.Fatalf("test %s/%s/%s: openssl command failed with %s: %s", sigalgroot, sigalginter, sigalgsigner, err, out) 547 | } 548 | 549 | // verify the signed content 550 | pemSignature, err := ioutil.ReadFile(tmpSignedFile.Name()) 551 | if err != nil { 552 | t.Fatal(err) 553 | } 554 | derBlock, _ := pem.Decode(pemSignature) 555 | if derBlock == nil { 556 | break 557 | } 558 | p7, err := Parse(derBlock.Bytes) 559 | if err != nil { 560 | t.Fatalf("Parse encountered unexpected error: %v", err) 561 | } 562 | if err := p7.VerifyWithChain(truststore); err != nil { 563 | t.Fatalf("Verify failed with error: %v", err) 564 | } 565 | // Verify the certificate chain to make sure the identified root 566 | // is the one we expect 567 | ee := getCertFromCertsByIssuerAndSerial(p7.Certificates, p7.Signers[0].IssuerAndSerialNumber) 568 | if ee == nil { 569 | t.Fatalf("No end-entity certificate found for signer") 570 | } 571 | chains, err := verifyCertChain(ee, p7.Certificates, truststore, time.Now()) 572 | if err != nil { 573 | t.Fatal(err) 574 | } 575 | if len(chains) != 1 { 576 | t.Fatalf("Expected to find one chain, but found %d", len(chains)) 577 | } 578 | if len(chains[0]) != 3 { 579 | t.Fatalf("Expected to find three certificates in chain, but found %d", len(chains[0])) 580 | } 581 | if chains[0][0].Subject.CommonName != "PKCS7 Test Signer Cert" { 582 | t.Fatalf("Expected to find EE certificate with subject 'PKCS7 Test Signer Cert', but found '%s'", chains[0][0].Subject.CommonName) 583 | } 584 | if chains[0][1].Subject.CommonName != "PKCS7 Test Intermediate Cert" { 585 | t.Fatalf("Expected to find intermediate certificate with subject 'PKCS7 Test Intermediate Cert', but found '%s'", chains[0][1].Subject.CommonName) 586 | } 587 | if chains[0][2].Subject.CommonName != "PKCS7 Test Root CA" { 588 | t.Fatalf("Expected to find root certificate with subject 'PKCS7 Test Root CA', but found '%s'", chains[0][2].Subject.CommonName) 589 | } 590 | os.Remove(tmpSignerCertFile.Name()) // clean up 591 | os.Remove(tmpSignerKeyFile.Name()) // clean up 592 | } 593 | os.Remove(tmpInterCertFile.Name()) // clean up 594 | } 595 | } 596 | os.Remove(tmpContentFile.Name()) // clean up 597 | } 598 | --------------------------------------------------------------------------------