
java.fedora.server.journal.helpers.PasswordCipher Maven / Gradle / Ivy
Show all versions of fcrepo-client Show documentation
/*
* -----------------------------------------------------------------------------
*
* License and Copyright: The contents of this file are subject to 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.fedora-commons.org/licenses.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
* the specific language governing rights and limitations under the License.
*
* The entire file consists of original code.
* Copyright © 2008 Fedora Commons, Inc.
*
Copyright © 2002-2007 The Rector and Visitors of the University of
* Virginia and Cornell University
* All rights reserved.
*
* -----------------------------------------------------------------------------
*/
package fedora.server.journal.helpers;
import com.oreilly.servlet.Base64Decoder;
import com.oreilly.servlet.Base64Encoder;
/**
*
* Title: PasswordCipher.java
*
*
* Description: Encipher the password so we aren't writing it to the
* file in clear.
*
*
* The encipher method does not allow options for cipher type. It should always
* use the latest and greatest. The decipher method accepts different cipher
* types so it can be used to read older Journal Files. Known types include:
*
* - "" or null - empty cipher, clear text is the same as cipher text
* - "1" - rotating key XOR cipher
*
*
*
* @author [email protected]
* @version $Id: PasswordCipher.java 5025 2006-09-01 22:08:17 +0000 (Fri, 01 Sep
* 2006) cwilper $
*/
public class PasswordCipher {
/**
* Two methods: String encode(String key, String clearText); String
* decode(String key, String decodedText); encoding: for each character in
* the clearText, get the byte from the char, get a byte from the key, XOR,
* and write to the byte array. Base64 encode and return. decoding: base64
* decode. for each byte in the result, XOR with a byte from the key to
* yield a char. combine into a string. return.
*/
/**
* Use the key to produce a ciphered String from the clearText.
*/
public static String encipher(String key, String clearText) {
if (key == null) {
throw new NullPointerException("key may not be null");
}
if (key == "") {
return clearText;
}
if (clearText == null) {
return null;
}
byte[] keyBytes = convertKeyToByteArray(key);
byte[] clearTextBytes = convertClearTextToByteArray(clearText);
byte[] cipherBytes = applyCipher(keyBytes, clearTextBytes);
return Base64Encoder.encode(cipherBytes);
}
/**
* Use the key to produce a clear text String from the cipherText. If no key
* is provided, or if no type is specified, just return the text as is.
*/
public static String decipher(String key,
String cipherText,
String cipherType) {
if (key == null || key == "") {
return cipherText;
}
if (cipherText == null) {
return null;
}
if (cipherType == null || cipherType == "") {
return cipherText;
} else if ("1".equalsIgnoreCase(cipherType)) {
byte[] keyBytes = convertKeyToByteArray(key);
byte[] cipherBytes = Base64Decoder.decodeToBytes(cipherText);
sanityCheckOnCipherBytes(cipherText, cipherBytes);
byte[] clearTextBytes = applyCipher(keyBytes, cipherBytes);
return convertByteArrayToClearText(clearTextBytes);
} else {
throw new IllegalArgumentException("Unrecognized cipher type: '"
+ cipherType + "'");
}
}
/**
* Convert the key to a byte array by compressing the 16-bit characters to
* bytes. This XOR compression insures that the top 8 bits are still
* significant, so a key of "\u0020" yields a different cipher than a key of
* "\u0120".
*/
private static byte[] convertKeyToByteArray(String key) {
byte[] result = new byte[key.length()];
for (int i = 0; i < result.length; i++) {
char thisChar = key.charAt(i);
result[i] = (byte) (thisChar >>> 8 & 0xFF ^ thisChar & 0xFF);
}
return result;
}
/**
* Convert the clear text to a byte array by splitting each 16-bit character
* into 2 bytes. This insures that no data is lost.
*/
private static byte[] convertClearTextToByteArray(String clearText) {
byte[] result = new byte[clearText.length() * 2];
for (int i = 0; i < clearText.length(); i++) {
char thisChar = clearText.charAt(i);
int pos = i * 2;
result[pos] = (byte) (thisChar >>> 8 & 0xFF);
result[pos + 1] = (byte) (thisChar & 0xFF);
}
return result;
}
/**
* If the cipher text decodes to an odd number of bytes, we can't go on!
*/
private static void sanityCheckOnCipherBytes(String cipherText,
byte[] cipherBytes) {
if (cipherBytes.length % 2 != 0) {
throw new IllegalStateException("Ciphered text decodes to an odd number of bytes! Text='"
+ cipherText
+ "', decodes to "
+ cipherBytes.length
+ " bytes.");
}
}
/**
* Convert a byte array to text by joining each two bytes into a 16-bit
* character.
*/
private static String convertByteArrayToClearText(byte[] bytes) {
StringBuffer result = new StringBuffer();
for (int i = 0; i < bytes.length; i += 2) {
char thisChar = (char) (bytes[i] << 8 | bytes[i + 1]);
result.append(thisChar);
}
return result.toString();
}
/**
* The same algorithm applies for enciphering or deciphering. Go through the
* text, XORing with successive bytes of the key. If you apply it twice, you
* get the original text back.
*/
private static byte[] applyCipher(byte[] keyBytes, byte[] textBytes) {
byte[] result = new byte[textBytes.length];
for (int i = 0; i < result.length; i++) {
int keyPos = i % keyBytes.length;
result[i] = (byte) (textBytes[i] ^ keyBytes[keyPos]);
}
return result;
}
}