Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
ee.sk.xmlenc.factory.EncryptedStreamSAXParser Maven / Gradle / Ivy
/*
* EncryptedStreamSAXParser.java
* PROJECT: JDigiDoc
* DESCRIPTION: Digi Doc functions for parsing encrypted
* data from streams. Designed to parse large encrypted
* files. Uses PKCS#11 driver to decrypt the transport key.
* This implementation uses SAX parser.
* AUTHOR: Veiko Sinivee, S|E|B IT Partner Estonia
*==================================================
* Copyright (C) AS Sertifitseerimiskeskus
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* GNU Lesser General Public Licence is available at
* http://www.gnu.org/copyleft/lesser.html
*==================================================
*/
package ee.sk.xmlenc.factory;
import ee.sk.digidoc.Base64Util;
import ee.sk.digidoc.DigiDocException;
import ee.sk.digidoc.SignedDoc;
import ee.sk.digidoc.TokenKeyInfo;
import ee.sk.digidoc.factory.PKCS11SignatureFactory;
import ee.sk.digidoc.factory.Pkcs12SignatureFactory;
import ee.sk.digidoc.factory.SAXDigiDocException;
import ee.sk.digidoc.factory.SignatureFactory;
import ee.sk.utils.ConfigManager;
import ee.sk.xmlenc.EncryptedData;
import ee.sk.xmlenc.EncryptedKey;
import ee.sk.xmlenc.EncryptionProperty;
import org.apache.log4j.Logger;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.Provider;
import java.security.Security;
import java.security.cert.X509Certificate;
import java.util.Stack;
import java.util.zip.Inflater;
/**
* Implementation class for reading and writing encrypted files using a SAX parser
*
* @author Veiko Sinivee
* @version 1.0
*/
public class EncryptedStreamSAXParser
extends DefaultHandler
implements EncryptedStreamParser {
private Stack m_tags;
private EncryptedData m_doc;
private StringBuffer m_sbCollectChars;
private TokenKeyInfo m_tki;
/**
* log4j logger
*/
private Logger m_logger = null;
private int m_totalDecrypted, m_totalDecompressed, m_totalInput;
/**
* stream to write decrypted data
*/
private OutputStream m_outStream;
/**
* value of Recipient atribute to select the
*/
private String m_recvName;
/**
* pin code used to decrypt the transport key
*/
private String m_pin;
/**
* index of PKCS#11 token used in decryption
*/
private int m_token;
/**
* transport key value
*/
private byte[] m_transpkey;
/**
* cipher used in decryption of data
*/
private Cipher m_cipher;
/**
* flag: decrypting / not decrypting
*/
private boolean m_bDecrypting;
/**
* decompressor
*/
private Inflater m_decompressor;
/** one single buffer */
private StringBuffer m_sbParseBuf;
private StringBuffer m_sbB64Buf;
private static final int ENC_BLOCK_SIZE = 256;
private X509Certificate m_decCert;
private SecretKey m_transportKey;
private int m_nBlockType;
private static int DENC_BLOCK_FIRST = 1;
private static int DENC_BLOCK_MIDDLE = 2;
private static int DENC_BLOCK_LAST = 3;
private SignatureFactory m_sigFac;
/**
* Creates new EncryptedStreamSAXParser and initializes the variables
*/
public EncryptedStreamSAXParser() {
m_tags = new Stack();
m_doc = null;
m_pin = null;
m_cipher = null;
m_outStream = null;
m_decCert = null;
m_recvName = null;
m_bDecrypting = false;
m_totalDecrypted = 0;
m_totalDecompressed = 0;
m_totalInput = 0;
m_token = 0;
m_sbCollectChars = null;
m_decompressor = null;
m_transportKey = null;
m_transpkey = null;
m_sigFac = null;
m_logger = Logger.getLogger(EncryptedStreamSAXParser.class);
}
/**
* initializes the implementation class
*
* @see ee.sk.xmlenc.factory.EncryptedDataParser#init()
*/
public void init() throws DigiDocException {
try {
Provider prv = (Provider)Class.forName(ConfigManager.
instance().getProperty("DIGIDOC_SECURITY_PROVIDER")).newInstance();
//m_logger.info("Provider");
//prv.list(System.out);
Security.addProvider(prv);
}
catch (Exception ex) {
DigiDocException.handleException(ex, DigiDocException.ERR_NOT_FAC_INIT);
}
}
/**
* Initializes the Recipient atribute value used for locating the right to be used for deryption
*
* @param s value of Recipient atribute
*/
public void setRecipientName(String s) {
m_recvName = s;
}
/**
* Initializes the output stream where to write decrypted data
*
* @param outs output stream already opened by the user
*/
public void setOutputStream(OutputStream outs) {
m_outStream = outs;
}
/**
* Initializes the PIN code used to decrypt the transport key
*
* @param pin PIN code
*/
public void setPin(String pin) {
m_pin = pin;
}
/**
* Initializes the PKCS#11 token index used for decryption
*
* @param tok PKCS#11 token index used for decryption
*/
public void setToken(int tok) {
m_token = tok;
}
/**
* Accessor for cdoc container
* @return cdoc container
*/
public EncryptedData getCdoc()
{
return m_doc;
}
/**
* Reads in a EncryptedData file (.cdoc)
*
* @param dencStream opened stream with EncrypyedData data The user must open and close it.
* @param outs output stream for decrypted data
* @param token index of PKCS#11 token used
* @param pin pin code to decrypt transport key using PKCS#11
* @param recipientName Recipient atribute value of used to locate the correct transport key to decrypt with
*
* @return number of bytes successfully decrypted
*
* @throws DigiDocException for decryption errors
*/
public int decryptStreamUsingRecipientName(InputStream dencStream,
OutputStream outs, int token, String pin, String recipientName)
throws DigiDocException
{
// Use an instance of ourselves as the SAX event handler
EncryptedStreamSAXParser handler = this;
handler.setRecipientName(recipientName);
handler.setOutputStream(outs);
handler.setPin(pin);
handler.setToken(token);
// try find cert of token to decrypt with
try {
// use default factory as configured
m_sigFac = ConfigManager.instance().getSignatureFactory();
m_decCert = m_sigFac.getAuthCertificate(token, pin);
} catch(Exception ex) {
throw new DigiDocException(DigiDocException.ERR_DIGIDOC_FORMAT,
"Error loading decryption cert!", null);
}
// Use the default (non-validating) parser
SAXParserFactory factory = SAXParserFactory.newInstance();
//factory.setNamespaceAware(true);
try {
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
SAXParser saxParser = factory.newSAXParser();
saxParser.parse(dencStream, handler);
}
catch (SAXDigiDocException ex) {
throw ex.getDigiDocException();
}
catch (Exception ex) {
DigiDocException.handleException(ex,
DigiDocException.ERR_PARSE_XML);
}
if (m_doc == null) {
throw new DigiDocException(DigiDocException.ERR_DIGIDOC_FORMAT,
"This document is not in EncryptedData format", null);
}
return m_totalDecrypted;
}
/**
* Reads in a EncryptedData file (.cdoc)
* @param dencStream opened stream with EncrypyedData data
* The user must open and close it.
* @param outs output stream for decrypted data
* @param slot PKCS#11 slot id
* @param label pkcs#11 token label
* @param pin pin code to decrypt transport key using PKCS#11
* used to locate the correct transport key to decrypt with
* @return number of bytes successfully decrypted
* @throws DigiDocException for decryption errors
*/
public int decryptStreamUsingRecipientSlotIdAndTokenLabel(InputStream dencStream,
OutputStream outs, int slot, String label, String pin)
throws DigiDocException
{
// Use an instance of ourselves as the SAX event handler
EncryptedStreamSAXParser handler = this;
handler.setOutputStream(outs);
handler.setPin(pin);
ConfigManager cfg = ConfigManager.instance();
PKCS11SignatureFactory p11SigFac = null;
//PKCS11SignatureFactory sigFac = (PKCS11SignatureFactory)cfg.
// getSignatureFactoryOfType(SignatureFactory.SIGFAC_TYPE_PKCS11);
SignatureFactory sigFac = cfg.getSignatureFactory();
if(sigFac instanceof PKCS11SignatureFactory)
p11SigFac = (PKCS11SignatureFactory)sigFac;
else
p11SigFac = (PKCS11SignatureFactory)cfg.
getSignatureFactoryOfType(SignatureFactory.SIGFAC_TYPE_PKCS11);
if(p11SigFac == null) {
m_logger.error("No PKCS11 signature factory");
return 0;
}
m_sigFac = p11SigFac;
TokenKeyInfo tki = p11SigFac.getTokenWithSlotIdAndLabel(slot, label);
if(tki == null) {
m_logger.error("No token with slot: " + slot + " and label: " + label);
return 0;
}
if(tki != null && !tki.isEncryptKey()) {
m_logger.error("Token with slot: " + slot + " and label: " + label + " is not an encryption key!");
return 0;
}
m_decCert = tki.getCert();
m_tki = tki;
if(m_logger.isDebugEnabled())
m_logger.debug("Decrypt with slot: " + slot + " label: " + label + " token: " + ((m_tki != null) ? "OK" : "NULL") + " cert: " + ((m_decCert != null) ? "OK" : "NULL"));
if(m_decCert == null) {
throw new DigiDocException(DigiDocException.ERR_DIGIDOC_FORMAT,
"Error loading decryption cert!", null);
}
// Use the default (non-validating) parser
SAXParserFactory factory = SAXParserFactory.newInstance();
//factory.setNamespaceAware(true);
try {
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
SAXParser saxParser = factory.newSAXParser();
saxParser.parse(dencStream, handler);
}
catch (SAXDigiDocException ex) {
throw ex.getDigiDocException();
}
catch (Exception ex) {
DigiDocException.handleException(ex,
DigiDocException.ERR_PARSE_XML);
}
if (m_doc == null) {
throw new DigiDocException(DigiDocException.ERR_DIGIDOC_FORMAT,
"This document is not in EncryptedData format", null);
}
return m_totalDecrypted;
}
/**
* Reads in a EncryptedData file (.cdoc)
* @param dencStream opened stream with EncrypyedData data The user must open and close it.
* @param outs output stream for decrypted data
* @param token index of PKCS#11 token used
* @param pin pin code to decrypt transport key using PKCS#11
* @param tokenType token type - PKCS11 or PKCS12
* @param pkcs12Keystore - PKCS12 keystore filename and path if pkcs12 is used
* @return number of bytes successfully decrypted
* @throws DigiDocException for decryption errors
*/
public int decryptStreamUsingTokenType(InputStream dencStream, OutputStream outs, int token, String pin,
String tokenType, String pkcs12Keystore)
throws DigiDocException
{
// Use an instance of ourselves as the SAX event handler
EncryptedStreamSAXParser handler = this;
handler.setOutputStream(outs);
handler.setPin(pin);
handler.setToken(token);
if(tokenType == null ||
(!tokenType.equals(SignatureFactory.SIGFAC_TYPE_PKCS11) &&
!tokenType.equals(SignatureFactory.SIGFAC_TYPE_PKCS12)))
throw new DigiDocException(DigiDocException.ERR_XMLENC_DECRYPT,
"Invalid token type. Must be PKCS11 or PKCS12!", null);
// try find cert of token to decrypt with
try {
m_sigFac = ConfigManager.instance().getSignatureFactoryOfType(tokenType);
if(m_sigFac != null && m_sigFac instanceof Pkcs12SignatureFactory) {
Pkcs12SignatureFactory pfac = (Pkcs12SignatureFactory)m_sigFac;
if(m_logger.isDebugEnabled())
m_logger.debug("Loading pkcs12 keystore: " + pkcs12Keystore);
pfac.load(pkcs12Keystore, tokenType, pin);
}
if(m_sigFac != null)
m_decCert = m_sigFac.getAuthCertificate(token, pin);
} catch(Exception ex) {
m_logger.error("Error loading decryption cert: " + ex);
throw new DigiDocException(DigiDocException.ERR_XMLENC_DECRYPT,
"Error loading decryption cert!", ex);
}
if(m_decCert == null)
throw new DigiDocException(DigiDocException.ERR_XMLENC_DECRYPT,
"Error loading decryption cert!", null);
// Use the default (non-validating) parser
SAXParserFactory factory = SAXParserFactory.newInstance();
//factory.setNamespaceAware(true);
try {
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
SAXParser saxParser = factory.newSAXParser();
saxParser.parse(dencStream, handler);
}
catch (SAXDigiDocException ex) {
throw ex.getDigiDocException();
}
catch (Exception ex) {
DigiDocException.handleException(ex, DigiDocException.ERR_PARSE_XML);
}
if (m_doc == null) {
throw new DigiDocException(DigiDocException.ERR_DIGIDOC_FORMAT,
"This document is not in EncryptedData format", null);
}
return m_totalDecrypted;
}
/**
* Reads in a EncryptedData file (.cdoc)
*
* @param dencStream opened stream with EncrypyedData data The user must open and close it.
* @param outs output stream for decrypted data
* @param deckey decryption key
* @param recipientName Recipient atribute value of used to locate the correct transport key to decrypt with
*
* @return number of bytes successfully decrypted
*
* @throws DigiDocException for decryption errors
*/
public int decryptStreamUsingRecipientNameAndKey(InputStream dencStream,
OutputStream outs, byte[] deckey, String recipientName)
throws DigiDocException
{
// Use an instance of ourselves as the SAX event handler
EncryptedStreamSAXParser handler = this;
handler.setRecipientName(recipientName);
handler.setOutputStream(outs);
m_transpkey = deckey;
m_transportKey = (SecretKey)new SecretKeySpec(m_transpkey,EncryptedData.DIGIDOC_ENCRYPTION_ALOGORITHM);
if(m_logger.isDebugEnabled())
m_logger.debug("Transport key: " + ((m_transportKey == null) ? "ERROR" : "OK") + " len: " + m_transpkey.length);
// Use the default (non-validating) parser
SAXParserFactory factory = SAXParserFactory.newInstance();
//factory.setNamespaceAware(true);
try {
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
SAXParser saxParser = factory.newSAXParser();
saxParser.parse(dencStream, handler);
}
catch (SAXDigiDocException ex) {
throw ex.getDigiDocException();
}
catch (Exception ex) {
DigiDocException.handleException(ex, DigiDocException.ERR_PARSE_XML);
}
if (m_doc == null) {
throw new DigiDocException(DigiDocException.ERR_DIGIDOC_FORMAT,
"This document is not in EncryptedData format", null);
}
return m_totalDecrypted;
}
/**
* Start Document handler
*/
public void startDocument() throws SAXException {
m_doc = null;
m_sbCollectChars = null;
m_decompressor = null;
m_totalDecrypted = 0;
m_totalDecompressed = 0;
m_totalInput = 0;
m_sbParseBuf = new StringBuffer();
m_sbB64Buf = new StringBuffer();
m_nBlockType = DENC_BLOCK_FIRST;
}
/**
* End Document handler
*/
public void endDocument() throws SAXException {
}
/**
* Finds the value of an atribute by name
*
* @param atts atributes
* @param attName name of atribute
*
* @return value of the atribute
*/
private String findAtributeValue(Attributes attrs, String attName) {
String value = null;
for (int i = 0; i < attrs.getLength(); i++) {
String key = attrs.getQName(i);
if (key.equals(attName) || key.indexOf(attName) != -1) {
value = attrs.getValue(i);
break;
}
}
return value;
}
/**
* Checks if this document is in format
*
* @throws SAXDigiDocException if the document is not in format
*/
private void checkEncryptedData()
throws SAXDigiDocException {
if (m_doc == null) {
throw new SAXDigiDocException(DigiDocException.ERR_XMLENC_NO_ENCRYPTED_DATA,
"This document is not in EncryptedData format!");
}
}
/**
* Checks if the objects exists
*
* @throws SAXDigiDocException if the objects does not exist
*/
private void checkEncryptedKey(EncryptedKey key)
throws SAXDigiDocException {
if (key == null) {
throw new SAXDigiDocException(DigiDocException.ERR_XMLENC_NO_ENCRYPTED_KEY,
"This object does not exist!");
}
}
/**
* Start Element handler
*
* @param namespaceURI namespace URI
* @param lName local name
* @param qName qualified name
* @param attrs attributes
*/
public void startElement(String namespaceURI, String lName, String qName, Attributes attrs)
throws SAXDigiDocException {
String tName = qName;
if (tName.indexOf(":") != -1) {
tName = qName.substring(qName.indexOf(":") + 1);
}
if (m_logger.isDebugEnabled()) {
m_logger.debug("Start Element: " + tName + " qname: " + qName + " lname: " + lName + " uri: " + namespaceURI);
}
m_tags.push(tName);
if (tName.equals("KeyName") ||
tName.equals("CarriedKeyName") ||
tName.equals("X509Certificate") ||
tName.equals("EncryptionProperty")) {
m_sbCollectChars = new StringBuffer();
}
if (tName.equals("CipherValue")) {
if (m_tags.search("EncryptedKey") != -1) { // child of
m_sbCollectChars = new StringBuffer();
}
else { // child of
m_sbCollectChars = null;
m_bDecrypting = true;
}
}
//
if (tName.equals("EncryptedData")) {
String str = findAtributeValue(attrs, "xmlns");
try {
m_doc = new EncryptedData(str);
str = findAtributeValue(attrs, "Id");
if (str != null) {
m_doc.setId(str);
}
str = findAtributeValue(attrs, "Type");
if (str != null) {
m_doc.setType(str);
}
str = findAtributeValue(attrs, "MimeType");
if (str != null) {
m_doc.setMimeType(str);
}
if (m_doc.getMimeType() != null && m_doc.getMimeType().equals(EncryptedData.DENC_ENCDATA_MIME_ZLIB)) {
m_decompressor = new Inflater();
}
}
catch (DigiDocException ex) {
SAXDigiDocException.handleException(ex);
}
try {
if(m_transportKey != null) {
byte[] iv = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
m_cipher = m_doc.getCipher(Cipher.DECRYPT_MODE, m_transportKey, iv);
}
} catch (DigiDocException ex) {
m_logger.error("Error using key: " + ((m_transpkey != null) ? Base64Util.encode(m_transpkey) : "NULL") + " - " + ex);
SAXDigiDocException.handleException(ex);
}
}
//
if (tName.equals("EncryptionMethod")) {
checkEncryptedData();
if(m_tags.search("EncryptedKey") != -1) { // child of
EncryptedKey ekey = m_doc.getLastEncryptedKey();
checkEncryptedKey(ekey);
try {
ekey.setEncryptionMethod(findAtributeValue(attrs, "Algorithm"));
}
catch (DigiDocException ex) {
SAXDigiDocException.handleException(ex);
}
}
else { // child of
try {
m_doc.setEncryptionMethod(findAtributeValue(attrs, "Algorithm"));
}
catch (DigiDocException ex) {
SAXDigiDocException.handleException(ex);
}
}
}
//
if (tName.equals("EncryptedKey")) {
checkEncryptedData();
EncryptedKey ekey = new EncryptedKey();
m_doc.addEncryptedKey(ekey);
String str = findAtributeValue(attrs, "Recipient");
if (str != null) {
ekey.setRecipient(str);
}
str = findAtributeValue(attrs, "Id");
if (str != null) {
ekey.setId(str);
}
}
//
if (tName.equals("EncryptionProperties")) {
checkEncryptedData();
String str = findAtributeValue(attrs, "Id");
if (str != null) {
m_doc.setEncryptionPropertiesId(str);
}
}
//
if (tName.equals("EncryptionProperty")) {
checkEncryptedData();
EncryptionProperty eprop = new EncryptionProperty();
m_doc.addProperty(eprop);
String str = findAtributeValue(attrs, "Id");
if (str != null) {
eprop.setId(str);
}
str = findAtributeValue(attrs, "Target");
if (str != null) {
eprop.setTarget(str);
}
str = findAtributeValue(attrs, "Name");
try {
if (str != null) {
eprop.setName(str);
}
}
catch (DigiDocException ex) {
SAXDigiDocException.handleException(ex);
}
}
}
/**
* End Element handler
*
* @param namespaceURI namespace URI
* @param lName local name
* @param qName qualified name
*/
public void endElement(String namespaceURI, String sName, String qName)
throws SAXException {
String tName = qName;
if (tName.indexOf(":") != -1) {
tName = qName.substring(tName.indexOf(":") + 1);
}
if (m_logger.isDebugEnabled()) {
m_logger.debug("End Element: " + tName);
}
// remove last tag from stack
String currTag = (String)m_tags.pop();
//
if (tName.equals("KeyName")) {
checkEncryptedData();
EncryptedKey ekey = m_doc.getLastEncryptedKey();
checkEncryptedKey(ekey);
ekey.setKeyName(m_sbCollectChars.toString());
m_sbCollectChars = null; // stop collecting
}
//
if (tName.equals("CarriedKeyName")) {
checkEncryptedData();
EncryptedKey ekey = m_doc.getLastEncryptedKey();
checkEncryptedKey(ekey);
ekey.setCarriedKeyName(m_sbCollectChars.toString());
m_sbCollectChars = null; // stop collecting
}
//
if (tName.equals("X509Certificate")) {
checkEncryptedData();
EncryptedKey ekey = m_doc.getLastEncryptedKey();
checkEncryptedKey(ekey);
try {
X509Certificate cert = SignedDoc.readCertificate(Base64Util.
decode(m_sbCollectChars.toString().getBytes()));
ekey.setRecipientsCertificate(cert);
}
catch (DigiDocException ex) {
SAXDigiDocException.handleException(ex);
}
m_sbCollectChars = null; // stop collecting
}
//
if(tName.equals("CipherValue")) {
checkEncryptedData();
if(m_tags.search("EncryptedKey") != -1) { // child of
if(m_cipher == null) { // if transport key has not been found yet
EncryptedKey ekey = m_doc.getLastEncryptedKey();
checkEncryptedKey(ekey);
ekey.setTransportKeyData(Base64Util.
decode(m_sbCollectChars.toString().getBytes()));
// decrypt transport key if possible
if(m_logger.isDebugEnabled())
m_logger.debug("Recipient: " + ekey.getRecipient() + " cert-nr: " + ekey.getRecipientsCertificate().getSerialNumber() + " decrypt-cert: " + m_decCert.getSerialNumber());
if (m_decCert != null && ekey.getRecipientsCertificate() != null &&
m_decCert.getSerialNumber().equals(ekey.getRecipientsCertificate().getSerialNumber())) {
// decrypt transport key
byte[] decdata = null;
if(m_sigFac == null) {
DigiDocException ex2 = new DigiDocException(DigiDocException.ERR_XMLENC_KEY_DECRYPT,
"SignatureFactory not initialized!", null);
SAXDigiDocException.handleException(ex2);
}
try {
if(m_logger.isDebugEnabled())
m_logger.debug("Decrypting key: " + m_recvName + " serial: " + m_decCert.getSerialNumber());
if(m_transpkey != null) {
decdata = m_transpkey;
}else if(m_tki != null) {
decdata = ((PKCS11SignatureFactory)m_sigFac).
decrypt(ekey.getTransportKeyData(), m_tki.getSlot(), m_tki.getLabel(), m_pin);
} else
decdata = m_sigFac.decrypt(ekey.getTransportKeyData(), m_token, m_pin);
//byte[] decdata = SignedDoc.hex2bin("85C9369D2F9B61EF133F6E62CF525235");
if(m_logger.isDebugEnabled())
m_logger.debug("Using key: " + m_recvName + " decdata: " + Base64Util.encode(decdata));
m_transportKey = (SecretKey)new SecretKeySpec(decdata,EncryptedData.DIGIDOC_ENCRYPTION_ALOGORITHM);
byte[] iv = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
m_cipher = m_doc.getCipher(Cipher.DECRYPT_MODE, m_transportKey, iv);
if(m_logger.isDebugEnabled())
m_logger.debug("Transport key: " + ((m_transportKey == null) ? "ERROR" : "OK") + " len: " + decdata.length);
} catch (DigiDocException ex) {
m_logger.error("Error decrypting key1: " + ((decdata != null) ? Base64Util.encode(decdata) : "NULL") + " - " + ex);
SAXDigiDocException.handleException(ex);
} catch (Exception ex) {
m_logger.error("Error decrypting key2: " + ((decdata != null) ? Base64Util.encode(decdata) : "NULL") + " - " + ex);
DigiDocException ex2 = new DigiDocException(DigiDocException.ERR_XMLENC_KEY_DECRYPT, ex.getMessage(), ex);
SAXDigiDocException.handleException(ex2);
}
}
} // if m_cipher == null
} // child of
else { // child of
m_bDecrypting = false;
decryptBlock(null, DENC_BLOCK_LAST);
if (m_logger.isInfoEnabled()) {
m_logger.info("Total input: " + m_totalInput + " decrypted: " + m_totalDecrypted + " decompressed: " + m_totalDecompressed);
}
}
m_sbCollectChars = null; // stop collecting
}
//
if (tName.equals("EncryptionProperty")) {
checkEncryptedData();
EncryptionProperty eprop = m_doc.getLastProperty();
try {
if(eprop != null && m_sbCollectChars != null)
eprop.setContent(m_sbCollectChars.toString());
} catch (DigiDocException ex) {
SAXDigiDocException.handleException(ex);
}
m_sbCollectChars = null; // stop collecting
}
}
private byte[] m_lblock = null;
private static final int DECBLOCK_SIZE = 8 * 1024;
/**
* Called with a block of base64 data that must be decoded, decrypted and possibly also decompressed
*
* @param data base64 encoded input data
* @param nBlockType type of block (first, middle, last)
*
* @throws SAXException
*/
private void decryptBlock(String data, int nBlockType)
throws SAXException {
// append new data to parse buffer
if(data != null && data.length() > 0)
m_sbParseBuf.append(data);
String indata = null;
if(nBlockType == DENC_BLOCK_LAST) {
indata = m_sbParseBuf.toString();
} else {
if(m_sbParseBuf.length() > ENC_BLOCK_SIZE) {
indata = m_sbParseBuf.substring(0, ENC_BLOCK_SIZE);
m_sbParseBuf.delete(0, ENC_BLOCK_SIZE);
}
}
if (m_logger.isDebugEnabled()) {
m_logger.debug("IN " + ((data != null) ? data.length() : 0) +
" input: " + ((indata != null) ? indata.length() : 0) +
" buffered: " + ((m_sbParseBuf != null) ? m_sbParseBuf.length() : 0) +
" b64left: " + ((m_sbB64Buf != null) ? m_sbB64Buf.length() : 0) +
" block-type: " + nBlockType);
}
// check that cipher has been initialized
if(m_cipher == null) {
DigiDocException de = new DigiDocException(DigiDocException.ERR_XMLENC_DECRYPT,
"Cipher has not been initialized! No transport key for selected recipient?", null);
SAXDigiDocException.handleException(de);
}
// add to data to be b64 decoded
if (indata != null) {
m_sbB64Buf.append(indata);
m_totalInput += indata.length();
} else {
return;
}
try {
byte[] encdata = null;
byte[] decdata = null;
// decode base64
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int nUsed = 0;
if (m_sbB64Buf.length() > 0) {
nUsed = Base64Util.decodeBlock(m_sbB64Buf.toString(), bos, nBlockType == DENC_BLOCK_LAST);
encdata = bos.toByteArray();
// get the cipher if first block of data
if (nBlockType == DENC_BLOCK_FIRST && encdata != null && encdata.length > 16) { // skip IV on first block
byte[] b1 = new byte[encdata.length - 16];
System.arraycopy(encdata, 16, b1, 0, b1.length);
if (m_logger.isDebugEnabled())
m_logger.debug("Removed IV from: " + encdata.length + " block1, left: " + b1.length);
encdata = b1;
}
bos = null;
if (m_logger.isDebugEnabled())
m_logger.debug("Decoding: " + m_sbB64Buf.length() + " got: " +
((encdata != null) ? encdata.length : 0) + " last: " + (nBlockType == DENC_BLOCK_LAST));
if(nUsed > 0)
m_sbB64Buf.delete(0, nUsed);
}
// decrypt the data
decdata = m_cipher.update(encdata);
if(m_logger.isDebugEnabled())
m_logger.debug("Decrypted input: " + indata.length() + " decoded: " +
((encdata != null) ? encdata.length : 0) +
" decrypted: " + ((decdata != null) ? decdata.length : 0));
if(m_totalDecrypted == 0 && decdata != null && decdata.length > 16) {
byte[] ddata = new byte[decdata.length - 16];
System.arraycopy(decdata, 16, ddata, 0, decdata.length - 16);
if(m_logger.isDebugEnabled())
m_logger.debug("Removing IV data from: " + decdata.length + " remaining: " + ddata.length);
decdata = ddata;
}
// padding check on last and before-last block
if(nBlockType == DENC_BLOCK_LAST) {
int n1 = ((m_lblock != null) ? m_lblock.length : 0);
int n2 = ((decdata != null) ? decdata.length : 0);
byte[] ddata = new byte[n1 + n2];
if(n1 > 0)
System.arraycopy(m_lblock, 0, ddata, 0, n1);
if(n2 > 0)
System.arraycopy(decdata, 0, ddata, n1, n2);
decdata = ddata;
if(m_logger.isDebugEnabled())
m_logger.debug("Last block: " + decdata.length);
m_lblock = null;
}
// remove padding on the last block
if (decdata != null && encdata != null && nBlockType == DENC_BLOCK_LAST) {
int nPadLen = new Integer(decdata[decdata.length - 1]).intValue();
if (m_logger.isDebugEnabled())
m_logger.debug("Check padding 1: " + nPadLen);
boolean bPadOk = checkPadding(decdata, nPadLen);
if(bPadOk)
decdata = removePadding(decdata, nPadLen);
if (m_logger.isDebugEnabled())
m_logger.debug("Decdata remaining: " + ((decdata != null) ? decdata.length : 0));
// second padding
if(decdata != null && decdata.length > 0) {
nPadLen = new Integer(decdata[decdata.length - 1]).intValue();
if (m_logger.isDebugEnabled())
m_logger.debug("Check padding 2: " + nPadLen);
if(nPadLen > 0 && nPadLen <= 16 && decdata.length > nPadLen) {
bPadOk = checkPadding(decdata, nPadLen);
if(bPadOk)
decdata = removePadding(decdata, nPadLen);
}
} else if(m_lblock != null) {
nPadLen = new Integer(m_lblock[m_lblock.length - 1]).intValue();
if (m_logger.isDebugEnabled())
m_logger.debug("Check padding 3: " + nPadLen);
if(nPadLen > 0 && nPadLen <= 16 && m_lblock.length > nPadLen) {
bPadOk = checkPadding(m_lblock, nPadLen);
if(bPadOk)
m_lblock = removePadding(m_lblock, nPadLen);
}
}
}
// decompress if necessary and write to output stream
if(m_lblock != null || decdata != null) {
// check compression
if (m_decompressor != null) {
if(nBlockType == DENC_BLOCK_LAST)
m_lblock = decdata;
int nDecomp = 0;
byte[] m_decbuf = null;
if(m_lblock != null) {
if(m_logger.isDebugEnabled())
m_logger.debug("Decompressing: " + m_lblock.length);
m_decompressor.setInput(m_lblock);
m_decbuf = new byte[DECBLOCK_SIZE];
if(m_logger.isDebugEnabled())
m_logger.debug("Decompressing: " + m_lblock.length + " into: " + m_decbuf.length);
while((nDecomp = m_decompressor.inflate(m_decbuf)) > 0) {
if(m_logger.isDebugEnabled())
m_logger.debug("Decompressed: " + m_lblock.length + " into: " + m_decbuf.length + " got: " + nDecomp);
m_outStream.write(m_decbuf, 0, nDecomp);
m_totalDecompressed += nDecomp;
}
}
if(nBlockType == DENC_BLOCK_LAST &&
(!m_decompressor.finished() || m_decompressor.getRemaining() > 0)) {
if(m_logger.isDebugEnabled())
m_logger.debug("Decompressor finished: " + m_decompressor.finished() + " remaining: " + m_decompressor.getRemaining());
m_decbuf = new byte[1024 * 8];
while((nDecomp = m_decompressor.inflate(m_decbuf)) > 0) {
m_outStream.write(m_decbuf, 0, nDecomp);
m_totalDecompressed += nDecomp;
if(m_logger.isDebugEnabled())
m_logger.debug("Decompressing final: " + nDecomp);
}
}
} else { // not compressed
if(m_lblock != null) // second block is first to be written
m_outStream.write(m_lblock);
if(nBlockType == DENC_BLOCK_LAST && decdata != null) // write also last block
m_outStream.write(decdata);
}
m_totalDecrypted += decdata.length;
}
// keep last block for possible padding check
m_lblock = decdata;
} catch (Exception ex) {
DigiDocException de = new DigiDocException(DigiDocException.ERR_XMLENC_DECRYPT,
"Error decrypting: " + ex, ex);
SAXDigiDocException.handleException(de);
}
}
private boolean checkPadding(byte[] data, int nPadLen)
{
boolean bPadOk = true;
if(m_logger.isDebugEnabled())
m_logger.debug("Checking padding: " + nPadLen + " bytes");
if(nPadLen < 0 || nPadLen > 16 || data == null || data.length < nPadLen) return false;
for (int i = data.length - nPadLen; nPadLen > 0 && i < data.length - 1; i++) {
if (m_logger.isDebugEnabled())
m_logger.debug("Data at: " + i + " = " + data[i]);
if ((data[i] != 0 && nPadLen != 16) || (nPadLen == 16 && data[i] != 16 && data[i] != 0)) {
if (m_logger.isDebugEnabled())
m_logger.debug("Data at: " + i + " = " + data[i] + " cancel padding");
bPadOk = false;
break;
}
}
return bPadOk;
}
private byte[] removePadding(byte[] data, int nPadLen)
{
if(m_logger.isDebugEnabled())
m_logger.debug("Removing padding: " + nPadLen + " bytes");
if(nPadLen < 0 || nPadLen > 16 || data == null || data.length < nPadLen) return data;
byte[] data2 = new byte[data.length - nPadLen];
System.arraycopy(data, 0, data2, 0, data.length - nPadLen);
return data2;
}
/**
* SAX characters event handler
*
* @param buf received bytes array
* @param offset offset to the array
* @param len length of data
*/
public void characters(char buf[], int offset, int len)
throws SAXException {
String s = new String(buf, offset, len);
// just collect the data since it could
// be on many lines and be processed in many events
if (s != null) {
if (m_sbCollectChars != null) {
m_sbCollectChars.append(s);
}
if (m_bDecrypting) {
decryptBlock(s, m_nBlockType);
if (m_nBlockType == DENC_BLOCK_FIRST) {
m_nBlockType = DENC_BLOCK_MIDDLE;
}
}
}
}
}