2010-05-27 13:23:27 +00:00
|
|
|
/* ====================================================================
|
|
|
|
|
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.
|
|
|
|
|
==================================================================== */
|
|
|
|
|
package org.apache.poi.poifs.crypt;
|
|
|
|
|
|
2011-05-10 10:38:17 +00:00
|
|
|
import java.io.ByteArrayInputStream;
|
|
|
|
|
|
2013-11-12 11:37:45 +00:00
|
|
|
import javax.xml.parsers.DocumentBuilderFactory;
|
2011-05-10 10:38:17 +00:00
|
|
|
|
2013-11-12 11:37:45 +00:00
|
|
|
import org.apache.commons.codec.binary.Base64;
|
|
|
|
|
import org.apache.poi.EncryptedDocumentException;
|
2010-05-27 13:23:27 +00:00
|
|
|
import org.apache.poi.poifs.filesystem.DocumentInputStream;
|
2013-11-12 11:37:45 +00:00
|
|
|
import org.w3c.dom.NamedNodeMap;
|
2011-05-10 10:38:17 +00:00
|
|
|
import org.w3c.dom.Node;
|
|
|
|
|
import org.w3c.dom.NodeList;
|
|
|
|
|
|
2010-05-27 13:23:27 +00:00
|
|
|
/**
|
2013-11-12 11:37:45 +00:00
|
|
|
* Used when checking if a key is valid for a document
|
2010-05-27 13:23:27 +00:00
|
|
|
*/
|
|
|
|
|
public class EncryptionVerifier {
|
2011-05-10 10:38:17 +00:00
|
|
|
private final byte[] salt;
|
|
|
|
|
private final byte[] verifier;
|
2010-05-27 13:23:27 +00:00
|
|
|
private final byte[] verifierHash;
|
2011-05-10 10:38:17 +00:00
|
|
|
private final byte[] encryptedKey;
|
2010-05-27 13:23:27 +00:00
|
|
|
private final int verifierHashSize;
|
2011-05-10 10:38:17 +00:00
|
|
|
private final int spinCount;
|
|
|
|
|
private final int algorithm;
|
|
|
|
|
private final int cipherMode;
|
|
|
|
|
|
|
|
|
|
public EncryptionVerifier(String descriptor) {
|
|
|
|
|
NamedNodeMap keyData = null;
|
|
|
|
|
try {
|
|
|
|
|
ByteArrayInputStream is;
|
|
|
|
|
is = new ByteArrayInputStream(descriptor.getBytes());
|
|
|
|
|
NodeList keyEncryptor = DocumentBuilderFactory.newInstance()
|
|
|
|
|
.newDocumentBuilder().parse(is)
|
|
|
|
|
.getElementsByTagName("keyEncryptor").item(0).getChildNodes();
|
|
|
|
|
for (int i = 0; i < keyEncryptor.getLength(); i++) {
|
|
|
|
|
Node node = keyEncryptor.item(i);
|
|
|
|
|
if (node.getNodeName().equals("p:encryptedKey")) {
|
|
|
|
|
keyData = node.getAttributes();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (keyData == null)
|
|
|
|
|
throw new EncryptedDocumentException("");
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
throw new EncryptedDocumentException("Unable to parse keyEncryptor");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
spinCount = Integer.parseInt(keyData.getNamedItem("spinCount")
|
|
|
|
|
.getNodeValue());
|
|
|
|
|
verifier = Base64.decodeBase64(keyData
|
|
|
|
|
.getNamedItem("encryptedVerifierHashInput")
|
|
|
|
|
.getNodeValue().getBytes());
|
|
|
|
|
salt = Base64.decodeBase64(keyData.getNamedItem("saltValue")
|
|
|
|
|
.getNodeValue().getBytes());
|
|
|
|
|
|
|
|
|
|
encryptedKey = Base64.decodeBase64(keyData
|
|
|
|
|
.getNamedItem("encryptedKeyValue")
|
|
|
|
|
.getNodeValue().getBytes());
|
|
|
|
|
|
|
|
|
|
int saltSize = Integer.parseInt(keyData.getNamedItem("saltSize")
|
|
|
|
|
.getNodeValue());
|
|
|
|
|
if (saltSize != salt.length)
|
|
|
|
|
throw new EncryptedDocumentException("Invalid salt size");
|
|
|
|
|
|
|
|
|
|
verifierHash = Base64.decodeBase64(keyData
|
|
|
|
|
.getNamedItem("encryptedVerifierHashValue")
|
|
|
|
|
.getNodeValue().getBytes());
|
|
|
|
|
|
|
|
|
|
int blockSize = Integer.parseInt(keyData.getNamedItem("blockSize")
|
|
|
|
|
.getNodeValue());
|
|
|
|
|
|
|
|
|
|
String alg = keyData.getNamedItem("cipherAlgorithm").getNodeValue();
|
2013-11-12 11:37:45 +00:00
|
|
|
|
|
|
|
|
int keyBits = Integer.parseInt(keyData.getNamedItem("keyBits")
|
|
|
|
|
.getNodeValue());
|
2011-05-10 10:38:17 +00:00
|
|
|
|
|
|
|
|
if ("AES".equals(alg)) {
|
2013-11-12 11:37:45 +00:00
|
|
|
switch (keyBits) {
|
|
|
|
|
case 128:
|
|
|
|
|
algorithm = EncryptionHeader.ALGORITHM_AES_128; break;
|
|
|
|
|
case 192:
|
|
|
|
|
algorithm = EncryptionHeader.ALGORITHM_AES_192; break;
|
|
|
|
|
case 256:
|
|
|
|
|
algorithm = EncryptionHeader.ALGORITHM_AES_256; break;
|
|
|
|
|
default:
|
|
|
|
|
throw new EncryptedDocumentException("Unsupported key size");
|
|
|
|
|
}
|
2011-05-10 10:38:17 +00:00
|
|
|
} else {
|
|
|
|
|
throw new EncryptedDocumentException("Unsupported cipher");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String chain = keyData.getNamedItem("cipherChaining").getNodeValue();
|
|
|
|
|
if ("ChainingModeCBC".equals(chain))
|
|
|
|
|
cipherMode = EncryptionHeader.MODE_CBC;
|
|
|
|
|
else if ("ChainingModeCFB".equals(chain))
|
|
|
|
|
cipherMode = EncryptionHeader.MODE_CFB;
|
|
|
|
|
else
|
|
|
|
|
throw new EncryptedDocumentException("Unsupported chaining mode");
|
|
|
|
|
|
|
|
|
|
verifierHashSize = Integer.parseInt(keyData.getNamedItem("hashSize")
|
|
|
|
|
.getNodeValue());
|
|
|
|
|
}
|
2010-05-27 13:23:27 +00:00
|
|
|
|
|
|
|
|
public EncryptionVerifier(DocumentInputStream is, int encryptedLength) {
|
|
|
|
|
int saltSize = is.readInt();
|
|
|
|
|
|
|
|
|
|
if (saltSize!=16) {
|
|
|
|
|
throw new RuntimeException("Salt size != 16 !?");
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-10 10:38:17 +00:00
|
|
|
salt = new byte[16];
|
2010-05-27 13:23:27 +00:00
|
|
|
is.readFully(salt);
|
2011-05-10 10:38:17 +00:00
|
|
|
verifier = new byte[16];
|
2010-05-27 13:23:27 +00:00
|
|
|
is.readFully(verifier);
|
|
|
|
|
|
|
|
|
|
verifierHashSize = is.readInt();
|
|
|
|
|
|
|
|
|
|
verifierHash = new byte[encryptedLength];
|
|
|
|
|
is.readFully(verifierHash);
|
2011-05-10 10:38:17 +00:00
|
|
|
|
|
|
|
|
spinCount = 50000;
|
|
|
|
|
algorithm = EncryptionHeader.ALGORITHM_AES_128;
|
|
|
|
|
cipherMode = EncryptionHeader.MODE_ECB;
|
|
|
|
|
encryptedKey = null;
|
2010-05-27 13:23:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public byte[] getSalt() {
|
|
|
|
|
return salt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public byte[] getVerifier() {
|
|
|
|
|
return verifier;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public byte[] getVerifierHash() {
|
|
|
|
|
return verifierHash;
|
|
|
|
|
}
|
2011-05-10 10:38:17 +00:00
|
|
|
|
|
|
|
|
public int getSpinCount() {
|
|
|
|
|
return spinCount;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int getCipherMode() {
|
|
|
|
|
return cipherMode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int getAlgorithm() {
|
|
|
|
|
return algorithm;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public byte[] getEncryptedKey() {
|
|
|
|
|
return encryptedKey;
|
|
|
|
|
}
|
2010-05-27 13:23:27 +00:00
|
|
|
}
|