/* ====================================================================
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
contributor license agreements. See the NOTICE file distributed with
|
this work for additional information regarding copyright ownership.
|
The ASF licenses this file to You under the Apache License, Version 2.0
|
(the "License"); you may not use this file except in compliance with
|
the License. You may obtain a copy of the License at
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
Unless required by applicable law or agreed to in writing, software
|
distributed under the License is distributed on an "AS IS" BASIS,
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
See the License for the specific language governing permissions and
|
limitations under the License.
|
==================================================================== */
|
|
//http://stackoverflow.com/questions/6843698/calculating-sha-1-hashes-in-java-and-c-sharp Leon
|
using System;
|
using System.Collections.Generic;
|
using System.Text;
|
using HH.WMS.Utils.NPOI.POIFS.FileSystem;
|
using System.Security.Cryptography;
|
using System.IO;
|
|
using HH.WMS.Utils.NPOI;
|
using HH.WMS.Utils.NPOI.Util;
|
|
|
namespace HH.WMS.Utils.NPOI.POIFS.Crypt
|
{
|
public class AgileDecryptor : Decryptor
|
{
|
private EncryptionInfo _info;
|
//private SecretKey _secretKey;
|
private byte[] _secretKey;
|
private static byte[] kVerifierInputBlock =
|
new byte[] { (byte)0xfe, (byte)0xa7, (byte)0xd2, (byte)0x76,
|
(byte)0x3b, (byte)0x4b, (byte)0x9e, (byte)0x79 };
|
|
private static byte[] kHashedVerifierBlock =
|
new byte[] { (byte)0xd7, (byte)0xaa, (byte)0x0f, (byte)0x6d,
|
(byte)0x30, (byte)0x61, (byte)0x34, (byte)0x4e };
|
|
|
private static byte[] kCryptoKeyBlock =
|
new byte[] { (byte)0x14, (byte)0x6e, (byte)0x0b, (byte)0xe7,
|
(byte)0xab, (byte)0xac, (byte)0xd0, (byte)0xd6 };
|
|
public AgileDecryptor()
|
{
|
}
|
|
|
public byte[] SecretKey
|
{
|
get { return _secretKey; }
|
set { _secretKey = value; }
|
}
|
public EncryptionInfo Info
|
{
|
get { return _info; }
|
set { _info = value; }
|
}
|
|
public override bool VerifyPassword(string password)
|
{
|
EncryptionVerifier verifier = _info.Verifier;
|
|
int algorithm = verifier.Algorithm;
|
int mode = verifier.CipherMode;
|
|
byte[] pwHash = HashPassword(_info, password);
|
byte[] iv = GenerateIv(algorithm, verifier.Salt, null);
|
|
byte[] skey;
|
skey = GenerateKey(pwHash, kVerifierInputBlock);
|
SymmetricAlgorithm cipher = GetCipher(algorithm, mode, skey, iv);
|
|
byte[] verifierHashInput;
|
|
//using (MemoryStream fStream = new MemoryStream(verifier.Verifier))
|
//{
|
// using (CryptoStream cStream = new CryptoStream(fStream, cipher.CreateDecryptor(cipher.Key, cipher.IV),
|
// CryptoStreamMode.Read))
|
// {
|
// verifierHashInput = new byte[cStream.Length];
|
// cStream.Read(verifierHashInput, 0, verifierHashInput.Length);
|
// }
|
//}
|
|
verifierHashInput = this.Decrypt(cipher, verifier.Verifier);
|
HashAlgorithm sha1 = HashAlgorithm.Create("SHA1");
|
byte[] trimmed = new byte[verifier.Salt.Length];
|
System.Array.Copy(verifierHashInput, 0, trimmed, 0, trimmed.Length);
|
|
byte[] hashedVerifier = sha1.ComputeHash(trimmed);
|
|
skey = GenerateKey(pwHash, kHashedVerifierBlock);
|
iv = GenerateIv(algorithm, verifier.Salt, null);
|
cipher = GetCipher(algorithm, mode, skey, iv);
|
byte[] verifierHash = Decrypt(cipher, verifier.VerifierHash);
|
trimmed = new byte[hashedVerifier.Length];
|
System.Array.Copy(verifierHash, 0, trimmed, 0, trimmed.Length);
|
|
if (Arrays.Equals(trimmed, hashedVerifier))
|
{
|
skey = GenerateKey(pwHash, kCryptoKeyBlock);
|
iv = GenerateIv(algorithm, verifier.Salt, null);
|
cipher = GetCipher(algorithm, mode, skey, iv);
|
byte[] inter = Decrypt(cipher, verifier.EncryptedKey);
|
byte[] keySpec = new byte[_info.Header.KeySize / 8];
|
Array.Copy(inter, 0, keySpec, 0, keySpec.Length);
|
_secretKey = keySpec;
|
return true;
|
}
|
else
|
return false;
|
}
|
|
public AgileDecryptor(EncryptionInfo info)
|
{
|
_info = info;
|
}
|
|
public override Stream GetDataStream(DirectoryNode dir)
|
{
|
DocumentInputStream dr = dir.CreateDocumentInputStream("EncryptedPackage");
|
long size = dr.ReadLong();
|
return new ChunkedCipherInputStream(dr, size, this);
|
}
|
|
|
public SymmetricAlgorithm GetCipher(int algorithm, int mode, /*SecretKey key,*/ byte[] key, byte[] vec)
|
{
|
try
|
{
|
string name = null;
|
string chain = null;
|
if (algorithm == EncryptionHeader.ALGORITHM_AES_128 ||
|
algorithm == EncryptionHeader.ALGORITHM_AES_192 ||
|
algorithm == EncryptionHeader.ALGORITHM_AES_256)
|
name = "AES";
|
|
if (mode == EncryptionHeader.MODE_CBC)
|
chain = "CBC";
|
else if (mode == EncryptionHeader.MODE_CFB)
|
chain = "CFB";
|
|
//SymmetricAlgorithm cipher = SymmetricAlgorithm.Create(name + "/" + chain + "/Nopadding");
|
SymmetricAlgorithm cipher = SymmetricAlgorithm.Create();
|
cipher.Key = key;
|
cipher.IV = vec;
|
cipher.Padding = PaddingMode.None;
|
cipher.Mode = chain == "CBC" ? CipherMode.CBC : CipherMode.CFB;
|
// cipher.Key = ;
|
//cipher.IV =
|
return cipher;
|
}
|
catch (CryptographicException ex)
|
{
|
throw ex;
|
}
|
}
|
private byte[] GetBlock(int algorithm, byte[] hash)
|
{
|
byte[] result = new byte[GetBlockSize(algorithm)];
|
for(int i = 0; i < result.Length; i++)
|
result[i] = (byte)0x36;
|
System.Array.Copy(hash, 0, result, 0, Math.Min(result.Length, hash.Length));
|
return result;
|
}
|
|
|
|
private byte[] GenerateKey(byte[] hash, byte[] blockKey)
|
{
|
SHA1 sha = new SHA1CryptoServiceProvider();
|
//sha1.update(hash); Leon
|
//return getBlock(_info.getVerifier().getAlgorithm(), sha1.digest(blockKey));
|
byte[] temp = new byte[hash.Length + blockKey.Length];
|
Array.Copy(hash, temp, hash.Length);
|
Array.Copy(blockKey, 0, temp, hash.Length, blockKey.Length);
|
|
return GetBlock(_info.Verifier.Algorithm, sha.ComputeHash(temp));
|
}
|
|
public byte[] GenerateIv(int algorithm, byte[] salt, byte[] blockKey)
|
{
|
try
|
{
|
if(blockKey == null)
|
return GetBlock(algorithm, salt);
|
// SHA1 sha = new SHA1CryptoServiceProvider();
|
//sha.ComputeHash(salt);
|
HashAlgorithm sha1 = HashAlgorithm.Create("SHA1");
|
// return GetBlock(algorithm, sha
|
// sha1.update(salt);
|
// return getBlock(algorithm, sha1.digest(blockKey)); Leon
|
sha1.ComputeHash(salt);
|
// sha1 = new SHA1CryptoServiceProvider();
|
// sha1
|
return GetBlock(algorithm, sha1.ComputeHash(blockKey));
|
}
|
catch(System.Security.Cryptography.CryptographicException ex)
|
{
|
throw ex;
|
}
|
}
|
|
}
|
|
public class ChunkedCipherInputStream : Stream
|
{
|
private int _lastIndex = 0;
|
private long _pos = 0;
|
private long _size;
|
private DocumentInputStream _stream;
|
private byte[] _chunk;
|
private SymmetricAlgorithm _cipher;
|
private AgileDecryptor _ag;
|
|
|
public ChunkedCipherInputStream(DocumentInputStream dis, long size, AgileDecryptor ag)
|
{
|
try
|
{
|
_size = size;
|
_stream = dis;
|
_ag = ag;
|
_cipher = _ag.GetCipher(_ag.Info.Header.Algorithm,
|
_ag.Info.Header.CipherMode,
|
_ag.SecretKey, _ag.Info.Header.KeySalt);
|
}
|
catch (System.Security.Cryptography.CryptographicException ex)
|
{
|
throw ex;
|
}
|
}
|
|
public int Read()
|
{
|
byte[] b = new byte[1];
|
if (Read(b) == 1)
|
return b[0];
|
return -1;
|
}
|
|
public int Read(byte[] b)
|
{
|
return Read(b, 0, b.Length);
|
}
|
|
public override int Read(byte[] b, int offset, int len)
|
{
|
int total = 0;
|
|
while (len > 0)
|
{
|
if (_chunk == null)
|
{
|
try
|
{
|
_chunk = NextChunk();
|
}
|
catch (CryptographicException ex)
|
{
|
throw new EncryptedDocumentException(ex.Message);
|
}
|
}
|
int count = (int)(4096L - (_pos & 0xfff));
|
System.Array.Copy(_chunk, (int)(_pos * 0xfff), b, offset, count);
|
offset += count;
|
len -= count;
|
_pos += count;
|
|
if ((_pos & 0xfff) == 0)
|
_chunk = null;
|
total += count;
|
}
|
|
return total;
|
}
|
|
public long Skip(long n)
|
{
|
long start = _pos;
|
long skip = Math.Min(Available(), n);
|
|
if ((((_pos + skip) ^ start) & ~0xfff) != 0)
|
{
|
_chunk = null;
|
}
|
_pos += skip;
|
|
return skip;
|
}
|
|
public int Available()
|
{
|
return (int)(_size - _pos);
|
}
|
public override void Close()
|
{
|
_stream.Close();
|
}
|
|
public bool MarkSupported()
|
{
|
return false;
|
}
|
|
private byte[] NextChunk()
|
{
|
int index = (int)(_pos >> 12);
|
byte[] blockKey = new byte[4];
|
LittleEndian.PutInt(blockKey, index);
|
|
byte[] iv = _ag.GenerateIv(_ag.Info.Header.Algorithm,
|
_ag.Info.Header.KeySalt, blockKey);
|
//_cipher.Mode = CipherMode.
|
_cipher.Key = _ag.SecretKey;
|
_cipher.IV = iv;
|
|
if (_lastIndex != index)
|
_stream.Skip((index - _lastIndex) << 12);
|
|
byte[] block = new byte[Math.Min(_stream.Available(), 4096)];
|
_stream.ReadFully(block);
|
_lastIndex = index + 1;
|
return _cipher.Key;
|
//return _cipher.doFinal(block); Leon
|
}
|
|
public override bool CanRead
|
{
|
get { return _stream.CanRead; }
|
}
|
|
public override bool CanSeek
|
{
|
get { throw new NotImplementedException(); }
|
}
|
|
public override bool CanWrite
|
{
|
get { throw new NotImplementedException(); }
|
}
|
|
public override void Flush()
|
{
|
throw new NotImplementedException();
|
}
|
|
public override long Length
|
{
|
get { throw new NotImplementedException(); }
|
}
|
|
public override long Position
|
{
|
get
|
{
|
throw new NotImplementedException();
|
}
|
set
|
{
|
throw new NotImplementedException();
|
}
|
}
|
|
public override long Seek(long offset, SeekOrigin origin)
|
{
|
return _stream.Seek(offset, origin);
|
}
|
|
public override void SetLength(long value)
|
{
|
throw new NotImplementedException();
|
}
|
|
public override void Write(byte[] buffer, int offset, int count)
|
{
|
throw new NotImplementedException();
|
}
|
}
|
}
|