Khám phá XML Encryption (mã hóa XML), Phần 2

Thực thi một cơ chế Mã hóa XML

Quay lại

Ví dụ 2. Lớp XmlEncryption
 /*
 	DW/BS
	20020521
	XmlEncryption.java
	Listing 2
	A wrapper class that can generate complete XML encrypted file.
	It uses all the other classes. 
	Users of the XML Encryption Engine will only need to interact with 
	this class.
 */

import java.io.*;
import java.util.*;
import org.w3c.dom.*;
import javax.xml.parsers.*;
import org.apache.crimson.tree.XmlDocument;
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import gokul.java.io.base64.*;

public class XmlEncryption {


	// Name of the algorithm which will be used to encrypt the data.
	private String algoName = null;
	
	// Name of the secret key which was previously agreed upon 
	// and saved with the given name.
	private String keyName = null; 
	
	// ID attribute of the main encryption type structure 
	private String encId = null;
	
	// It will be used to get a new Document objects.
	private DocumentBuilder docBuilder = null;
	
	// The Document object holding the XML file
	private Document clearDoc = null;
	
	//The encryption key
	private Key encKey = null;
	
	//The decryption key
	private SecretKey decKey = null;

	
	// The default constructor
	public XmlEncryption() {
		// Create DocumentBuilder object from DocumentBuilderFactory.
		try {
			docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
		} 
		catch (ParserConfigurationException e) { 
			docBuilder = null; 
		}
	}
	
		
	public void setAlgoName(String name) {
		this.algoName = name;
	}// End setAlgoName() 
	
	
	public String getAlgoName() {
		return this.algoName;
	}// End getAlgoName() 
	
	
	public void setKeyName (String key) {
		this.keyName = key; 
	}// End setKeyName()
	
	
	public String getKeyName() {
		return this.keyName; 
	}// End getKeyName()
	
	
	public void setEncId (String id) {
		this.encId = id;
	}// End setEncId()
	
	
	public String getEncId() {
		return this.encId;
	}// End setEncId()
	
	
	public void setClearDoc(String fileString) {
		try {
			ByteArrayInputStream bais = new ByteArrayInputStream
				(fileString.getBytes());
			this.clearDoc = docBuilder.parse(bais);
		}
		catch(Exception e) {
			e.printStackTrace();
		}
	}// End setClearDoc()
	
	
	public Document getClearDoc() {
		return this.clearDoc;
	}// End getClearDoc()
	
	
	public void setEncKey(Key encKey) {
		this.encKey = encKey;
	}// End setEncKey()
	
	
	public Key getEncKey() {
		return this.encKey;
	}// End getEncKey()
	
	
	public void setDecKey(SecretKey decKey) {
		this.decKey = decKey;
	}// End setDecKey()
	
	
	public Key getDecKey() {
		return this.decKey;
	}// End getDecKey()
	
	
	// Generate a Complete XML Encrypted File.
	public String encryptCompleteXmlFile() {
		// Take an Object of EncryptedData Class. 
		// It represents an <EncryptedData> Element.
		EncryptedData encDataObj = this.getEncryptedDataDoc(this.encId, "DOCUMENT");
		// Get XML Structure for <EncryptionMehtod> element.
		Document encMethodDoc = this.getEncryptionMethodDoc(this.algoName);	
		// Get XML Structure for <KeyInfo> element.		
		Document encKeyInfoDoc = this.getKeyInfoDoc(this.keyName);
		// Read the given file data which will be encrypted.
		String plainData = this.getString((XmlDocument)this.clearDoc);
		// Use of JCA/JCE to get encrypted data.
		String cipherData = this.getEncryptedData(plainData);
		// Get XML Structure for <CipherData> element.				
		Document cipherDataDoc = this.getCipherDataDoc(cipherData);
		// Join these XML Structures.
		encDataObj.addChild(encMethodDoc);
		encDataObj.addChild(encKeyInfoDoc);
		encDataObj.addChild(cipherDataDoc);
		//return the resultant in a file
		return getString((XmlDocument)encDataObj.getEncData());
	}// End encryptCompleteXmlFile()

	
	// Generate element encrypted XML file.
	public String encryptElementOfXmlFile(String elementName) {
	
		// Take an Object of EncryptedData Class. 
		// It represents <EncryptedData> Element.
		EncryptedData encDataObj = this.getEncryptedDataDoc(this.encId, "ELEMENT");
		// Get XML Structure for <EncryptionMehtod> element.
		Document encMethodDoc = this.getEncryptionMethodDoc(this.algoName);	
		// Get XML Structure for <KeyInfo> element.		
		Document encKeyInfoDoc = this.getKeyInfoDoc(this.keyName);
		//The node object is transformend into a string.
		String plainData = this.getClearNode(elementName).toString().trim();
		// Method call to get encrypted and base 64 encoded data.
		String base64cipherData = this.getEncryptedData(plainData);
		// Get XML Structure for <CipherData> element.				
		Document cipherDataDoc = this.getCipherDataDoc(base64cipherData);
		// Join these XML Structures.
		encDataObj.addChild(encMethodDoc);
		encDataObj.addChild(encKeyInfoDoc);
		encDataObj.addChild(cipherDataDoc);
		// Return the encrypted content as a Document type object.
		Document encElementDoc = encDataObj.getEncData();	
		//Replace the specified element with its encrypted form
		boolean repstatus = false;
		repstatus = replaceElement(encElementDoc,elementName);
		return getString((XmlDocument)clearDoc);
	}// End encryptElementOfXmlFile()


	// Generate element content encrypted XML file.
	public String encryptElementContentOfXmlFile(String elementName) {
		// Take an Object of EncryptedData Class. 
		// It represents <EncryptedData> Element.
		EncryptedData encDataObj = this.getEncryptedDataDoc(this.encId, "CONTENT");
		// Get XML Structure for <EncryptionMehtod> element.
		Document encMethodDoc = this.getEncryptionMethodDoc(this.algoName);	
		// Get XML Structure for <KeyInfo> element.		
		Document encKeyInfoDoc = this.getKeyInfoDoc(this.keyName);
		//The node object is transformed into a string.
		String plainData = this.getElementContent(elementName).trim();
		// Method call to get encrypted and base 64 encoded data.
		String base64cipherData = this.getEncryptedData(plainData);
		// Get XML Structure for <CipherData> element.				
		Document cipherDataDoc = this.getCipherDataDoc(base64cipherData);
		// Join these XML Structures.
		encDataObj.addChild(encMethodDoc);
		encDataObj.addChild(encKeyInfoDoc);
		encDataObj.addChild(cipherDataDoc);//
		// Return the encrypted content as a Document type object.
		Document encElementDoc = encDataObj.getEncData();
		//Replace the specified element with its encrypted form
		boolean repstatus = false;
		repstatus = replaceContent(encElementDoc, elementName);
		return getString((XmlDocument)clearDoc);
	}// End encryptElementOfXmlFile()

	
	// This is where the actual JCA/JCE data encryption takes place.
	public String getEncryptedData(String data) {
		String clearData = new String(data);
		String cipherDataBase64	= null;
		try {
			//padding the last byte with " "
			int pad = clearData.length()%8;
			for(int i=0;i<(8-pad);i++) 
				clearData += " ";
			// Creating a Cipher Object.
			Cipher cipherObj = Cipher.getInstance(this.algoName+"/CBC/NoPadding");
			// Initializing the Cipher Object in encryption mode.
			cipherObj.init(Cipher.ENCRYPT_MODE,this.encKey);
			// Retreiving the IV
			byte [] iv = cipherObj.getIV();
			//Ciphering the clear text.
			byte[] cipherTemp = cipherObj.doFinal(clearData.getBytes());
			//Prefixing the IV to the cipher string to produce cipherData
			int ivlen = iv.length;
			int cTlen = cipherTemp.length;
			int cDlen = ivlen+cTlen	;
			byte [] cipherData = new byte[cDlen];
			// First 8 bytes are IV bytes
			for (int i=0;i<ivlen;i++) 
				cipherData[i] = (byte)iv[i];
			// Rest of the bytes are cipher bytes	
			for (int i=ivlen;i<cDlen;i++)
				cipherData[i] = (byte)cipherTemp[i-8];	
			//Base 64 encoding of cipherData
			cipherDataBase64 = getBase64Encoded(cipherData);
		}
		catch (Exception e) {
			System.out.println("Problem in getEncryptedData()");
			e.printStackTrace();
		}
		return cipherDataBase64;
	}// End getEncryptedData()
	
	//Decrypting the XML Encrypted file
	public String getDecryptedData(String encString) {
		String decString = "UNDER DEVELOPMENT";
		try	{
			//get the encrypted XML file string parsed into a Document object
			ByteArrayInputStream bais = new ByteArrayInputStream
				(encString.getBytes());
			Document encDoc = docBuilder.parse(bais);
			//Get a list of all the EncryptedData tags 
			NodeList nl = encDoc.getElementsByTagName("EncryptedData");
			//Load, decrypt and replace 
			//each EncryptedData tag in the Document object
			for(int i=0;i<nl.getLength();i++) {
				//Loading an element
				Node edata = nl.item(i);
				//Extracting the values of  Algorithm, KeyName, 
				//Type(of encryption) and CipherValue
				String algo = null;
				String keyname = null;
				String encType = null;
				String ciphervalue = null;
				//Setting the values 
				edata.normalize();
				//Setting the value of encType
				encType = edata.getAttributes().getNamedItem("Type")
					.getNodeValue();
				//Setting the values of the remaining parameters
				NodeList algoNL = edata.getChildNodes();
				for(int j=0;j<algoNL.getLength();j++) {
					//Setting the value of algo
					if(algoNL.item(j).getNodeName().equals
						("EncryptionMethod"))
						algo = algoNL.item(j).getAttributes().
							getNamedItem("Algorithm").getNodeValue();
					//Setting the value of keyname
					if(algoNL.item(j).getNodeName().equals("ds:KeyInfo")) {
						NodeList knNL = algoNL.item(j).getChildNodes();
						for(int k=0;k<knNL.getLength();k++)	{
							if(knNL.item(k).getNodeName().equals
								("KeyName"))
								keyname = (knNL.item(k).getFirstChild().
									getNodeValue());
						}
					}
					//Setting the value of ciphervalue
					if(algoNL.item(j).getNodeName().equals("CipherData")) {
						NodeList cvNL = algoNL.item(j).getChildNodes();
						for(int v=0;v<cvNL.getLength();v++)	{
							if(cvNL.item(v).getNodeName().equals
								("CipherValue"))
								ciphervalue = (cvNL.item(v)
									.getFirstChild().getNodeValue());
						}
					}
				}
				if (algo.equals
					("http://www.w3.org/2001/04/xmlenc#tripledes-cbc"))
					algo = "DESede";
				//Reading the key file and generating/setting decKey
				this.generateDecKey(keyname, algo);
				//Decrypt the cipher 
				String decbit = Decrypt(ciphervalue,this.decKey,algo).trim();
				//Replacement Logic
				//For replacing an entire XML file
				if(encType.equals(
			"http://www.isi.edu/in-notes/iana/assignments/media-types/text/xml"))
					decString = decbit;
				//Decrypted element replacement	
				else 
				if(encType.equals("http://www.w3.org/2001/04/xmlenc#Element")) {
					try	{
						byte [] a = decbit.getBytes();
						ByteArrayInputStream bais2 = 
							new ByteArrayInputStream(a);
						Document decdoc = docBuilder.parse(bais2); 
						Node decNode = 
							encDoc.importNode
								(decdoc.getFirstChild(), true);
						edata.getParentNode().replaceChild(decNode,edata);
					}
					catch(org.xml.sax.SAXParseException spe) {
						Text decText = encDoc.createTextNode(decbit);
						edata.getParentNode().replaceChild(decText,edata);
					}
					decString = (getString((XmlDocument)encDoc));
				}
				//Decrypted content replacement
				else if(encType.equals
					("http://www.w3.org/2001/04/xmlenc#Content")) {
					try {
						// Works if the content is a single child element.
						byte [] a = decbit.getBytes();
						ByteArrayInputStream bais2 = 
							new ByteArrayInputStream(a);
						Document decdoc = docBuilder.parse(bais2); 
						Node decNode = 
							encDoc.importNode
							(decdoc.getFirstChild(), true);
						edata.getParentNode().replaceChild(decNode,edata);
					}
					catch(org.xml.sax.SAXParseException spe) {
						//In case the content is plain text 
						//or a group of child elements
						Text decText = encDoc.createTextNode(decbit);
						edata.getParentNode().replaceChild(decText,edata);
					}
					decString = (getString((XmlDocument)encDoc));
				}
			}		
		}
		catch(Exception e) {
			e.printStackTrace();
		}
		return decString;			
	}// End getDecryptedData()
	
		
	// This is where the actual JCA/JCE data decryption takes place.
	private String Decrypt(String encString, Key decKey, String algo) {
		// Decoding the Base 64 Encoded IV+cipher String into a byte array
		byte[] g = getBase64Decoded(encString);
		int glen = g.length;
		// Separating the IV from the byte array
		byte [] iv = new byte[8]; 
		for(int t=0;t<8;t++)
			iv[t] = g[t];
		// Separating the cipher from the byte array
		byte [] Enc = new byte[glen-8];
		for(int p=8;p<glen;p++)
			Enc[p-8] = g[p];
		// This will hold the decrypted String
		String decString = null;
		// Decrypting the cipher and setting decString:
		try {
			IvParameterSpec ivps = new IvParameterSpec(iv);
			AlgorithmParameters aparam = AlgorithmParameters.getInstance(algo);
			aparam.init(ivps);
		    Cipher cipherObj = Cipher.getInstance(algo+"/CBC/NoPadding");
			cipherObj.init(Cipher.DECRYPT_MODE, decKey, aparam);
			decString = new String(cipherObj.update(Enc));
		}
		catch(Exception e) {
			System.out.println("Problem in Decrypt()");
			e.printStackTrace();
		}
		return decString;
	}//End Decrypt()
	
	
	//Base 64 Encoding a byte array and returning it as a String
	private String getBase64Encoded(byte[] plainText) {
		String encoded = null;
		try {
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			Base64OutputStream out= new Base64OutputStream(baos,true);
			out.write(plainText);
			out.close();
			String encText = new String(baos.toByteArray());
			baos.close();
			encoded = encText;
		}
		catch(Exception e) {
			e.printStackTrace();
		}
		return encoded;
	}//End getBase64Encoded()
	
	//Base 64 Decoding a String and returning it as a byte array
	private byte[] getBase64Decoded(String codedText) {
		String ct = codedText;
		int lenEnc = ct.length();
		int lenDec = 0;
		// To calculate the max size of the decoded byte array	
		if (ct.charAt(lenEnc-1) == '=')
				if (ct.charAt(lenEnc-2) == '=') {
					lenDec = ((lenEnc-4)* 3/4);
					lenDec = ((lenDec - (lenDec/77))+1);
				}
				else {
					lenDec = ((lenEnc-4)* 3/4);
					lenDec = ((lenDec - (lenDec/77))+2);
				}
			else {
				lenDec = (lenEnc * 3/4);
				lenDec = (lenDec - (lenDec/77));
			}
		byte [] decoded = new byte[lenDec];
		try {
			
			ByteArrayInputStream bais = new ByteArrayInputStream(ct.getBytes());
			Base64InputStream in = new Base64InputStream(bais);
			in.read(decoded);
			in.close();
		}
		catch(Exception e) {
			e.printStackTrace();
		}
		return decoded;
	}// End getBase64Decoded()
	
	// To generate the named Key Object and to set the decryption key
	private void generateDecKey(String keyname, String algo) {
		try {
			FileInputStream dkFIS = new FileInputStream(keyname);
			byte [] dk = new byte[dkFIS.available()];
			dkFIS.read(dk);
			dkFIS.close();
			// Generating/setting the decryption key.
			try {
				SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(algo);
				DESedeKeySpec dkSpec = new DESedeKeySpec(dk);
				SecretKey sKey = keyFactory.generateSecret(dkSpec);
				this.setDecKey(sKey); 
			}
			catch(Exception e) {
				System.out.println("Unable to generate/set the decryption key.");
				e.printStackTrace();
			}
		}
		catch(Exception e) {
			System.out.println("Unable to set the decryption key.");
			e.printStackTrace();
		}
	}// End generateDecKey()

		
	//Replace the specified element with the encrypted data element
	private boolean replaceElement(Document edDoc, String elementName) {
		boolean isReplaced = false;
		try {
			Node clearNode = this.getClearNode(elementName);
			Node parentNode = clearNode.getParentNode();
			Node edNode = clearDoc.importNode(edDoc.getFirstChild(), true);
			parentNode.replaceChild(edNode,clearNode);
			isReplaced = true;
		}
		catch(Exception e) {
			isReplaced = false;
			System.out.println("\nReplacement Failed.");
			e.printStackTrace();
		}
		return isReplaced;
	}//End replaceElement()
	
	
	//Replace the specified element's content with the encrypted data element
	private boolean replaceContent(Document edDoc, String elementName) {
		boolean isReplaced = false;
		try {
			if (removeAllChildNodes(elementName)) {
				Node parentNode = this.getClearNode(elementName);
				//Importing the encryptedData Node
				Node edNode = clearDoc.importNode(edDoc.getFirstChild(), true);
				//Appending to an already empty parentNode
				parentNode.appendChild(edNode);
				isReplaced = true;		
			}
			else 
				System.out.println(
					"Child nodes not removed; replacement did not initiate.");
		}
		catch(Exception e) {
			isReplaced = false;
			System.out.println("\nContent replacement failed.");
			e.printStackTrace();
		}
		return isReplaced;
	}//End replaceContent()
	
	
	//Search the document tree and return the specified element as a Node Object.
	private Node getElement(Document doc, String elementName) {
 		NodeList nList = doc.getElementsByTagName(elementName);
		Node theNode = nList.item(0);
		return theNode;	
	}// End getElement();
	
	
	//Get a clear node by its element name
	private Node getClearNode(String elementName) {
		return getElement(this.clearDoc, elementName);
	}// End getClearNode()
		
	
	//Get a clear node's content by its element name
	public String getElementContent(String elementName) {
		String elementContent = new String();
		NodeList nl = this.getClearNode(elementName).getChildNodes();
		for(int i=0;i<nl.getLength();i++) 
			elementContent += (nl.item(i)).toString().trim();
		return elementContent;	
	}// End setElementContent()
	
	
	//Remove all the child nodes of clearNode
	private boolean removeAllChildNodes(String elementName) {
		boolean removed = false;
		Node clearNode = this.getClearNode(elementName);
		try	{
			while(clearNode.hasChildNodes()) {
				NodeList t = clearNode.getChildNodes();	
				// Removing a child Node 
				clearNode.removeChild(t.item(0));
			}//All children removed
			removed = true;
		}
		catch(Exception e) {
			removed = false;
			e.printStackTrace();
		}
		return removed;
	}//End removeAllChildNodes()
	
	
	// Get the new Document object for Document Builder
	private Document getNewDocument() {
		if (docBuilder != null)
			return docBuilder.newDocument();
		else
			return null;	
	}


	// Returns the EncryptedData object.
	public EncryptedData getEncryptedDataDoc(String Id, String encType) {
		EncryptedData ed = new EncryptedData(this.getNewDocument());
		ed.setId(Id);
		if (encType.equals("DOCUMENT"))
			ed.setType(AlgoNames.DOCUMENT);
		if (encType.equals("ELEMENT"))
			ed.setType(AlgoNames.ELEMENT);	
		if (encType.equals("CONTENT"))
			ed.setType(AlgoNames.CONTENT);	
		return ed;
	}// End getEncryptedDataDoc()
	
	
	// Returns the <EncryptionMehtod> structure.
	public Document getEncryptionMethodDoc (String algoName) {
		EncryptionMethod em = new EncryptionMethod(this.getNewDocument());
		if (algoName.equals("DESede"))
			em.setAlgorithm(AlgoNames.TRIPLE_DES);
		return em.getEncMethod();
	}// End getEncryptionMethodDoc()


	// Returns the <KeyInfo> structure.
	public Document getKeyInfoDoc (String keyName) {
		GenericKeyInfo ki = 
			new GenericKeyInfo(this.getNewDocument(),"ds", AlgoNames.XML_DSIG);
		ki.setKeyName(keyName);
		return ki.getKeyInfo();	
	}// End getKeyInfoDoc()


	// Returns the <CipherData> structure.
	public Document getCipherDataDoc (String data) {
		CipherData cd = new CipherData(this.getNewDocument());
		cd.setValue(data);
		return cd.getCipherData();
	}// getCipherDataDoc()
	
		
	//Convert an XmlDocument type object into a String
	public String getString(XmlDocument xmldoc) {	
		String DocumentAsString = null;
		try	{
			ByteArrayOutputStream bos = new ByteArrayOutputStream();
			xmldoc.write(bos);
			DocumentAsString = bos.toString();
		}
		catch(Exception e) {
			e.printStackTrace();
		}
		return DocumentAsString;
	}//End getString()
	
	
	//Convert a Document type object into a String 
	//The output string does not contain initial encoding tags
	public String getString(Document doc) {	
		String DocumentAsString = null;
		try	{
			if (doc.getFirstChild().getNodeName().equals("#comment")) {
				DocumentAsString = "<!--"+doc.getFirstChild().getNodeValue()+
					"-->\n\n";
				DocumentAsString += doc.getDocumentElement().toString();
			}
			else
				DocumentAsString = doc.getDocumentElement().toString();
		}
		catch(Exception e) {
			e.printStackTrace();
		}
		return DocumentAsString;
	}//End getString() 
	

}// End class demoXmlEncApp

Quay lại