diff --git a/go.mod b/go.mod index 3004c97..ce0914d 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,9 @@ go 1.17 require ( github.com/digitorus/pdf v0.1.2 - github.com/digitorus/pkcs7 v0.0.0-20221212123742-001c36b64ec3 - github.com/digitorus/timestamp v0.0.0-20221118121739-0faba6f30e54 + github.com/digitorus/pkcs7 v0.0.0-20230220124406-51331ccfc40f + github.com/digitorus/timestamp v0.0.0-20230220124323-d542479a2425 github.com/mattetti/filebuffer v1.0.1 golang.org/x/crypto v0.7.0 + golang.org/x/text v0.8.0 ) diff --git a/go.sum b/go.sum index cbf0ba1..0619613 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,10 @@ github.com/digitorus/pdf v0.1.2 h1:RjYEJNbiV6Kcn8QzRi6pwHuOaSieUUrg4EZo4b7KuIQ= github.com/digitorus/pdf v0.1.2/go.mod h1:05fDDJhPswBRM7GTfqCxNiDyeNcN0f+IobfOAl5pdXw= github.com/digitorus/pkcs7 v0.0.0-20221019075359-21b8b40e6bb4/go.mod h1:SKVExuS+vpu2l9IoOc0RwqE7NYnb0JlcFHFnEJkVDzc= -github.com/digitorus/pkcs7 v0.0.0-20221212123742-001c36b64ec3 h1:rjCXeRWazGsbcBlExMcAW8H1LGdgJ9r619y7+aeKgds= -github.com/digitorus/pkcs7 v0.0.0-20221212123742-001c36b64ec3/go.mod h1:SKVExuS+vpu2l9IoOc0RwqE7NYnb0JlcFHFnEJkVDzc= -github.com/digitorus/timestamp v0.0.0-20221118121739-0faba6f30e54 h1:VPfvrlgI2O3mQjBBLywtPxjDMlpWYWobXN3nk26Ht0k= -github.com/digitorus/timestamp v0.0.0-20221118121739-0faba6f30e54/go.mod h1:6V2ND8Yf8TOJ4h+9pmUlx8kXvNLBB2QplToVVZQ3rF0= +github.com/digitorus/pkcs7 v0.0.0-20230220124406-51331ccfc40f h1:AoHV/iJ6LjW24bRWrg0zm1xD3Uh83PlNSK0QWH11J0E= +github.com/digitorus/pkcs7 v0.0.0-20230220124406-51331ccfc40f/go.mod h1:SKVExuS+vpu2l9IoOc0RwqE7NYnb0JlcFHFnEJkVDzc= +github.com/digitorus/timestamp v0.0.0-20230220124323-d542479a2425 h1:cbnavmdMqZ3b4hcCxizSO/jO+BxyXp/hU9jyzULJ9g8= +github.com/digitorus/timestamp v0.0.0-20230220124323-d542479a2425/go.mod h1:6V2ND8Yf8TOJ4h+9pmUlx8kXvNLBB2QplToVVZQ3rF0= github.com/mattetti/filebuffer v1.0.1 h1:gG7pyfnSIZCxdoKq+cPa8T0hhYtD9NxCdI4D7PTjRLM= github.com/mattetti/filebuffer v1.0.1/go.mod h1:YdMURNDOttIiruleeVr6f56OrMc+MydEnTcXwtkxNVs= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= @@ -37,6 +37,7 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= diff --git a/sign/helpers.go b/sign/helpers.go index 1781785..d2b60b8 100644 --- a/sign/helpers.go +++ b/sign/helpers.go @@ -11,6 +11,9 @@ import ( "time" "github.com/digitorus/pdf" + + "golang.org/x/text/encoding/unicode" + "golang.org/x/text/transform" ) func findFirstPage(parent pdf.Value) (pdf.Value, error) { @@ -35,11 +38,28 @@ func findFirstPage(parent pdf.Value) (pdf.Value, error) { } func pdfString(text string) string { + if !isASCII(text) { + // UTF-16BE + enc := unicode.UTF16(unicode.BigEndian, unicode.UseBOM).NewEncoder() + res, _, err := transform.String(enc, text) + if err != nil { + panic(err) + } + return "(" + res + ")" + } + + // UTF-8 + // (\357\273\277Layer 1) % UTF-8 Layer 1 Name + // % UTF-8 Layer 2 Name + // text = "\357\273\277" + text + // text = hex.EncodeToString([]byte(text)) + // text = "<" + text + ">" + + // PDFDocEncoded text = strings.Replace(text, "\\", "\\\\", -1) text = strings.Replace(text, ")", "\\)", -1) text = strings.Replace(text, "(", "\\(", -1) text = strings.Replace(text, "\r", "\\r", -1) - text = "(" + text + ")" return text @@ -167,3 +187,12 @@ func getOIDFromHashAlgorithm(target crypto.Hash) asn1.ObjectIdentifier { } return nil } + +func isASCII(s string) bool { + for _, r := range s { + if r > '\u007F' { + return false + } + } + return true +} diff --git a/sign/sign_test.go b/sign/sign_test.go index 44a211c..e329307 100644 --- a/sign/sign_test.go +++ b/sign/sign_test.go @@ -68,7 +68,7 @@ func TestReaderCanReadPDF(t *testing.T) { if ext != ".pdf" { continue } - + fileName := f.Name() t.Run(fileName, func(st *testing.T) { st.Parallel() @@ -285,6 +285,82 @@ func TestSignPDFFile(t *testing.T) { } } +func TestSignPDFFileUTF8(t *testing.T) { + certificate_data_block, _ := pem.Decode([]byte(signCertPem)) + if certificate_data_block == nil { + t.Errorf("failed to parse PEM block containing the certificate") + return + } + + cert, err := x509.ParseCertificate(certificate_data_block.Bytes) + if err != nil { + t.Errorf("%s", err.Error()) + return + } + + key_data_block, _ := pem.Decode([]byte(signKeyPem)) + if key_data_block == nil { + t.Errorf("failed to parse PEM block containing the private key") + return + } + + pkey, err := x509.ParsePKCS1PrivateKey(key_data_block.Bytes) + if err != nil { + t.Errorf("%s", err.Error()) + return + } + + tmpfile, err := os.CreateTemp("", "pdfsign_test") + if err != nil { + t.Errorf("%s", err.Error()) + return + } + + signerName := "姓名" + signerLocation := "位置" + err = SignFile("../testfiles/testfile20.pdf", tmpfile.Name(), SignData{ + Signature: SignDataSignature{ + Info: SignDataSignatureInfo{ + Name: signerName, + Location: signerLocation, + Reason: "Test with UTF-8", + ContactInfo: "None", + Date: time.Now().Local(), + }, + CertType: CertificationSignature, + DocMDPPerm: AllowFillingExistingFormFieldsAndSignaturesPerms, + }, + DigestAlgorithm: crypto.SHA512, + Signer: pkey, + Certificate: cert, + }) + + if err != nil { + os.Remove(tmpfile.Name()) + t.Errorf("%s: %s", "testfile20.pdf", err.Error()) + return + } + + info, err := verify.File(tmpfile) + if err != nil { + t.Errorf("%s: %s", tmpfile.Name(), err.Error()) + + err2 := os.Rename(tmpfile.Name(), "../testfiles/failed/testfile20.pdf") + if err2 != nil { + t.Error(err2) + } + } else { + if info.Signers[0].Name != signerName { + t.Errorf("expected %q, got %q", signerName, info.Signers[0].Name) + } + if info.Signers[0].Location != signerLocation { + t.Errorf("expected %q, got %q", signerLocation, info.Signers[0].Location) + } + + os.Remove(tmpfile.Name()) + } +} + func BenchmarkSignPDF(b *testing.B) { certificate_data_block, _ := pem.Decode([]byte(signCertPem)) if certificate_data_block == nil {