mirror of https://github.com/jwtk/jjwt.git
113: initial JWE (shared key AES) encryption support
This commit is contained in:
parent
3dfae9a31d
commit
716c6fd500
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright (C) 2016 jsonwebtoken.io
|
||||
*
|
||||
* Licensed 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 io.jsonwebtoken.impl.crypto;
|
||||
|
||||
public abstract class AbstractCryptoRequest implements CryptoRequest {
|
||||
|
||||
private final byte[] key;
|
||||
private final byte[] iv;
|
||||
|
||||
public AbstractCryptoRequest(byte[] key, byte[] iv) {
|
||||
this.key = key;
|
||||
this.iv = iv;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getKey() {
|
||||
return this.key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getInitializationVector() {
|
||||
return this.iv;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright (C) 2016 jsonwebtoken.io
|
||||
*
|
||||
* Licensed 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 io.jsonwebtoken.impl.crypto;
|
||||
|
||||
public interface AssociatedDataSource {
|
||||
|
||||
byte[] getAssociatedData();
|
||||
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright (C) 2016 jsonwebtoken.io
|
||||
*
|
||||
* Licensed 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 io.jsonwebtoken.impl.crypto;
|
||||
|
||||
public interface AuthenticatedDecryptionRequest extends DecryptionRequest, AssociatedDataSource, AuthenticationTagSource {
|
||||
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright (C) 2016 jsonwebtoken.io
|
||||
*
|
||||
* Licensed 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 io.jsonwebtoken.impl.crypto;
|
||||
|
||||
public interface AuthenticatedEncryptionRequest extends EncryptionRequest, AssociatedDataSource {
|
||||
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright (C) 2016 jsonwebtoken.io
|
||||
*
|
||||
* Licensed 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 io.jsonwebtoken.impl.crypto;
|
||||
|
||||
public interface AuthenticatedEncryptionResult extends EncryptionResult, AuthenticationTagSource {
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright (C) 2016 jsonwebtoken.io
|
||||
*
|
||||
* Licensed 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 io.jsonwebtoken.impl.crypto;
|
||||
|
||||
public interface AuthenticationTagSource {
|
||||
|
||||
byte[] getAuthenticationTag();
|
||||
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright (C) 2016 jsonwebtoken.io
|
||||
*
|
||||
* Licensed 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 io.jsonwebtoken.impl.crypto;
|
||||
|
||||
public class CryptoException extends RuntimeException {
|
||||
|
||||
public CryptoException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright (C) 2016 jsonwebtoken.io
|
||||
*
|
||||
* Licensed 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 io.jsonwebtoken.impl.crypto;
|
||||
|
||||
public interface CryptoRequest {
|
||||
|
||||
byte[] getKey();
|
||||
|
||||
byte[] getInitializationVector();
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright (C) 2016 jsonwebtoken.io
|
||||
*
|
||||
* Licensed 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 io.jsonwebtoken.impl.crypto;
|
||||
|
||||
public interface DecryptionRequest extends CryptoRequest {
|
||||
|
||||
byte[] getCiphertext();
|
||||
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright (C) 2016 jsonwebtoken.io
|
||||
*
|
||||
* Licensed 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 io.jsonwebtoken.impl.crypto;
|
||||
|
||||
public interface DecryptionRequestBuilder {
|
||||
|
||||
DecryptionRequestBuilder setInitializationVector(byte[] iv);
|
||||
|
||||
DecryptionRequestBuilder setKey(byte[] key);
|
||||
|
||||
DecryptionRequestBuilder setCiphertext(byte[] plaintext);
|
||||
|
||||
DecryptionRequestBuilder setAdditionalAuthenticatedData(byte[] aad);
|
||||
|
||||
DecryptionRequestBuilder setAuthenticationTag(byte[] tag);
|
||||
|
||||
DecryptionRequest build();
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright (C) 2016 jsonwebtoken.io
|
||||
*
|
||||
* Licensed 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 io.jsonwebtoken.impl.crypto;
|
||||
|
||||
public final class DecryptionRequests {
|
||||
|
||||
//only for 100% test coverage.
|
||||
private static final DecryptionRequests INSTANCE = new DecryptionRequests();
|
||||
|
||||
private DecryptionRequests(){}
|
||||
|
||||
public static DecryptionRequestBuilder builder() {
|
||||
return new DefaultDecryptionRequestBuilder();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,225 @@
|
|||
/*
|
||||
* Copyright (C) 2016 jsonwebtoken.io
|
||||
*
|
||||
* Licensed 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 io.jsonwebtoken.impl.crypto;
|
||||
|
||||
import io.jsonwebtoken.lang.Arrays;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
|
||||
import javax.crypto.*;
|
||||
import javax.crypto.spec.GCMParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
/**
|
||||
* Default {@link EncryptionService} implementation that uses AES in GCM mode.
|
||||
*/
|
||||
public class DefaultAesEncryptionService implements EncryptionService {
|
||||
|
||||
private static final int GCM_TAG_SIZE = 16; //number of bytes, not bits. Highest for GCM is 128 bits and recommended
|
||||
private static final int GCM_NONCE_SIZE = 12; //number of bytes, not bits. 12 is recommended for GCM for efficiency
|
||||
|
||||
public static final SecureRandom DEFAULT_RANDOM = new SecureRandom();
|
||||
protected static final String DECRYPT_NO_IV = "This EncryptionService implementation rejects decryption " +
|
||||
"requests that do not include initialization vectors. AES " +
|
||||
"ciphertext without an IV is weak and should never be used.";
|
||||
|
||||
private final SecretKey key;
|
||||
|
||||
private final SecureRandom random;
|
||||
|
||||
public DefaultAesEncryptionService(byte[] key) {
|
||||
this(key, DEFAULT_RANDOM);
|
||||
}
|
||||
|
||||
public DefaultAesEncryptionService(byte[] key, SecureRandom random) {
|
||||
Assert.notEmpty(key, "Encryption key cannot be null or empty.");
|
||||
Assert.notNull(random, "SecureRandom instance cannot be null or empty.");
|
||||
this.key = new SecretKeySpec(key, "AES");
|
||||
this.random = random;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encrypt(byte[] plaintext) {
|
||||
EncryptionRequest req = EncryptionRequests.builder().setPlaintext(plaintext).build();
|
||||
EncryptionResult res = encrypt(req);
|
||||
return res.compact();
|
||||
}
|
||||
|
||||
protected Cipher newCipher() throws NoSuchPaddingException, NoSuchAlgorithmException {
|
||||
return Cipher.getInstance("AES/GCM/NoPadding");
|
||||
}
|
||||
|
||||
protected Cipher newCipher(int mode, byte[] nonce, SecretKey key)
|
||||
throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException,
|
||||
InvalidKeyException {
|
||||
|
||||
Cipher aesGcm = newCipher();
|
||||
|
||||
GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_SIZE * Byte.SIZE, nonce);
|
||||
|
||||
aesGcm.init(mode, key, spec);
|
||||
|
||||
return aesGcm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] decrypt(byte[] compact) {
|
||||
byte[] nonce = getCiphertextNonce(compact);
|
||||
byte[] ciphertext = getTaggedCiphertext(compact);
|
||||
DecryptionRequest req = DecryptionRequests.builder()
|
||||
.setInitializationVector(nonce).setCiphertext(ciphertext).build();
|
||||
return decrypt(req);
|
||||
}
|
||||
|
||||
protected byte[] getCiphertextNonce(byte[] ciphertext) {
|
||||
byte[] nonce = new byte[GCM_NONCE_SIZE];
|
||||
System.arraycopy(ciphertext, 0, nonce, 0, GCM_NONCE_SIZE);
|
||||
return nonce;
|
||||
}
|
||||
|
||||
protected byte[] getTaggedCiphertext(byte[] ciphertext) {
|
||||
int taggedCiphertextLength = ciphertext.length - GCM_NONCE_SIZE;
|
||||
byte[] taggedCipherText = new byte[taggedCiphertextLength];
|
||||
//remaining data is the tagged ciphertext. Isolate it:
|
||||
System.arraycopy(ciphertext, GCM_NONCE_SIZE, taggedCipherText, 0, taggedCiphertextLength);
|
||||
return taggedCipherText;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EncryptionResult encrypt(EncryptionRequest req) throws CryptoException {
|
||||
try {
|
||||
Assert.notNull(req, "EncryptionRequest cannot be null.");
|
||||
return doEncrypt(req);
|
||||
} catch (Exception e) {
|
||||
String msg = "Unable to perform encryption: " + e.getMessage();
|
||||
throw new CryptoException(msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
protected EncryptionResult doEncrypt(EncryptionRequest req)
|
||||
throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, InvalidKeyException,
|
||||
NoSuchPaddingException, BadPaddingException, IllegalBlockSizeException {
|
||||
|
||||
//Ensure IV:
|
||||
byte[] iv = req.getInitializationVector();
|
||||
int ivLength = Arrays.length(iv);
|
||||
if (ivLength == 0) {
|
||||
iv = new byte[GCM_NONCE_SIZE]; //for AES GCM, the IV is often called the nonce
|
||||
random.nextBytes(iv);
|
||||
}
|
||||
|
||||
//Ensure Key:
|
||||
SecretKey key = this.key;
|
||||
byte[] keyBytes = req.getKey();
|
||||
int keyBytesLength = Arrays.length(keyBytes);
|
||||
if (keyBytesLength > 0) {
|
||||
key = new SecretKeySpec(keyBytes, "AES");
|
||||
}
|
||||
|
||||
Cipher aesGcm = newCipher(Cipher.ENCRYPT_MODE, iv, key);
|
||||
|
||||
//Support Additional Associated Data if necessary:
|
||||
int aadLength = 0;
|
||||
if (req instanceof AssociatedDataSource) {
|
||||
byte[] aad = ((AssociatedDataSource) req).getAssociatedData();
|
||||
aadLength = Arrays.length(aad);
|
||||
if (aadLength > 0) {
|
||||
aesGcm.updateAAD(aad);
|
||||
}
|
||||
}
|
||||
|
||||
//now for the actual encryption:
|
||||
byte[] plaintext = req.getPlaintext();
|
||||
byte[] ciphertext = aesGcm.doFinal(plaintext);
|
||||
|
||||
if (aadLength > 0) { //authenticated
|
||||
|
||||
byte[] taggedCiphertext = ciphertext; //ciphertext is actually tagged
|
||||
|
||||
//separate the tag from the ciphertext:
|
||||
int ciphertextLength = taggedCiphertext.length - GCM_TAG_SIZE;
|
||||
ciphertext = new byte[ciphertextLength];
|
||||
System.arraycopy(taggedCiphertext, 0, ciphertext, 0, ciphertextLength);
|
||||
|
||||
byte[] tag = new byte[GCM_TAG_SIZE];
|
||||
System.arraycopy(taggedCiphertext, ciphertextLength, tag, 0, GCM_TAG_SIZE);
|
||||
return new DefaultAuthenticatedEncryptionResult(iv, ciphertext, tag);
|
||||
}
|
||||
|
||||
return new DefaultEncryptionResult(iv, ciphertext);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] decrypt(DecryptionRequest req) throws CryptoException {
|
||||
try {
|
||||
Assert.notNull(req, "DecryptionRequest cannot be null.");
|
||||
return doDecrypt(req);
|
||||
} catch (Exception e) {
|
||||
String msg = "Unable to perform decryption: " + e.getMessage();
|
||||
throw new CryptoException(msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
protected byte[] doDecrypt(DecryptionRequest req)
|
||||
throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, InvalidKeyException,
|
||||
NoSuchPaddingException, BadPaddingException, IllegalBlockSizeException {
|
||||
|
||||
byte[] iv = req.getInitializationVector();
|
||||
Assert.notEmpty(iv, DECRYPT_NO_IV);
|
||||
|
||||
//Ensure Key:
|
||||
SecretKey key = this.key;
|
||||
byte[] keyBytes = req.getKey();
|
||||
if (keyBytes != null && keyBytes.length > 0) {
|
||||
key = new SecretKeySpec(keyBytes, "AES");
|
||||
}
|
||||
|
||||
final byte[] ciphertext = req.getCiphertext();
|
||||
byte[] finalBytes = ciphertext; //by default, unless there is an authentication tag
|
||||
|
||||
Cipher aesGcm = newCipher(Cipher.DECRYPT_MODE, iv, key);
|
||||
|
||||
//Support Additional Associated Data:
|
||||
if (req instanceof AuthenticatedDecryptionRequest) {
|
||||
|
||||
AuthenticatedDecryptionRequest areq = (AuthenticatedDecryptionRequest) req;
|
||||
|
||||
byte[] aad = areq.getAssociatedData();
|
||||
Assert.notEmpty(aad, "AuthenticatedDecryptionRRequests must include Additional Authenticated Data.");
|
||||
|
||||
aesGcm.updateAAD(aad);
|
||||
|
||||
//for tagged GCM, the JVM spec requires that the tag be appended to the end of the ciphertext
|
||||
//byte array. So we'll append it here:
|
||||
|
||||
byte[] tag = areq.getAuthenticationTag();
|
||||
Assert.notEmpty(tag, "AuthenticatedDecryptionReqeusts must include an authentication tag.");
|
||||
|
||||
byte[] taggedCiphertext = new byte[ciphertext.length + tag.length];
|
||||
System.arraycopy(ciphertext, 0, taggedCiphertext, 0, ciphertext.length);
|
||||
|
||||
System.arraycopy(tag, 0, taggedCiphertext, ciphertext.length, tag.length);
|
||||
|
||||
finalBytes = taggedCiphertext;
|
||||
}
|
||||
|
||||
return aesGcm.doFinal(finalBytes);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright (C) 2016 jsonwebtoken.io
|
||||
*
|
||||
* Licensed 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 io.jsonwebtoken.impl.crypto;
|
||||
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
|
||||
public class DefaultAuthenticatedDecryptionRequest extends DefaultDecryptionRequest
|
||||
implements AuthenticatedDecryptionRequest {
|
||||
|
||||
private final byte[] aad;
|
||||
|
||||
private final byte[] tag;
|
||||
|
||||
public DefaultAuthenticatedDecryptionRequest(byte[] key, byte[] iv, byte[] ciphertext, byte[] aad, byte[] tag) {
|
||||
super(key, iv, ciphertext);
|
||||
Assert.notEmpty(aad, "Additional Authenticated Data cannot be null or empty.");
|
||||
Assert.notEmpty(tag, "Authentication tag cannot be null or empty.");
|
||||
this.aad = aad;
|
||||
this.tag = tag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getAssociatedData() {
|
||||
return this.aad;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getAuthenticationTag() {
|
||||
return this.tag;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright (C) 2016 jsonwebtoken.io
|
||||
*
|
||||
* Licensed 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 io.jsonwebtoken.impl.crypto;
|
||||
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
|
||||
public class DefaultAuthenticatedEncryptionRequest extends DefaultEncryptionRequest
|
||||
implements AuthenticatedEncryptionRequest {
|
||||
|
||||
private final byte[] aad;
|
||||
|
||||
public DefaultAuthenticatedEncryptionRequest(byte[] key, byte[] iv, byte[] plaintext, byte[] aad) {
|
||||
super(key, iv, plaintext);
|
||||
Assert.notEmpty(aad, "Additional Authenticated Data cannot be null or empty.");
|
||||
this.aad = aad;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getAssociatedData() {
|
||||
return this.aad;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright (C) 2016 jsonwebtoken.io
|
||||
*
|
||||
* Licensed 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 io.jsonwebtoken.impl.crypto;
|
||||
|
||||
import io.jsonwebtoken.lang.Arrays;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
|
||||
public class DefaultAuthenticatedEncryptionResult extends DefaultEncryptionResult
|
||||
implements AuthenticatedEncryptionResult {
|
||||
|
||||
private final byte[] tag;
|
||||
|
||||
public DefaultAuthenticatedEncryptionResult(byte[] iv, byte[] ciphertext, byte[] tag) {
|
||||
super(iv, ciphertext);
|
||||
Assert.notEmpty(tag, "authentication tag cannot be null or empty.");
|
||||
this.tag = tag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getAuthenticationTag() {
|
||||
return this.tag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] compact() {
|
||||
|
||||
int ivLength = Arrays.length(iv);
|
||||
int ciphertextLength = Arrays.length(ciphertext);
|
||||
int tagLength = Arrays.length(tag);
|
||||
|
||||
int outputLength = ivLength + ciphertextLength + tagLength;
|
||||
byte[] output = new byte[outputLength];
|
||||
|
||||
//iv
|
||||
if (ivLength > 0) {
|
||||
output = new byte[outputLength];
|
||||
System.arraycopy(iv, 0, output, 0, ivLength);
|
||||
}
|
||||
|
||||
//ciphertext
|
||||
System.arraycopy(ciphertext, 0, output, ivLength, ciphertextLength);
|
||||
|
||||
//tag can never be empty based on the assertion in the constructor
|
||||
assert tagLength > 0;
|
||||
|
||||
System.arraycopy(tag, 0, output, ivLength + ciphertextLength, tagLength);
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (C) 2016 jsonwebtoken.io
|
||||
*
|
||||
* Licensed 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 io.jsonwebtoken.impl.crypto;
|
||||
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
|
||||
public class DefaultDecryptionRequest extends AbstractCryptoRequest implements DecryptionRequest {
|
||||
|
||||
private final byte[] ciphertext;
|
||||
|
||||
public DefaultDecryptionRequest(byte[] key, byte[] iv, byte[] ciphertext) {
|
||||
super(key, iv);
|
||||
Assert.notEmpty(ciphertext, "ciphertext cannot be null or empty.");
|
||||
this.ciphertext = ciphertext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getCiphertext() {
|
||||
return this.ciphertext;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Copyright (C) 2016 jsonwebtoken.io
|
||||
*
|
||||
* Licensed 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 io.jsonwebtoken.impl.crypto;
|
||||
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
|
||||
import static io.jsonwebtoken.lang.Arrays.length;
|
||||
|
||||
public class DefaultDecryptionRequestBuilder implements DecryptionRequestBuilder {
|
||||
|
||||
public static final String AAD_NEEDS_TAG_MSG = "If you specify additional authentication data during " +
|
||||
"decryption, you must also specify the authentication tag " +
|
||||
"computed during encryption.";
|
||||
|
||||
public static final String TAG_NEEDS_AAD_MSG = "If you specify an authentication tag during decryption, you must " +
|
||||
"also specify the additional authenticated data used " +
|
||||
"during encryption.";
|
||||
private byte[] iv;
|
||||
private byte[] key;
|
||||
private byte[] ciphertext;
|
||||
private byte[] aad;
|
||||
private byte[] tag;
|
||||
|
||||
@Override
|
||||
public DecryptionRequestBuilder setInitializationVector(byte[] iv) {
|
||||
this.iv = length(iv) > 0 ? iv : null;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DecryptionRequestBuilder setKey(byte[] key) {
|
||||
this.key = length(key) > 0 ? key : null;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DecryptionRequestBuilder setCiphertext(byte[] ciphertext) {
|
||||
Assert.notEmpty(ciphertext, "Ciphertext cannot be null or empty.");
|
||||
this.ciphertext = ciphertext;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DecryptionRequestBuilder setAdditionalAuthenticatedData(byte[] aad) {
|
||||
this.aad = length(aad) > 0 ? aad : null;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DecryptionRequestBuilder setAuthenticationTag(byte[] tag) {
|
||||
this.tag = length(tag) > 0 ? tag : null;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DecryptionRequest build() {
|
||||
Assert.notEmpty(ciphertext, "Ciphertext cannot be null or empty.");
|
||||
|
||||
int aadLength = length(aad);
|
||||
int tagLength = length(tag);
|
||||
|
||||
if (aadLength > 0 && tagLength == 0) {
|
||||
String msg = AAD_NEEDS_TAG_MSG;
|
||||
throw new IllegalArgumentException(msg);
|
||||
}
|
||||
if (tagLength > 0 && aadLength == 0) {
|
||||
String msg = TAG_NEEDS_AAD_MSG;
|
||||
throw new IllegalArgumentException(msg);
|
||||
}
|
||||
|
||||
if (aadLength > 0 || tagLength > 0) {
|
||||
return new DefaultAuthenticatedDecryptionRequest(key, iv, ciphertext, aad, tag);
|
||||
}
|
||||
|
||||
return new DefaultDecryptionRequest(key, iv, ciphertext);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (C) 2016 jsonwebtoken.io
|
||||
*
|
||||
* Licensed 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 io.jsonwebtoken.impl.crypto;
|
||||
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
|
||||
public class DefaultEncryptionRequest extends AbstractCryptoRequest implements EncryptionRequest {
|
||||
|
||||
private final byte[] plaintext;
|
||||
|
||||
public DefaultEncryptionRequest(byte[] key, byte[] iv, byte[] plaintext) {
|
||||
super(key, iv);
|
||||
Assert.notEmpty(plaintext, "plaintext cannot be null or empty.");
|
||||
this.plaintext = plaintext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getPlaintext() {
|
||||
return this.plaintext;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright (C) 2016 jsonwebtoken.io
|
||||
*
|
||||
* Licensed 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 io.jsonwebtoken.impl.crypto;
|
||||
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
|
||||
import static io.jsonwebtoken.lang.Arrays.length;
|
||||
|
||||
public class DefaultEncryptionRequestBuilder implements EncryptionRequestBuilder {
|
||||
|
||||
private byte[] iv;
|
||||
private byte[] key;
|
||||
private byte[] plaintext;
|
||||
private byte[] aad;
|
||||
|
||||
@Override
|
||||
public EncryptionRequestBuilder setInitializationVector(byte[] iv) {
|
||||
this.iv = length(iv) > 0 ? iv : null;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EncryptionRequestBuilder setKey(byte[] key) {
|
||||
this.key = length(key) > 0 ? key : null;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EncryptionRequestBuilder setPlaintext(byte[] plaintext) {
|
||||
Assert.notEmpty(plaintext, "Plaintext cannot be null or empty.");
|
||||
this.plaintext = plaintext;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EncryptionRequestBuilder setAdditionalAuthenticatedData(byte[] aad) {
|
||||
this.aad = length(aad) > 0 ? aad : null;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EncryptionRequest build() {
|
||||
Assert.notEmpty(plaintext, "Plaintext cannot be null or empty.");
|
||||
|
||||
if (length(aad) > 0) {
|
||||
return new DefaultAuthenticatedEncryptionRequest(key, iv, plaintext, aad);
|
||||
}
|
||||
|
||||
return new DefaultEncryptionRequest(key, iv, plaintext);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright (C) 2016 jsonwebtoken.io
|
||||
*
|
||||
* Licensed 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 io.jsonwebtoken.impl.crypto;
|
||||
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
|
||||
import static io.jsonwebtoken.lang.Arrays.length;
|
||||
|
||||
public class DefaultEncryptionResult implements EncryptionResult {
|
||||
|
||||
protected final byte[] iv;
|
||||
protected final byte[] ciphertext;
|
||||
|
||||
public DefaultEncryptionResult(byte[] iv, byte[] ciphertext) {
|
||||
Assert.notEmpty(ciphertext, "ciphertext cannot be null or empty.");
|
||||
this.ciphertext = ciphertext;
|
||||
this.iv = iv;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getInitializationVector() {
|
||||
return this.iv;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getCiphertext() {
|
||||
return this.ciphertext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] compact() {
|
||||
|
||||
int ivLength = length(iv);
|
||||
int ciphertextLength = length(ciphertext);
|
||||
|
||||
int outputLength = ivLength + ciphertextLength;
|
||||
|
||||
byte[] output = ciphertext; //default
|
||||
|
||||
if (ivLength > 0) {
|
||||
|
||||
output = new byte[outputLength];
|
||||
|
||||
System.arraycopy(iv, 0, output, 0, ivLength);
|
||||
|
||||
System.arraycopy(ciphertext, 0, output, ivLength, ciphertextLength);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright (C) 2016 jsonwebtoken.io
|
||||
*
|
||||
* Licensed 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 io.jsonwebtoken.impl.crypto;
|
||||
|
||||
public interface EncryptionRequest extends CryptoRequest {
|
||||
|
||||
byte[] getPlaintext();
|
||||
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (C) 2016 jsonwebtoken.io
|
||||
*
|
||||
* Licensed 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 io.jsonwebtoken.impl.crypto;
|
||||
|
||||
public interface EncryptionRequestBuilder {
|
||||
|
||||
EncryptionRequestBuilder setInitializationVector(byte[] iv);
|
||||
|
||||
EncryptionRequestBuilder setKey(byte[] key);
|
||||
|
||||
EncryptionRequestBuilder setPlaintext(byte[] plaintext);
|
||||
|
||||
EncryptionRequestBuilder setAdditionalAuthenticatedData(byte[] aad);
|
||||
|
||||
EncryptionRequest build();
|
||||
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package io.jsonwebtoken.impl.crypto;
|
||||
|
||||
public final class EncryptionRequests {
|
||||
|
||||
//100% code coverage
|
||||
private static final EncryptionRequests INSTANCE = new EncryptionRequests();
|
||||
|
||||
private EncryptionRequests(){} //prevent instantiation
|
||||
|
||||
public static EncryptionRequestBuilder builder() {
|
||||
return new DefaultEncryptionRequestBuilder();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright (C) 2016 jsonwebtoken.io
|
||||
*
|
||||
* Licensed 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 io.jsonwebtoken.impl.crypto;
|
||||
|
||||
public interface EncryptionResult {
|
||||
|
||||
byte[] getInitializationVector();
|
||||
|
||||
byte[] getCiphertext();
|
||||
|
||||
byte[] compact();
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright (C) 2016 jsonwebtoken.io
|
||||
*
|
||||
* Licensed 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 io.jsonwebtoken.impl.crypto;
|
||||
|
||||
public interface EncryptionService {
|
||||
|
||||
byte[] encrypt(byte[] plaintext) throws CryptoException;
|
||||
|
||||
byte[] decrypt(byte[] ciphertext) throws CryptoException;
|
||||
|
||||
EncryptionResult encrypt(EncryptionRequest request) throws CryptoException;
|
||||
|
||||
byte[] decrypt(DecryptionRequest request) throws CryptoException;
|
||||
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright (C) 2016 jsonwebtoken.io
|
||||
*
|
||||
* Licensed 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 io.jsonwebtoken.impl.crypto;
|
||||
|
||||
public interface InitializationVectorSource {
|
||||
|
||||
byte[] getInitializationVector();
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package io.jsonwebtoken.lang;
|
||||
|
||||
/**
|
||||
* @since 0.6
|
||||
*/
|
||||
public abstract class Arrays {
|
||||
|
||||
public static int length(byte[] bytes) {
|
||||
return bytes != null ? bytes.length : 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,203 @@
|
|||
/*
|
||||
* Copyright (C) 2016 jsonwebtoken.io
|
||||
*
|
||||
* Licensed 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 io.jsonwebtoken.impl.crypto
|
||||
|
||||
import groovy.json.internal.Charsets
|
||||
import org.junit.Test
|
||||
|
||||
import javax.crypto.*
|
||||
import java.security.InvalidAlgorithmParameterException
|
||||
import java.security.InvalidKeyException
|
||||
import java.security.NoSuchAlgorithmException
|
||||
import java.security.SecureRandom
|
||||
|
||||
import static org.junit.Assert.*
|
||||
|
||||
class DefaultAesEncryptionServiceTest {
|
||||
|
||||
private static final String PLAINTEXT =
|
||||
'''Bacon ipsum dolor amet venison beef pork chop, doner jowl pastrami ground round alcatra.
|
||||
Beef leberkas filet mignon ball tip pork spare ribs kevin short loin ribeye ground round
|
||||
biltong jerky short ribs corned beef. Strip steak turducken meatball porchetta beef ribs
|
||||
shoulder pork belly doner salami corned beef kielbasa cow filet mignon drumstick. Bacon
|
||||
tenderloin pancetta flank frankfurter ham kevin leberkas meatball turducken beef ribs.
|
||||
Cupim short loin short ribs shankle tenderloin. Ham ribeye hamburger flank tenderloin
|
||||
cupim t-bone, shank tri-tip venison salami sausage pancetta. Pork belly chuck salami
|
||||
alcatra sirloin.
|
||||
|
||||
以ケ ホゥ婧詃 橎ちゅぬ蛣埣 禧ざしゃ蟨廩 椥䤥グ曣わ 基覧 滯っ䶧きょメ Ủ䧞以ケ妣 择禤槜谣お 姨のドゥ,
|
||||
らボみょば䪩 苯礊觊ツュ婃 䩦ディふげセ げセりょ 禤槜 Ủ䧞以ケ妣 せがみゅちょ䰯 择禤槜谣お 難ゞ滧 蝥ちゃ,
|
||||
滯っ䶧きょメ らボみょば䪩 礯みゃ楦と饥 椥䤥グ ウァ槚 訤をりゃしゑ びゃ驨も氩簥 栨キョ奎婨榞 ヌに楃 以ケ,
|
||||
姚奊べ 椥䤥グ曣わ 栨キョ奎婨榞 ちょ䰯 Ủ䧞以ケ妣 誧姨のドゥろ よ苯礊 く涥, りゅぽ槞 馣ぢゃ尦䦎ぎ
|
||||
大た䏩䰥ぐ 郎きや楺橯 䧎キェ, 難ゞ滧 栧择 谯䧟簨訧ぎょ 椥䤥グ曣わ'''
|
||||
|
||||
private static final byte[] PLAINTEXT_BYTES = PLAINTEXT.getBytes(Charsets.UTF_8)
|
||||
|
||||
private static final String AAD = 'You can get with this, or you can get with that'
|
||||
private static final byte[] AAD_BYTES = AAD.getBytes(Charsets.UTF_8)
|
||||
|
||||
private byte[] generateKey() {
|
||||
KeyGenerator kg = KeyGenerator.getInstance("AES");
|
||||
kg.init(256);
|
||||
return kg.generateKey().getEncoded();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSimple() {
|
||||
|
||||
byte[] key = generateKey();
|
||||
|
||||
def service = new DefaultAesEncryptionService(key);
|
||||
|
||||
def ciphertext = service.encrypt(PLAINTEXT_BYTES);
|
||||
|
||||
def decryptedPlaintextBytes = service.decrypt(ciphertext);
|
||||
|
||||
def decryptedPlaintext = new String(decryptedPlaintextBytes, Charsets.UTF_8);
|
||||
|
||||
assertEquals(PLAINTEXT, decryptedPlaintext);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDoEncryptFailure() {
|
||||
String msg = 'foo'
|
||||
def key = generateKey()
|
||||
def service = new DefaultAesEncryptionService(key) {
|
||||
@Override
|
||||
protected EncryptionResult doEncrypt(EncryptionRequest req) throws InvalidAlgorithmParameterException, InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException, ShortBufferException, BadPaddingException, IllegalBlockSizeException {
|
||||
throw new IllegalArgumentException(msg)
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
service.encrypt(key /*any byte array will do */)
|
||||
fail("Encryption should have failed")
|
||||
} catch (CryptoException expected) {
|
||||
assertEquals('Unable to perform encryption: ' + msg, expected.message)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDoDecryptFailure() {
|
||||
String msg = 'foo'
|
||||
def key = generateKey()
|
||||
def service = new DefaultAesEncryptionService(key) {
|
||||
@Override
|
||||
protected byte[] doDecrypt(DecryptionRequest req) throws InvalidAlgorithmParameterException, InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException, ShortBufferException, BadPaddingException, IllegalBlockSizeException {
|
||||
throw new IllegalArgumentException(msg)
|
||||
}
|
||||
}
|
||||
try {
|
||||
service.decrypt(key /*any byte array will do */)
|
||||
fail("Decryption should have failed")
|
||||
} catch (CryptoException expected) {
|
||||
assertEquals('Unable to perform decryption: ' + msg, expected.message)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEncryptWithSpecifiedKey() {
|
||||
|
||||
def service = new DefaultAesEncryptionService(generateKey());
|
||||
|
||||
//use a custom key for this request:
|
||||
def key = generateKey()
|
||||
|
||||
EncryptionRequest ereq = EncryptionRequests.builder().setKey(key).setPlaintext(PLAINTEXT_BYTES).build()
|
||||
|
||||
EncryptionResult eres = service.encrypt(ereq);
|
||||
|
||||
DecryptionRequest dreq = DecryptionRequests.builder().setKey(key)
|
||||
.setInitializationVector(eres.getInitializationVector())
|
||||
.setCiphertext(eres.getCiphertext())
|
||||
.build()
|
||||
|
||||
def decryptedPlaintextBytes = service.decrypt(dreq)
|
||||
|
||||
def decryptedPlaintext = new String(decryptedPlaintextBytes, Charsets.UTF_8);
|
||||
|
||||
assertEquals(PLAINTEXT, decryptedPlaintext);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEncryptWithSpecifiedIv() {
|
||||
|
||||
def service = new DefaultAesEncryptionService(generateKey());
|
||||
|
||||
byte[] iv = new byte[12]; //AES GCM tends to use nonces of 12 bytes for efficiency
|
||||
new SecureRandom().nextBytes(iv);
|
||||
|
||||
EncryptionRequest ereq = EncryptionRequests.builder()
|
||||
.setInitializationVector(iv)
|
||||
.setPlaintext(PLAINTEXT_BYTES)
|
||||
.build()
|
||||
|
||||
EncryptionResult eres = service.encrypt(ereq);
|
||||
|
||||
DecryptionRequest dreq = DecryptionRequests.builder()
|
||||
.setInitializationVector(iv)
|
||||
.setCiphertext(eres.getCiphertext())
|
||||
.build()
|
||||
|
||||
def decryptedPlaintextBytes = service.decrypt(dreq)
|
||||
|
||||
def decryptedPlaintext = new String(decryptedPlaintextBytes, Charsets.UTF_8);
|
||||
|
||||
assertEquals(PLAINTEXT, decryptedPlaintext);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDecryptWithEmptyIv() {
|
||||
|
||||
def service = new DefaultAesEncryptionService(generateKey());
|
||||
|
||||
DecryptionRequest dreq = DecryptionRequests.builder().setCiphertext(generateKey()).build()
|
||||
|
||||
try {
|
||||
service.decrypt(dreq)
|
||||
fail()
|
||||
} catch (CryptoException expected) {
|
||||
assertTrue expected.getMessage().endsWith(DefaultAesEncryptionService.DECRYPT_NO_IV);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEncryptAdditionalAuthenticatedData() {
|
||||
|
||||
def service = new DefaultAesEncryptionService(generateKey());
|
||||
|
||||
EncryptionRequest ereq = EncryptionRequests.builder()
|
||||
.setPlaintext(PLAINTEXT_BYTES)
|
||||
.setAdditionalAuthenticatedData(AAD_BYTES)
|
||||
.build()
|
||||
|
||||
AuthenticatedEncryptionResult eres = (AuthenticatedEncryptionResult) service.encrypt(ereq);
|
||||
|
||||
DecryptionRequest dreq = DecryptionRequests.builder()
|
||||
.setInitializationVector(eres.getInitializationVector())
|
||||
.setCiphertext(eres.getCiphertext())
|
||||
.setAdditionalAuthenticatedData(AAD_BYTES)
|
||||
.setAuthenticationTag(eres.getAuthenticationTag())
|
||||
.build()
|
||||
|
||||
def decryptedPlaintextBytes = service.decrypt(dreq)
|
||||
|
||||
def decryptedPlaintext = new String(decryptedPlaintextBytes, Charsets.UTF_8);
|
||||
|
||||
assertEquals(PLAINTEXT, decryptedPlaintext);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (C) 2016 jsonwebtoken.io
|
||||
*
|
||||
* Licensed 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 io.jsonwebtoken.impl.crypto
|
||||
|
||||
import org.junit.Test
|
||||
import static org.junit.Assert.*
|
||||
|
||||
class DefaultAuthenticatedEncryptionResultTest {
|
||||
|
||||
private byte[] generateData() {
|
||||
byte[] data = new byte[32];
|
||||
new Random().nextBytes(data) //does not need to be secure for this test
|
||||
return data;
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCompactWithoutIv() {
|
||||
|
||||
byte[] ciphertext = generateData()
|
||||
byte[] tag = generateData()
|
||||
|
||||
byte[] combined = new byte[ciphertext.length + tag.length];
|
||||
System.arraycopy(ciphertext, 0, combined, 0, ciphertext.length);
|
||||
System.arraycopy(tag, 0, combined, ciphertext.length, tag.length);
|
||||
|
||||
def res = new DefaultAuthenticatedEncryptionResult(null, ciphertext, tag)
|
||||
byte[] compact = res.compact()
|
||||
|
||||
assertTrue(Arrays.equals(combined, compact))
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCompactWithIv() {
|
||||
|
||||
byte[] iv = generateData()
|
||||
byte[] ciphertext = generateData()
|
||||
byte[] tag = generateData()
|
||||
|
||||
byte[] combined = new byte[iv.length + ciphertext.length + tag.length];
|
||||
System.arraycopy(iv, 0, combined, 0, iv.length)
|
||||
System.arraycopy(ciphertext, 0, combined, iv.length, ciphertext.length);
|
||||
System.arraycopy(tag, 0, combined, iv.length + ciphertext.length, tag.length);
|
||||
|
||||
def res = new DefaultAuthenticatedEncryptionResult(iv, ciphertext, tag)
|
||||
byte[] compact = res.compact()
|
||||
|
||||
assertTrue(Arrays.equals(combined, compact))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright (C) 2016 jsonwebtoken.io
|
||||
*
|
||||
* Licensed 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 io.jsonwebtoken.impl.crypto
|
||||
|
||||
import org.junit.Test
|
||||
import static org.junit.Assert.*
|
||||
|
||||
class DefaultDecryptionRequestBuilderTest {
|
||||
|
||||
private byte[] generateData() {
|
||||
byte[] data = new byte[32];
|
||||
new Random().nextBytes(data) //does not need to be secure for this test
|
||||
return data;
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAadWithoutTag() {
|
||||
try {
|
||||
new DefaultDecryptionRequestBuilder().setCiphertext(generateData())
|
||||
.setAdditionalAuthenticatedData(generateData()).build()
|
||||
fail()
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertEquals(DefaultDecryptionRequestBuilder.AAD_NEEDS_TAG_MSG, expected.getMessage())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTagWithoutAad() {
|
||||
try {
|
||||
new DefaultDecryptionRequestBuilder().setCiphertext(generateData())
|
||||
.setAuthenticationTag(generateData()).build()
|
||||
fail()
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertEquals(DefaultDecryptionRequestBuilder.TAG_NEEDS_AAD_MSG, expected.getMessage())
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue