You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by am...@apache.org on 2018/08/10 06:36:07 UTC
svn commit: r1837776 - in /jackrabbit/oak/trunk/oak-blob-plugins/src:
main/java/org/apache/jackrabbit/oak/plugins/blob/datastore/directaccess/
test/java/org/apache/jackrabbit/oak/plugins/blob/datastore/directaccess/
Author: amitj
Date: Fri Aug 10 06:36:07 2018
New Revision: 1837776
URL: http://svn.apache.org/viewvc?rev=1837776&view=rev
Log:
OAK-7692: [DirectBinaryAccess] Upload token HMAC signature must be base64 encoded
Patch from Alexander Klimetschek
Added:
jackrabbit/oak/trunk/oak-blob-plugins/src/test/java/org/apache/jackrabbit/oak/plugins/blob/datastore/directaccess/DataRecordUploadTokenTest.java (with props)
Modified:
jackrabbit/oak/trunk/oak-blob-plugins/src/main/java/org/apache/jackrabbit/oak/plugins/blob/datastore/directaccess/DataRecordUploadToken.java
Modified: jackrabbit/oak/trunk/oak-blob-plugins/src/main/java/org/apache/jackrabbit/oak/plugins/blob/datastore/directaccess/DataRecordUploadToken.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-blob-plugins/src/main/java/org/apache/jackrabbit/oak/plugins/blob/datastore/directaccess/DataRecordUploadToken.java?rev=1837776&r1=1837775&r2=1837776&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-blob-plugins/src/main/java/org/apache/jackrabbit/oak/plugins/blob/datastore/directaccess/DataRecordUploadToken.java (original)
+++ jackrabbit/oak/trunk/oak-blob-plugins/src/main/java/org/apache/jackrabbit/oak/plugins/blob/datastore/directaccess/DataRecordUploadToken.java Fri Aug 10 06:36:07 2018
@@ -18,6 +18,7 @@
*/
package org.apache.jackrabbit.oak.plugins.blob.datastore.directaccess;
+import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.Instant;
@@ -27,8 +28,9 @@ import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import com.google.common.base.Joiner;
+
+import org.apache.commons.codec.binary.Base64;
import org.apache.jackrabbit.oak.spi.blob.AbstractSharedBackend;
-import org.apache.jackrabbit.util.Base64;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
@@ -93,22 +95,22 @@ public class DataRecordUploadToken {
*/
public static DataRecordUploadToken fromEncodedToken(@NotNull String encoded, @NotNull byte[] secret)
throws IllegalArgumentException {
- String[] parts = encoded.split("#", 2);
+ final String[] parts = encoded.split("#", 2);
if (parts.length < 2) {
- throw new IllegalArgumentException("Encoded string is missing the signature");
+ throw new IllegalArgumentException("Invalid upload token");
}
- String toBeDecoded = parts[0];
- String expectedSig = Base64.decode(parts[1]);
- String actualSig = getSignedString(toBeDecoded, secret);
+ final String toBeDecoded = parts[0];
+ final String expectedSig = parts[1];
+ final String actualSig = getSignedString(toBeDecoded, secret);
if (!expectedSig.equals(actualSig)) {
- throw new IllegalArgumentException("Upload token signature does not match");
+ throw new IllegalArgumentException("Invalid upload token");
}
- String decoded = Base64.decode(toBeDecoded);
+ String decoded = decodeBase64(toBeDecoded);
String decodedParts[] = decoded.split("#");
if (decodedParts.length < 2) {
- throw new IllegalArgumentException("Not all upload token parts provided");
+ throw new IllegalArgumentException("Invalid upload token");
}
return new DataRecordUploadToken(decodedParts[0], decodedParts.length > 2 ? decodedParts[2] : null);
@@ -133,19 +135,20 @@ public class DataRecordUploadToken {
String toBeEncoded = uploadId.isPresent() ?
Joiner.on("#").join(blobId, now, uploadId.get()) :
Joiner.on("#").join(blobId, now);
- String toBeSigned = Base64.encode(toBeEncoded);
+ String toBeSigned = encodeBase64(toBeEncoded);
String sig = getSignedString(toBeSigned, secret);
return sig != null ? Joiner.on("#").join(toBeSigned, sig) : toBeSigned;
}
+ /** Returns the base64 encoded HMAC signature */
private static String getSignedString(String toBeSigned, byte[] secret) {
try {
final String algorithm = "HmacSHA1";
Mac mac = Mac.getInstance(algorithm);
mac.init(new SecretKeySpec(secret, algorithm));
- byte[] hash = mac.doFinal(toBeSigned.getBytes());
- return new String(hash);
+ byte[] hash = mac.doFinal(toBeSigned.getBytes(StandardCharsets.UTF_8));
+ return encodeBase64(hash);
}
catch (NoSuchAlgorithmException | InvalidKeyException e) {
LOG.warn("Could not sign upload token", e);
@@ -153,6 +156,18 @@ public class DataRecordUploadToken {
return null;
}
+ private static String encodeBase64(String string) {
+ return Base64.encodeBase64String(string.getBytes(StandardCharsets.UTF_8));
+ }
+
+ private static String encodeBase64(byte[] bytes) {
+ return Base64.encodeBase64String(bytes);
+ }
+
+ private static String decodeBase64(String encodedString) {
+ return new String(Base64.decodeBase64(encodedString), StandardCharsets.UTF_8);
+ }
+
/**
* Returns the blob ID of this instance.
*
Added: jackrabbit/oak/trunk/oak-blob-plugins/src/test/java/org/apache/jackrabbit/oak/plugins/blob/datastore/directaccess/DataRecordUploadTokenTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-blob-plugins/src/test/java/org/apache/jackrabbit/oak/plugins/blob/datastore/directaccess/DataRecordUploadTokenTest.java?rev=1837776&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-blob-plugins/src/test/java/org/apache/jackrabbit/oak/plugins/blob/datastore/directaccess/DataRecordUploadTokenTest.java (added)
+++ jackrabbit/oak/trunk/oak-blob-plugins/src/test/java/org/apache/jackrabbit/oak/plugins/blob/datastore/directaccess/DataRecordUploadTokenTest.java Fri Aug 10 06:36:07 2018
@@ -0,0 +1,73 @@
+/*
+ * 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.apache.jackrabbit.oak.plugins.blob.datastore.directaccess;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import java.nio.charset.StandardCharsets;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.lang3.StringUtils;
+import org.junit.Test;
+
+public class DataRecordUploadTokenTest {
+
+ private static final String BLOB_ID = "blob";
+ private static final String UPLOAD_ID = "upload";
+ private static final byte[] SECRET = "1234567890".getBytes(StandardCharsets.UTF_8);
+
+ @Test
+ public void testUploadToken() {
+ String encodedToken = new DataRecordUploadToken(BLOB_ID, UPLOAD_ID).getEncodedToken(SECRET);
+
+ // also check token can be parsed and is valid
+ DataRecordUploadToken parsedToken = DataRecordUploadToken.fromEncodedToken(encodedToken, SECRET);
+ assertEquals(BLOB_ID, parsedToken.getBlobId());
+ assertTrue(parsedToken.getUploadId().isPresent());
+ assertEquals(UPLOAD_ID, parsedToken.getUploadId().get());
+ }
+
+ @Test
+ public void testUploadTokenIsAscii() {
+
+ // run a few times to rule out the (low) chance it is ascii just by chance; the seed will change regularly
+ for (int i = 0; i < 1000; i++) {
+ String encodedToken = new DataRecordUploadToken(BLOB_ID, UPLOAD_ID).getEncodedToken(SECRET);
+ assertTrue("upload token is not ascii: " + encodedToken, StringUtils.isAsciiPrintable(encodedToken));
+
+ // also check token can be parsed and is valid
+ DataRecordUploadToken parsedToken = DataRecordUploadToken.fromEncodedToken(encodedToken, SECRET);
+ assertEquals(BLOB_ID, parsedToken.getBlobId());
+ assertTrue(parsedToken.getUploadId().isPresent());
+ assertEquals(UPLOAD_ID, parsedToken.getUploadId().get());
+ }
+ }
+
+ @Test
+ public void testUploadTokenSignature() {
+ // simple test to check the signature is present and validated
+ String spoofedToken = Base64.encodeBase64String((BLOB_ID + "#" + UPLOAD_ID).getBytes(StandardCharsets.UTF_8));
+
+ try {
+ DataRecordUploadToken.fromEncodedToken(spoofedToken, SECRET);
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+}
Propchange: jackrabbit/oak/trunk/oak-blob-plugins/src/test/java/org/apache/jackrabbit/oak/plugins/blob/datastore/directaccess/DataRecordUploadTokenTest.java
------------------------------------------------------------------------------
svn:eol-style = native