Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
此号慢热型
excelize
提交
01afc6e0
excelize
项目概览
此号慢热型
/
excelize
与 Fork 源项目一致
Fork自
xuri / excelize
通知
2
Star
0
Fork
0
代码
文件
提交
分支
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,发现更多精彩内容 >>
提交
01afc6e0
编写于
9月 06, 2020
作者:
xurime
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
init ECMA-376 agile encryption support
上级
1111de2f
变更
4
隐藏空白更改
内联
并排
Showing
4 changed file
with
208 addition
and
28 deletion
+208
-28
crypt.go
crypt.go
+163
-18
crypt_test.go
crypt_test.go
+23
-0
excelize.go
excelize.go
+5
-9
file.go
file.go
+17
-1
未找到文件。
crypt.go
浏览文件 @
01afc6e0
...
...
@@ -13,6 +13,7 @@ import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/hmac"
"crypto/md5"
"crypto/sha1"
"crypto/sha256"
...
...
@@ -22,6 +23,8 @@ import (
"encoding/xml"
"errors"
"hash"
"math/rand"
"reflect"
"strings"
"github.com/richardlehane/mscfb"
...
...
@@ -32,7 +35,11 @@ import (
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
blockKeyHmacKey
=
[]
byte
{
0x5f
,
0xb2
,
0xad
,
0x01
,
0x0c
,
0xb9
,
0xe1
,
0xf6
}
blockKeyHmacValue
=
[]
byte
{
0xa0
,
0x67
,
0x7f
,
0x02
,
0xb2
,
0x2c
,
0x84
,
0x33
}
blockKeyVerifierHashInput
=
[]
byte
{
0xfe
,
0xa7
,
0xd2
,
0x76
,
0x3b
,
0x4b
,
0x9e
,
0x79
}
blockKeyVerifierHashValue
=
[]
byte
{
0xd7
,
0xaa
,
0x0f
,
0x6d
,
0x30
,
0x61
,
0x34
,
0x4e
}
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
...
...
@@ -50,6 +57,7 @@ var (
// Encryption specifies the encryption structure, streams, and storages are
// required when encrypting ECMA-376 documents.
type
Encryption
struct
{
XMLName
xml
.
Name
`xml:"encryption"`
KeyData
KeyData
`xml:"keyData"`
DataIntegrity
DataIntegrity
`xml:"dataIntegrity"`
KeyEncryptors
KeyEncryptors
`xml:"keyEncryptors"`
...
...
@@ -150,6 +158,125 @@ func Decrypt(raw []byte, opt *Options) (packageBuf []byte, err error) {
return
}
// Encrypt API encrypt data with the password.
func
Encrypt
(
raw
[]
byte
,
opt
*
Options
)
(
packageBuf
[]
byte
,
err
error
)
{
// Generate a random key to use to encrypt the document. Excel uses 32 bytes. We'll use the password to encrypt this key.
packageKey
,
_
:=
randomBytes
(
32
)
keyDataSaltValue
,
_
:=
randomBytes
(
16
)
keyEncryptors
,
_
:=
randomBytes
(
16
)
encryptionInfo
:=
Encryption
{
KeyData
:
KeyData
{
BlockSize
:
16
,
KeyBits
:
len
(
packageKey
)
*
8
,
HashSize
:
64
,
CipherAlgorithm
:
"AES"
,
CipherChaining
:
"ChainingModeCBC"
,
HashAlgorithm
:
"SHA512"
,
SaltValue
:
base64
.
StdEncoding
.
EncodeToString
(
keyDataSaltValue
),
},
KeyEncryptors
:
KeyEncryptors
{
KeyEncryptor
:
[]
KeyEncryptor
{{
EncryptedKey
:
EncryptedKey
{
SpinCount
:
100000
,
KeyData
:
KeyData
{
CipherAlgorithm
:
"AES"
,
CipherChaining
:
"ChainingModeCBC"
,
HashAlgorithm
:
"SHA512"
,
HashSize
:
64
,
BlockSize
:
16
,
KeyBits
:
256
,
SaltValue
:
base64
.
StdEncoding
.
EncodeToString
(
keyEncryptors
)},
}}},
},
}
// Package Encryption
// Encrypt package using the package key.
encryptedPackage
,
err
:=
cryptPackage
(
true
,
packageKey
,
raw
,
encryptionInfo
)
if
err
!=
nil
{
return
}
// Data Integrity
// Create the data integrity fields used by clients for integrity checks.
// Generate a random array of bytes to use in HMAC. The docs say to use the same length as the key salt, but Excel seems to use 64.
hmacKey
,
_
:=
randomBytes
(
64
)
if
err
!=
nil
{
return
}
// Create an initialization vector using the package encryption info and the appropriate block key.
hmacKeyIV
,
err
:=
createIV
(
blockKeyHmacKey
,
encryptionInfo
)
if
err
!=
nil
{
return
}
// Use the package key and the IV to encrypt the HMAC key.
encryptedHmacKey
,
err
:=
crypt
(
true
,
encryptionInfo
.
KeyData
.
CipherAlgorithm
,
encryptionInfo
.
KeyData
.
CipherChaining
,
packageKey
,
hmacKeyIV
,
hmacKey
)
// Create the HMAC.
h
:=
hmac
.
New
(
sha512
.
New
,
append
(
hmacKey
,
encryptedPackage
...
))
for
_
,
buf
:=
range
[][]
byte
{
hmacKey
,
encryptedPackage
}
{
h
.
Write
(
buf
)
}
hmacValue
:=
h
.
Sum
(
nil
)
// Generate an initialization vector for encrypting the resulting HMAC value.
hmacValueIV
,
err
:=
createIV
(
blockKeyHmacValue
,
encryptionInfo
)
if
err
!=
nil
{
return
}
// Encrypt the value.
encryptedHmacValue
,
err
:=
crypt
(
true
,
encryptionInfo
.
KeyData
.
CipherAlgorithm
,
encryptionInfo
.
KeyData
.
CipherChaining
,
packageKey
,
hmacValueIV
,
hmacValue
)
// Put the encrypted key and value on the encryption info.
encryptionInfo
.
DataIntegrity
.
EncryptedHmacKey
=
base64
.
StdEncoding
.
EncodeToString
(
encryptedHmacKey
)
encryptionInfo
.
DataIntegrity
.
EncryptedHmacValue
=
base64
.
StdEncoding
.
EncodeToString
(
encryptedHmacValue
)
// Key Encryption
// Convert the password to an encryption key.
key
,
err
:=
convertPasswdToKey
(
opt
.
Password
,
blockKey
,
encryptionInfo
)
if
err
!=
nil
{
return
}
// Encrypt the package key with the encryption key.
encryptedKeyValue
,
err
:=
crypt
(
true
,
encryptionInfo
.
KeyEncryptors
.
KeyEncryptor
[
0
]
.
EncryptedKey
.
CipherAlgorithm
,
encryptionInfo
.
KeyEncryptors
.
KeyEncryptor
[
0
]
.
EncryptedKey
.
CipherChaining
,
key
,
keyEncryptors
,
packageKey
)
encryptionInfo
.
KeyEncryptors
.
KeyEncryptor
[
0
]
.
EncryptedKey
.
EncryptedKeyValue
=
base64
.
StdEncoding
.
EncodeToString
(
encryptedKeyValue
)
// Verifier hash
// Create a random byte array for hashing.
verifierHashInput
,
_
:=
randomBytes
(
16
)
// Create an encryption key from the password for the input.
verifierHashInputKey
,
err
:=
convertPasswdToKey
(
opt
.
Password
,
blockKeyVerifierHashInput
,
encryptionInfo
)
if
err
!=
nil
{
return
}
// Use the key to encrypt the verifier input.
encryptedVerifierHashInput
,
err
:=
crypt
(
true
,
encryptionInfo
.
KeyData
.
CipherAlgorithm
,
encryptionInfo
.
KeyData
.
CipherChaining
,
verifierHashInputKey
,
keyEncryptors
,
verifierHashInput
)
if
err
!=
nil
{
return
}
encryptionInfo
.
KeyEncryptors
.
KeyEncryptor
[
0
]
.
EncryptedKey
.
EncryptedVerifierHashInput
=
base64
.
StdEncoding
.
EncodeToString
(
encryptedVerifierHashInput
)
// Create a hash of the input.
verifierHashValue
:=
hashing
(
encryptionInfo
.
KeyData
.
HashAlgorithm
,
verifierHashInput
)
// Create an encryption key from the password for the hash.
verifierHashValueKey
,
err
:=
convertPasswdToKey
(
opt
.
Password
,
blockKeyVerifierHashValue
,
encryptionInfo
)
if
err
!=
nil
{
return
}
// Use the key to encrypt the hash value.
encryptedVerifierHashValue
,
err
:=
crypt
(
true
,
encryptionInfo
.
KeyData
.
CipherAlgorithm
,
encryptionInfo
.
KeyData
.
CipherChaining
,
verifierHashValueKey
,
keyEncryptors
,
verifierHashValue
)
if
err
!=
nil
{
return
}
encryptionInfo
.
KeyEncryptors
.
KeyEncryptor
[
0
]
.
EncryptedKey
.
EncryptedVerifierHashValue
=
base64
.
StdEncoding
.
EncodeToString
(
encryptedVerifierHashValue
)
// Marshal the encryption info buffer.
encryptionInfoBuffer
,
err
:=
xml
.
Marshal
(
encryptionInfo
)
if
err
!=
nil
{
return
}
// TODO: Create a new CFB.
_
,
_
=
encryptedPackage
,
encryptionInfoBuffer
err
=
errors
.
New
(
"not support encryption currently"
)
return
}
// extractPart extract data from storage by specified part name.
func
extractPart
(
doc
*
mscfb
.
Reader
)
(
encryptionInfoBuf
,
encryptedPackageBuf
[]
byte
)
{
for
entry
,
err
:=
doc
.
Next
();
err
==
nil
;
entry
,
err
=
doc
.
Next
()
{
...
...
@@ -265,11 +392,11 @@ func standardConvertPasswdToKey(header StandardEncryptionHeader, verifier Standa
}
key
:=
hashing
(
"sha1"
,
verifier
.
Salt
,
passwordBuffer
)
for
i
:=
0
;
i
<
iterCount
;
i
++
{
iterator
:=
createUInt32LEBuffer
(
i
)
iterator
:=
createUInt32LEBuffer
(
i
,
4
)
key
=
hashing
(
"sha1"
,
iterator
,
key
)
}
var
block
int
hfinal
:=
hashing
(
"sha1"
,
key
,
createUInt32LEBuffer
(
block
))
hfinal
:=
hashing
(
"sha1"
,
key
,
createUInt32LEBuffer
(
block
,
4
))
cbRequiredKeyLength
:=
int
(
header
.
KeySize
)
/
8
cbHash
:=
sha1
.
Size
buf1
:=
bytes
.
Repeat
([]
byte
{
0x36
},
64
)
...
...
@@ -299,15 +426,14 @@ func standardXORBytes(a, b []byte) []byte {
// 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.
// 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
)
key
,
err
:=
convertPasswdToKey
(
opt
.
Password
,
blockKey
,
encryptionInfo
)
if
err
!=
nil
{
return
}
...
...
@@ -327,7 +453,7 @@ func agileDecrypt(encryptionInfoBuf, encryptedPackageBuf []byte, opt *Options) (
}
// convertPasswdToKey convert the password into an encryption key.
func
convertPasswdToKey
(
passwd
string
,
encryption
Encryption
)
(
key
[]
byte
,
err
error
)
{
func
convertPasswdToKey
(
passwd
string
,
blockKey
[]
byte
,
encryption
Encryption
)
(
key
[]
byte
,
err
error
)
{
var
b
bytes
.
Buffer
saltValue
,
err
:=
base64
.
StdEncoding
.
DecodeString
(
encryption
.
KeyEncryptors
.
KeyEncryptor
[
0
]
.
EncryptedKey
.
SaltValue
)
if
err
!=
nil
{
...
...
@@ -344,7 +470,7 @@ func convertPasswdToKey(passwd string, encryption Encryption) (key []byte, err e
key
=
hashing
(
encryption
.
KeyData
.
HashAlgorithm
,
b
.
Bytes
())
// Now regenerate until spin count.
for
i
:=
0
;
i
<
encryption
.
KeyEncryptors
.
KeyEncryptor
[
0
]
.
EncryptedKey
.
SpinCount
;
i
++
{
iterator
:=
createUInt32LEBuffer
(
i
)
iterator
:=
createUInt32LEBuffer
(
i
,
4
)
key
=
hashing
(
encryption
.
KeyData
.
HashAlgorithm
,
iterator
,
key
)
}
// Now generate the final hash.
...
...
@@ -385,8 +511,8 @@ func hashing(hashAlgorithm string, buffer ...[]byte) (key []byte) {
// createUInt32LEBuffer create buffer with little endian 32-bit unsigned
// integer.
func
createUInt32LEBuffer
(
value
int
)
[]
byte
{
buf
:=
make
([]
byte
,
4
)
func
createUInt32LEBuffer
(
value
int
,
bufferSize
int
)
[]
byte
{
buf
:=
make
([]
byte
,
bufferSize
)
binary
.
LittleEndian
.
PutUint32
(
buf
,
uint32
(
value
))
return
buf
}
...
...
@@ -404,7 +530,12 @@ func crypt(encrypt bool, cipherAlgorithm, cipherChaining string, key, iv, input
if
err
!=
nil
{
return
input
,
err
}
stream
:=
cipher
.
NewCBCDecrypter
(
block
,
iv
)
var
stream
cipher
.
BlockMode
if
encrypt
{
stream
=
cipher
.
NewCBCEncrypter
(
block
,
iv
)
}
else
{
stream
=
cipher
.
NewCBCDecrypter
(
block
,
iv
)
}
stream
.
CryptBlocks
(
input
,
input
)
return
input
,
nil
}
...
...
@@ -440,7 +571,7 @@ func cryptPackage(encrypt bool, packageKey, input []byte, encryption Encryption)
inputChunk
=
append
(
inputChunk
,
make
([]
byte
,
encryptedKey
.
BlockSize
-
remainder
)
...
)
}
// Create the initialization vector
iv
,
err
=
createIV
(
encrypt
,
i
,
encryption
)
iv
,
err
=
createIV
(
i
,
encryption
)
if
err
!=
nil
{
return
}
...
...
@@ -452,24 +583,29 @@ func cryptPackage(encrypt bool, packageKey, input []byte, encryption Encryption)
outputChunks
=
append
(
outputChunks
,
outputChunk
...
)
i
++
}
if
encrypt
{
outputChunks
=
append
(
createUInt32LEBuffer
(
len
(
input
),
8
),
outputChunks
...
)
}
return
}
// createIV create an initialization vector (IV).
func
createIV
(
encrypt
bool
,
blockKey
int
,
encryption
Encryption
)
([]
byte
,
error
)
{
func
createIV
(
blockKey
interface
{}
,
encryption
Encryption
)
([]
byte
,
error
)
{
encryptedKey
:=
encryption
.
KeyData
// Create the block key from the current index
blockKeyBuf
:=
createUInt32LEBuffer
(
blockKey
)
var
b
bytes
.
Buffer
var
blockKeyBuf
[]
byte
if
reflect
.
TypeOf
(
blockKey
)
.
Kind
()
==
reflect
.
Int
{
blockKeyBuf
=
createUInt32LEBuffer
(
blockKey
.
(
int
),
4
)
}
else
{
blockKeyBuf
=
blockKey
.
([]
byte
)
}
saltValue
,
err
:=
base64
.
StdEncoding
.
DecodeString
(
encryptedKey
.
SaltValue
)
if
err
!=
nil
{
return
nil
,
err
}
b
.
Write
(
saltValue
)
b
.
Write
(
blockKeyBuf
)
// Create the initialization vector by hashing the salt with the block key.
// Truncate or pad as needed to meet the block size.
iv
:=
hashing
(
encryptedKey
.
HashAlgorithm
,
b
.
Bytes
(
))
iv
:=
hashing
(
encryptedKey
.
HashAlgorithm
,
append
(
saltValue
,
blockKeyBuf
...
))
if
len
(
iv
)
<
encryptedKey
.
BlockSize
{
tmp
:=
make
([]
byte
,
0x36
)
iv
=
append
(
iv
,
tmp
...
)
...
...
@@ -479,3 +615,12 @@ func createIV(encrypt bool, blockKey int, encryption Encryption) ([]byte, error)
}
return
iv
,
nil
}
// randomBytes returns securely generated random bytes. It will return an error if the system's
// secure random number generator fails to function correctly, in which case the caller should not
// continue.
func
randomBytes
(
n
int
)
([]
byte
,
error
)
{
b
:=
make
([]
byte
,
n
)
_
,
err
:=
rand
.
Read
(
b
)
return
b
,
err
}
crypt_test.go
0 → 100644
浏览文件 @
01afc6e0
// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
// and read from XLSX files. Support reads and writes XLSX file generated by
// Microsoft Excel™ 2007 and later. Support save file without losing original
// charts of XLSX. This library needs Go version 1.10 or later.
package
excelize
import
(
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
)
func
TestEncrypt
(
t
*
testing
.
T
)
{
f
,
err
:=
OpenFile
(
filepath
.
Join
(
"test"
,
"encryptSHA1.xlsx"
),
Options
{
Password
:
"password"
})
assert
.
NoError
(
t
,
err
)
assert
.
EqualError
(
t
,
f
.
SaveAs
(
filepath
.
Join
(
"test"
,
"TestEncrypt.xlsx"
),
Options
{
Password
:
"password"
}),
"not support encryption currently"
)
}
excelize.go
浏览文件 @
01afc6e0
...
...
@@ -32,6 +32,7 @@ import (
// File define a populated spreadsheet file struct.
type
File
struct
{
sync
.
Mutex
options
*
Options
xmlAttr
map
[
string
][]
xml
.
Attr
checked
map
[
string
]
bool
sheetMap
map
[
string
]
string
...
...
@@ -75,11 +76,7 @@ func OpenFile(filename string, opt ...Options) (*File, error) {
return
nil
,
err
}
defer
file
.
Close
()
var
option
Options
for
_
,
o
:=
range
opt
{
option
=
o
}
f
,
err
:=
OpenReader
(
file
,
option
)
f
,
err
:=
OpenReader
(
file
,
opt
...
)
if
err
!=
nil
{
return
nil
,
err
}
...
...
@@ -111,12 +108,12 @@ func OpenReader(r io.Reader, opt ...Options) (*File, error) {
if
err
!=
nil
{
return
nil
,
err
}
f
:=
newFile
()
if
bytes
.
Contains
(
b
,
oleIdentifier
)
{
var
option
Options
for
_
,
o
:=
range
opt
{
option
=
o
f
.
options
=
&
o
}
b
,
err
=
Decrypt
(
b
,
&
option
)
b
,
err
=
Decrypt
(
b
,
f
.
options
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"decrypted file failed"
)
}
...
...
@@ -130,7 +127,6 @@ func OpenReader(r io.Reader, opt ...Options) (*File, error) {
if
err
!=
nil
{
return
nil
,
err
}
f
:=
newFile
()
f
.
SheetCount
,
f
.
XLSX
=
sheetCount
,
file
f
.
CalcChain
=
f
.
calcChainReader
()
f
.
sheetMap
=
f
.
getSheetMap
()
...
...
file.go
浏览文件 @
01afc6e0
...
...
@@ -64,7 +64,7 @@ func (f *File) Save() error {
// SaveAs provides a function to create or update to an xlsx file at the
// provided path.
func
(
f
*
File
)
SaveAs
(
name
string
)
error
{
func
(
f
*
File
)
SaveAs
(
name
string
,
opt
...
Options
)
error
{
if
len
(
name
)
>
FileNameLength
{
return
errors
.
New
(
"file name length exceeds maximum limit"
)
}
...
...
@@ -73,6 +73,9 @@ func (f *File) SaveAs(name string) error {
return
err
}
defer
file
.
Close
()
for
_
,
o
:=
range
opt
{
f
.
options
=
&
o
}
return
f
.
Write
(
file
)
}
...
...
@@ -118,5 +121,18 @@ func (f *File) WriteToBuffer() (*bytes.Buffer, error) {
return
buf
,
err
}
}
if
f
.
options
!=
nil
{
if
err
:=
zw
.
Close
();
err
!=
nil
{
return
buf
,
err
}
b
,
err
:=
Encrypt
(
buf
.
Bytes
(),
f
.
options
)
if
err
!=
nil
{
return
buf
,
err
}
buf
.
Reset
()
buf
.
Write
(
b
)
return
buf
,
nil
}
return
buf
,
zw
.
Close
()
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录