/**
* 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.company.security.csp;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.ProviderException;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.CipherSpi;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.ShortBufferException;
public class CSPCipher extends CipherSpi {
private static final int MODE_ENCRYPT = 1;
private static final int MODE_DECRYPT = 2;
private static final int MODE_SIGN = 3;
private static final int MODE_VERIFY = 4;
private static final int KP_BLOCKLEN = 8; // Block size of the cipher
private static final int KP_KEYLEN = 9; // Length of key in bits
private int mode;
private String paddingType;
private int paddingLength = 0;
private int outputSize;
private CSPKey publicKey;
private CSPKey privateKey;
public CSPCipher() {
}
@Override
protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
}
@Override
protected void engineSetPadding(String padding) throws NoSuchPaddingException {
paddingType = padding;
}
@Override
protected int engineGetBlockSize() {
return 0;
}
@Override
protected int engineGetOutputSize(int inputLen) {
return outputSize;
}
@Override
protected byte[] engineGetIV() {
return null;
}
@Override
protected AlgorithmParameters engineGetParameters() {
return null;
}
@Override
protected void engineInit(int opmode, Key key, SecureRandom random) throws InvalidKeyException {
init(opmode, key);
}
@Override
protected void engineInit(int opmode, Key key,
AlgorithmParameterSpec params, SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
if(params != null)
throw new InvalidAlgorithmParameterException("Parameters not supported");
init(opmode, key);
}
@Override
protected void engineInit(int opmode, Key key, AlgorithmParameters params,
SecureRandom random) throws InvalidKeyException,
InvalidAlgorithmParameterException {
if(params != null)
throw new InvalidAlgorithmParameterException("Parameters not supported");
init(opmode, key);
}
private void init(int opmode, Key key) throws InvalidKeyException {
boolean encrypt;
switch(opmode) {
case Cipher.ENCRYPT_MODE:
case Cipher.WRAP_MODE:
encrypt = true;
break;
case Cipher.DECRYPT_MODE:
case Cipher.UNWRAP_MODE:
paddingLength = 0;
encrypt = false;
break;
default:
throw new InvalidKeyException("Unknown mode: " + opmode);
}
if(!(key instanceof CSPKey)) {
if(key instanceof PublicKey) {
CSPPublicKey publicKey = importPublicKey(key);
if(publicKey == null) {
throw new InvalidKeyException("Unsupported key type: " + key);
}
key = publicKey;
}
else {
throw new InvalidKeyException("Unsupported key type: " + key);
}
}
else if(encrypt) {
paddingLength = getPaddingLength(((CSPKey) key).getHCryptKey());
}
if(key instanceof PublicKey) {
mode = encrypt ? MODE_ENCRYPT : MODE_VERIFY;
publicKey = (CSPPublicKey) key;
privateKey = null;
outputSize = publicKey.length() / 8;
}
else if(key instanceof java.security.PrivateKey) {
mode = encrypt ? MODE_SIGN : MODE_DECRYPT;
privateKey = (CSPPrivateKey) key;
publicKey = null;
outputSize = privateKey.length() / 8;
}
else {
throw new InvalidKeyException("Unsupported key type: " + key);
}
}
private CSPPublicKey importPublicKey(Key key) throws InvalidKeyException {
byte[] keyEncoded = key.getEncoded();
try {
return NativeCrypto.initPublicKey(keyEncoded, keyEncoded.length);
} catch (NoSuchAlgorithmException e) {
throw new InvalidKeyException(e.getMessage(), e);
}
}
private int getPaddingLength(long hCryptoKey) {
try {
return NativeCrypto.getKeyParam(hCryptoKey, KP_BLOCKLEN);
} catch (InvalidKeyException e) {
throw new IllegalArgumentException(e.getMessage(), e);
}
}
@Override
protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) {
return engine(input, inputOffset, inputLen, false);
}
@Override
protected int engineUpdate(byte[] input, int inputOffset, int inputLen,
byte[] output, int outputOffset) throws ShortBufferException {
byte[] result = engine(input, inputOffset, inputLen, true);
int n = result.length;
if(outputOffset + n > output.length) {
throw new ShortBufferException("Data must not be longer than " + (output.length - outputOffset) + " bytes");
}
System.arraycopy(result, 0, output, outputOffset, n);
return n;
}
@Override
protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
throws IllegalBlockSizeException, BadPaddingException {
return engine(input, inputOffset, inputLen, true);
}
@Override
protected int engineDoFinal(byte[] input, int inputOffset, int inputLen,
byte[] output, int outputOffset) throws ShortBufferException,
IllegalBlockSizeException, BadPaddingException {
byte[] result = engine(input, inputOffset, inputLen, true);
int n = result.length;
if(outputOffset + n > output.length) {
if(n > inputLen)
throw new BadPaddingException("Data must not be longer (" + n + ") than " + (output.length - outputOffset) + " bytes");
else
throw new IllegalBlockSizeException("Data must not be longer than " + (output.length - outputOffset) + " bytes");
}
System.arraycopy(result, 0, output, outputOffset, n);
return n;
}
private byte[] engine(byte[] input, int inputOffset, int inputLen, boolean doFinal) {
try {
switch (mode) {
case MODE_SIGN:
return encryptDecrypt(input, inputOffset, inputLen,
privateKey.getHCryptKey(), true, doFinal, paddingLength);
case MODE_VERIFY:
return encryptDecrypt(input, inputOffset, inputLen,
publicKey.getHCryptKey(), false, doFinal, paddingLength);
case MODE_ENCRYPT:
return encryptDecrypt(input, inputOffset, inputLen,
publicKey.getHCryptKey(), true, doFinal, paddingLength);
case MODE_DECRYPT:
return encryptDecrypt(input, inputOffset, inputLen,
privateKey.getHCryptKey(), false, doFinal, paddingLength);
default:
throw new AssertionError("Internal error");
}
} catch (InvalidKeyException e) {
throw new ProviderException(e);
}
}
private byte[] encryptDecrypt(byte[] data, int dataOffset, int dataSize, long hCryptKey,
boolean doEncrypt, boolean doFinal,
int paddingLength) throws InvalidKeyException {
return NativeCrypto.encryptDecrypt(data, dataOffset, dataSize, hCryptKey, doEncrypt, doFinal, paddingLength);
}
}