Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
xuri
excelize
提交
98f1a699
excelize
项目概览
xuri
/
excelize
通知
13
Star
2
Fork
4
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
excelize
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
已验证
提交
98f1a699
编写于
9月 02, 2020
作者:
xurime
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
support ECMA-376 document standard encryption, ref #199
上级
4177c158
变更
6
隐藏空白更改
内联
并排
Showing
6 changed file
with
209 addition
and
30 deletion
+209
-30
crypt.go
crypt.go
+199
-22
drawing.go
drawing.go
+0
-3
excelize.go
excelize.go
+1
-1
excelize_test.go
excelize_test.go
+9
-1
test/encryptAES.xlsx
test/encryptAES.xlsx
+0
-0
xmlChart.go
xmlChart.go
+0
-3
未找到文件。
en
crypt.go
→
crypt.go
浏览文件 @
98f1a699
...
...
@@ -20,6 +20,7 @@ import (
"encoding/base64"
"encoding/binary"
"encoding/xml"
"errors"
"hash"
"strings"
...
...
@@ -33,6 +34,7 @@ var (
blockKey
=
[]
byte
{
0x14
,
0x6e
,
0x0b
,
0xe7
,
0xab
,
0xac
,
0xd0
,
0xd6
}
// Block keys used for encryption
packageOffset
=
8
// First 8 bytes are the size of the stream
packageEncryptionChunkSize
=
4096
iterCount
=
50000
cryptoIdentifier
=
[]
byte
{
// checking protect workbook by [MS-OFFCRYPTO] - v20181211 3.1 FeatureIdentifier
0x3c
,
0x00
,
0x00
,
0x00
,
0x4d
,
0x00
,
0x69
,
0x00
,
0x63
,
0x00
,
0x72
,
0x00
,
0x6f
,
0x00
,
0x73
,
0x00
,
0x6f
,
0x00
,
0x66
,
0x00
,
0x74
,
0x00
,
0x2e
,
0x00
,
0x43
,
0x00
,
0x6f
,
0x00
,
0x6e
,
0x00
,
0x74
,
0x00
,
...
...
@@ -40,6 +42,9 @@ var (
0x74
,
0x00
,
0x61
,
0x00
,
0x53
,
0x00
,
0x70
,
0x00
,
0x61
,
0x00
,
0x63
,
0x00
,
0x65
,
0x00
,
0x73
,
0x00
,
0x01
,
0x00
,
0x00
,
0x00
,
0x01
,
0x00
,
0x00
,
0x00
,
0x01
,
0x00
,
0x00
,
0x00
,
}
oleIdentifier
=
[]
byte
{
0xd0
,
0xcf
,
0x11
,
0xe0
,
0xa1
,
0xb1
,
0x1a
,
0xe1
,
}
)
// Encryption specifies the encryption structure, streams, and storages are
...
...
@@ -93,37 +98,56 @@ type EncryptedKey struct {
KeyData
}
// Decrypt API decrypt the CFB file format with Agile Encryption. Support
// cryptographic algorithm: MD4, MD5, RIPEMD-160, SHA1, SHA256, SHA384 and
// SHA512.
// StandardEncryptionHeader structure is used by ECMA-376 document encryption
// [ECMA-376] and Office binary document RC4 CryptoAPI encryption, to specify
// encryption properties for an encrypted stream.
type
StandardEncryptionHeader
struct
{
Flags
uint32
SizeExtra
uint32
AlgID
uint32
AlgIDHash
uint32
KeySize
uint32
ProviderType
uint32
Reserved1
uint32
Reserved2
uint32
CspName
string
}
// StandardEncryptionVerifier structure is used by Office Binary Document RC4
// CryptoAPI Encryption and ECMA-376 Document Encryption. Every usage of this
// structure MUST specify the hashing algorithm and encryption algorithm used
// in the EncryptionVerifier structure.
type
StandardEncryptionVerifier
struct
{
SaltSize
uint32
Salt
[]
byte
EncryptedVerifier
[]
byte
VerifierHashSize
uint32
EncryptedVerifierHash
[]
byte
}
// Decrypt API decrypt the CFB file format with ECMA-376 agile encryption and
// standard encryption. Support cryptographic algorithm: MD4, MD5, RIPEMD-160,
// SHA1, SHA256, SHA384 and SHA512 currently.
func
Decrypt
(
raw
[]
byte
,
opt
*
Options
)
(
packageBuf
[]
byte
,
err
error
)
{
doc
,
err
:=
mscfb
.
New
(
bytes
.
NewReader
(
raw
))
if
err
!=
nil
{
return
}
encryptionInfoBuf
,
encryptedPackageBuf
:=
extractPart
(
doc
)
var
encryptionInfo
Encryption
if
e
ncryptionInfo
,
err
=
parseEncryptionInfo
(
encryptionInfoBuf
[
8
:
]);
err
!=
nil
{
mechanism
,
err
:=
encryptionMechanism
(
encryptionInfoBuf
)
if
e
rr
!=
nil
||
mechanism
==
"extensible"
{
return
}
// Convert the password into an encryption key.
key
,
err
:=
convertPasswdToKey
(
opt
.
Password
,
encryptionInfo
)
if
err
!=
nil
{
return
switch
mechanism
{
case
"agile"
:
return
agileDecrypt
(
encryptionInfoBuf
,
encryptedPackageBuf
,
opt
)
case
"standard"
:
return
standardDecrypt
(
encryptionInfoBuf
,
encryptedPackageBuf
,
opt
)
default
:
err
=
errors
.
New
(
"unsupport encryption mechanism"
)
break
}
// Use the key to decrypt the package key.
encryptedKey
:=
encryptionInfo
.
KeyEncryptors
.
KeyEncryptor
[
0
]
.
EncryptedKey
saltValue
,
err
:=
base64
.
StdEncoding
.
DecodeString
(
encryptedKey
.
SaltValue
)
if
err
!=
nil
{
return
}
encryptedKeyValue
,
err
:=
base64
.
StdEncoding
.
DecodeString
(
encryptedKey
.
EncryptedKeyValue
)
if
err
!=
nil
{
return
}
packageKey
,
err
:=
crypt
(
false
,
encryptedKey
.
CipherAlgorithm
,
encryptedKey
.
CipherChaining
,
key
,
saltValue
,
encryptedKeyValue
)
// Use the package key to decrypt the package.
return
cryptPackage
(
false
,
packageKey
,
encryptedPackageBuf
,
encryptionInfo
)
return
}
// extractPart extract data from storage by specified part name.
...
...
@@ -149,6 +173,159 @@ func extractPart(doc *mscfb.Reader) (encryptionInfoBuf, encryptedPackageBuf []by
return
}
// encryptionMechanism parse password-protected documents created mechanism.
func
encryptionMechanism
(
buffer
[]
byte
)
(
mechanism
string
,
err
error
)
{
if
len
(
buffer
)
<
4
{
err
=
errors
.
New
(
"unknown encryption mechanism"
)
return
}
versionMajor
,
versionMinor
:=
binary
.
LittleEndian
.
Uint16
(
buffer
[
0
:
2
]),
binary
.
LittleEndian
.
Uint16
(
buffer
[
2
:
4
])
if
versionMajor
==
4
&&
versionMinor
==
4
{
mechanism
=
"agile"
return
}
else
if
(
2
<=
versionMajor
&&
versionMajor
<=
4
)
&&
versionMinor
==
2
{
mechanism
=
"standard"
return
}
else
if
(
versionMajor
==
3
||
versionMajor
==
4
)
&&
versionMinor
==
3
{
mechanism
=
"extensible"
}
err
=
errors
.
New
(
"unsupport encryption mechanism"
)
return
}
// ECMA-376 Standard Encryption
// standardDecrypt decrypt the CFB file format with ECMA-376 standard encryption.
func
standardDecrypt
(
encryptionInfoBuf
,
encryptedPackageBuf
[]
byte
,
opt
*
Options
)
([]
byte
,
error
)
{
encryptionHeaderSize
:=
binary
.
LittleEndian
.
Uint32
(
encryptionInfoBuf
[
8
:
12
])
block
:=
encryptionInfoBuf
[
12
:
12
+
encryptionHeaderSize
]
header
:=
StandardEncryptionHeader
{
Flags
:
binary
.
LittleEndian
.
Uint32
(
block
[
:
4
]),
SizeExtra
:
binary
.
LittleEndian
.
Uint32
(
block
[
4
:
8
]),
AlgID
:
binary
.
LittleEndian
.
Uint32
(
block
[
8
:
12
]),
AlgIDHash
:
binary
.
LittleEndian
.
Uint32
(
block
[
12
:
16
]),
KeySize
:
binary
.
LittleEndian
.
Uint32
(
block
[
16
:
20
]),
ProviderType
:
binary
.
LittleEndian
.
Uint32
(
block
[
20
:
24
]),
Reserved1
:
binary
.
LittleEndian
.
Uint32
(
block
[
24
:
28
]),
Reserved2
:
binary
.
LittleEndian
.
Uint32
(
block
[
28
:
32
]),
CspName
:
string
(
block
[
32
:
]),
}
block
=
encryptionInfoBuf
[
12
+
encryptionHeaderSize
:
]
algIDMap
:=
map
[
uint32
]
string
{
0x0000660E
:
"AES-128"
,
0x0000660F
:
"AES-192"
,
0x00006610
:
"AES-256"
,
}
algorithm
:=
"AES"
_
,
ok
:=
algIDMap
[
header
.
AlgID
]
if
!
ok
{
algorithm
=
"RC4"
}
verifier
:=
standardEncryptionVerifier
(
algorithm
,
block
)
secretKey
,
err
:=
standardConvertPasswdToKey
(
header
,
verifier
,
opt
)
if
err
!=
nil
{
return
nil
,
err
}
// decrypted data
x
:=
encryptedPackageBuf
[
8
:
]
blob
,
err
:=
aes
.
NewCipher
(
secretKey
)
if
err
!=
nil
{
return
nil
,
err
}
decrypted
:=
make
([]
byte
,
len
(
x
))
size
:=
16
for
bs
,
be
:=
0
,
size
;
bs
<
len
(
x
);
bs
,
be
=
bs
+
size
,
be
+
size
{
blob
.
Decrypt
(
decrypted
[
bs
:
be
],
x
[
bs
:
be
])
}
return
decrypted
,
err
}
// standardEncryptionVerifier extract ECMA-376 standard encryption verifier.
func
standardEncryptionVerifier
(
algorithm
string
,
blob
[]
byte
)
StandardEncryptionVerifier
{
verifier
:=
StandardEncryptionVerifier
{
SaltSize
:
binary
.
LittleEndian
.
Uint32
(
blob
[
:
4
]),
Salt
:
blob
[
4
:
20
],
EncryptedVerifier
:
blob
[
20
:
36
],
VerifierHashSize
:
binary
.
LittleEndian
.
Uint32
(
blob
[
36
:
40
]),
}
if
algorithm
==
"RC4"
{
verifier
.
EncryptedVerifierHash
=
blob
[
40
:
60
]
}
else
if
algorithm
==
"AES"
{
verifier
.
EncryptedVerifierHash
=
blob
[
40
:
72
]
}
return
verifier
}
// standardConvertPasswdToKey generate intermediate key from given password.
func
standardConvertPasswdToKey
(
header
StandardEncryptionHeader
,
verifier
StandardEncryptionVerifier
,
opt
*
Options
)
([]
byte
,
error
)
{
encoder
:=
unicode
.
UTF16
(
unicode
.
LittleEndian
,
unicode
.
IgnoreBOM
)
.
NewEncoder
()
passwordBuffer
,
err
:=
encoder
.
Bytes
([]
byte
(
opt
.
Password
))
if
err
!=
nil
{
return
nil
,
err
}
key
:=
hashing
(
"sha1"
,
verifier
.
Salt
,
passwordBuffer
)
for
i
:=
0
;
i
<
iterCount
;
i
++
{
iterator
:=
createUInt32LEBuffer
(
i
)
key
=
hashing
(
"sha1"
,
iterator
,
key
)
}
var
block
int
hfinal
:=
hashing
(
"sha1"
,
key
,
createUInt32LEBuffer
(
block
))
cbRequiredKeyLength
:=
int
(
header
.
KeySize
)
/
8
cbHash
:=
sha1
.
Size
buf1
:=
bytes
.
Repeat
([]
byte
{
0x36
},
64
)
buf1
=
append
(
standardXORBytes
(
hfinal
,
buf1
[
:
cbHash
]),
buf1
[
cbHash
:
]
...
)
x1
:=
hashing
(
"sha1"
,
buf1
)
buf2
:=
bytes
.
Repeat
([]
byte
{
0x5c
},
64
)
buf2
=
append
(
standardXORBytes
(
hfinal
,
buf2
[
:
cbHash
]),
buf2
[
cbHash
:
]
...
)
x2
:=
hashing
(
"sha1"
,
buf2
)
x3
:=
append
(
x1
,
x2
...
)
keyDerived
:=
x3
[
:
cbRequiredKeyLength
]
return
keyDerived
,
err
}
// standardXORBytes perform XOR operations for two bytes slice.
func
standardXORBytes
(
a
,
b
[]
byte
)
[]
byte
{
r
:=
make
([][
2
]
byte
,
len
(
a
),
len
(
a
))
for
i
,
e
:=
range
a
{
r
[
i
]
=
[
2
]
byte
{
e
,
b
[
i
]}
}
buf
:=
make
([]
byte
,
len
(
a
))
for
p
,
q
:=
range
r
{
buf
[
p
]
=
q
[
0
]
^
q
[
1
]
}
return
buf
}
// ECMA-376 Agile Encryption
// agileDecrypt decrypt the CFB file format with ECMA-376 agile encryption.
// Support cryptographic algorithm: MD4, MD5, RIPEMD-160, SHA1, SHA256, SHA384
// and SHA512.
func
agileDecrypt
(
encryptionInfoBuf
,
encryptedPackageBuf
[]
byte
,
opt
*
Options
)
(
packageBuf
[]
byte
,
err
error
)
{
var
encryptionInfo
Encryption
if
encryptionInfo
,
err
=
parseEncryptionInfo
(
encryptionInfoBuf
[
8
:
]);
err
!=
nil
{
return
}
// Convert the password into an encryption key.
key
,
err
:=
convertPasswdToKey
(
opt
.
Password
,
encryptionInfo
)
if
err
!=
nil
{
return
}
// Use the key to decrypt the package key.
encryptedKey
:=
encryptionInfo
.
KeyEncryptors
.
KeyEncryptor
[
0
]
.
EncryptedKey
saltValue
,
err
:=
base64
.
StdEncoding
.
DecodeString
(
encryptedKey
.
SaltValue
)
if
err
!=
nil
{
return
}
encryptedKeyValue
,
err
:=
base64
.
StdEncoding
.
DecodeString
(
encryptedKey
.
EncryptedKeyValue
)
if
err
!=
nil
{
return
}
packageKey
,
err
:=
crypt
(
false
,
encryptedKey
.
CipherAlgorithm
,
encryptedKey
.
CipherChaining
,
key
,
saltValue
,
encryptedKeyValue
)
// Use the package key to decrypt the package.
return
cryptPackage
(
false
,
packageKey
,
encryptedPackageBuf
,
encryptionInfo
)
}
// convertPasswdToKey convert the password into an encryption key.
func
convertPasswdToKey
(
passwd
string
,
encryption
Encryption
)
(
key
[]
byte
,
err
error
)
{
var
b
bytes
.
Buffer
...
...
drawing.go
浏览文件 @
98f1a699
...
...
@@ -59,10 +59,7 @@ func (f *File) prepareChartSheetDrawing(xlsx *xlsxChartsheet, drawingID int, she
func
(
f
*
File
)
addChart
(
formatSet
*
formatChart
,
comboCharts
[]
*
formatChart
)
{
count
:=
f
.
countCharts
()
xlsxChartSpace
:=
xlsxChartSpace
{
XMLNSc
:
NameSpaceDrawingMLChart
.
Value
,
XMLNSa
:
NameSpaceDrawingML
.
Value
,
XMLNSr
:
SourceRelationship
.
Value
,
XMLNSc16r2
:
SourceRelationshipChart201506
.
Value
,
Date1904
:
&
attrValBool
{
Val
:
boolPtr
(
false
)},
Lang
:
&
attrValString
{
Val
:
stringPtr
(
"en-US"
)},
RoundedCorners
:
&
attrValBool
{
Val
:
boolPtr
(
false
)},
...
...
excelize.go
浏览文件 @
98f1a699
...
...
@@ -111,7 +111,7 @@ func OpenReader(r io.Reader, opt ...Options) (*File, error) {
if
err
!=
nil
{
return
nil
,
err
}
if
bytes
.
Contains
(
b
,
crypto
Identifier
)
{
if
bytes
.
Contains
(
b
,
ole
Identifier
)
{
var
option
Options
for
_
,
o
:=
range
opt
{
option
=
o
...
...
excelize_test.go
浏览文件 @
98f1a699
...
...
@@ -201,15 +201,23 @@ func TestCharsetTranscoder(t *testing.T) {
func
TestOpenReader
(
t
*
testing
.
T
)
{
_
,
err
:=
OpenReader
(
strings
.
NewReader
(
""
))
assert
.
EqualError
(
t
,
err
,
"zip: not a valid zip file"
)
_
,
err
=
OpenReader
(
bytes
.
NewReader
(
crypto
Identifier
))
_
,
err
=
OpenReader
(
bytes
.
NewReader
(
ole
Identifier
))
assert
.
EqualError
(
t
,
err
,
"decrypted file failed"
)
// Test open password protected spreadsheet created by Microsoft Office Excel 2010.
f
,
err
:=
OpenFile
(
filepath
.
Join
(
"test"
,
"encryptSHA1.xlsx"
),
Options
{
Password
:
"password"
})
assert
.
NoError
(
t
,
err
)
val
,
err
:=
f
.
GetCellValue
(
"Sheet1"
,
"A1"
)
assert
.
NoError
(
t
,
err
)
assert
.
Equal
(
t
,
"SECRET"
,
val
)
// Test open password protected spreadsheet created by LibreOffice 7.0.0.3.
f
,
err
=
OpenFile
(
filepath
.
Join
(
"test"
,
"encryptAES.xlsx"
),
Options
{
Password
:
"password"
})
assert
.
NoError
(
t
,
err
)
val
,
err
=
f
.
GetCellValue
(
"Sheet1"
,
"A1"
)
assert
.
NoError
(
t
,
err
)
assert
.
Equal
(
t
,
"SECRET"
,
val
)
// Test unexpected EOF.
var
b
bytes
.
Buffer
w
:=
gzip
.
NewWriter
(
&
b
)
...
...
test/encryptAES.xlsx
0 → 100644
浏览文件 @
98f1a699
文件已添加
xmlChart.go
浏览文件 @
98f1a699
...
...
@@ -18,10 +18,7 @@ import "encoding/xml"
// charts, pie charts, scatter charts, or other types of charts.
type
xlsxChartSpace
struct
{
XMLName
xml
.
Name
`xml:"http://schemas.openxmlformats.org/drawingml/2006/chart chartSpace"`
XMLNSc
string
`xml:"xmlns:c,attr"`
XMLNSa
string
`xml:"xmlns:a,attr"`
XMLNSr
string
`xml:"xmlns:r,attr"`
XMLNSc16r2
string
`xml:"xmlns:c16r2,attr"`
Date1904
*
attrValBool
`xml:"date1904"`
Lang
*
attrValString
`xml:"lang"`
RoundedCorners
*
attrValBool
`xml:"roundedCorners"`
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录