Remove invalid agile certificate encryption

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1874351 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Andreas Beeker 2020-02-22 00:36:32 +00:00
parent a5e157c06f
commit 39ab347d99
5 changed files with 27 additions and 379 deletions

View File

@ -27,14 +27,11 @@ import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.MessageDigest;
import java.security.cert.X509Certificate;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.RC2ParameterSpec;
@ -49,7 +46,6 @@ import org.apache.poi.poifs.crypt.Decryptor;
import org.apache.poi.poifs.crypt.EncryptionHeader;
import org.apache.poi.poifs.crypt.EncryptionInfo;
import org.apache.poi.poifs.crypt.HashAlgorithm;
import org.apache.poi.poifs.crypt.agile.AgileEncryptionVerifier.AgileCertificateEntry;
import org.apache.poi.poifs.filesystem.DirectoryNode;
import org.apache.poi.poifs.filesystem.DocumentInputStream;
import org.apache.poi.util.LittleEndian;
@ -90,7 +86,7 @@ public class AgileDecryptor extends Decryptor {
byte[] pwHash = hashPassword(password, ver.getHashAlgorithm(), ver.getSalt(), ver.getSpinCount());
/**
/*
* encryptedVerifierHashInput: This attribute MUST be generated by using the following steps:
* 1. Generate a random array of bytes with the number of bytes used specified by the saltSize
* attribute.
@ -108,7 +104,7 @@ public class AgileDecryptor extends Decryptor {
MessageDigest hashMD = getMessageDigest(ver.getHashAlgorithm());
byte[] verifierHash = hashMD.digest(verfierInputEnc);
/**
/*
* encryptedVerifierHashValue: This attribute MUST be generated by using the following steps:
* 1. Obtain the hash value of the random array of bytes generated in step 1 of the steps for
* encryptedVerifierHashInput.
@ -123,7 +119,7 @@ public class AgileDecryptor extends Decryptor {
byte[] verifierHashDec = hashInput(ver, pwHash, kHashedVerifierBlock, ver.getEncryptedVerifierHash(), Cipher.DECRYPT_MODE);
verifierHashDec = getBlock0(verifierHashDec, ver.getHashAlgorithm().hashSize);
/**
/*
* encryptedKeyValue: This attribute MUST be generated by using the following steps:
* 1. Generate a random array of bytes that is the same size as specified by the
* Encryptor.KeyData.keyBits attribute of the parent element.
@ -140,7 +136,7 @@ public class AgileDecryptor extends Decryptor {
keyspec = getBlock0(keyspec, header.getKeySize()/8);
SecretKeySpec secretKey = new SecretKeySpec(keyspec, header.getCipherAlgorithm().jceId);
/**
/*
* 1. Obtain the intermediate key by decrypting the encryptedKeyValue from a KeyEncryptor
* contained within the KeyEncryptors sequence. Use this key for encryption operations in the
* remaining steps of this section.
@ -159,7 +155,7 @@ public class AgileDecryptor extends Decryptor {
byte[] hmacKey = cipher.doFinal(header.getEncryptedHmacKey());
hmacKey = getBlock0(hmacKey, header.getHashAlgorithm().hashSize);
/**
/*
* 5. Generate an HMAC, as specified in [RFC2104], of the encrypted form of the data (message),
* which the DataIntegrity element will verify by using the Salt generated in step 2 as the key.
* Note that the entire EncryptedPackage stream (1), including the StreamSize field, MUST be
@ -183,69 +179,8 @@ public class AgileDecryptor extends Decryptor {
}
}
/**
* instead of a password, it's also possible to decrypt via certificate.
* Warning: this code is experimental and hasn't been validated
*
* @see <a href="http://social.msdn.microsoft.com/Forums/en-US/cc9092bb-0c82-4b5b-ae21-abf643bdb37c/agile-encryption-with-certificates">Agile encryption with certificates</a>
*
* @param keyPair
* @param x509
* @return true, when the data can be successfully decrypted with the given private key
* @throws GeneralSecurityException
*/
public boolean verifyPassword(KeyPair keyPair, X509Certificate x509) throws GeneralSecurityException {
AgileEncryptionVerifier ver = (AgileEncryptionVerifier)getEncryptionInfo().getVerifier();
AgileEncryptionHeader header = (AgileEncryptionHeader)getEncryptionInfo().getHeader();
HashAlgorithm hashAlgo = header.getHashAlgorithm();
CipherAlgorithm cipherAlgo = header.getCipherAlgorithm();
int blockSize = header.getBlockSize();
AgileCertificateEntry ace = null;
for (AgileCertificateEntry aceEntry : ver.getCertificates()) {
if (x509.equals(aceEntry.x509)) {
ace = aceEntry;
break;
}
}
if (ace == null) {
return false;
}
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate());
byte[] keyspec = cipher.doFinal(ace.encryptedKey);
SecretKeySpec secretKey = new SecretKeySpec(keyspec, ver.getCipherAlgorithm().jceId);
Mac x509Hmac = CryptoFunctions.getMac(hashAlgo);
x509Hmac.init(secretKey);
byte[] certVerifier = x509Hmac.doFinal(ace.x509.getEncoded());
byte[] vec = CryptoFunctions.generateIv(hashAlgo, header.getKeySalt(), kIntegrityKeyBlock, blockSize);
cipher = getCipher(secretKey, cipherAlgo, header.getChainingMode(), vec, Cipher.DECRYPT_MODE);
byte[] hmacKey = cipher.doFinal(header.getEncryptedHmacKey());
hmacKey = getBlock0(hmacKey, hashAlgo.hashSize);
vec = CryptoFunctions.generateIv(hashAlgo, header.getKeySalt(), kIntegrityValueBlock, blockSize);
cipher = getCipher(secretKey, cipherAlgo, header.getChainingMode(), vec, Cipher.DECRYPT_MODE);
byte[] hmacValue = cipher.doFinal(header.getEncryptedHmacValue());
hmacValue = getBlock0(hmacValue, hashAlgo.hashSize);
if (Arrays.equals(ace.certVerifier, certVerifier)) {
setSecretKey(secretKey);
setIntegrityHmacKey(hmacKey);
setIntegrityHmacValue(hmacValue);
return true;
} else {
return false;
}
}
protected static int getNextBlockSize(int inputLen, int blockSize) {
int fillSize;
for (fillSize=blockSize; fillSize<inputLen; fillSize+=blockSize);
return fillSize;
return (int)Math.ceil(inputLen / (double)blockSize) * blockSize;
}
/* package */ static byte[] hashInput(AgileEncryptionVerifier ver, byte[] pwHash, byte[] blockKey, byte[] inputKey, int cipherMode) {

View File

@ -16,18 +16,11 @@
==================================================================== */
package org.apache.poi.poifs.crypt.agile;
import java.io.ByteArrayInputStream;
import java.security.GeneralSecurityException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import com.microsoft.schemas.office.x2006.encryption.CTKeyEncryptor;
import com.microsoft.schemas.office.x2006.encryption.EncryptionDocument;
import com.microsoft.schemas.office.x2006.encryption.STCipherChaining;
import com.microsoft.schemas.office.x2006.keyEncryptor.certificate.CTCertificateKeyEncryptor;
import com.microsoft.schemas.office.x2006.keyEncryptor.password.CTPasswordKeyEncryptor;
import org.apache.poi.EncryptedDocumentException;
import org.apache.poi.poifs.crypt.ChainingMode;
@ -40,24 +33,10 @@ import org.apache.poi.poifs.crypt.HashAlgorithm;
*/
public class AgileEncryptionVerifier extends EncryptionVerifier {
public static class AgileCertificateEntry {
X509Certificate x509;
byte[] encryptedKey;
byte[] certVerifier;
public AgileCertificateEntry() {}
public AgileCertificateEntry(AgileCertificateEntry other) {
x509 = other.x509;
encryptedKey = (other.encryptedKey == null) ? null : other.encryptedKey.clone();
certVerifier = (other.certVerifier == null) ? null : other.certVerifier.clone();
}
}
private final List<AgileCertificateEntry> certList = new ArrayList<>();
private int keyBits = -1;
private int blockSize = -1;
@SuppressWarnings("unused")
public AgileEncryptionVerifier(String descriptor) {
this(AgileEncryptionInfoBuilder.parseDescriptor(descriptor));
}
@ -114,24 +93,6 @@ public class AgileEncryptionVerifier extends EncryptionVerifier {
default:
throw new EncryptedDocumentException("Unsupported chaining mode - "+ keyData.getCipherChaining());
}
if (!encList.hasNext()) {
return;
}
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
while (encList.hasNext()) {
CTCertificateKeyEncryptor certKey = encList.next().getEncryptedCertificateKey();
AgileCertificateEntry ace = new AgileCertificateEntry();
ace.certVerifier = certKey.getCertVerifier();
ace.encryptedKey = certKey.getEncryptedKeyValue();
ace.x509 = (X509Certificate)cf.generateCertificate(new ByteArrayInputStream(certKey.getX509Certificate()));
certList.add(ace);
}
} catch (GeneralSecurityException e) {
throw new EncryptedDocumentException("can't parse X509 certificate", e);
}
}
public AgileEncryptionVerifier(CipherAlgorithm cipherAlgorithm, HashAlgorithm hashAlgorithm, int keyBits, int blockSize, ChainingMode chainingMode) {
@ -147,7 +108,6 @@ public class AgileEncryptionVerifier extends EncryptionVerifier {
super(other);
keyBits = other.keyBits;
blockSize = other.blockSize;
other.certList.stream().map(AgileCertificateEntry::new).forEach(certList::add);
}
@Override
@ -176,16 +136,6 @@ public class AgileEncryptionVerifier extends EncryptionVerifier {
super.setEncryptedKey(encryptedKey);
}
public void addCertificate(X509Certificate x509) {
AgileCertificateEntry ace = new AgileCertificateEntry();
ace.x509 = x509;
certList.add(ace);
}
public List<AgileCertificateEntry> getCertificates() {
return certList;
}
@Override
public AgileEncryptionVerifier copy() {
return new AgileEncryptionVerifier(this);

View File

@ -39,7 +39,6 @@ import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.security.cert.CertificateEncodingException;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
@ -58,7 +57,6 @@ import com.microsoft.schemas.office.x2006.encryption.EncryptionDocument;
import com.microsoft.schemas.office.x2006.encryption.STCipherAlgorithm;
import com.microsoft.schemas.office.x2006.encryption.STCipherChaining;
import com.microsoft.schemas.office.x2006.encryption.STHashAlgorithm;
import com.microsoft.schemas.office.x2006.keyEncryptor.certificate.CTCertificateKeyEncryptor;
import com.microsoft.schemas.office.x2006.keyEncryptor.password.CTPasswordKeyEncryptor;
import org.apache.poi.EncryptedDocumentException;
import org.apache.poi.poifs.crypt.ChunkedCipherOutputStream;
@ -67,8 +65,6 @@ import org.apache.poi.poifs.crypt.DataSpaceMapUtils;
import org.apache.poi.poifs.crypt.EncryptionInfo;
import org.apache.poi.poifs.crypt.Encryptor;
import org.apache.poi.poifs.crypt.HashAlgorithm;
import org.apache.poi.poifs.crypt.agile.AgileEncryptionVerifier.AgileCertificateEntry;
import org.apache.poi.poifs.crypt.standard.EncryptionRecord;
import org.apache.poi.poifs.filesystem.DirectoryNode;
import org.apache.poi.util.IOUtils;
import org.apache.poi.util.LittleEndian;
@ -127,7 +123,7 @@ public class AgileEncryptor extends Encryptor {
pwHash = hashPassword(password, ver.getHashAlgorithm(), verifierSalt, ver.getSpinCount());
/**
/*
* encryptedVerifierHashInput: This attribute MUST be generated by using the following steps:
* 1. Generate a random array of bytes with the number of bytes used specified by the saltSize
* attribute.
@ -144,7 +140,7 @@ public class AgileEncryptor extends Encryptor {
ver.setEncryptedVerifier(encryptedVerifier);
/**
/*
* encryptedVerifierHashValue: This attribute MUST be generated by using the following steps:
* 1. Obtain the hash value of the random array of bytes generated in step 1 of the steps for
* encryptedVerifierHashInput.
@ -161,7 +157,7 @@ public class AgileEncryptor extends Encryptor {
byte[] encryptedVerifierHash = hashInput(ver, pwHash, kHashedVerifierBlock, hashedVerifier, Cipher.ENCRYPT_MODE);
ver.setEncryptedVerifierHash(encryptedVerifierHash);
/**
/*
* encryptedKeyValue: This attribute MUST be generated by using the following steps:
* 1. Generate a random array of bytes that is the same size as specified by the
* Encryptor.KeyData.keyBits attribute of the parent element.
@ -212,15 +208,6 @@ public class AgileEncryptor extends Encryptor {
byte[] hmacKey = getBlock0(this.integritySalt, getNextBlockSize(this.integritySalt.length, blockSize));
byte[] encryptedHmacKey = cipher.doFinal(hmacKey);
header.setEncryptedHmacKey(encryptedHmacKey);
cipher = Cipher.getInstance("RSA");
for (AgileCertificateEntry ace : ver.getCertificates()) {
cipher.init(Cipher.ENCRYPT_MODE, ace.x509.getPublicKey());
ace.encryptedKey = cipher.doFinal(getSecretKey().getEncoded());
Mac x509Hmac = CryptoFunctions.getMac(header.getHashAlgorithm());
x509Hmac.init(getSecretKey());
ace.certVerifier = x509Hmac.doFinal(ace.x509.getEncoded());
}
} catch (GeneralSecurityException e) {
throw new EncryptedDocumentException(e);
}
@ -276,8 +263,6 @@ public class AgileEncryptor extends Encryptor {
private final CTKeyEncryptor.Uri.Enum passwordUri =
CTKeyEncryptor.Uri.HTTP_SCHEMAS_MICROSOFT_COM_OFFICE_2006_KEY_ENCRYPTOR_PASSWORD;
private final CTKeyEncryptor.Uri.Enum certificateUri =
CTKeyEncryptor.Uri.HTTP_SCHEMAS_MICROSOFT_COM_OFFICE_2006_KEY_ENCRYPTOR_CERTIFICATE;
protected EncryptionDocument createEncryptionDocument() {
AgileEncryptionVerifier ver = (AgileEncryptionVerifier)getEncryptionInfo().getVerifier();
@ -343,19 +328,6 @@ public class AgileEncryptor extends Encryptor {
hmacData.setEncryptedHmacKey(header.getEncryptedHmacKey());
hmacData.setEncryptedHmacValue(header.getEncryptedHmacValue());
for (AgileCertificateEntry ace : ver.getCertificates()) {
keyEnc = keyEncList.addNewKeyEncryptor();
keyEnc.setUri(certificateUri);
CTCertificateKeyEncryptor certData = keyEnc.addNewEncryptedCertificateKey();
try {
certData.setX509Certificate(ace.x509.getEncoded());
} catch (CertificateEncodingException e) {
throw new EncryptedDocumentException(e);
}
certData.setEncryptedKeyValue(ace.encryptedKey);
certData.setCertVerifier(ace.certVerifier);
}
return ed;
}
@ -372,7 +344,6 @@ public class AgileEncryptor extends Encryptor {
xo.setCharacterEncoding("UTF-8");
Map<String,String> nsMap = new HashMap<>();
nsMap.put(passwordUri.toString(),"p");
nsMap.put(certificateUri.toString(), "c");
xo.setUseDefaultNamespace();
xo.setSaveSuggestedPrefixes(nsMap);
xo.setSaveNamespacesFirst();
@ -391,31 +362,6 @@ public class AgileEncryptor extends Encryptor {
}
}
protected void createEncryptionInfoEntry(DirectoryNode dir, File tmpFile)
throws IOException, GeneralSecurityException {
DataSpaceMapUtils.addDefaultDataSpace(dir);
final EncryptionInfo info = getEncryptionInfo();
EncryptionRecord er = new EncryptionRecord(){
@Override
public void write(LittleEndianByteArrayOutputStream bos) {
// EncryptionVersionInfo (4 bytes): A Version structure (section 2.1.4), where
// Version.vMajor MUST be 0x0004 and Version.vMinor MUST be 0x0004
bos.writeShort(info.getVersionMajor());
bos.writeShort(info.getVersionMinor());
// Reserved (4 bytes): A value that MUST be 0x00000040
bos.writeInt(info.getEncryptionFlags());
EncryptionDocument ed = createEncryptionDocument();
marshallEncryptionDocument(ed, bos);
}
};
createEncryptionEntry(dir, "EncryptionInfo", er);
}
/**
* 2.3.4.15 Data Encryption (Agile Encryption)
*
@ -451,8 +397,23 @@ public class AgileEncryptor extends Encryptor {
@Override
protected void createEncryptionInfoEntry(DirectoryNode dir, File tmpFile)
throws IOException, GeneralSecurityException {
AgileEncryptor.this.createEncryptionInfoEntry(dir, tmpFile);
throws IOException {
DataSpaceMapUtils.addDefaultDataSpace(dir);
createEncryptionEntry(dir, "EncryptionInfo", this::marshallEncryptionRecord);
}
private void marshallEncryptionRecord(LittleEndianByteArrayOutputStream bos) {
final EncryptionInfo info = getEncryptionInfo();
// EncryptionVersionInfo (4 bytes): A Version structure (section 2.1.4), where
// Version.vMajor MUST be 0x0004 and Version.vMinor MUST be 0x0004
bos.writeShort(info.getVersionMajor());
bos.writeShort(info.getVersionMinor());
// Reserved (4 bytes): A value that MUST be 0x00000040
bos.writeInt(info.getEncryptionFlags());
EncryptionDocument ed = createEncryptionDocument();
marshallEncryptionDocument(ed, bos);
}
}

View File

@ -30,7 +30,6 @@ import org.junit.runners.Suite;
, TestDecryptor.class
, TestEncryptor.class
, TestAgileEncryptionParameters.class
, TestCertificateEncryption.class
})
public final class AllPOIFSCryptoTests {
}

View File

@ -1,197 +0,0 @@
/* ====================================================================
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;
import static org.hamcrest.core.IsEqual.equalTo;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import org.apache.poi.POIDataSamples;
import org.apache.poi.poifs.crypt.agile.AgileDecryptor;
import org.apache.poi.poifs.crypt.agile.AgileEncryptionVerifier;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.util.IOUtils;
import org.junit.Test;
/*
import org.junit.BeforeClass;
import java.util.Date;
import java.math.BigInteger;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import sun.security.x509.AlgorithmId;
import sun.security.x509.CertificateAlgorithmId;
import sun.security.x509.CertificateIssuerName;
import sun.security.x509.CertificateSerialNumber;
import sun.security.x509.CertificateSubjectName;
import sun.security.x509.CertificateValidity;
import sun.security.x509.CertificateVersion;
import sun.security.x509.CertificateX509Key;
import sun.security.x509.X500Name;
import sun.security.x509.X509CertImpl;
import sun.security.x509.X509CertInfo;
*/
/**
* @see <a href="http://stackoverflow.com/questions/1615871/creating-an-x509-certificate-in-java-without-bouncycastle">creating a self-signed certificate</a>
*/
public class TestCertificateEncryption {
/**
* how many days from now the Certificate is valid for
*/
static final int days = 1000;
/**
* the signing algorithm, eg "SHA1withRSA"
*/
static final String algorithm = "SHA1withRSA";
static final String password = "foobaa";
static final String certAlias = "poitest";
/**
* the X.509 Distinguished Name, eg "CN=Test, L=London, C=GB"
*/
static final String certDN = "CN=poitest";
// static final File pfxFile = TempFile.createTempFile("poitest", ".pfx");
static byte[] pfxFileBytes;
static class CertData {
KeyPair keypair;
X509Certificate x509;
}
/**
* Create a self-signed X.509 Certificate
*
* The keystore generation / loading is split, because normally the keystore would
* already exist.
*/
/* @BeforeClass
public static void initKeystore() throws GeneralSecurityException, IOException {
CertData certData = new CertData();
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(1024);
certData.keypair = keyGen.generateKeyPair();
PrivateKey privkey = certData.keypair.getPrivate();
PublicKey publkey = certData.keypair.getPublic();
X509CertInfo info = new X509CertInfo();
Date from = new Date();
Date to = new Date(from.getTime() + days * 86400000l);
CertificateValidity interval = new CertificateValidity(from, to);
BigInteger sn = new BigInteger(64, new SecureRandom());
X500Name owner = new X500Name(certDN);
info.set(X509CertInfo.VALIDITY, interval);
info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(sn));
info.set(X509CertInfo.SUBJECT, new CertificateSubjectName(owner));
info.set(X509CertInfo.ISSUER, new CertificateIssuerName(owner));
info.set(X509CertInfo.KEY, new CertificateX509Key(publkey));
info.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3));
AlgorithmId algo = new AlgorithmId(AlgorithmId.md5WithRSAEncryption_oid);
info.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(algo));
// Sign the cert to identify the algorithm that's used.
X509CertImpl cert = new X509CertImpl(info);
cert.sign(privkey, algorithm);
// Update the algorith, and resign.
algo = (AlgorithmId)cert.get(X509CertImpl.SIG_ALG);
info.set(CertificateAlgorithmId.NAME + "." + CertificateAlgorithmId.ALGORITHM, algo);
cert = new X509CertImpl(info);
cert.sign(privkey, algorithm);
certData.x509 = cert;
KeyStore keystore = KeyStore.getInstance("PKCS12");
keystore.load(null, password.toCharArray());
keystore.setKeyEntry(certAlias, certData.keypair.getPrivate(), password.toCharArray(), new Certificate[]{certData.x509});
ByteArrayOutputStream bos = new ByteArrayOutputStream();
keystore.store(bos, password.toCharArray());
pfxFileBytes = bos.toByteArray();
} */
public CertData loadKeystore()
throws GeneralSecurityException, IOException {
KeyStore keystore = KeyStore.getInstance("PKCS12");
// InputStream fis = new ByteArrayInputStream(pfxFileBytes);
InputStream fis = POIDataSamples.getPOIFSInstance().openResourceAsStream("poitest.pfx");
keystore.load(fis, password.toCharArray());
fis.close();
X509Certificate x509 = (X509Certificate)keystore.getCertificate(certAlias);
PrivateKey privateKey = (PrivateKey)keystore.getKey(certAlias, password.toCharArray());
PublicKey publicKey = x509.getPublicKey();
CertData certData = new CertData();
certData.keypair = new KeyPair(publicKey, privateKey);
certData.x509 = x509;
return certData;
}
@Test
public void testCertificateEncryption() throws Exception {
POIFSFileSystem fs = new POIFSFileSystem();
EncryptionInfo info = new EncryptionInfo(EncryptionMode.agile, CipherAlgorithm.aes128, HashAlgorithm.sha1, -1, -1, ChainingMode.cbc);
AgileEncryptionVerifier aev = (AgileEncryptionVerifier)info.getVerifier();
CertData certData = loadKeystore();
aev.addCertificate(certData.x509);
Encryptor enc = info.getEncryptor();
enc.confirmPassword("foobaa");
File file = POIDataSamples.getDocumentInstance().getFile("VariousPictures.docx");
InputStream fis = new FileInputStream(file);
byte[] byteExpected = IOUtils.toByteArray(fis);
fis.close();
OutputStream os = enc.getDataStream(fs);
IOUtils.copy(new ByteArrayInputStream(byteExpected), os);
os.close();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
fs.writeFilesystem(bos);
bos.close();
fs = new POIFSFileSystem(new ByteArrayInputStream(bos.toByteArray()));
info = new EncryptionInfo(fs);
AgileDecryptor agDec = (AgileDecryptor)info.getDecryptor();
boolean passed = agDec.verifyPassword(certData.keypair, certData.x509);
assertTrue("certificate verification failed", passed);
fis = agDec.getDataStream(fs);
byte[] byteActual = IOUtils.toByteArray(fis);
fis.close();
assertThat(byteExpected, equalTo(byteActual));
}
}