From 099b765c01e182db6c652e2ec724a87c55bf467d Mon Sep 17 00:00:00 2001 From: Jeroen Bobbeldijk Date: Tue, 11 Jul 2017 20:25:15 +0200 Subject: [PATCH] Add TSA --- sign.go | 3 ++ sign/pdfsignature.go | 102 ++++++++++++++++++++++++++++++++++++++++++- sign/sign.go | 7 +++ 3 files changed, 111 insertions(+), 1 deletion(-) diff --git a/sign.go b/sign.go index 698bfb6..057dc45 100644 --- a/sign.go +++ b/sign.go @@ -103,6 +103,9 @@ func main() { }, Signer: pkey, Certificate: cert, + TSA: sign.TSA{ + URL: "http://aatl-timestamp.globalsign.com/tsa/aohfewat2389535fnasgnlg5m23", + }, }) if err != nil { log.Println(err) diff --git a/sign/pdfsignature.go b/sign/pdfsignature.go index 956bd2e..d2a93cb 100644 --- a/sign/pdfsignature.go +++ b/sign/pdfsignature.go @@ -2,14 +2,32 @@ package sign import ( "bytes" + "encoding/asn1" "encoding/hex" + "errors" + "fmt" "io" + "io/ioutil" + "net/http" "strconv" "strings" "github.com/digitorus/pkcs7" + "github.com/digitorus/timestamp" ) +type pkiStatusInfo struct { + Status int + StatusString string `asn1:"optional"` + FailInfo int `asn1:"optional"` +} + +// 2.4.2. Response Format +type TSAResponse struct { + Status pkiStatusInfo + TimeStampToken asn1.RawValue +} + var signatureMaxLength = uint32(11742) var signatureByteRangePlaceholder = "/ByteRange[0 ********** ********** **********]" @@ -88,8 +106,41 @@ func (context *SignContext) createSignature() ([]byte, error) { return nil, err } + signer_config := pkcs7.SignerInfoConfig{} + + if context.SignData.TSA.URL != "" { + timestamp_response, err := context.GetTSA(sign_content) + if err != nil { + return nil, err + } + + var rest []byte + var resp TSAResponse + if rest, err = asn1.Unmarshal(timestamp_response, &resp); err != nil { + return nil, err + } + if len(rest) > 0 { + return nil, errors.New("trailing data in Time-Stamp response") + } + + if resp.Status.Status > 0 { + return nil, errors.New(fmt.Sprintf("%s: %s", timestamp.FailureInfo(resp.Status.FailInfo).String(), resp.Status.StatusString)) + } + + if len(resp.TimeStampToken.Bytes) == 0 { + return nil, errors.New("no pkcs7 data in Time-Stamp response") + } + + timestamp_attribute := pkcs7.Attribute{ + Type: asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 16, 2, 14}, + Value: resp.TimeStampToken, + } + signer_config.ExtraUnsignedAttributes = append(signer_config.ExtraSignedAttributes, timestamp_attribute) + } + // Add the signer and sign the data. - if err := signed_data.AddSignerChain(context.SignData.Certificate, context.SignData.Signer, context.SignData.CertificateChain, pkcs7.SignerInfoConfig{}); err != nil { + if err := signed_data.AddSignerChain(context.SignData.Certificate, context.SignData.Signer, context.SignData.CertificateChain, signer_config); err != nil { + return nil, err } @@ -99,6 +150,55 @@ func (context *SignContext) createSignature() ([]byte, error) { return signed_data.Finish() } +func (context *SignContext) GetTSA(sign_content []byte) (timestamp_response []byte, err error) { + sign_reader := bytes.NewReader(sign_content) + ts_request, err := timestamp.CreateRequest(sign_reader, nil) + if err != nil { + return nil, err + } + + ts_request_reader := bytes.NewReader(ts_request) + req, err := http.NewRequest("POST", context.SignData.TSA.URL, ts_request_reader) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", "application/timestamp-query") + req.Header.Add("Content-Transfer-Encoding", "binary") + + if context.SignData.TSA.Username != "" && context.SignData.TSA.Password != "" { + req.SetBasicAuth(context.SignData.TSA.Username, context.SignData.TSA.Password) + } + + client := &http.Client{} + resp, err := client.Do(req) + code := 0 + + if resp != nil { + code = resp.StatusCode + } + + if err != nil || (code < 200 || code > 299) { + if err == nil { + defer resp.Body.Close() + body, _ := ioutil.ReadAll(resp.Body) + err = errors.New("Non success response (" + strconv.Itoa(code) + "): " + string(body)) + } else { + err = errors.New("Non success response (" + strconv.Itoa(code) + ")") + } + + return nil, err + } + + defer resp.Body.Close() + timestamp_response_body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + return timestamp_response_body, nil +} + func (context *SignContext) replaceSignature() error { signature, err := context.createSignature() if err != nil { diff --git a/sign/sign.go b/sign/sign.go index e8c1d30..b92b7e1 100644 --- a/sign/sign.go +++ b/sign/sign.go @@ -16,12 +16,19 @@ type CatalogData struct { RootString string } +type TSA struct { + URL string + Username string + Password string +} + type SignData struct { ObjectId uint32 Signature SignDataSignature Signer crypto.Signer Certificate *x509.Certificate CertificateChain []*x509.Certificate + TSA TSA } type VisualSignData struct {