mirror of https://github.com/apache/jclouds.git
Issue 1120: support base64 encode/decode for url applications
This commit is contained in:
parent
312420a761
commit
a551d912c4
|
@ -79,6 +79,53 @@ public class CryptoStreams {
|
|||
return Base64.encodeBytes(in, Base64.DONT_BREAK_LINES);
|
||||
}
|
||||
|
||||
/**
|
||||
* encodes value substituting {@code '-' and '_'} for {@code '+' and '/'},
|
||||
* and without adding trailing {@code '='} padding.
|
||||
*
|
||||
* @see <a
|
||||
* href="http://en.wikipedia.org/wiki/Base64#URL_applications">url-safe
|
||||
* encoding</a>
|
||||
*/
|
||||
public static String base64URLSafe(byte[] in) {
|
||||
String provisional = base64(in);
|
||||
int length = provisional.length();
|
||||
if (length == 0)
|
||||
return provisional;
|
||||
// we know base64 is in 4 character chunks, so out of bounds risk here
|
||||
else if (provisional.charAt((length - 2)) == '=')
|
||||
length-=2;
|
||||
else if (provisional.charAt((length - 1)) == '=')
|
||||
length-=1;
|
||||
|
||||
char[] tmp = new char[length];
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
char c = provisional.charAt(i);
|
||||
switch (c) {
|
||||
case '+':
|
||||
tmp[i] = '-';
|
||||
break;
|
||||
case '/':
|
||||
tmp[i] = '_';
|
||||
break;
|
||||
default:
|
||||
tmp[i] = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return new String(tmp);
|
||||
}
|
||||
|
||||
/**
|
||||
* decodes base 64 encoded string, regardless of whether padding {@code '='} padding is present.
|
||||
*
|
||||
* Note this seamlessly handles the URL-safe case where {@code '-' and '_'} are substituted for {@code '+' and '/'}.
|
||||
*
|
||||
* @see <a
|
||||
* href="http://en.wikipedia.org/wiki/Base64#URL_applications">url-safe
|
||||
* encoding</a>
|
||||
*/
|
||||
public static byte[] base64(String in) {
|
||||
return Base64.decode(in);
|
||||
}
|
||||
|
|
|
@ -45,7 +45,6 @@ import java.security.spec.RSAPublicKeySpec;
|
|||
import java.util.Map;
|
||||
|
||||
import org.bouncycastle.openssl.PEMWriter;
|
||||
import org.jclouds.encryption.internal.Base64;
|
||||
import org.jclouds.io.InputSuppliers;
|
||||
import org.jclouds.util.Strings2;
|
||||
|
||||
|
@ -102,7 +101,7 @@ public class SshKeys {
|
|||
Iterable<String> parts = Splitter.on(' ').split(Strings2.toStringAndClose(stream));
|
||||
checkArgument(Iterables.size(parts) >= 2 && "ssh-rsa".equals(Iterables.get(parts, 0)),
|
||||
"bad format, should be: ssh-rsa AAAAB3...");
|
||||
stream = new ByteArrayInputStream(Base64.decode(Iterables.get(parts, 1)));
|
||||
stream = new ByteArrayInputStream(CryptoStreams.base64(Iterables.get(parts, 1)));
|
||||
String marker = new String(readLengthFirst(stream));
|
||||
checkArgument("ssh-rsa".equals(marker), "looking for marker ssh-rsa but got %s", marker);
|
||||
BigInteger publicExponent = new BigInteger(readLengthFirst(stream));
|
||||
|
|
|
@ -152,6 +152,12 @@ public class Base64
|
|||
/**
|
||||
* Translates a Base64 value to either its 6-bit reconstruction value
|
||||
* or a negative number indicating some other meaning.
|
||||
*
|
||||
* <h4>Note</h4>
|
||||
* {@code +} and {@code -} both decode to 62. {@code /} and {@code _} both decode to 63
|
||||
*
|
||||
* This accomodates URL-safe encoding
|
||||
* @see <a href="http://en.wikipedia.org/wiki/Base64#URL_applications">url-safe base64</a>
|
||||
**/
|
||||
private final static byte[] DECODABET =
|
||||
{
|
||||
|
@ -164,7 +170,9 @@ public class Base64
|
|||
-5, // Whitespace: Space
|
||||
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42
|
||||
62, // Plus sign at decimal 43
|
||||
-9,-9,-9, // Decimal 44 - 46
|
||||
-9, // Decimal 44
|
||||
62, // Hyphen at decimal 45
|
||||
-9, // Decimal 45
|
||||
63, // Slash at decimal 47
|
||||
52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine
|
||||
-9,-9,-9, // Decimal 58 - 60
|
||||
|
@ -172,7 +180,9 @@ public class Base64
|
|||
-9,-9,-9, // Decimal 62 - 64
|
||||
0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N'
|
||||
14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z'
|
||||
-9,-9,-9,-9,-9,-9, // Decimal 91 - 96
|
||||
-9,-9,-9,-9, // Decimal 91 - 94
|
||||
63, // Underscore at decimal 95
|
||||
-9, // Decimal 96
|
||||
26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm'
|
||||
39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z'
|
||||
-9,-9,-9,-9 // Decimal 123 - 126
|
||||
|
@ -199,7 +209,7 @@ public class Base64
|
|||
|
||||
|
||||
|
||||
/* ******** E N C O D I N G ApiMetadata E T H O D S ******** */
|
||||
/* ******** E N C O D I N G M E T H O D S ******** */
|
||||
|
||||
|
||||
/**
|
||||
|
@ -727,6 +737,16 @@ public class Base64
|
|||
*/
|
||||
public static byte[] decode( String s )
|
||||
{
|
||||
int modulo = s.length() % 4;
|
||||
switch (modulo) {
|
||||
case 2:
|
||||
s += "==";
|
||||
break;
|
||||
case 3:
|
||||
s += "=";
|
||||
break;
|
||||
}
|
||||
|
||||
byte[] bytes;
|
||||
try
|
||||
{
|
||||
|
|
|
@ -31,7 +31,7 @@ import com.google.common.base.Charsets;
|
|||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Test(groups = "unit", sequential = true)
|
||||
@Test(groups = "unit", singleThreaded = true)
|
||||
public class CryptoStreamsTest {
|
||||
|
||||
@Test
|
||||
|
@ -43,11 +43,65 @@ public class CryptoStreamsTest {
|
|||
|
||||
@Test
|
||||
public void testBase64Decode() throws IOException {
|
||||
|
||||
byte[] decoded = CryptoStreams.base64Decode(Payloads.newStringPayload("aGVsbG8gd29ybGQ="));
|
||||
assertEquals(new String(decoded, Charsets.UTF_8), "hello world");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBase64DecodeURLSafeJson() throws IOException {
|
||||
byte[] decoded = CryptoStreams.base64("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9");
|
||||
assertEquals(new String(decoded, Charsets.UTF_8), "{\"alg\":\"RS256\",\"typ\":\"JWT\"}");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBase64DecodeURLSafeNoPadding() throws IOException {
|
||||
|
||||
byte[] decoded = CryptoStreams
|
||||
.base64("eyJpc3MiOiI3NjEzMjY3OTgwNjktcjVtbGpsbG4xcmQ0bHJiaGc3NWVmZ2lncDM2bTc4ajVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJzY29wZSI6Imh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2F1dGgvcHJlZGljdGlvbiIsImF1ZCI6Imh0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbS9vL29hdXRoMi90b2tlbiIsImV4cCI6MTMyODU1NDM4NSwiaWF0IjoxMzI4NTUwNzg1fQ");
|
||||
|
||||
assertEquals(new String(decoded, Charsets.UTF_8), "{"
|
||||
+ "\"iss\":\"761326798069-r5mljlln1rd4lrbhg75efgigp36m78j5@developer.gserviceaccount.com\","
|
||||
+ "\"scope\":\"https://www.googleapis.com/auth/prediction\","
|
||||
+ "\"aud\":\"https://accounts.google.com/o/oauth2/token\"," + "\"exp\":1328554385," + "\"iat\":1328550785"
|
||||
+ "}");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBase64EncodeURLSafeNoSinglePad() {
|
||||
assertEquals(CryptoStreams.base64("any carnal pleasu".getBytes(Charsets.UTF_8)), "YW55IGNhcm5hbCBwbGVhc3U=");
|
||||
assertEquals(CryptoStreams.base64URLSafe("any carnal pleasu".getBytes(Charsets.UTF_8)), "YW55IGNhcm5hbCBwbGVhc3U");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBase64EncodeURLSafeNoDoublePad() {
|
||||
assertEquals(CryptoStreams.base64("any carnal pleas".getBytes(Charsets.UTF_8)), "YW55IGNhcm5hbCBwbGVhcw==");
|
||||
assertEquals(CryptoStreams.base64URLSafe("any carnal pleas".getBytes(Charsets.UTF_8)), "YW55IGNhcm5hbCBwbGVhcw");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBase64EncodeURLSafeHyphenNotPlus() {
|
||||
assertEquals(CryptoStreams.base64("i?>".getBytes(Charsets.UTF_8)), "aT8+");
|
||||
assertEquals(CryptoStreams.base64URLSafe("i?>".getBytes(Charsets.UTF_8)), "aT8-");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBase64EncodeURLSafeUnderscoreNotSlash() {
|
||||
assertEquals(CryptoStreams.base64("i??".getBytes(Charsets.UTF_8)), "aT8/");
|
||||
assertEquals(CryptoStreams.base64URLSafe("i??".getBytes(Charsets.UTF_8)), "aT8_");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBase64DecodeWithoutSinglePad() {
|
||||
assertEquals(new String(CryptoStreams.base64("YW55IGNhcm5hbCBwbGVhc3U="), Charsets.UTF_8), "any carnal pleasu");
|
||||
assertEquals(new String(CryptoStreams.base64("YW55IGNhcm5hbCBwbGVhc3U"), Charsets.UTF_8), "any carnal pleasu");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBase64DecodeWithoutDoublePad() {
|
||||
assertEquals(new String(CryptoStreams.base64("YW55IGNhcm5hbCBwbGVhcw=="), Charsets.UTF_8), "any carnal pleas");
|
||||
assertEquals(new String(CryptoStreams.base64("YW55IGNhcm5hbCBwbGVhcw"), Charsets.UTF_8), "any carnal pleas");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHexEncode() throws IOException {
|
||||
|
||||
|
|
|
@ -29,10 +29,11 @@ import java.util.concurrent.CompletionService;
|
|||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorCompletionService;
|
||||
|
||||
import static javax.xml.bind.DatatypeConverter.*;
|
||||
|
||||
import org.jclouds.PerformanceTest;
|
||||
import org.jclouds.crypto.Crypto;
|
||||
import org.jclouds.crypto.CryptoStreams;
|
||||
import org.jclouds.encryption.internal.Base64;
|
||||
import org.testng.annotations.BeforeTest;
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
|
@ -46,7 +47,7 @@ import com.google.inject.Injector;
|
|||
* @author Adrian Cole
|
||||
*/
|
||||
// NOTE:without testName, this will not call @Before* and fail w/NPE during surefire
|
||||
@Test(groups = "performance", sequential = true, timeOut = 2 * 60 * 1000, testName = "CryptoTest")
|
||||
@Test(groups = "performance", singleThreaded = true, timeOut = 2 * 60 * 1000, testName = "CryptoTest")
|
||||
public class CryptoTest extends PerformanceTest {
|
||||
|
||||
protected Crypto crypto;
|
||||
|
@ -57,17 +58,13 @@ public class CryptoTest extends PerformanceTest {
|
|||
crypto = i.getInstance(Crypto.class);
|
||||
}
|
||||
|
||||
public final static Object[][] base64KeyMessageDigest = {
|
||||
{ Base64.decode("CwsLCwsLCwsLCwsLCwsLCwsLCws="), "Hi There", "thcxhlUFcmTii8C2+zeMjvFGvgA=" },
|
||||
{ Base64.decode("SmVmZQ=="), "what do ya want for nothing?", "7/zfauXrL6LSdBbV8YTfnCWafHk=" },
|
||||
{ Base64.decode("DAwMDAwMDAwMDAwMDAwMDAwMDAw="), "Test With Truncation", "TBoDQktV4H/n8nvh1Yu5MkqaWgQ=" },
|
||||
{
|
||||
Base64
|
||||
.decode("qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqo="),
|
||||
public static final Object[][] base64KeyMessageDigest = {
|
||||
{ parseBase64Binary("CwsLCwsLCwsLCwsLCwsLCwsLCws="), "Hi There", "thcxhlUFcmTii8C2+zeMjvFGvgA=" },
|
||||
{ parseBase64Binary("SmVmZQ=="), "what do ya want for nothing?", "7/zfauXrL6LSdBbV8YTfnCWafHk=" },
|
||||
{ parseBase64Binary("DAwMDAwMDAwMDAwMDAwMDAwMDAw="), "Test With Truncation", "TBoDQktV4H/n8nvh1Yu5MkqaWgQ=" },
|
||||
{ parseBase64Binary("qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqo="),
|
||||
"Test Using Larger Than Block-Size Key - Hash Key First", "qkrl4VJy0A6VcFY3zoo7Ve1AIRI=" },
|
||||
{
|
||||
Base64
|
||||
.decode("qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqo="),
|
||||
{ parseBase64Binary("qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqo="),
|
||||
"Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data",
|
||||
"6OmdD0UjfXhta7qnllx4CLv/GpE=" } };
|
||||
|
||||
|
|
Loading…
Reference in New Issue