blob: 0b85eedac5df3a41c51adb65a3cf06ca11e7250c [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.felix.useradmin.impl;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import org.apache.felix.useradmin.Base64;
/**
* Base64 encoding implementation.
* @see org.apache.felix.useradmin.Base64
*
* @version $Rev$ $Date$
*/
public class Base64Impl implements Base64
{
private String charset;
/**
* 111111 six bit mask to get 6 bits
*/
private static final int BIT6_MASK = 0x3f;
/**
* 11111111 8 bit mask to get 8 bits
*/
private static final int BIT8_MASK = 0xff;
/**
* SIGN used to check if byte is signed
*/
private static final int SIGN = -128;
/**
* used for keeping sign bit of signed numbers.
* when is added to sign number is keep 8 bit as 1.
*/
private static final int SIGN256 = 256;
/**
* @see org.apache.felix.useradmin.Base64#encrypt(java.lang.Object)
*/
public Object encrypt(Object value)
{
byte[] bytes;
try
{
bytes = value instanceof String ? ((String) value).getBytes(charset)
: (byte[]) value;
}
catch (UnsupportedEncodingException e)
{
bytes = ((String) value).getBytes();
}
StringBuffer encrypted = new StringBuffer();
for (int i = 0; i < bytes.length; i += 3)
{
// every block of 3 bytes is going to 4*6 bits array of chars
encrypted.append(encryptBlock(bytes, i));
}
if (value instanceof String)
{
return encrypted.toString();
}
else
{
return encrypted.toString().getBytes();
}
}
/**
* This method is encrypting block of 3 bytes if are available if
* not pads '=' are added.
*
* @param bytes to be encrypted
* @param i index
* @return encrypted block represented by base64 alphabet
*/
private char[] encryptBlock(byte[] bytes, int i)
{
// every block of 3 bytes is going to 4*6 bits array of chars
char[] base64chars = new char[4];
// block of 32 bits
int bits32 = 0;
// space left on byte array
int space = bytes.length - i - 1;
int blockSize = (space >= 2) ? 2 : space;
for (int k = 0; k <= blockSize; k++)
{
// if value is signed we need to keep
// need to keep sign bit 100000000 if byte is signed.
int val = (bytes[i + k] & SIGN) == 0 ? bytes[i + k] : bytes[i + k] + SIGN256;
// shift bits left to build one 32 bits block
bits32 += val << (8 * (2 - k));
}
for (int j = 0; j < 4; j++)
{
// shift bits to right and take 6 bits using mask
int bit6 = bits32 >>> (6 * (3 - j)) & BIT6_MASK;
base64chars[j] = (convertToBase64Alpabet(bit6));
if (space < 1)
{
base64chars[2] = '=';
}
if (space < 2)
{
base64chars[3] = '=';
}
}
return base64chars;
}
/**
* @see org.apache.felix.useradmin.Base64#decrypt(java.lang.Object)
*/
public Object decrypt(Object val)
{
byte[] decrypted = decryptToByteArray(val);
if (val instanceof String)
{
return convertToString(decrypted);
}
return decrypted;
}
/**
* @see org.apache.felix.useradmin.Base64#decryptToByteArray(Object)
*/
public byte[] decryptToByteArray(Object val)
{
String value;
try
{
value = val instanceof String ? (String) val : new String((byte[]) val, charset);
}
catch (UnsupportedEncodingException e)
{
value = new String((byte[]) val);
}
int pads = 0;
// looking for pads
for (int i = value.length() - 1; value.charAt(i) == '='; i--)
{
pads++;
}
// lenght of byte array
int length = value.length() * 6 / 8 - pads;
byte[] decrypted = new byte[length];
int rawIndex = 0;
for (int i = 0; i < value.length(); i += 4)
{
int block = (getValueFromBase64Alphabet(value.charAt(i)) << 18) + (getValueFromBase64Alphabet(value.charAt(i + 1)) << 12) + (getValueFromBase64Alphabet(value.charAt(i + 2)) << 6) + (getValueFromBase64Alphabet(value.charAt(i + 3)));
for (int j = 0; j < 3 && rawIndex + j < decrypted.length; j++)
{
decrypted[rawIndex + j] = (byte) ((block >> (8 * (2 - j))) & BIT8_MASK);
}
rawIndex += 3;
}
return decrypted;
}
private String convertToString(byte[] raw)
{
try
{
return new String(raw, charset);
}
catch (UnsupportedEncodingException e)
{
// log
return new String(raw);
}
}
/**
* <p>This method is converting 6 bit number to char.
* The number should be from 0-63 range. Then need
* to convert to ASCI [A-Z][a-z]{0-9}'+'/'.</p>
* @param number 6 bit number to be converted.
* @return converted 6bit number to char.
*/
private char convertToBase64Alpabet(int number)
{
// 65-90
if (number >= 0 && number <= 25)
{
return (char) ('A' + number);
}
// 97-122
if (number > 25 && number <= 51)
{
return (char) ('a' + (number - 26));
}
// 47-57
if (number > 51 && number <= 62)
{
return (char) ('0' + (number - 52));
}
if (number == 62)
{
return '+';
}
if (number == 63)
{
return '/';
}
return '?';
}
/**
* <p>This method is converting characters from base64 to int value</p>.
* @param character char for which int value is taken.
* @return int value of provided character
*/
private int getValueFromBase64Alphabet(char character)
{
if (character >= 'A' && character <= 'Z')
{
return character - 'A';
}
if (character >= 'a' && character <= 'z')
{
return character - 'a' + 26;
}
if (character >= '0' && character <= '9')
{
return character - '0' + 52;
}
if (character == '+')
{
return 62;
}
if (character == '/')
{
return 63;
}
if (character == '=')
{
return 0;
}
return -1;
}
/**
* @see org.apache.felix.useradmin.Base64#verify(java.lang.Object, java.lang.Object)
*/
public boolean verify(Object value, Object encrypted)
{
if (value instanceof String && encrypted instanceof String)
{
String encryptedValue = (String) encrypt(value);
return encryptedValue.equals(encrypted);
}
else if (value instanceof byte[] && encrypted instanceof byte[])
{
byte[] encryptedValue = (byte[]) encrypt(value);
return Arrays.equals(encryptedValue, (byte[]) encrypted);
}
return false;
}
/**
* @see org.apache.felix.useradmin.Base64#setCharset(java.lang.String)
*/
public void setCharset(String charset)
{
this.charset = charset;
}
}