Tuesday, September 29, 2009
XML Encryption with Apache XML Security
這幾天在弄這個東西搞得很煩不過還是弄好了
所以在這裡記錄一下以免之後忘掉還是怎樣
很奇怪他library裡面對於encryption與decryption附的sample code
是用secret key(symmetric key)加密內容後再用另一把secret key加密那把secret key
然後傳來傳去的就是那把secret key
我不是很了解其中的原因
所以把它改成我知道比較ok的方式就是
使用public key加密那把加密內容的secret key
傳過去後再用自己的private key解開
但這小小的變更卻是麻煩的開始
之前總是出現一些奇怪的Exception像是
org.apache.xml.security.encryption.XMLEncryptionException: decryptElement called without a key and unable to resolve
或是
javax.crypto.BadPaddingException: Given final block not properly padded
不過弄了幾天後
在我下面這個例子裡面是沒有出現啦~
首先是我拿來測試用的XML檔案
swanky.xml
<?xml version="1.0" encoding="UTF-8"?>
<Test>
<Name>Swanky Hsiao</Name>
<Blog>http://swanbear.blogspot.com/</Blog>
<Flickr>http://www.flickr.com/photos/swanky-hsiao/</Flickr>
<Plurk>http://www.plurk.com/swanky</Plurk>
<Facebook>http://www.facebook.com/swanky</Facebook>
<FacebookFansGroup>http://www.facebook.com/pages/0e801f0a/172447000328</FacebookFansGroup>
</Test>
然後用keytool產生keystore
輸入後除了一開始要你指定key的密碼是打password之外都可以亂打
keytool -keystore swanky.keystore -genkey -alias swanky-key -keyalg RSA -keypass password
XML檔案跟keystore準備好後就可以測試了~
我這裡是都寫在一個class裡面方便使用!
除了可以直接用encryElement()指定要加密的Element外
也可以像我這例子裡面放多個xpath進行多個Element的加密
import java.io.CharArrayWriter;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.apache.xml.security.encryption.EncryptedKey;
import org.apache.xml.security.encryption.XMLCipher;
import org.apache.xml.security.keys.KeyInfo;
import org.apache.xml.security.utils.EncryptionConstants;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import com.sun.org.apache.xml.internal.serialize.OutputFormat;
import com.sun.org.apache.xml.internal.serialize.XMLSerializer;
public class ApacheXMLSecurityTest {
static {
org.apache.xml.security.Init.init();
}
static private String keystoreName = "swanky.keystore";
static private String keystorePassword = "password";
static private String keyAlias = "swanky-key";
public static void main(String[] args) {
Document doc = parseXML("swanky.xml");
String[] xpaths = new String[] { "Test/Plurk", "Test/Facebook" };
encryptXPaths(doc, xpaths);
System.out.println("Encrypt:\n" + serialDoc(doc));
decryptDoc(doc);
System.out.println("Decrypt:\n" + serialDoc(doc));
}
public static Document parseXML(String uri) {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
Document doc = null;
try {
DocumentBuilder docBuilder = factory.newDocumentBuilder();
doc = docBuilder.parse(uri);
} catch (Exception e) {
e.printStackTrace();
}
return doc;
}
public static Writer serialDoc(Document doc) {
CharArrayWriter writer = new CharArrayWriter();
OutputFormat format = new OutputFormat(doc);
format.setLineWidth(65);
format.setIndenting(true);
format.setIndent(2);
XMLSerializer serializer = new XMLSerializer(writer, format);
try {
serializer.serialize(doc);
} catch (IOException e) {
e.printStackTrace();
}
return writer;
}
public static void encryptXPaths(Document doc, String[] xpaths) {
try {
for (int i = 0; i < xpaths.length; i++) {
String xpath = xpaths[i];
NodeList list = evaluateXPath(doc, xpath);
for (int j = 0; j < list.getLength(); j++) {
Node node = (Node) list.item(j);
Element element = (Element) node;
encryElement(doc, element);
}
}
} catch (XPathExpressionException e) {
e.printStackTrace();
}
}
private static NodeList evaluateXPath(Document doc, String expression)
throws XPathExpressionException {
NodeList nodes;
XPathFactory factory = XPathFactory.newInstance();
XPath xpath = factory.newXPath();
XPathExpression expr = xpath.compile(expression);
Object result = expr.evaluate(doc, XPathConstants.NODESET);
nodes = (NodeList) result;
return nodes;
}
public static void encryElement(Document doc, Element element) {
try {
SecretKey symmetricKey = generateDataEncryptionKey();
XMLCipher xmlCipher = XMLCipher.getInstance(XMLCipher.AES_128);
xmlCipher.init(XMLCipher.ENCRYPT_MODE, symmetricKey);
PublicKey pubKey = getKeyStore(keystoreName, keystorePassword)
.getCertificate("swanky-key").getPublicKey();
XMLCipher keyCipher = XMLCipher.getInstance(XMLCipher.RSA_v1dot5);
keyCipher.init(XMLCipher.WRAP_MODE, pubKey);
EncryptedKey encryptedKey = keyCipher.encryptKey(doc, symmetricKey);
KeyInfo keyInfo = new KeyInfo(doc);
keyInfo.add(encryptedKey);
xmlCipher.getEncryptedData().setKeyInfo(keyInfo);
xmlCipher.doFinal(doc, element, true);
} catch (Exception e) {
e.printStackTrace();
}
}
static KeyStore getKeyStore(String keyStoreFileName, String password) {
KeyStore store = null;
try {
store = KeyStore.getInstance("JKS", "SUN");
InputStream in = new FileInputStream(keyStoreFileName);
store.load(in, password.toCharArray());
in.close();
} catch (Exception e) {
e.printStackTrace();
}
return store;
}
private static SecretKey kek;
private static SecretKey generateDataEncryptionKey() {
if (kek == null) {
try {
String jceAlgorithmName = "AES";
KeyGenerator keyGenerator = KeyGenerator
.getInstance(jceAlgorithmName);
keyGenerator.init(128);
kek = keyGenerator.generateKey();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
return kek;
}
public static Document decryptDoc(Document doc) {
try {
XMLCipher xmlCipher = XMLCipher.getInstance();
xmlCipher.init(XMLCipher.DECRYPT_MODE, null);
PrivateKey privateKey = (PrivateKey) getKeyStore(keystoreName,
keystorePassword).getKey(keyAlias,
keystorePassword.toCharArray());
xmlCipher.setKEK(privateKey);
NodeList list = doc.getElementsByTagNameNS(
EncryptionConstants.EncryptionSpecNS,
EncryptionConstants._TAG_ENCRYPTEDDATA);
while (list.getLength() > 0) {
Node node = (Node) list.item(0);
Element encryptedDataElement = (Element) node;
xmlCipher.doFinal(doc, encryptedDataElement);
}
} catch (Exception e) {
e.printStackTrace();
}
return doc;
}
}
執行結果是像這樣的
Encrypt:
<?xml version="1.0" encoding="UTF-8"?>
<Test>
<Name>Swanky Hsiao</Name>
<Blog>http://swanbear.blogspot.com/</Blog>
<Flickr>http://www.flickr.com/photos/swanky-hsiao/</Flickr>
<Plurk>
<xenc:EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Content" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
<xenc:EncryptionMethod
Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"/>
<ds:KeyInfo>
<xenc:EncryptedKey xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
<xenc:EncryptionMethod
Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"/>
<xenc:CipherData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
<xenc:CipherValue xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">n0VFmIo5y+Ra+0Qnd0zQIbNK2N8QuLz6rQAd2UgHKjfLnhjAzFy9lX+wpcLdk31F5zvxw+G4yFKt
VFZaQn7/bW1tOUoHmoepdIZ9ZsQHWS7NZRsaKUjaLjIj777Rt5paCQUGOhI6yf7QyPh+zGkUwoPI
lbIyZeEXO9w6MjetnI0=</xenc:CipherValue>
</xenc:CipherData>
</xenc:EncryptedKey>
</ds:KeyInfo>
<xenc:CipherData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
<xenc:CipherValue xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">Vo/J6LhCT8wA8ohdgo0g2CgmDnFznQ/hz3GkpMxJZu5ELTA6Paj+FVR9hgEicDSg</xenc:CipherValue>
</xenc:CipherData>
</xenc:EncryptedData>
</Plurk>
<Facebook>
<xenc:EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Content" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
<xenc:EncryptionMethod
Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"/>
<ds:KeyInfo>
<xenc:EncryptedKey xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
<xenc:EncryptionMethod
Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"/>
<xenc:CipherData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
<xenc:CipherValue xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">OY5WBZW/aFssBJ/ZaHkSo+UncYCQF/fdnOnLsXrZBZVtKKvfVZkVEMzKNYmtdquqZKcOd5FYfet7
3SW7P6GNEhuTGPmA3L/+tlKroONg50ATe6HBb5CbkPhArfDJmtEu8zvQCxOkXUACO3KCYc2RQ7/g
Ri3BH+q3M9YLpDT/GVE=</xenc:CipherValue>
</xenc:CipherData>
</xenc:EncryptedKey>
</ds:KeyInfo>
<xenc:CipherData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
<xenc:CipherValue xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">8UufIfFlEUXHhQ+l1owFOq1m/MDEW+4Psn/kCqY5Y2RDLWRZu7smOPfveYUAJmJA</xenc:CipherValue>
</xenc:CipherData>
</xenc:EncryptedData>
</Facebook>
<FacebookFansGroup>http://www.facebook.com/pages/0e801f0a/172447000328</FacebookFansGroup>
</Test>
Decrypt:
<?xml version="1.0" encoding="UTF-8"?>
<Test>
<Name>Swanky Hsiao</Name>
<Blog>http://swanbear.blogspot.com/</Blog>
<Flickr>http://www.flickr.com/photos/swanky-hsiao/</Flickr>
<Plurk>http://www.plurk.com/swanky</Plurk>
<Facebook>http://www.facebook.com/swanky</Facebook>
<FacebookFansGroup>http://www.facebook.com/pages/0e801f0a/172447000328</FacebookFansGroup>
</Test>
Labels: code, encryption, example, java, security, XML