blob: b970272a165411c786811471172faa7fc440d001 [file] [log] [blame]
/*
* 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.jackrabbit.vault.fs.config;
import java.io.ByteArrayOutputStream;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.jcr.Credentials;
import javax.jcr.SimpleCredentials;
import org.apache.jackrabbit.vault.util.Text;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
/**
* {@code SimpleCredentialsConfig}...
*
*/
public class SimpleCredentialsConfig extends CredentialsConfig {
/**
* key length
*/
private final static int KEY_LENGTH = 8;
/**
* encryption prefix
*/
private final static String PREFIX = "{DES}";
/**
* default logger
*/
private static final Logger log = LoggerFactory.getLogger(SimpleCredentialsConfig.class);
private final SimpleCredentials creds;
public static final String ELEM_USER = "user";
public static final String ATTR_NAME = "name";
public static final String ATTR_PASSWORD = "password";
public SimpleCredentialsConfig(SimpleCredentials creds) {
super("simple");
this.creds = creds;
}
public Credentials getCredentials() {
return creds;
}
public static SimpleCredentialsConfig load(Element elem) throws ConfigurationException {
assert elem.getNodeName().equals(ELEM_CREDETIALS);
NodeList nl = elem.getChildNodes();
for (int i=0; i<nl.getLength(); i++) {
Node child = nl.item(i);
if (child.getNodeType() == Node.ELEMENT_NODE) {
if (child.getNodeName().equals(ELEM_USER)) {
Element e = (Element) child;
String name = e.getAttribute(ATTR_NAME);
String pass = decrypt(e.getAttribute(ATTR_PASSWORD));
return new SimpleCredentialsConfig(
new SimpleCredentials(
name,
pass == null ? new char[0] : pass.toCharArray()));
}
}
}
throw new ConfigurationException("mandatory element <user> missing.");
}
public void writeInner(ContentHandler handler) throws SAXException {
if (creds != null) {
AttributesImpl attrs = new AttributesImpl();
attrs.addAttribute("", ATTR_NAME, "", "CDATA", creds.getUserID());
attrs.addAttribute("", ATTR_PASSWORD, "", "CDATA", encrypt(new String(creds.getPassword())));
handler.startElement("", ELEM_USER, "", attrs);
handler.endElement("", ELEM_USER, "");
}
}
/**
* Encrypts the given string in a fairly secure way so that it can be
* {@link #decrypt(String) decrypted} again.
*
* @param s string to encrypt
* @return the encrypted string with a "{AES}" prefix.
*/
private static String encrypt(String s) {
try {
SecretKey key = KeyGenerator.getInstance("DES").generateKey();
Cipher cipher = Cipher.getInstance("DES");
byte[] keyBytes = key.getEncoded();
byte[] data = s.getBytes("utf-8");
ByteArrayOutputStream out = new ByteArrayOutputStream(keyBytes.length + data.length);
out.write(keyBytes);
cipher.init(Cipher.ENCRYPT_MODE, key);
out.write(cipher.update(data));
out.write(cipher.doFinal());
StringBuilder ret = new StringBuilder(PREFIX);
for (byte b: out.toByteArray()) {
ret.append(Text.hexTable[b>>4 & 0x0f]).append(Text.hexTable[b&0x0f]);
}
return ret.toString();
} catch (Exception e) {
log.warn("Unable to encrypt string: " + e);
return null;
}
}
/**
* Decrypts a string that was previously {@link #encrypt(String)} encrypted}.
*
* @param s the data to decrypt
* @return the string or {@code null} if an internal error occurred
*/
private static String decrypt(String s) {
if (s == null || !s.startsWith(PREFIX)) {
return s;
}
try {
byte[] data = new byte[(s.length() - PREFIX.length())/2];
for (int i=PREFIX.length(),b=0; i<s.length(); i+=2, b++) {
data[b] = (byte) (Integer.parseInt(s.substring(i, i+2), 16) &0xff);
}
SecretKeySpec key = new SecretKeySpec(data, 0, KEY_LENGTH, "DES");
Cipher cipher = Cipher.getInstance("DES");
try (ByteArrayOutputStream out = new ByteArrayOutputStream(data.length)) {
cipher.init(Cipher.DECRYPT_MODE, key);
out.write(cipher.update(data, KEY_LENGTH, data.length - KEY_LENGTH));
out.write(cipher.doFinal());
return out.toString("utf-8");
}
} catch (Exception e) {
log.warn("Unable to decrypt data: " + e);
return null;
}
}
}