mirror of https://github.com/apache/jclouds.git
Issue 1120: support base64 encode/decode for url applications
This commit is contained in:
parent
f4333157b9
commit
2c900d1bab
|
@ -78,6 +78,53 @@ public class CryptoStreams {
|
||||||
return Base64.encodeBytes(in, Base64.DONT_BREAK_LINES);
|
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) {
|
public static byte[] base64(String in) {
|
||||||
return Base64.decode(in);
|
return Base64.decode(in);
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,6 @@ import java.security.spec.RSAPublicKeySpec;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.bouncycastle.openssl.PEMWriter;
|
import org.bouncycastle.openssl.PEMWriter;
|
||||||
import org.jclouds.encryption.internal.Base64;
|
|
||||||
import org.jclouds.io.InputSuppliers;
|
import org.jclouds.io.InputSuppliers;
|
||||||
import org.jclouds.util.Strings2;
|
import org.jclouds.util.Strings2;
|
||||||
|
|
||||||
|
@ -102,7 +101,7 @@ public class SshKeys {
|
||||||
Iterable<String> parts = Splitter.on(' ').split(Strings2.toStringAndClose(stream));
|
Iterable<String> parts = Splitter.on(' ').split(Strings2.toStringAndClose(stream));
|
||||||
checkArgument(Iterables.size(parts) >= 2 && "ssh-rsa".equals(Iterables.get(parts, 0)),
|
checkArgument(Iterables.size(parts) >= 2 && "ssh-rsa".equals(Iterables.get(parts, 0)),
|
||||||
"bad format, should be: ssh-rsa AAAAB3...");
|
"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));
|
String marker = new String(readLengthFirst(stream));
|
||||||
checkArgument("ssh-rsa".equals(marker), "looking for marker ssh-rsa but got %s", marker);
|
checkArgument("ssh-rsa".equals(marker), "looking for marker ssh-rsa but got %s", marker);
|
||||||
BigInteger publicExponent = new BigInteger(readLengthFirst(stream));
|
BigInteger publicExponent = new BigInteger(readLengthFirst(stream));
|
||||||
|
|
|
@ -152,6 +152,12 @@ public class Base64
|
||||||
/**
|
/**
|
||||||
* Translates a Base64 value to either its 6-bit reconstruction value
|
* Translates a Base64 value to either its 6-bit reconstruction value
|
||||||
* or a negative number indicating some other meaning.
|
* 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 static final byte[] DECODABET =
|
private static final byte[] DECODABET =
|
||||||
{
|
{
|
||||||
|
@ -164,7 +170,9 @@ public class Base64
|
||||||
-5, // Whitespace: Space
|
-5, // Whitespace: Space
|
||||||
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42
|
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42
|
||||||
62, // Plus sign at decimal 43
|
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
|
63, // Slash at decimal 47
|
||||||
52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine
|
52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine
|
||||||
-9,-9,-9, // Decimal 58 - 60
|
-9,-9,-9, // Decimal 58 - 60
|
||||||
|
@ -172,7 +180,9 @@ public class Base64
|
||||||
-9,-9,-9, // Decimal 62 - 64
|
-9,-9,-9, // Decimal 62 - 64
|
||||||
0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N'
|
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'
|
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'
|
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'
|
39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z'
|
||||||
-9,-9,-9,-9 // Decimal 123 - 126
|
-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 )
|
public static byte[] decode( String s )
|
||||||
{
|
{
|
||||||
|
int modulo = s.length() % 4;
|
||||||
|
switch (modulo) {
|
||||||
|
case 2:
|
||||||
|
s += "==";
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
s += "=";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
byte[] bytes;
|
byte[] bytes;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
|
@ -31,7 +31,7 @@ import com.google.common.base.Charsets;
|
||||||
*
|
*
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
*/
|
*/
|
||||||
@Test(groups = "unit", sequential = true)
|
@Test(groups = "unit", singleThreaded = true)
|
||||||
public class CryptoStreamsTest {
|
public class CryptoStreamsTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -43,11 +43,65 @@ public class CryptoStreamsTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBase64Decode() throws IOException {
|
public void testBase64Decode() throws IOException {
|
||||||
|
|
||||||
byte[] decoded = CryptoStreams.base64Decode(Payloads.newStringPayload("aGVsbG8gd29ybGQ="));
|
byte[] decoded = CryptoStreams.base64Decode(Payloads.newStringPayload("aGVsbG8gd29ybGQ="));
|
||||||
assertEquals(new String(decoded, Charsets.UTF_8), "hello world");
|
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
|
@Test
|
||||||
public void testHexEncode() throws IOException {
|
public void testHexEncode() throws IOException {
|
||||||
|
|
||||||
|
|
|
@ -29,10 +29,11 @@ import java.util.concurrent.CompletionService;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.ExecutorCompletionService;
|
import java.util.concurrent.ExecutorCompletionService;
|
||||||
|
|
||||||
|
import static javax.xml.bind.DatatypeConverter.*;
|
||||||
|
|
||||||
import org.jclouds.PerformanceTest;
|
import org.jclouds.PerformanceTest;
|
||||||
import org.jclouds.crypto.Crypto;
|
import org.jclouds.crypto.Crypto;
|
||||||
import org.jclouds.crypto.CryptoStreams;
|
import org.jclouds.crypto.CryptoStreams;
|
||||||
import org.jclouds.encryption.internal.Base64;
|
|
||||||
import org.testng.annotations.BeforeTest;
|
import org.testng.annotations.BeforeTest;
|
||||||
import org.testng.annotations.DataProvider;
|
import org.testng.annotations.DataProvider;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
@ -46,7 +47,7 @@ import com.google.inject.Injector;
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
*/
|
*/
|
||||||
// NOTE:without testName, this will not call @Before* and fail w/NPE during surefire
|
// 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 {
|
public class CryptoTest extends PerformanceTest {
|
||||||
|
|
||||||
protected Crypto crypto;
|
protected Crypto crypto;
|
||||||
|
@ -58,16 +59,12 @@ public class CryptoTest extends PerformanceTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final Object[][] base64KeyMessageDigest = {
|
public static final Object[][] base64KeyMessageDigest = {
|
||||||
{ Base64.decode("CwsLCwsLCwsLCwsLCwsLCwsLCws="), "Hi There", "thcxhlUFcmTii8C2+zeMjvFGvgA=" },
|
{ parseBase64Binary("CwsLCwsLCwsLCwsLCwsLCwsLCws="), "Hi There", "thcxhlUFcmTii8C2+zeMjvFGvgA=" },
|
||||||
{ Base64.decode("SmVmZQ=="), "what do ya want for nothing?", "7/zfauXrL6LSdBbV8YTfnCWafHk=" },
|
{ parseBase64Binary("SmVmZQ=="), "what do ya want for nothing?", "7/zfauXrL6LSdBbV8YTfnCWafHk=" },
|
||||||
{ Base64.decode("DAwMDAwMDAwMDAwMDAwMDAwMDAw="), "Test With Truncation", "TBoDQktV4H/n8nvh1Yu5MkqaWgQ=" },
|
{ parseBase64Binary("DAwMDAwMDAwMDAwMDAwMDAwMDAw="), "Test With Truncation", "TBoDQktV4H/n8nvh1Yu5MkqaWgQ=" },
|
||||||
{
|
{ parseBase64Binary("qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqo="),
|
||||||
Base64
|
|
||||||
.decode("qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqo="),
|
|
||||||
"Test Using Larger Than Block-Size Key - Hash Key First", "qkrl4VJy0A6VcFY3zoo7Ve1AIRI=" },
|
"Test Using Larger Than Block-Size Key - Hash Key First", "qkrl4VJy0A6VcFY3zoo7Ve1AIRI=" },
|
||||||
{
|
{ parseBase64Binary("qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqo="),
|
||||||
Base64
|
|
||||||
.decode("qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqo="),
|
|
||||||
"Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data",
|
"Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data",
|
||||||
"6OmdD0UjfXhta7qnllx4CLv/GpE=" } };
|
"6OmdD0UjfXhta7qnllx4CLv/GpE=" } };
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue