![JAR search and dependency download from the Maven repository](/logo.png)
nl.open.jwtdependency.org.bouncycastle.crypto.engines.RFC5649WrapEngine Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of java-jwt-nodependencies Show documentation
Show all versions of java-jwt-nodependencies Show documentation
This is a drop in replacement for the auth0 java-jwt library (see https://github.com/auth0/java-jwt). This jar makes sure there are no external dependencies (e.g. fasterXml, Apacha Commons) needed. This is useful when deploying to an application server (e.g. tomcat with Alfreso or Pega).
The newest version!
package org.bouncycastle.crypto.engines;
import org.bouncycastle.crypto.BlockCipher;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.Wrapper;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.Pack;
/**
* An implementation of the AES Key Wrap with Padding specification
* as described in RFC 5649.
*
* For details on the specification see:
* https://tools.ietf.org/html/rfc5649
*
*/
public class RFC5649WrapEngine
implements Wrapper
{
private BlockCipher engine;
private KeyParameter param;
private boolean forWrapping;
// The AIV as defined in the RFC
private byte[] highOrderIV = {(byte)0xa6, (byte)0x59, (byte)0x59, (byte)0xa6};
private byte[] preIV = highOrderIV;
private byte[] extractedAIV = null;
public RFC5649WrapEngine(BlockCipher engine)
{
this.engine = engine;
}
public void init(boolean forWrapping, CipherParameters param)
{
this.forWrapping = forWrapping;
if (param instanceof ParametersWithRandom)
{
param = ((ParametersWithRandom)param).getParameters();
}
if (param instanceof KeyParameter)
{
this.param = (KeyParameter)param;
}
else if (param instanceof ParametersWithIV)
{
this.preIV = ((ParametersWithIV)param).getIV();
this.param = (KeyParameter)((ParametersWithIV)param).getParameters();
if (this.preIV.length != 4)
{
throw new IllegalArgumentException("IV length not equal to 4");
}
}
}
public String getAlgorithmName()
{
return engine.getAlgorithmName();
}
/**
* Pads the plaintext (i.e., the key to be wrapped)
* as per section 4.1 of RFC 5649.
*
* @param plaintext The key being wrapped.
* @return The padded key.
*/
private byte[] padPlaintext(byte[] plaintext)
{
int plaintextLength = plaintext.length;
int numOfZerosToAppend = (8 - (plaintextLength % 8)) % 8;
byte[] paddedPlaintext = new byte[plaintextLength + numOfZerosToAppend];
System.arraycopy(plaintext, 0, paddedPlaintext, 0, plaintextLength);
if (numOfZerosToAppend != 0)
{
// plaintext (i.e., key to be wrapped) does not have
// a multiple of 8 octet blocks so it must be padded
byte[] zeros = new byte[numOfZerosToAppend];
System.arraycopy(zeros, 0, paddedPlaintext, plaintextLength, numOfZerosToAppend);
}
return paddedPlaintext;
}
public byte[] wrap(byte[] in, int inOff, int inLen)
{
if (!forWrapping)
{
throw new IllegalStateException("not set for wrapping");
}
byte[] iv = new byte[8];
// MLI = size of key to be wrapped
byte[] mli = Pack.intToBigEndian(inLen);
// copy in the fixed portion of the AIV
System.arraycopy(preIV, 0, iv, 0, preIV.length);
// copy in the MLI after the AIV
System.arraycopy(mli, 0, iv, preIV.length, mli.length);
// get the relevant plaintext to be wrapped
byte[] relevantPlaintext = new byte[inLen];
System.arraycopy(in, inOff, relevantPlaintext, 0, inLen);
byte[] paddedPlaintext = padPlaintext(relevantPlaintext);
if (paddedPlaintext.length == 8)
{
// if the padded plaintext contains exactly 8 octets,
// then prepend iv and encrypt using AES in ECB mode.
// prepend the IV to the plaintext
byte[] paddedPlainTextWithIV = new byte[paddedPlaintext.length + iv.length];
System.arraycopy(iv, 0, paddedPlainTextWithIV, 0, iv.length);
System.arraycopy(paddedPlaintext, 0, paddedPlainTextWithIV, iv.length, paddedPlaintext.length);
engine.init(true, param);
for (int i = 0; i < paddedPlainTextWithIV.length; i += engine.getBlockSize())
{
engine.processBlock(paddedPlainTextWithIV, i, paddedPlainTextWithIV, i);
}
return paddedPlainTextWithIV;
}
else
{
// otherwise, apply the RFC 3394 wrap to
// the padded plaintext with the new IV
Wrapper wrapper = new RFC3394WrapEngine(engine);
ParametersWithIV paramsWithIV = new ParametersWithIV(param, iv);
wrapper.init(true, paramsWithIV);
return wrapper.wrap(paddedPlaintext, 0, paddedPlaintext.length);
}
}
public byte[] unwrap(byte[] in, int inOff, int inLen)
throws InvalidCipherTextException
{
if (forWrapping)
{
throw new IllegalStateException("not set for unwrapping");
}
int n = inLen / 8;
if ((n * 8) != inLen)
{
throw new InvalidCipherTextException("unwrap data must be a multiple of 8 bytes");
}
if (n == 1)
{
throw new InvalidCipherTextException("unwrap data must be at least 16 bytes");
}
byte[] relevantCiphertext = new byte[inLen];
System.arraycopy(in, inOff, relevantCiphertext, 0, inLen);
byte[] decrypted = new byte[inLen];
byte[] paddedPlaintext;
if (n == 2)
{
// When there are exactly two 64-bit blocks of ciphertext,
// they are decrypted as a single block using AES in ECB.
engine.init(false, param);
for (int i = 0; i < relevantCiphertext.length; i += engine.getBlockSize())
{
engine.processBlock(relevantCiphertext, i, decrypted, i);
}
// extract the AIV
extractedAIV = new byte[8];
System.arraycopy(decrypted, 0, extractedAIV, 0, extractedAIV.length);
paddedPlaintext = new byte[decrypted.length - extractedAIV.length];
System.arraycopy(decrypted, extractedAIV.length, paddedPlaintext, 0, paddedPlaintext.length);
}
else
{
// Otherwise, unwrap as per RFC 3394 but don't check IV the same way
decrypted = rfc3394UnwrapNoIvCheck(in, inOff, inLen);
paddedPlaintext = decrypted;
}
// Decompose the extracted AIV to the fixed portion and the MLI
byte[] extractedHighOrderAIV = new byte[4];
byte[] mliBytes = new byte[4];
System.arraycopy(extractedAIV, 0, extractedHighOrderAIV, 0, extractedHighOrderAIV.length);
System.arraycopy(extractedAIV, extractedHighOrderAIV.length, mliBytes, 0, mliBytes.length);
int mli = Pack.bigEndianToInt(mliBytes, 0);
// Even if a check fails we still continue and check everything
// else in order to avoid certain timing based side-channel attacks.
boolean isValid = true;
// Check the fixed portion of the AIV
if (!Arrays.constantTimeAreEqual(extractedHighOrderAIV, preIV))
{
isValid = false;
}
// Check the MLI against the actual length
int upperBound = paddedPlaintext.length;
int lowerBound = upperBound - 8;
if (mli <= lowerBound)
{
isValid = false;
}
if (mli > upperBound)
{
isValid = false;
}
// Check the number of padded zeros
int expectedZeros = upperBound - mli;
if (expectedZeros >= paddedPlaintext.length)
{
isValid = false;
expectedZeros = paddedPlaintext.length;
}
byte[] zeros = new byte[expectedZeros];
byte[] pad = new byte[expectedZeros];
System.arraycopy(paddedPlaintext, paddedPlaintext.length - expectedZeros, pad, 0, expectedZeros);
if (!Arrays.constantTimeAreEqual(pad, zeros))
{
isValid = false;
}
if (!isValid)
{
throw new InvalidCipherTextException("checksum failed");
}
// Extract the plaintext from the padded plaintext
byte[] plaintext = new byte[mli];
System.arraycopy(paddedPlaintext, 0, plaintext, 0, plaintext.length);
return plaintext;
}
/**
* Performs steps 1 and 2 of the unwrap process defined in RFC 3394.
* This code is duplicated from RFC3394WrapEngine because that class
* will throw an error during unwrap because the IV won't match up.
*
* @param in
* @param inOff
* @param inLen
* @return Unwrapped data.
*/
private byte[] rfc3394UnwrapNoIvCheck(byte[] in, int inOff, int inLen)
{
byte[] iv = new byte[8];
byte[] block = new byte[inLen - iv.length];
byte[] a = new byte[iv.length];
byte[] buf = new byte[8 + iv.length];
System.arraycopy(in, inOff, a, 0, iv.length);
System.arraycopy(in, inOff + iv.length, block, 0, inLen - iv.length);
engine.init(false, param);
int n = inLen / 8;
n = n - 1;
for (int j = 5; j >= 0; j--)
{
for (int i = n; i >= 1; i--)
{
System.arraycopy(a, 0, buf, 0, iv.length);
System.arraycopy(block, 8 * (i - 1), buf, iv.length, 8);
int t = n * j + i;
for (int k = 1; t != 0; k++)
{
byte v = (byte)t;
buf[iv.length - k] ^= v;
t >>>= 8;
}
engine.processBlock(buf, 0, buf, 0);
System.arraycopy(buf, 0, a, 0, 8);
System.arraycopy(buf, 8, block, 8 * (i - 1), 8);
}
}
// set the extracted AIV
extractedAIV = a;
return block;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy