From e7f98c0e02b8ed63fca55e1fd83b1a36bbde788f Mon Sep 17 00:00:00 2001 From: Georg Koester Date: Thu, 14 Apr 2016 18:57:27 +0200 Subject: [PATCH 01/23] Fixed content truncation by looping through all packages and parsing each packge. The parsed result then gets concatenated. --- pkcs7.go | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/pkcs7.go b/pkcs7.go index c5ee084..977ed85 100644 --- a/pkcs7.go +++ b/pkcs7.go @@ -161,15 +161,31 @@ func parseSignedData(data []byte) (*PKCS7, error) { // The Content.Bytes maybe empty on PKI responses. if len(sd.ContentInfo.Content.Bytes) > 0 { + var totalBytes []byte + if _, err := asn1.Unmarshal(sd.ContentInfo.Content.Bytes, &compound); err != nil { return nil, err } } // Compound octet string if compound.IsCompound { - if _, err = asn1.Unmarshal(compound.Bytes, &content); err != nil { + var parsedContent []byte + rest, err := asn1.Unmarshal(compound.Bytes, &parsedContent) + if err != nil { return nil, err } + content = append(content, parsedContent...) + + //loop through all elements and concatenate all parsed byte slices to get the complete unsigned file. + for len(rest) != 0 { + var parsedContent []byte + + rest, err = asn1.Unmarshal(rest, &parsedContent) + if err != nil { + return nil, err + } + content = append(content, parsedContent...) + } } else { // assuming this is tag 04 content = compound.Bytes From 35fe2c3f9253e318de89d0bf343f7e8ed41c95df Mon Sep 17 00:00:00 2001 From: Georg Koester Date: Thu, 14 Apr 2016 18:59:34 +0200 Subject: [PATCH 02/23] Removed unnecessary variable declaration --- pkcs7.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/pkcs7.go b/pkcs7.go index 977ed85..cd70655 100644 --- a/pkcs7.go +++ b/pkcs7.go @@ -161,8 +161,6 @@ func parseSignedData(data []byte) (*PKCS7, error) { // The Content.Bytes maybe empty on PKI responses. if len(sd.ContentInfo.Content.Bytes) > 0 { - var totalBytes []byte - if _, err := asn1.Unmarshal(sd.ContentInfo.Content.Bytes, &compound); err != nil { return nil, err } From 6221838c4903cfb82971a1fd1a5b4fb9d1d05147 Mon Sep 17 00:00:00 2001 From: Timo Gatsonides Date: Sat, 30 Jul 2016 11:16:23 +0200 Subject: [PATCH 03/23] Bug fix and hack to allow unsigning certain files Most files could be unsigned, but some were failing. This commit successfully parses at least one example (received by email from Frank). --- ber.go | 2 +- pkcs7.go | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ber.go b/ber.go index 45924ba..2f380d8 100644 --- a/ber.go +++ b/ber.go @@ -212,7 +212,7 @@ func readObject(ber []byte, offset int) (asn1Object, int, error) { for offset < contentEnd { var subObj asn1Object var err error - subObj, offset, err = readObject(ber[:contentEnd], offset) + subObj, offset, err = readObject(ber[:contentEnd+hack], offset) if err != nil { return nil, 0, err } diff --git a/pkcs7.go b/pkcs7.go index cd70655..b255619 100644 --- a/pkcs7.go +++ b/pkcs7.go @@ -17,6 +17,7 @@ import ( "fmt" "math/big" "sort" + "strings" "time" _ "crypto/sha1" // for crypto.SHA1 @@ -180,6 +181,9 @@ func parseSignedData(data []byte) (*PKCS7, error) { rest, err = asn1.Unmarshal(rest, &parsedContent) if err != nil { + if strings.Contains(err.Error(), "tags don't match") && len(rest) == 0 { + break // there is an unexpected, but optional tag that should not result in an error + } return nil, err } content = append(content, parsedContent...) From 43ffad51ce7ca420730250ed32e6285551b85e19 Mon Sep 17 00:00:00 2001 From: Victor Date: Sun, 5 Jun 2016 13:43:14 -0400 Subject: [PATCH 04/23] Correctly marshal degenerate certificates (#7) Fixes an issue where more than one certificate would not be marshalled correctly. This is now tested against openssl * correctly marshal degenerate certificates * update DegenerateCertificates test * add marshalCertificateBytes function and test against openssl --- pkcs7.go | 28 ++++++++++++++++++++-------- pkcs7_test.go | 29 +++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 8 deletions(-) diff --git a/pkcs7.go b/pkcs7.go index b255619..07881b6 100644 --- a/pkcs7.go +++ b/pkcs7.go @@ -673,22 +673,34 @@ func marshalCertificates(certs []*x509.Certificate) rawCertificates { for _, cert := range certs { buf.Write(cert.Raw) } - // Even though, the tag & length are stripped out during marshalling the - // RawContent, we have to encode it into the RawContent. If its missing, - // then `asn1.Marshal()` will strip out the certificate wrapper instead. - var val = asn1.RawValue{Bytes: buf.Bytes(), Class: 2, Tag: 0, IsCompound: true} - b, _ := asn1.Marshal(val) - return rawCertificates{Raw: b} + rawCerts, _ := marshalCertificateBytes(buf.Bytes()) + return rawCerts +} + +// Even though, the tag & length are stripped out during marshalling the +// RawContent, we have to encode it into the RawContent. If its missing, +// then `asn1.Marshal()` will strip out the certificate wrapper instead. +func marshalCertificateBytes(certs []byte) (rawCertificates, error) { + var val = asn1.RawValue{Bytes: certs, Class: 2, Tag: 0, IsCompound: true} + b, err := asn1.Marshal(val) + if err != nil { + return rawCertificates{}, err + } + return rawCertificates{Raw: b}, nil } // DegenerateCertificate creates a signed data structure containing only the -// provided certificate +// provided certificate or certificate chain. func DegenerateCertificate(cert []byte) ([]byte, error) { + rawCert, err := marshalCertificateBytes(cert) + if err != nil { + return nil, err + } emptyContent := contentInfo{ContentType: oidData} sd := signedData{ Version: 1, ContentInfo: emptyContent, - Certificates: rawCertificates{Raw: cert}, + Certificates: rawCert, CRLs: []pkix.CertificateList{}, } content, err := asn1.Marshal(sd) diff --git a/pkcs7_test.go b/pkcs7_test.go index ad02c9d..c66f8fe 100644 --- a/pkcs7_test.go +++ b/pkcs7_test.go @@ -11,7 +11,10 @@ import ( "encoding/pem" "fmt" "io" + "io/ioutil" "math/big" + "os" + "os/exec" "testing" "time" ) @@ -70,9 +73,35 @@ func TestDegenerateCertificate(t *testing.T) { if err != nil { t.Fatal(err) } + testOpenSSLParse(t, deg) + fmt.Printf("=== BEGIN DEGENERATE CERT ===\n% X\n=== END DEGENERATE CERT ===\n", deg) } +// writes the cert to a temporary file and tests that openssl can read it. +func testOpenSSLParse(t *testing.T, certBytes []byte) { + tmpCertFile, err := ioutil.TempFile("", "testCertificate") + if err != nil { + t.Fatal(err) + } + defer os.Remove(tmpCertFile.Name()) // clean up + + if _, err := tmpCertFile.Write(certBytes); err != nil { + t.Fatal(err) + } + + opensslCMD := exec.Command("openssl", "pkcs7", "-inform", "der", "-in", tmpCertFile.Name()) + _, err = opensslCMD.Output() + if err != nil { + t.Fatal(err) + } + + if err := tmpCertFile.Close(); err != nil { + t.Fatal(err) + } + +} + func TestSign(t *testing.T) { cert, err := createTestCertificate() if err != nil { From 477c9f6bf473ac8155acfd3ae4851b2cfba06101 Mon Sep 17 00:00:00 2001 From: Stevie Hryciw Date: Tue, 19 Jul 2016 14:45:17 -0700 Subject: [PATCH 05/23] AES-128-GCM support in Encrypt() and Decrypt() (#8) * Added AES-128-GCM support to Encrypt() and Decrypt() --- pkcs7.go | 162 ++++++++++++++++++++++++++++++++++++++++++++++---- pkcs7_test.go | 45 ++++++++------ 2 files changed, 179 insertions(+), 28 deletions(-) diff --git a/pkcs7.go b/pkcs7.go index 07881b6..73227aa 100644 --- a/pkcs7.go +++ b/pkcs7.go @@ -321,7 +321,7 @@ func (p7 *PKCS7) GetOnlySigner() *x509.Certificate { } // ErrUnsupportedAlgorithm tells you when our quick dev assumptions have failed -var ErrUnsupportedAlgorithm = errors.New("pkcs7: cannot decrypt data: only RSA, DES, DES-EDE3 and AES-256-CBC supported") +var ErrUnsupportedAlgorithm = errors.New("pkcs7: cannot decrypt data: only RSA, DES, DES-EDE3, AES-256-CBC and AES-128-GCM supported") // ErrNotEncryptedContent is returned when attempting to Decrypt data that is not encrypted data var ErrNotEncryptedContent = errors.New("pkcs7: content data is a decryptable data type") @@ -351,10 +351,14 @@ func (p7 *PKCS7) Decrypt(cert *x509.Certificate, pk crypto.PrivateKey) ([]byte, var oidEncryptionAlgorithmDESCBC = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 7} var oidEncryptionAlgorithmDESEDE3CBC = asn1.ObjectIdentifier{1, 2, 840, 113549, 3, 7} var oidEncryptionAlgorithmAES256CBC = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 42} +var oidEncryptionAlgorithmAES128GCM = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 6} func (eci encryptedContentInfo) decrypt(key []byte) ([]byte, error) { alg := eci.ContentEncryptionAlgorithm.Algorithm - if !alg.Equal(oidEncryptionAlgorithmDESCBC) && !alg.Equal(oidEncryptionAlgorithmDESEDE3CBC) && !alg.Equal(oidEncryptionAlgorithmAES256CBC) { + if !alg.Equal(oidEncryptionAlgorithmDESCBC) && + !alg.Equal(oidEncryptionAlgorithmDESEDE3CBC) && + !alg.Equal(oidEncryptionAlgorithmAES256CBC) && + !alg.Equal(oidEncryptionAlgorithmAES128GCM) { fmt.Printf("Unsupported Content Encryption Algorithm: %s\n", alg) return nil, ErrUnsupportedAlgorithm } @@ -389,12 +393,44 @@ func (eci encryptedContentInfo) decrypt(key []byte) ([]byte, error) { case alg.Equal(oidEncryptionAlgorithmDESEDE3CBC): block, err = des.NewTripleDESCipher(key) case alg.Equal(oidEncryptionAlgorithmAES256CBC): + fallthrough + case alg.Equal(oidEncryptionAlgorithmAES128GCM): block, err = aes.NewCipher(key) } + if err != nil { return nil, err } + if alg.Equal(oidEncryptionAlgorithmAES128GCM) { + params := aesGCMParameters{} + paramBytes := eci.ContentEncryptionAlgorithm.Parameters.Bytes + + _, err := asn1.Unmarshal(paramBytes, ¶ms) + if err != nil { + return nil, err + } + + gcm, err := cipher.NewGCM(block) + if err != nil { + return nil, err + } + + if len(params.Nonce) != gcm.NonceSize() { + return nil, errors.New("pkcs7: encryption algorithm parameters are incorrect") + } + if params.ICVLen != gcm.Overhead() { + return nil, errors.New("pkcs7: encryption algorithm parameters are incorrect") + } + + plaintext, err := gcm.Open(nil, params.Nonce, cyphertext, nil) + if err != nil { + return nil, err + } + + return plaintext, nil + } + iv := eci.ContentEncryptionAlgorithm.Parameters.Bytes if len(iv) != block.BlockSize() { return nil, errors.New("pkcs7: encryption algorithm parameters are malformed") @@ -714,27 +750,98 @@ func DegenerateCertificate(cert []byte) ([]byte, error) { return asn1.Marshal(signedContent) } -// Encrypt creates and returns an envelope data PKCS7 structure with encrypted -// recipient keys for each recipient public key -// TODO(fullsailor): Add support for encrypting content with other algorithms -func Encrypt(content []byte, recipients []*x509.Certificate) ([]byte, error) { +const ( + EncryptionAlgorithmDESCBC = iota + EncryptionAlgorithmAES128GCM +) + +// ContentEncryptionAlgorithm determines the algorithm used to encrypt the +// plaintext message. Change the value of this variable to change which +// algorithm is used in the Encrypt() function. +var ContentEncryptionAlgorithm = EncryptionAlgorithmDESCBC + +// ErrUnsupportedEncryptionAlgorithm is returned when attempting to encrypt +// content with an unsupported algorithm. +var ErrUnsupportedEncryptionAlgorithm = errors.New("pkcs7: cannot encrypt content: only DES-CBC and AES-128-GCM supported") + +const nonceSize = 12 + +type aesGCMParameters struct { + Nonce []byte `asn1:"tag:4"` + ICVLen int +} + +func encryptAES128GCM(content []byte) ([]byte, *encryptedContentInfo, error) { + // Create AES key and nonce + key := make([]byte, 16) + nonce := make([]byte, nonceSize) + + _, err := rand.Read(key) + if err != nil { + return nil, nil, err + } + + _, err = rand.Read(nonce) + if err != nil { + return nil, nil, err + } + + // Encrypt content + block, err := aes.NewCipher(key) + if err != nil { + return nil, nil, err + } + + gcm, err := cipher.NewGCM(block) + if err != nil { + return nil, nil, err + } + + ciphertext := gcm.Seal(nil, nonce, content, nil) + + // Prepare ASN.1 Encrypted Content Info + paramSeq := aesGCMParameters{ + Nonce: nonce, + ICVLen: gcm.Overhead(), + } + + paramBytes, err := asn1.Marshal(paramSeq) + if err != nil { + return nil, nil, err + } + + eci := encryptedContentInfo{ + ContentType: oidData, + ContentEncryptionAlgorithm: pkix.AlgorithmIdentifier{ + Algorithm: oidEncryptionAlgorithmAES128GCM, + Parameters: asn1.RawValue{ + Tag: asn1.TagSequence, + Bytes: paramBytes, + }, + }, + EncryptedContent: marshalEncryptedContent(ciphertext), + } + + return key, &eci, nil +} +func encryptDESCBC(content []byte) ([]byte, *encryptedContentInfo, error) { // Create DES key & CBC IV key := make([]byte, 8) iv := make([]byte, des.BlockSize) _, err := rand.Read(key) if err != nil { - return nil, err + return nil, nil, err } _, err = rand.Read(iv) if err != nil { - return nil, err + return nil, nil, err } // Encrypt padded content block, err := des.NewCipher(key) if err != nil { - return nil, err + return nil, nil, err } mode := cipher.NewCBCEncrypter(block, iv) plaintext, err := pad(content, mode.BlockSize()) @@ -751,6 +858,41 @@ func Encrypt(content []byte, recipients []*x509.Certificate) ([]byte, error) { EncryptedContent: marshalEncryptedContent(cyphertext), } + return key, &eci, nil +} + +// Encrypt creates and returns an envelope data PKCS7 structure with encrypted +// recipient keys for each recipient public key. +// +// The algorithm used to perform encryption is determined by the current value +// of the global ContentEncryptionAlgorithm package variable. By default, the +// value is EncryptionAlgorithmDESCBC. To use a different algorithm, change the +// value before calling Encrypt(). For example: +// +// ContentEncryptionAlgorithm = EncryptionAlgorithmAES128GCM +// +// TODO(fullsailor): Add support for encrypting content with other algorithms +func Encrypt(content []byte, recipients []*x509.Certificate) ([]byte, error) { + var eci *encryptedContentInfo + var key []byte + var err error + + // Apply chosen symmetric encryption method + switch ContentEncryptionAlgorithm { + case EncryptionAlgorithmDESCBC: + key, eci, err = encryptDESCBC(content) + + case EncryptionAlgorithmAES128GCM: + key, eci, err = encryptAES128GCM(content) + + default: + return nil, ErrUnsupportedEncryptionAlgorithm + } + + if err != nil { + return nil, err + } + // Prepare each recipient's encrypted cipher key recipientInfos := make([]recipientInfo, len(recipients)) for i, recipient := range recipients { @@ -775,7 +917,7 @@ func Encrypt(content []byte, recipients []*x509.Certificate) ([]byte, error) { // Prepare envelope content envelope := envelopedData{ - EncryptedContentInfo: eci, + EncryptedContentInfo: *eci, Version: 0, RecipientInfos: recipientInfos, } diff --git a/pkcs7_test.go b/pkcs7_test.go index c66f8fe..a92760f 100644 --- a/pkcs7_test.go +++ b/pkcs7_test.go @@ -134,25 +134,34 @@ func TestSign(t *testing.T) { } func TestEncrypt(t *testing.T) { - plaintext := []byte("Hello Secret World!") - cert, err := createTestCertificate() - if err != nil { - t.Fatal(err) - } - encrypted, err := Encrypt(plaintext, []*x509.Certificate{cert.Certificate}) - if err != nil { - t.Fatal(err) + modes := []int{ + EncryptionAlgorithmDESCBC, + EncryptionAlgorithmAES128GCM, } - p7, err := Parse(encrypted) - if err != nil { - t.Fatalf("cannot Parse encrypted result: %s", err) - } - result, err := p7.Decrypt(cert.Certificate, cert.PrivateKey) - if err != nil { - t.Fatalf("cannot Decrypt encrypted result: %s", err) - } - if bytes.Compare(plaintext, result) != 0 { - t.Errorf("encrypted data does not match plaintext:\n\tExpected: %s\n\tActual: %s", plaintext, result) + + for _, mode := range modes { + ContentEncryptionAlgorithm = mode + + plaintext := []byte("Hello Secret World!") + cert, err := createTestCertificate() + if err != nil { + t.Fatal(err) + } + encrypted, err := Encrypt(plaintext, []*x509.Certificate{cert.Certificate}) + if err != nil { + t.Fatal(err) + } + p7, err := Parse(encrypted) + if err != nil { + t.Fatalf("cannot Parse encrypted result: %s", err) + } + result, err := p7.Decrypt(cert.Certificate, cert.PrivateKey) + if err != nil { + t.Fatalf("cannot Decrypt encrypted result: %s", err) + } + if bytes.Compare(plaintext, result) != 0 { + t.Errorf("encrypted data does not match plaintext:\n\tExpected: %s\n\tActual: %s", plaintext, result) + } } } From bcb2b81a1b4d7a0f31055ed8f8482a6d7139776f Mon Sep 17 00:00:00 2001 From: Andrew Smith Date: Sun, 24 Jul 2016 13:33:28 -0400 Subject: [PATCH 06/23] Fix signers without AuthenticatedAttributes According to the specification, when AuthenticatedAttributes isn't included the pkcs7 content should be used. Fixes #9 --- pkcs7.go | 15 ++++--- pkcs7_test.go | 121 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 129 insertions(+), 7 deletions(-) diff --git a/pkcs7.go b/pkcs7.go index 73227aa..d306a13 100644 --- a/pkcs7.go +++ b/pkcs7.go @@ -259,19 +259,20 @@ func verifySignature(p7 *PKCS7, signer signerInfo) error { ActualDigest: computed, } } + // TODO(fullsailor): Optionally verify certificate chain + // TODO(fullsailor): Optionally verify signingTime against certificate NotAfter/NotBefore + signedData, err = marshalAttributes(signer.AuthenticatedAttributes) + if err != nil { + return err + } } cert := getCertFromCertsByIssuerAndSerial(p7.Certificates, signer.IssuerAndSerialNumber) if cert == nil { return errors.New("pkcs7: No certificate for signer") } - // TODO(fullsailor): Optionally verify certificate chain - // TODO(fullsailor): Optionally verify signingTime against certificate NotAfter/NotBefore - encodedAttributes, err := marshalAttributes(signer.AuthenticatedAttributes) - if err != nil { - return err - } + algo := x509.SHA1WithRSA - return cert.CheckSignature(algo, encodedAttributes, signer.EncryptedDigest) + return cert.CheckSignature(algo, signedData, signer.EncryptedDigest) } func marshalAttributes(attrs []attribute) ([]byte, error) { diff --git a/pkcs7_test.go b/pkcs7_test.go index a92760f..9518fdc 100644 --- a/pkcs7_test.go +++ b/pkcs7_test.go @@ -48,6 +48,17 @@ func TestVerifyEC2(t *testing.T) { } } +func TestVerifyAppStore(t *testing.T) { + fixture := UnmarshalTestFixture(AppStoreRecieptFixture) + p7, err := Parse(fixture.Input) + if err != nil { + t.Errorf("Parse encountered unexpected error: %v", err) + } + if err := p7.Verify(); err != nil { + t.Errorf("Verify failed with error: %v", err) + } +} + func TestDecrypt(t *testing.T) { fixture := UnmarshalTestFixture(EncryptedTestFixture) p7, err := Parse(fixture.Input) @@ -446,3 +457,113 @@ MXrs3IgIb6+hUIB+S8dz8/mmO0bpr76RoZVCXYab2CZedFut7qc3WUH9+EUAH5mw vSeDCOUMYQR7R9LINYwouHIziqQYMAkGByqGSM44BAMDLwAwLAIUWXBlk40xTwSw 7HX32MxXYruse9ACFBNGmdX2ZBrVNGrN9N2f6ROk0k9K -----END CERTIFICATE-----` + +var AppStoreRecieptFixture = ` +-----BEGIN PKCS7----- +MIITtgYJKoZIhvcNAQcCoIITpzCCE6MCAQExCzAJBgUrDgMCGgUAMIIDVwYJKoZI +hvcNAQcBoIIDSASCA0QxggNAMAoCAQgCAQEEAhYAMAoCARQCAQEEAgwAMAsCAQEC +AQEEAwIBADALAgEDAgEBBAMMATEwCwIBCwIBAQQDAgEAMAsCAQ8CAQEEAwIBADAL +AgEQAgEBBAMCAQAwCwIBGQIBAQQDAgEDMAwCAQoCAQEEBBYCNCswDAIBDgIBAQQE +AgIAjTANAgENAgEBBAUCAwFgvTANAgETAgEBBAUMAzEuMDAOAgEJAgEBBAYCBFAy +NDcwGAIBAgIBAQQQDA5jb20uemhpaHUudGVzdDAYAgEEAgECBBCS+ZODNMHwT1Nz +gWYDXyWZMBsCAQACAQEEEwwRUHJvZHVjdGlvblNhbmRib3gwHAIBBQIBAQQU4nRh +YCEZx70Flzv7hvJRjJZckYIwHgIBDAIBAQQWFhQyMDE2LTA3LTIzVDA2OjIxOjEx +WjAeAgESAgEBBBYWFDIwMTMtMDgtMDFUMDc6MDA6MDBaMD0CAQYCAQEENbR21I+a +8+byMXo3NPRoDWQmSXQF2EcCeBoD4GaL//ZCRETp9rGFPSg1KekCP7Kr9HAqw09m +MEICAQcCAQEEOlVJozYYBdugybShbiiMsejDMNeCbZq6CrzGBwW6GBy+DGWxJI91 +Y3ouXN4TZUhuVvLvN1b0m5T3ggQwggFaAgERAgEBBIIBUDGCAUwwCwICBqwCAQEE +AhYAMAsCAgatAgEBBAIMADALAgIGsAIBAQQCFgAwCwICBrICAQEEAgwAMAsCAgaz +AgEBBAIMADALAgIGtAIBAQQCDAAwCwICBrUCAQEEAgwAMAsCAga2AgEBBAIMADAM +AgIGpQIBAQQDAgEBMAwCAgarAgEBBAMCAQEwDAICBq4CAQEEAwIBADAMAgIGrwIB +AQQDAgEAMAwCAgaxAgEBBAMCAQAwGwICBqcCAQEEEgwQMTAwMDAwMDIyNTMyNTkw +MTAbAgIGqQIBAQQSDBAxMDAwMDAwMjI1MzI1OTAxMB8CAgaoAgEBBBYWFDIwMTYt +MDctMjNUMDY6MjE6MTFaMB8CAgaqAgEBBBYWFDIwMTYtMDctMjNUMDY6MjE6MTFa +MCACAgamAgEBBBcMFWNvbS56aGlodS50ZXN0LnRlc3RfMaCCDmUwggV8MIIEZKAD +AgECAggO61eH554JjTANBgkqhkiG9w0BAQUFADCBljELMAkGA1UEBhMCVVMxEzAR +BgNVBAoMCkFwcGxlIEluYy4xLDAqBgNVBAsMI0FwcGxlIFdvcmxkd2lkZSBEZXZl +bG9wZXIgUmVsYXRpb25zMUQwQgYDVQQDDDtBcHBsZSBXb3JsZHdpZGUgRGV2ZWxv +cGVyIFJlbGF0aW9ucyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xNTExMTMw +MjE1MDlaFw0yMzAyMDcyMTQ4NDdaMIGJMTcwNQYDVQQDDC5NYWMgQXBwIFN0b3Jl +IGFuZCBpVHVuZXMgU3RvcmUgUmVjZWlwdCBTaWduaW5nMSwwKgYDVQQLDCNBcHBs +ZSBXb3JsZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczETMBEGA1UECgwKQXBwbGUg +SW5jLjELMAkGA1UEBhMCVVMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQClz4H9JaKBW9aH7SPaMxyO4iPApcQmyz3Gn+xKDVWG/6QC15fKOVRtfX+yVBid +xCxScY5ke4LOibpJ1gjltIhxzz9bRi7GxB24A6lYogQ+IXjV27fQjhKNg0xbKmg3 +k8LyvR7E0qEMSlhSqxLj7d0fmBWQNS3CzBLKjUiB91h4VGvojDE2H0oGDEdU8zeQ +uLKSiX1fpIVK4cCc4Lqku4KXY/Qrk8H9Pm/KwfU8qY9SGsAlCnYO3v6Z/v/Ca/Vb +XqxzUUkIVonMQ5DMjoEC0KCXtlyxoWlph5AQaCYmObgdEHOwCl3Fc9DfdjvYLdmI +HuPsB8/ijtDT+iZVge/iA0kjAgMBAAGjggHXMIIB0zA/BggrBgEFBQcBAQQzMDEw +LwYIKwYBBQUHMAGGI2h0dHA6Ly9vY3NwLmFwcGxlLmNvbS9vY3NwMDMtd3dkcjA0 +MB0GA1UdDgQWBBSRpJz8xHa3n6CK9E31jzZd7SsEhTAMBgNVHRMBAf8EAjAAMB8G +A1UdIwQYMBaAFIgnFwmpthhgi+zruvZHWcVSVKO3MIIBHgYDVR0gBIIBFTCCAREw +ggENBgoqhkiG92NkBQYBMIH+MIHDBggrBgEFBQcCAjCBtgyBs1JlbGlhbmNlIG9u +IHRoaXMgY2VydGlmaWNhdGUgYnkgYW55IHBhcnR5IGFzc3VtZXMgYWNjZXB0YW5j +ZSBvZiB0aGUgdGhlbiBhcHBsaWNhYmxlIHN0YW5kYXJkIHRlcm1zIGFuZCBjb25k +aXRpb25zIG9mIHVzZSwgY2VydGlmaWNhdGUgcG9saWN5IGFuZCBjZXJ0aWZpY2F0 +aW9uIHByYWN0aWNlIHN0YXRlbWVudHMuMDYGCCsGAQUFBwIBFipodHRwOi8vd3d3 +LmFwcGxlLmNvbS9jZXJ0aWZpY2F0ZWF1dGhvcml0eS8wDgYDVR0PAQH/BAQDAgeA +MBAGCiqGSIb3Y2QGCwEEAgUAMA0GCSqGSIb3DQEBBQUAA4IBAQANphvTLj3jWysH +bkKWbNPojEMwgl/gXNGNvr0PvRr8JZLbjIXDgFnf4+LXLgUUrA3btrj+/DUufMut +F2uOfx/kd7mxZ5W0E16mGYZ2+FogledjjA9z/Ojtxh+umfhlSFyg4Cg6wBA3Lbmg +BDkfc7nIBf3y3n8aKipuKwH8oCBc2et9J6Yz+PWY4L5E27FMZ/xuCk/J4gao0pfz +p45rUaJahHVl0RYEYuPBX/UIqc9o2ZIAycGMs/iNAGS6WGDAfK+PdcppuVsq1h1o +bphC9UynNxmbzDscehlD86Ntv0hgBgw2kivs3hi1EdotI9CO/KBpnBcbnoB7OUdF +MGEvxxOoMIIEIjCCAwqgAwIBAgIIAd68xDltoBAwDQYJKoZIhvcNAQEFBQAwYjEL +MAkGA1UEBhMCVVMxEzARBgNVBAoTCkFwcGxlIEluYy4xJjAkBgNVBAsTHUFwcGxl +IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRYwFAYDVQQDEw1BcHBsZSBSb290IENB +MB4XDTEzMDIwNzIxNDg0N1oXDTIzMDIwNzIxNDg0N1owgZYxCzAJBgNVBAYTAlVT +MRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3JsZHdpZGUg +RGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3aWRlIERl +dmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDKOFSmy1aqyCQ5SOmM7uxfuH8mkbw0 +U3rOfGOAYXdkXqUHI7Y5/lAtFVZYcC1+xG7BSoU+L/DehBqhV8mvexj/avoVEkkV +CBmsqtsqMu2WY2hSFT2Miuy/axiV4AOsAX2XBWfODoWVN2rtCbauZ81RZJ/GXNG8 +V25nNYB2NqSHgW44j9grFU57Jdhav06DwY3Sk9UacbVgnJ0zTlX5ElgMhrgWDcHl +d0WNUEi6Ky3klIXh6MSdxmilsKP8Z35wugJZS3dCkTm59c3hTO/AO0iMpuUhXf1q +arunFjVg0uat80YpyejDi+l5wGphZxWy8P3laLxiX27Pmd3vG2P+kmWrAgMBAAGj +gaYwgaMwHQYDVR0OBBYEFIgnFwmpthhgi+zruvZHWcVSVKO3MA8GA1UdEwEB/wQF +MAMBAf8wHwYDVR0jBBgwFoAUK9BpR5R2Cf70a40uQKb3R01/CF4wLgYDVR0fBCcw +JTAjoCGgH4YdaHR0cDovL2NybC5hcHBsZS5jb20vcm9vdC5jcmwwDgYDVR0PAQH/ +BAQDAgGGMBAGCiqGSIb3Y2QGAgEEAgUAMA0GCSqGSIb3DQEBBQUAA4IBAQBPz+9Z +viz1smwvj+4ThzLoBTWobot9yWkMudkXvHcs1Gfi/ZptOllc34MBvbKuKmFysa/N +w0Uwj6ODDc4dR7Txk4qjdJukw5hyhzs+r0ULklS5MruQGFNrCk4QttkdUGwhgAqJ +TleMa1s8Pab93vcNIx0LSiaHP7qRkkykGRIZbVf1eliHe2iK5IaMSuviSRSqpd1V +AKmuu0swruGgsbwpgOYJd+W+NKIByn/c4grmO7i77LpilfMFY0GCzQ87HUyVpNur ++cmV6U/kTecmmYHpvPm0KdIBembhLoz2IYrF+Hjhga6/05Cdqa3zr/04GpZnMBxR +pVzscYqCtGwPDBUfMIIEuzCCA6OgAwIBAgIBAjANBgkqhkiG9w0BAQUFADBiMQsw +CQYDVQQGEwJVUzETMBEGA1UEChMKQXBwbGUgSW5jLjEmMCQGA1UECxMdQXBwbGUg +Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkxFjAUBgNVBAMTDUFwcGxlIFJvb3QgQ0Ew +HhcNMDYwNDI1MjE0MDM2WhcNMzUwMjA5MjE0MDM2WjBiMQswCQYDVQQGEwJVUzET +MBEGA1UEChMKQXBwbGUgSW5jLjEmMCQGA1UECxMdQXBwbGUgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkxFjAUBgNVBAMTDUFwcGxlIFJvb3QgQ0EwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQDkkakJH5HbHkdQ6wXtXnmELes2oldMVeyLGYne ++Uts9QerIjAC6Bg++FAJ039BqJj50cpmnCRrEdCju+QbKsMflZ56DKRHi1vUFjcz +y8QPTc4UadHJGXL1XQ7Vf1+b8iUDulWPTV0N8WQ1IxVLFVkds5T39pyez1C6wVhQ +Z48ItCD3y6wsIG9wtj8BMIy3Q88PnT3zK0koGsj+zrW5DtleHNbLPbU6rfQPDgCS +C7EhFi501TwN22IWq6NxkkdTVcGvL0Gz+PvjcM3mo0xFfh9Ma1CWQYnEdGILEINB +hzOKgbEwWOxaBDKMaLOPHd5lc/9nXmW8Sdh2nzMUZaF3lMktAgMBAAGjggF6MIIB +djAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUK9Bp +R5R2Cf70a40uQKb3R01/CF4wHwYDVR0jBBgwFoAUK9BpR5R2Cf70a40uQKb3R01/ +CF4wggERBgNVHSAEggEIMIIBBDCCAQAGCSqGSIb3Y2QFATCB8jAqBggrBgEFBQcC +ARYeaHR0cHM6Ly93d3cuYXBwbGUuY29tL2FwcGxlY2EvMIHDBggrBgEFBQcCAjCB +thqBs1JlbGlhbmNlIG9uIHRoaXMgY2VydGlmaWNhdGUgYnkgYW55IHBhcnR5IGFz +c3VtZXMgYWNjZXB0YW5jZSBvZiB0aGUgdGhlbiBhcHBsaWNhYmxlIHN0YW5kYXJk +IHRlcm1zIGFuZCBjb25kaXRpb25zIG9mIHVzZSwgY2VydGlmaWNhdGUgcG9saWN5 +IGFuZCBjZXJ0aWZpY2F0aW9uIHByYWN0aWNlIHN0YXRlbWVudHMuMA0GCSqGSIb3 +DQEBBQUAA4IBAQBcNplMLXi37Yyb3PN3m/J20ncwT8EfhYOFG5k9RzfyqZtAjizU +sZAS2L70c5vu0mQPy3lPNNiiPvl4/2vIB+x9OYOLUyDTOMSxv5pPCmv/K/xZpwUJ +fBdAVhEedNO3iyM7R6PVbyTi69G3cN8PReEnyvFteO3ntRcXqNx+IjXKJdXZD9Zr +1KIkIxH3oayPc4FgxhtbCS+SsvhESPBgOJ4V9T0mZyCKM2r3DYLP3uujL/lTaltk +wGMzd/c6ByxW69oPIQ7aunMZT7XZNn/Bh1XZp5m5MkL72NVxnn6hUrcbvZNCJBIq +xw8dtk2cXmPIS4AXUKqK1drk/NAJBzewdXUhMYIByzCCAccCAQEwgaMwgZYxCzAJ +BgNVBAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBX +b3JsZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29y +bGR3aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkCCA7rV4fnngmNMAkGBSsOAwIaBQAwDQYJKoZIhvcNAQEBBQAEggEAasPtnide +NWyfUtewW9OSgcQA8pW+5tWMR0469cBPZR84uJa0gyfmPspySvbNOAwnrwzZHYLa +ujOxZLip4DUw4F5s3QwUa3y4BXpF4J+NSn9XNvxNtnT/GcEQtCuFwgJ0o3F0ilhv +MTHrwiwyx/vr+uNDqlORK8lfK+1qNp+A/kzh8eszMrn4JSeTh9ZYxLHE56WkTQGD +VZXl0gKgxSOmDrcp1eQxdlymzrPv9U60wUJ0bkPfrU9qZj3mJrmrkQk61JTe3j6/ +QfjfFBG9JG2mUmYQP1KQ3SypGHzDW8vngvsGu//tNU0NFfOqQu4bYU4VpQl0nPtD +4B85NkrgvQsWAQ== +-----END PKCS7-----` From 6fc3400eb6f1196949805d18ebe59ccaf6c8e4f3 Mon Sep 17 00:00:00 2001 From: Andrew Smith Date: Sun, 24 Jul 2016 19:55:29 -0400 Subject: [PATCH 07/23] Fix an unstaged git hunk. From a676cd56a6d32a3c7abab297e9ec6181d155eee8 --- pkcs7.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkcs7.go b/pkcs7.go index d306a13..92dbeca 100644 --- a/pkcs7.go +++ b/pkcs7.go @@ -239,6 +239,7 @@ func (p7 *PKCS7) Verify() (err error) { } func verifySignature(p7 *PKCS7, signer signerInfo) error { + signedData := p7.Content if len(signer.AuthenticatedAttributes) > 0 { // TODO(fullsailor): First check the content type match var digest []byte From b1490d5871c64558cafced71bfd415536058029d Mon Sep 17 00:00:00 2001 From: Andrew Smith Date: Sun, 24 Jul 2016 20:01:56 -0400 Subject: [PATCH 08/23] Add travis.yml --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..ee9065e --- /dev/null +++ b/.travis.yml @@ -0,0 +1,5 @@ +language: go + +go: + - release + - tip From 544cda02225e17837dcef55ead22b201880c7394 Mon Sep 17 00:00:00 2001 From: Andrew Smith Date: Sun, 24 Jul 2016 20:10:21 -0400 Subject: [PATCH 09/23] Update travis.yml release -> 1.6 I believe I was lied to. (or followed out of date advice) --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ee9065e..925de96 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: go go: - - release + - 1.6 - tip From a5d962cc399f4b938bd27de41d3923cab15087f8 Mon Sep 17 00:00:00 2001 From: Andrew Smith Date: Sun, 24 Jul 2016 20:16:40 -0400 Subject: [PATCH 10/23] Add build status to README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7ec77b5..bfd948f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # pkcs7 [![GoDoc](https://godoc.org/github.com/fullsailor/pkcs7?status.svg)](https://godoc.org/github.com/fullsailor/pkcs7) +[![Build Status](https://travis-ci.org/fullsailor/pkcs7.svg?branch=master)](https://travis-ci.org/fullsailor/pkcs7) pkcs7 implements parsing and creating signed and enveloped messages. From 7041c07fa5e0f9c843e02305c47e3a291e6d9437 Mon Sep 17 00:00:00 2001 From: Victor Vrantchan Date: Fri, 11 Nov 2016 21:12:31 -0500 Subject: [PATCH 11/23] add support for aes128-CBC OID 2.16.840.1.101.3.4.1.2 --- pkcs7.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkcs7.go b/pkcs7.go index 92dbeca..6e2c1bc 100644 --- a/pkcs7.go +++ b/pkcs7.go @@ -354,12 +354,14 @@ var oidEncryptionAlgorithmDESCBC = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 7} var oidEncryptionAlgorithmDESEDE3CBC = asn1.ObjectIdentifier{1, 2, 840, 113549, 3, 7} var oidEncryptionAlgorithmAES256CBC = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 42} var oidEncryptionAlgorithmAES128GCM = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 6} +var oidEncryptionAlgorithmAES128CBC = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 2} func (eci encryptedContentInfo) decrypt(key []byte) ([]byte, error) { alg := eci.ContentEncryptionAlgorithm.Algorithm if !alg.Equal(oidEncryptionAlgorithmDESCBC) && !alg.Equal(oidEncryptionAlgorithmDESEDE3CBC) && !alg.Equal(oidEncryptionAlgorithmAES256CBC) && + !alg.Equal(oidEncryptionAlgorithmAES128CBC) && !alg.Equal(oidEncryptionAlgorithmAES128GCM) { fmt.Printf("Unsupported Content Encryption Algorithm: %s\n", alg) return nil, ErrUnsupportedAlgorithm @@ -396,7 +398,7 @@ func (eci encryptedContentInfo) decrypt(key []byte) ([]byte, error) { block, err = des.NewTripleDESCipher(key) case alg.Equal(oidEncryptionAlgorithmAES256CBC): fallthrough - case alg.Equal(oidEncryptionAlgorithmAES128GCM): + case alg.Equal(oidEncryptionAlgorithmAES128GCM), alg.Equal(oidEncryptionAlgorithmAES128CBC): block, err = aes.NewCipher(key) } From 10edc641b8c62aa621f7142423dedceb9f8e2596 Mon Sep 17 00:00:00 2001 From: Julien Vehent Date: Thu, 29 Dec 2016 16:38:43 -0500 Subject: [PATCH 12/23] Support S/MIME detached signatures, and improve tests --- .travis.yml | 1 + pkcs7.go | 6 +++ pkcs7_test.go | 137 ++++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 130 insertions(+), 14 deletions(-) diff --git a/.travis.yml b/.travis.yml index 925de96..c7154e4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,4 +2,5 @@ language: go go: - 1.6 + - 1.7 - tip diff --git a/pkcs7.go b/pkcs7.go index 6e2c1bc..6a9db6f 100644 --- a/pkcs7.go +++ b/pkcs7.go @@ -667,6 +667,12 @@ func (sd *SignedData) AddCertificate(cert *x509.Certificate) { sd.certs = append(sd.certs, cert) } +// Detach removes content from the signed data struct to make it a detached signature. +// This must be called right before Finish() +func (sd *SignedData) Detach() { + sd.sd.ContentInfo = contentInfo{ContentType: oidSignedData} +} + // Finish marshals the content and its signers func (sd *SignedData) Finish() ([]byte, error) { sd.sd.Certificates = marshalCertificates(sd.certs) diff --git a/pkcs7_test.go b/pkcs7_test.go index 9518fdc..cbe2c5c 100644 --- a/pkcs7_test.go +++ b/pkcs7_test.go @@ -85,8 +85,7 @@ func TestDegenerateCertificate(t *testing.T) { t.Fatal(err) } testOpenSSLParse(t, deg) - - fmt.Printf("=== BEGIN DEGENERATE CERT ===\n% X\n=== END DEGENERATE CERT ===\n", deg) + pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: deg}) } // writes the cert to a temporary file and tests that openssl can read it. @@ -119,28 +118,132 @@ func TestSign(t *testing.T) { t.Fatal(err) } content := []byte("Hello World") + for _, testDetach := range []bool{false, true} { + toBeSigned, err := NewSignedData(content) + if err != nil { + t.Fatalf("Cannot initialize signed data: %s", err) + } + if err := toBeSigned.AddSigner(cert.Certificate, cert.PrivateKey, SignerInfoConfig{}); err != nil { + t.Fatalf("Cannot add signer: %s", err) + } + if testDetach { + t.Log("Testing detached signature") + toBeSigned.Detach() + } else { + t.Log("Testing attached signature") + } + signed, err := toBeSigned.Finish() + if err != nil { + t.Fatalf("Cannot finish signing data: %s", err) + } + pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: signed}) + p7, err := Parse(signed) + if err != nil { + t.Fatalf("Cannot parse our signed data: %s", err) + } + if testDetach { + p7.Content = content + } + if bytes.Compare(content, p7.Content) != 0 { + t.Errorf("Our content was not in the parsed data:\n\tExpected: %s\n\tActual: %s", content, p7.Content) + } + if err := p7.Verify(); err != nil { + t.Errorf("Cannot verify our signed data: %s", err) + } + } +} + +func ExampleSignedData() { + // generate a signing cert or load a key pair + cert, err := createTestCertificate() + if err != nil { + fmt.Printf("Cannot create test certificates: %s", err) + } + + // Initialize a SignedData struct with content to be signed + signedData, err := NewSignedData([]byte("Example data to be signed")) + if err != nil { + fmt.Printf("Cannot initialize signed data: %s", err) + } + + // Add the signing cert and private key + if err := signedData.AddSigner(cert.Certificate, cert.PrivateKey, SignerInfoConfig{}); err != nil { + fmt.Printf("Cannot add signer: %s", err) + } + + // Call Detach() is you want to remove content from the signature + // and generate an S/MIME detached signature + signedData.Detach() + + // Finish() to obtain the signature bytes + detachedSignature, err := signedData.Finish() + if err != nil { + fmt.Printf("Cannot finish signing data: %s", err) + } + pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: detachedSignature}) +} + +func TestOpenSSLVerifyDetachedSignature(t *testing.T) { + rootCert, err := createTestCertificateByIssuer("PKCS7 Test Root CA", nil) + if err != nil { + t.Fatalf("Cannot generate root cert: %s", err) + } + signerCert, err := createTestCertificateByIssuer("PKCS7 Test Signer Cert", rootCert) + if err != nil { + t.Fatalf("Cannot generate signer cert: %s", err) + } + content := []byte("Hello World") toBeSigned, err := NewSignedData(content) if err != nil { t.Fatalf("Cannot initialize signed data: %s", err) } - if err := toBeSigned.AddSigner(cert.Certificate, cert.PrivateKey, SignerInfoConfig{}); err != nil { + if err := toBeSigned.AddSigner(signerCert.Certificate, signerCert.PrivateKey, SignerInfoConfig{}); err != nil { t.Fatalf("Cannot add signer: %s", err) } + toBeSigned.Detach() signed, err := toBeSigned.Finish() if err != nil { t.Fatalf("Cannot finish signing data: %s", err) } - fmt.Printf("=== BEGIN SIGNED RESULT ===\n% X\n=== END SIGNED RESULT ===\n", signed) - p7, err := Parse(signed) + // write the root cert to a temp file + tmpRootCertFile, err := ioutil.TempFile("", "pkcs7TestRootCA") if err != nil { - t.Fatalf("Cannot parse our signed data: %s", err) + t.Fatal(err) } - if bytes.Compare(content, p7.Content) != 0 { - t.Errorf("Our content was not in the parsed data:\n\tExpected: %s\n\tActual: %s", content, p7.Content) + defer os.Remove(tmpRootCertFile.Name()) // clean up + fd, err := os.OpenFile(tmpRootCertFile.Name(), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755) + if err != nil { + t.Fatal(err) } - if err := p7.Verify(); err != nil { - t.Errorf("Cannot verify our signed data: %s", err) + pem.Encode(fd, &pem.Block{Type: "CERTIFICATE", Bytes: rootCert.Certificate.Raw}) + fd.Close() + + // write the signature to a temp file + tmpSignatureFile, err := ioutil.TempFile("", "pkcs7Signature") + if err != nil { + t.Fatal(err) + } + defer os.Remove(tmpSignatureFile.Name()) // clean up + ioutil.WriteFile(tmpSignatureFile.Name(), signed, 0755) + + // write the content to a temp file + tmpContentFile, err := ioutil.TempFile("", "pkcs7Content") + if err != nil { + t.Fatal(err) + } + defer os.Remove(tmpContentFile.Name()) // clean up + ioutil.WriteFile(tmpContentFile.Name(), content, 0755) + + // call openssl to verify the signature on the content using the root + opensslCMD := exec.Command("openssl", "smime", "-verify", + "-in", tmpSignatureFile.Name(), "-inform", "DER", + "-content", tmpContentFile.Name(), + "-CAfile", tmpRootCertFile.Name()) + out, err := opensslCMD.Output() + t.Logf("%s", out) + if err != nil { + t.Fatalf("openssl command failed with %s", err) } } @@ -239,15 +342,18 @@ func createTestCertificate() (certKeyPair, error) { if err != nil { return certKeyPair{}, err } + fmt.Println("Created root cert") + pem.Encode(os.Stdout, &pem.Block{Type: "CERTIFICATE", Bytes: signer.Certificate.Raw}) pair, err := createTestCertificateByIssuer("Jon Snow", signer) if err != nil { return certKeyPair{}, err } + fmt.Println("Created signer cert") + pem.Encode(os.Stdout, &pem.Block{Type: "CERTIFICATE", Bytes: pair.Certificate.Raw}) return *pair, nil } func createTestCertificateByIssuer(name string, issuer *certKeyPair) (*certKeyPair, error) { - priv, err := rsa.GenerateKey(rand.Reader, 1024) if err != nil { return nil, err @@ -265,9 +371,10 @@ func createTestCertificateByIssuer(name string, issuer *certKeyPair) (*certKeyPa CommonName: name, Organization: []string{"Acme Co"}, }, - NotBefore: time.Now(), - NotAfter: time.Now().AddDate(1, 0, 0), - KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + NotBefore: time.Now(), + NotAfter: time.Now().AddDate(1, 0, 0), + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageEmailProtection}, } var issuerCert *x509.Certificate var issuerKey crypto.PrivateKey @@ -275,6 +382,8 @@ func createTestCertificateByIssuer(name string, issuer *certKeyPair) (*certKeyPa issuerCert = issuer.Certificate issuerKey = issuer.PrivateKey } else { + template.IsCA = true + template.KeyUsage |= x509.KeyUsageCertSign issuerCert = &template issuerKey = priv } From a2fc7eee1a1e3d3db152b7fd1510707137b97db0 Mon Sep 17 00:00:00 2001 From: addie9000 Date: Tue, 11 Apr 2017 17:57:15 +0900 Subject: [PATCH 13/23] Fix indefinite form parsing to parse nested indefinite form correctly. --- ber.go | 45 +++++++++++++++++++++++++++++++++------------ ber_test.go | 38 +++++++++++++++++++++++++++++++++++++- 2 files changed, 70 insertions(+), 13 deletions(-) diff --git a/ber.go b/ber.go index 2f380d8..ce7576d 100644 --- a/ber.go +++ b/ber.go @@ -161,7 +161,7 @@ func readObject(ber []byte, offset int) (asn1Object, int, error) { var length int l := ber[offset] offset++ - hack := 0 + indefinite := false if l > 0x80 { numberOfBytes := (int)(l & 0x7F) if numberOfBytes > 4 { // int is only guaranteed to be 32bit @@ -180,14 +180,7 @@ func readObject(ber []byte, offset int) (asn1Object, int, error) { offset++ } } else if l == 0x80 { - // find length by searching content - markerIndex := bytes.LastIndex(ber[offset:], []byte{0x0, 0x0}) - if markerIndex == -1 { - return nil, 0, errors.New("ber2der: Invalid BER format") - } - length = markerIndex - hack = 2 - //fmt.Printf("--> (compute length) marker found at offset: %d\n", markerIndex+offset) + indefinite = true } else { length = (int)(l) } @@ -201,6 +194,9 @@ func readObject(ber []byte, offset int) (asn1Object, int, error) { //fmt.Printf("--> content end : %d\n", contentEnd) //fmt.Printf("--> content : % X\n", ber[offset:contentEnd]) var obj asn1Object + if indefinite && kind == 0 { + return nil, 0, errors.New("ber2der: Indefinite form tag must have constructed encoding") + } if kind == 0 { obj = asn1Primitive{ tagBytes: ber[tagStart:tagEnd], @@ -209,14 +205,26 @@ func readObject(ber []byte, offset int) (asn1Object, int, error) { } } else { var subObjects []asn1Object - for offset < contentEnd { + for (offset < contentEnd) || indefinite { var subObj asn1Object var err error - subObj, offset, err = readObject(ber[:contentEnd+hack], offset) + + subObj, offset, err = readObject(ber, offset) if err != nil { return nil, 0, err } subObjects = append(subObjects, subObj) + + if indefinite { + terminated, err := isIndefiniteTermination(ber, offset) + if err != nil { + return nil, 0, err + } + + if terminated { + break + } + } } obj = asn1Structured{ tagBytes: ber[tagStart:tagEnd], @@ -224,5 +232,18 @@ func readObject(ber []byte, offset int) (asn1Object, int, error) { } } - return obj, contentEnd + hack, nil + // Apply indefinite form length with 0x0000 terminator. + if indefinite { + contentEnd = offset + 2 + } + + return obj, contentEnd, nil +} + +func isIndefiniteTermination(ber []byte, offset int) (bool, error) { + if len(ber)-offset < 2 { + return false, errors.New("ber2der: Invalid BER format") + } + + return bytes.Index(ber[offset:], []byte{0x0, 0x0}) == 0, nil } diff --git a/ber_test.go b/ber_test.go index 32dc88a..19a0f51 100644 --- a/ber_test.go +++ b/ber_test.go @@ -45,7 +45,7 @@ func TestBer2Der_Negatives(t *testing.T) { {[]byte{0x30, 0x85}, "length too long"}, {[]byte{0x30, 0x84, 0x80, 0x0, 0x0, 0x0}, "length is negative"}, {[]byte{0x30, 0x82, 0x0, 0x1}, "length has leading zero"}, - {[]byte{0x30, 0x80, 0x1, 0x2}, "Invalid BER format"}, + {[]byte{0x30, 0x80, 0x1, 0x2, 0x1, 0x2}, "Invalid BER format"}, {[]byte{0x30, 0x03, 0x01, 0x02}, "length is more than available data"}, } @@ -59,3 +59,39 @@ func TestBer2Der_Negatives(t *testing.T) { } } } + +func TestBer2Der_NestedMultipleIndefinite(t *testing.T) { + // indefinite length fixture + ber := []byte{0x30, 0x80, 0x30, 0x80, 0x02, 0x01, 0x01, 0x00, 0x00, 0x30, 0x80, 0x02, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00} + expected := []byte{0x30, 0x0A, 0x30, 0x03, 0x02, 0x01, 0x01, 0x30, 0x03, 0x02, 0x01, 0x02} + + der, err := ber2der(ber) + if err != nil { + t.Fatalf("ber2der failed with error: %v", err) + } + if bytes.Compare(der, expected) != 0 { + t.Errorf("ber2der result did not match.\n\tExpected: % X\n\tActual: % X", expected, der) + } + + if der2, err := ber2der(der); err != nil { + t.Errorf("ber2der on DER bytes failed with error: %v", err) + } else { + if !bytes.Equal(der, der2) { + t.Error("ber2der is not idempotent") + } + } + var thing struct { + Nest1 struct { + Number int + } + Nest2 struct { + Number int + } + } + rest, err := asn1.Unmarshal(der, &thing) + if err != nil { + t.Errorf("Cannot parse resulting DER because: %v", err) + } else if len(rest) > 0 { + t.Errorf("Resulting DER has trailing data: % X", rest) + } +} From fa02d3678994001ed5650bfcd96fe9e4498ee372 Mon Sep 17 00:00:00 2001 From: Andrew Smith Date: Thu, 22 Feb 2018 17:43:26 -0500 Subject: [PATCH 14/23] Fix hardcoded signature algorithm for verifying signers Since Go 1.10, the SignatureAlgorithm is now validated against the public key type. It was "working" before because the Amazon key used DSA with SHA1, and so the RSA/DSA mismatch didn't matter since the SHA1 hash type did. This uses some unexported code from `crypto/x509` that looks up a signature algorithm for a given `pxix.AlgorithmIdentifier` Fixes #27 Fixes #28 Fixes #29 --- pkcs7.go | 4 +- x509.go | 130 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+), 2 deletions(-) create mode 100644 x509.go diff --git a/pkcs7.go b/pkcs7.go index 6a9db6f..abe66a5 100644 --- a/pkcs7.go +++ b/pkcs7.go @@ -272,7 +272,7 @@ func verifySignature(p7 *PKCS7, signer signerInfo) error { return errors.New("pkcs7: No certificate for signer") } - algo := x509.SHA1WithRSA + algo := getSignatureAlgorithmFromAI(signer.DigestEncryptionAlgorithm) return cert.CheckSignature(algo, signedData, signer.EncryptedDigest) } @@ -651,7 +651,7 @@ func (sd *SignedData) AddSigner(cert *x509.Certificate, pkey crypto.PrivateKey, signer := signerInfo{ AuthenticatedAttributes: finalAttrs, DigestAlgorithm: pkix.AlgorithmIdentifier{Algorithm: oidDigestAlgorithmSHA1}, - DigestEncryptionAlgorithm: pkix.AlgorithmIdentifier{Algorithm: oidEncryptionAlgorithmRSA}, + DigestEncryptionAlgorithm: pkix.AlgorithmIdentifier{Algorithm: oidSignatureSHA1WithRSA}, IssuerAndSerialNumber: ias, EncryptedDigest: signature, Version: 1, diff --git a/x509.go b/x509.go new file mode 100644 index 0000000..3dc85fb --- /dev/null +++ b/x509.go @@ -0,0 +1,130 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the go/golang LICENSE file. + +package pkcs7 + +// These are private constants and functions from the crypto/x509 package that +// are useful when dealing with signatures verified by x509 certificates + +import ( + "bytes" + "crypto" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" +) + +var ( + oidSignatureMD2WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2} + oidSignatureMD5WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4} + oidSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5} + oidSignatureSHA256WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11} + oidSignatureSHA384WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12} + oidSignatureSHA512WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13} + oidSignatureRSAPSS = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 10} + oidSignatureDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3} + oidSignatureDSAWithSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 3, 2} + oidSignatureECDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1} + oidSignatureECDSAWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2} + oidSignatureECDSAWithSHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3} + oidSignatureECDSAWithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4} + + oidSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 1} + oidSHA384 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 2} + oidSHA512 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 3} + + oidMGF1 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 8} + + // oidISOSignatureSHA1WithRSA means the same as oidSignatureSHA1WithRSA + // but it's specified by ISO. Microsoft's makecert.exe has been known + // to produce certificates with this OID. + oidISOSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 29} +) + +var signatureAlgorithmDetails = []struct { + algo x509.SignatureAlgorithm + name string + oid asn1.ObjectIdentifier + pubKeyAlgo x509.PublicKeyAlgorithm + hash crypto.Hash +}{ + {x509.MD2WithRSA, "MD2-RSA", oidSignatureMD2WithRSA, x509.RSA, crypto.Hash(0) /* no value for MD2 */}, + {x509.MD5WithRSA, "MD5-RSA", oidSignatureMD5WithRSA, x509.RSA, crypto.MD5}, + {x509.SHA1WithRSA, "SHA1-RSA", oidSignatureSHA1WithRSA, x509.RSA, crypto.SHA1}, + {x509.SHA1WithRSA, "SHA1-RSA", oidISOSignatureSHA1WithRSA, x509.RSA, crypto.SHA1}, + {x509.SHA256WithRSA, "SHA256-RSA", oidSignatureSHA256WithRSA, x509.RSA, crypto.SHA256}, + {x509.SHA384WithRSA, "SHA384-RSA", oidSignatureSHA384WithRSA, x509.RSA, crypto.SHA384}, + {x509.SHA512WithRSA, "SHA512-RSA", oidSignatureSHA512WithRSA, x509.RSA, crypto.SHA512}, + {x509.SHA256WithRSAPSS, "SHA256-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA256}, + {x509.SHA384WithRSAPSS, "SHA384-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA384}, + {x509.SHA512WithRSAPSS, "SHA512-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA512}, + {x509.DSAWithSHA1, "DSA-SHA1", oidSignatureDSAWithSHA1, x509.DSA, crypto.SHA1}, + {x509.DSAWithSHA256, "DSA-SHA256", oidSignatureDSAWithSHA256, x509.DSA, crypto.SHA256}, + {x509.ECDSAWithSHA1, "ECDSA-SHA1", oidSignatureECDSAWithSHA1, x509.ECDSA, crypto.SHA1}, + {x509.ECDSAWithSHA256, "ECDSA-SHA256", oidSignatureECDSAWithSHA256, x509.ECDSA, crypto.SHA256}, + {x509.ECDSAWithSHA384, "ECDSA-SHA384", oidSignatureECDSAWithSHA384, x509.ECDSA, crypto.SHA384}, + {x509.ECDSAWithSHA512, "ECDSA-SHA512", oidSignatureECDSAWithSHA512, x509.ECDSA, crypto.SHA512}, +} + +// pssParameters reflects the parameters in an AlgorithmIdentifier that +// specifies RSA PSS. See https://tools.ietf.org/html/rfc3447#appendix-A.2.3 +type pssParameters struct { + // The following three fields are not marked as + // optional because the default values specify SHA-1, + // which is no longer suitable for use in signatures. + Hash pkix.AlgorithmIdentifier `asn1:"explicit,tag:0"` + MGF pkix.AlgorithmIdentifier `asn1:"explicit,tag:1"` + SaltLength int `asn1:"explicit,tag:2"` + TrailerField int `asn1:"optional,explicit,tag:3,default:1"` +} + +func getSignatureAlgorithmFromAI(ai pkix.AlgorithmIdentifier) x509.SignatureAlgorithm { + if !ai.Algorithm.Equal(oidSignatureRSAPSS) { + for _, details := range signatureAlgorithmDetails { + if ai.Algorithm.Equal(details.oid) { + return details.algo + } + } + return x509.UnknownSignatureAlgorithm + } + + // RSA PSS is special because it encodes important parameters + // in the Parameters. + + var params pssParameters + if _, err := asn1.Unmarshal(ai.Parameters.FullBytes, ¶ms); err != nil { + return x509.UnknownSignatureAlgorithm + } + + var mgf1HashFunc pkix.AlgorithmIdentifier + if _, err := asn1.Unmarshal(params.MGF.Parameters.FullBytes, &mgf1HashFunc); err != nil { + return x509.UnknownSignatureAlgorithm + } + + // PSS is greatly overburdened with options. This code forces + // them into three buckets by requiring that the MGF1 hash + // function always match the message hash function (as + // recommended in + // https://tools.ietf.org/html/rfc3447#section-8.1), that the + // salt length matches the hash length, and that the trailer + // field has the default value. + if !bytes.Equal(params.Hash.Parameters.FullBytes, asn1.NullBytes) || + !params.MGF.Algorithm.Equal(oidMGF1) || + !mgf1HashFunc.Algorithm.Equal(params.Hash.Algorithm) || + !bytes.Equal(mgf1HashFunc.Parameters.FullBytes, asn1.NullBytes) || + params.TrailerField != 1 { + return x509.UnknownSignatureAlgorithm + } + + switch { + case params.Hash.Algorithm.Equal(oidSHA256) && params.SaltLength == 32: + return x509.SHA256WithRSAPSS + case params.Hash.Algorithm.Equal(oidSHA384) && params.SaltLength == 48: + return x509.SHA384WithRSAPSS + case params.Hash.Algorithm.Equal(oidSHA512) && params.SaltLength == 64: + return x509.SHA512WithRSAPSS + } + + return x509.UnknownSignatureAlgorithm +} From d0374ff8c52e434ce89737cc07e53f86edd6744e Mon Sep 17 00:00:00 2001 From: Andrew Smith Date: Thu, 22 Feb 2018 17:44:29 -0500 Subject: [PATCH 15/23] Bump tested Go versions to include 1.9 & 1.10 --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index c7154e4..cdbeb0b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,7 @@ language: go go: - - 1.6 - - 1.7 + - 1.8 + - 1.9 + - 1.10 - tip From 6a48d77de6f4961a416da801e5528955ff8f3658 Mon Sep 17 00:00:00 2001 From: Andrew Smith Date: Thu, 22 Feb 2018 19:09:23 -0500 Subject: [PATCH 16/23] Fix verifying App Store receipts App Store receipts have an unusual DigestEncryptionAlgorithm, this may be because it doesn't have any authenticated attributes. If the new strategy for determining the correct x509.SignatureAlgorithm fails, we fallback to the old SHA1WithRSA algo. --- pkcs7.go | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/pkcs7.go b/pkcs7.go index abe66a5..b7175b7 100644 --- a/pkcs7.go +++ b/pkcs7.go @@ -240,6 +240,10 @@ func (p7 *PKCS7) Verify() (err error) { func verifySignature(p7 *PKCS7, signer signerInfo) error { signedData := p7.Content + hash, err := getHashForOID(signer.DigestAlgorithm.Algorithm) + if err != nil { + return err + } if len(signer.AuthenticatedAttributes) > 0 { // TODO(fullsailor): First check the content type match var digest []byte @@ -247,10 +251,6 @@ func verifySignature(p7 *PKCS7, signer signerInfo) error { if err != nil { return err } - hash, err := getHashForOID(signer.DigestAlgorithm.Algorithm) - if err != nil { - return err - } h := hash.New() h.Write(p7.Content) computed := h.Sum(nil) @@ -273,6 +273,17 @@ func verifySignature(p7 *PKCS7, signer signerInfo) error { } algo := getSignatureAlgorithmFromAI(signer.DigestEncryptionAlgorithm) + if algo == x509.UnknownSignatureAlgorithm { + // I'm not sure what the spec here is, and the openssl sources were not + // helpful. But, this is what App Store receipts appear to do. + // The DigestEncryptionAlgorithm is just "rsaEncryption (PKCS #1)" + // But we're expecting a digest + encryption algorithm. So... we're going + // to determine an algorithm based on the DigestAlgorithm and this + // encryption algorithm. + if signer.DigestEncryptionAlgorithm.Algorithm.Equal(oidEncryptionAlgorithmRSA) { + algo = getRSASignatureAlgorithmForDigestAlgorithm(hash) + } + } return cert.CheckSignature(algo, signedData, signer.EncryptedDigest) } @@ -312,6 +323,15 @@ func getHashForOID(oid asn1.ObjectIdentifier) (crypto.Hash, error) { return crypto.Hash(0), ErrUnsupportedAlgorithm } +func getRSASignatureAlgorithmForDigestAlgorithm(hash crypto.Hash) x509.SignatureAlgorithm { + for _, details := range signatureAlgorithmDetails { + if details.pubKeyAlgo == x509.RSA && details.hash == hash { + return details.algo + } + } + return x509.UnknownSignatureAlgorithm +} + // GetOnlySigner returns an x509.Certificate for the first signer of the signed // data payload. If there are more or less than one signer, nil is returned func (p7 *PKCS7) GetOnlySigner() *x509.Certificate { From 917222b36540e20db3513e779a58aa3f5618c52b Mon Sep 17 00:00:00 2001 From: Andrew Smith Date: Thu, 22 Feb 2018 19:14:41 -0500 Subject: [PATCH 17/23] Don't use asn1.NullBytes, its unavailable in Go 1.8 --- x509.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/x509.go b/x509.go index 3dc85fb..54f165e 100644 --- a/x509.go +++ b/x509.go @@ -79,6 +79,9 @@ type pssParameters struct { TrailerField int `asn1:"optional,explicit,tag:3,default:1"` } +// asn1.NullBytes is not available prior to Go 1.9 +var nullBytes = []byte{asn1.TagNull, 0} + func getSignatureAlgorithmFromAI(ai pkix.AlgorithmIdentifier) x509.SignatureAlgorithm { if !ai.Algorithm.Equal(oidSignatureRSAPSS) { for _, details := range signatureAlgorithmDetails { @@ -109,10 +112,10 @@ func getSignatureAlgorithmFromAI(ai pkix.AlgorithmIdentifier) x509.SignatureAlgo // https://tools.ietf.org/html/rfc3447#section-8.1), that the // salt length matches the hash length, and that the trailer // field has the default value. - if !bytes.Equal(params.Hash.Parameters.FullBytes, asn1.NullBytes) || + if !bytes.Equal(params.Hash.Parameters.FullBytes, nullBytes) || !params.MGF.Algorithm.Equal(oidMGF1) || !mgf1HashFunc.Algorithm.Equal(params.Hash.Algorithm) || - !bytes.Equal(mgf1HashFunc.Parameters.FullBytes, asn1.NullBytes) || + !bytes.Equal(mgf1HashFunc.Parameters.FullBytes, nullBytes) || params.TrailerField != 1 { return x509.UnknownSignatureAlgorithm } From 6569da0c90d030533b65a7ddbeccdb74af92ad9e Mon Sep 17 00:00:00 2001 From: Andrew Smith Date: Thu, 22 Feb 2018 19:14:52 -0500 Subject: [PATCH 18/23] Test in Go 1.10, not Go 1.1 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index cdbeb0b..bc12043 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,5 +3,5 @@ language: go go: - 1.8 - 1.9 - - 1.10 + - "1.10" - tip From 663621a0444a567a3b30a3c98ac3b9ece054f81a Mon Sep 17 00:00:00 2001 From: Andrew Smith Date: Thu, 22 Feb 2018 19:23:17 -0500 Subject: [PATCH 19/23] Don't use asn1.TagNull, it was also introduced in Go 1.9 --- x509.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x509.go b/x509.go index 54f165e..195fd0e 100644 --- a/x509.go +++ b/x509.go @@ -80,7 +80,7 @@ type pssParameters struct { } // asn1.NullBytes is not available prior to Go 1.9 -var nullBytes = []byte{asn1.TagNull, 0} +var nullBytes = []byte{5, 0} func getSignatureAlgorithmFromAI(ai pkix.AlgorithmIdentifier) x509.SignatureAlgorithm { if !ai.Algorithm.Equal(oidSignatureRSAPSS) { From 22bc9242bbf67da5c303565113faffd815ab28a8 Mon Sep 17 00:00:00 2001 From: Andrew Smith Date: Sat, 21 Apr 2018 22:06:01 -0400 Subject: [PATCH 20/23] Fix failure to parse enveloped data in Go 1.10 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Go 1.10 is more strict about Asn.1 annotations. This removes the incorrect “explicit” annotation from encryptedContentInfo.EncryptedContent. I’m also using openssl to generate the fixture now so that we aren’t testing with our own output for `Decrypt()` Fixes #31 --- pkcs7.go | 2 +- pkcs7_test.go | 15 ++++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/pkcs7.go b/pkcs7.go index b7175b7..b135663 100644 --- a/pkcs7.go +++ b/pkcs7.go @@ -85,7 +85,7 @@ type recipientInfo struct { type encryptedContentInfo struct { ContentType asn1.ObjectIdentifier ContentEncryptionAlgorithm pkix.AlgorithmIdentifier - EncryptedContent asn1.RawValue `asn1:"tag:0,optional,explicit"` + EncryptedContent asn1.RawValue `asn1:"tag:0,optional"` } type attribute struct { diff --git a/pkcs7_test.go b/pkcs7_test.go index cbe2c5c..55ede8b 100644 --- a/pkcs7_test.go +++ b/pkcs7_test.go @@ -490,15 +490,16 @@ JBOFyaCnMotGNioSHY5hAkEAxyXcNixQ2RpLXJTQZtwnbk0XDcbgB+fBgXnv/4f3 BCvcu85DqJeJyQv44Oe1qsXEX9BfcQIOVaoep35RPlKi9g== -----END PRIVATE KEY-----` -// Content is "This is a test" +// echo -n "This is a test" > test.txt +// openssl cms -encrypt -in test.txt cert.pem var EncryptedTestFixture = ` -----BEGIN PKCS7----- -MIIBFwYJKoZIhvcNAQcDoIIBCDCCAQQCAQAxgcowgccCAQAwMjApMRAwDgYDVQQK -EwdBY21lIENvMRUwEwYDVQQDEwxFZGRhcmQgU3RhcmsCBQDL+CvWMAsGCSqGSIb3 -DQEBAQSBgKyP/5WlRTZD3dWMrLOX6QRNDrXEkQjhmToRwFZdY3LgUh25ZU0S/q4G -dHPV21Fv9lQD+q7l3vfeHw8M6Z1PKi9sHMVfxAkQpvaI96DTIT3YHtuLC1w3geCO -8eFWTq2qS4WChSuS/yhYosjA1kTkE0eLnVZcGw0z/WVuEZznkdyIMDIGCSqGSIb3 -DQEHATARBgUrDgMCBwQImpKsUyMPpQigEgQQRcWWrCRXqpD5Njs0GkJl+g== +MIIBGgYJKoZIhvcNAQcDoIIBCzCCAQcCAQAxgcwwgckCAQAwMjApMRAwDgYDVQQK +EwdBY21lIENvMRUwEwYDVQQDEwxFZGRhcmQgU3RhcmsCBQDL+CvWMA0GCSqGSIb3 +DQEBAQUABIGAyFz7bfI2noUs4FpmYfztm1pVjGyB00p9x0H3gGHEYNXdqlq8VG8d +iq36poWtEkatnwsOlURWZYECSi0g5IAL0U9sj82EN0xssZNaK0S5FTGnB3DPvYgt +HJvcKq7YvNLKMh4oqd17C6GB4oXyEBDj0vZnL7SUoCAOAWELPeC8CTUwMwYJKoZI +hvcNAQcBMBQGCCqGSIb3DQMHBAhEowTkot3a7oAQFD//J/IhFnk+JbkH7HZQFA== -----END PKCS7----- -----BEGIN CERTIFICATE----- MIIB1jCCAUGgAwIBAgIFAMv4K9YwCwYJKoZIhvcNAQELMCkxEDAOBgNVBAoTB0Fj From 720ab682191aa5cb5debb7fe648d719ac0ae4066 Mon Sep 17 00:00:00 2001 From: Andrew Smith Date: Sat, 21 Apr 2018 22:55:57 -0400 Subject: [PATCH 21/23] Fix detached data content type The content type should be oidData. Fixes #24 --- pkcs7.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkcs7.go b/pkcs7.go index b135663..7b20cdc 100644 --- a/pkcs7.go +++ b/pkcs7.go @@ -690,7 +690,7 @@ func (sd *SignedData) AddCertificate(cert *x509.Certificate) { // Detach removes content from the signed data struct to make it a detached signature. // This must be called right before Finish() func (sd *SignedData) Detach() { - sd.sd.ContentInfo = contentInfo{ContentType: oidSignedData} + sd.sd.ContentInfo = contentInfo{ContentType: oidData} } // Finish marshals the content and its signers From 37e4b649f70adfdef774738927041682860c84ba Mon Sep 17 00:00:00 2001 From: Kevin Tezlaf Date: Mon, 23 Apr 2018 09:52:53 -0700 Subject: [PATCH 22/23] Add support for verifying SHA256 signer digest --- pkcs7.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkcs7.go b/pkcs7.go index 7b20cdc..1839f49 100644 --- a/pkcs7.go +++ b/pkcs7.go @@ -319,6 +319,8 @@ func getHashForOID(oid asn1.ObjectIdentifier) (crypto.Hash, error) { switch { case oid.Equal(oidDigestAlgorithmSHA1): return crypto.SHA1, nil + case oid.Equal(oidSHA256): + return crypto.SHA256, nil } return crypto.Hash(0), ErrUnsupportedAlgorithm } From aa41f78c3cf874011e83bdab67ce07c39151f27f Mon Sep 17 00:00:00 2001 From: Leonty Chudinov <34669449+lchudinov@users.noreply.github.com> Date: Wed, 30 Jan 2019 12:39:14 +0500 Subject: [PATCH 23/23] Comment out all lines with encodeIndent variable --- ber.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ber.go b/ber.go index ce7576d..3fa3ddd 100644 --- a/ber.go +++ b/ber.go @@ -5,7 +5,7 @@ import ( "errors" ) -var encodeIndent = 0 +// var encodeIndent = 0 type asn1Object interface { EncodeTo(writer *bytes.Buffer) error @@ -18,7 +18,7 @@ type asn1Structured struct { func (s asn1Structured) EncodeTo(out *bytes.Buffer) error { //fmt.Printf("%s--> tag: % X\n", strings.Repeat("| ", encodeIndent), s.tagBytes) - encodeIndent++ + //encodeIndent++ inner := new(bytes.Buffer) for _, obj := range s.content { err := obj.EncodeTo(inner) @@ -26,7 +26,7 @@ func (s asn1Structured) EncodeTo(out *bytes.Buffer) error { return err } } - encodeIndent-- + //encodeIndent-- out.Write(s.tagBytes) encodeLength(out, inner.Len()) out.Write(inner.Bytes())