![JAR search and dependency download from the Maven repository](/logo.png)
com.ociweb.pronghorn.network.OAuth1HeaderBuilder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of Pronghorn Show documentation
Show all versions of Pronghorn Show documentation
Primary dependency for any project using the Pronghorn framework
The newest version!
package com.ociweb.pronghorn.network;
import static com.ociweb.pronghorn.pipe.Pipe.blobMask;
import static com.ociweb.pronghorn.pipe.Pipe.byteBackingArray;
import static com.ociweb.pronghorn.pipe.Pipe.bytePosition;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import javax.crypto.Mac;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.SecretKeySpec;
import com.ociweb.pronghorn.pipe.DataOutputBlobWriter;
import com.ociweb.pronghorn.pipe.Pipe;
import com.ociweb.pronghorn.pipe.RawDataSchema;
import com.ociweb.pronghorn.util.Appendables;
public class OAuth1HeaderBuilder {
private static final String OAUTH_VERSION = "oauth_version"; //optional for all calls
private static final String OAUTH_NONCE = "oauth_nonce"; //required for all calls
private static final String OAUTH_TIMESTAMP = "oauth_timestamp"; //required for all calls
private static final String OAUTH_SIGNATURE_METHOD = "oauth_signature_method"; //required for all calls
private static final String OAUTH_SIGNATURE = "oauth_signature"; //required for all calls
private static final String OAUTH_CONSUMER_KEY = "oauth_consumer_key"; //required for all calls
private static final String OAUTH_TOKEN = "oauth_token"; //only for E and G
private static final String OAUTH_VERIFIER = "oauth_verifier"; // the pin only for E
private static final String OAUTH_CALLBACK = "oauth_callback"; //only for A
private final SecureRandom secureRandom = new SecureRandom();
private final StringBuilder nonceBuilder = new StringBuilder();
private final StringBuilder timeBuilder = new StringBuilder();
private final StringBuilder tokenBuilder = new StringBuilder();
private final StringBuilder consumerKeyBuilder = new StringBuilder();
private final StringBuilder callbackBuilder = new StringBuilder();
private final StringBuilder verifierBuilder = new StringBuilder();
private final List macParams = new ArrayList(); //in alpha order...
private final Pipe workingPipe;
private final String formalPath;
private final Mac mac;
//custom values
private SecretKeySpec secretKeySpec;
///////////////////////
//For a better understanding of A, E and G read the following
//https://oauth.net/core/1.0/#anchor9
///////////////////////
public OAuth1HeaderBuilder(int port, String scheme, String host, String path) {
try {
this.mac = Mac.getInstance("HmacSHA1");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
consumerKeyBuilder.setLength(0);
this.addMACParam(OAUTH_CONSUMER_KEY,consumerKeyBuilder);
this.addMACParam(OAUTH_NONCE,nonceBuilder);
this.addMACParam(OAUTH_SIGNATURE_METHOD,"HMAC-SHA1");
this.addMACParam(OAUTH_TIMESTAMP,timeBuilder);
this.addMACParam(OAUTH_VERSION,"1.0");
tokenBuilder.setLength(0);
this.addMACParam(OAUTH_TOKEN,tokenBuilder);
callbackBuilder.setLength(0);
this.addMACParam(OAUTH_CALLBACK, callbackBuilder);
verifierBuilder.setLength(0);
this.addMACParam(OAUTH_VERIFIER, verifierBuilder);
this.workingPipe = RawDataSchema.instance.newPipe(2, 1000);
this.workingPipe.initBuffers();
this.formalPath = buildFormalPath(port, scheme, host, path);
}
public void setupStep1(String consumerKey, String callback) {
// A
this.consumerKeyBuilder.setLength(0);
this.consumerKeyBuilder.append(consumerKey);
assert(callback==null
|| callback.indexOf("%3")>0
|| callback.equals("oob")) : "value must be url encoded or set to oob for pin mode";
//example oauth_callback="http%3A%2F%2Fmyapp.com%3A3005%2Ftwitter%2Fprocess_callback"
this.callbackBuilder.setLength(0);
if (null!=callback) {
this.callbackBuilder.append(callback);
}
this.tokenBuilder.setLength(0);
this.secretKeySpec = new SecretKeySpec("anonymous&".getBytes(), "HmacSHA1");
//required for twitter
//oauth_callback =
// oauth_consumer_key:
// The Consumer Key.
// oauth_signature_method:
// The signature method the Consumer used to sign the request.
// oauth_signature:
// The signature as defined in Signing Requests.
// oauth_timestamp:
// As defined in Nonce and Timestamp.
// oauth_nonce:
// As defined in Nonce and Timestamp.
// oauth_version:
// OPTIONAL. If present, value MUST be 1.0 . Service Providers MUST assume the protocol version to be 1.0 if this parameter is not present. Service Providers’ response to non-1.0 value is left undefined.
// Additional parameters:
// Any additional parameters, as defined by the Service Provider.
////////////
//responds with
////////////
// oauth_token:
// The Request Token.
// oauth_token_secret:
// The Token Secret.
// Additional parameters:
// Any additional parameters, as defined by the Service Provider.
}
public void setupStep2(String consumerKey, String consumerSecret, String token, String tokenSecret ) {
// E
this.consumerKeyBuilder.setLength(0);
this.consumerKeyBuilder.append(consumerKey);
this.tokenBuilder.setLength(0);
this.tokenBuilder.append(token);
this.secretKeySpec = new SecretKeySpec((consumerSecret + "&").getBytes(), "HmacSHA1");
// oauth_consumer_key:
// The Consumer Key.
// oauth_token:
// The Request Token obtained previously.
// oauth_signature_method:
// The signature method the Consumer used to sign the request.
// oauth_signature:
// The signature as defined in Signing Requests.
// oauth_timestamp:
// As defined in Nonce and Timestamp.
// oauth_nonce:
// As defined in Nonce and Timestamp.
// oauth_version:
// OPTIONAL. If present, value MUST be 1.0 . Service Providers MUST assume the protocol version to be 1.0 if this parameter is not present. Service Providers’ response to non-1.0 value is left undefined.
//
// oauth_token:
// The Access Token.
// oauth_token_secret:
// The Token Secret.
// Additional parameters:
// Any additional parameters, as defined by the Service Provider.
}
public void setupStep3(String consumerKey, String consumerSecret, String token, String tokenSecret ) {
this.consumerKeyBuilder.setLength(0);
this.consumerKeyBuilder.append(consumerKey);
this.tokenBuilder.setLength(0);
this.tokenBuilder.append(token);
this.secretKeySpec = new SecretKeySpec((consumerSecret + "&" + tokenSecret).getBytes(), "HmacSHA1");
}
public void addMACParam(CharSequence key, CharSequence dynamicValue) {
try {
macParams.add(new CharSequence[]{
key,
dynamicValue
});
} catch (Exception e) {
throw new RuntimeException(e);
}
Collections.sort(macParams, charComparitor);
}
public void addMACParam(String key, String value) {
try {
macParams.add(new CharSequence[]{
URLEncoder.encode(key,"UTF-8"),
null==value?null:URLEncoder.encode(value,"UTF-8")
});
} catch (Exception e) {
throw new RuntimeException(e);
}
Collections.sort(macParams, charComparitor);
}
private final int charCompare(CharSequence thisCS, CharSequence thatCS) {
int len1 = thisCS.length();
int len2 = thatCS.length();
int lim = Math.min(len1, len2);
int k = -1;
while (++k < lim) {
char c1 = thisCS.charAt(k);
char c2 = thatCS.charAt(k);
if (c1 != c2) {
return c1 - c2;
}
}
return len1 - len2;
}
private final Comparator charComparitor = new Comparator() {
@Override
public int compare(CharSequence[] thisPair, CharSequence[] thatPair) {
// sort params first by key, then by value
int keyCompare = charCompare(thisPair[0],thatPair[0]);
if (keyCompare == 0) {
return charCompare(thisPair[1],thatPair[1]);
} else {
return keyCompare;
}
}
};
public A addHeaders(A builder, String upperVerb) {
final long now = System.currentTimeMillis();
nonceBuilder.setLength(0);
Appendables.appendValue(nonceBuilder, Math.abs(secureRandom.nextLong()));
Appendables.appendValue(nonceBuilder, now);
timeBuilder.setLength(0);
Appendables.appendValue(timeBuilder, now / 1000);
///////////////////////////////////////
//https://oauth.net/core/1.0/#anchor9
//////////////////////
//This call is part G
//////////////////////////////////////
try {
builder.append("Authorization: ");
builder.append("OAuth ");
if (callbackBuilder.length()>0) {
builder.append(OAUTH_CALLBACK).append("=\"").append(callbackBuilder).append("\", ");
}
builder.append(OAUTH_CONSUMER_KEY).append("=\"").append(consumerKeyBuilder).append("\", ");
if (tokenBuilder.length()>0) {
builder.append(OAUTH_TOKEN).append("=\"").append(tokenBuilder).append("\", ");
}
if (verifierBuilder.length()>0) {
builder.append(OAUTH_VERIFIER).append("=\"").append(verifierBuilder).append("\", ");
}
mac.init(secretKeySpec);
doFinalHMAC(builder.append(OAUTH_SIGNATURE).append("=\""), upperVerb, formalPath, mac);
builder.append("\", ");
builder.append(OAUTH_SIGNATURE_METHOD).append("=\"").append("HMAC-SHA1").append("\", ");
builder.append(OAUTH_TIMESTAMP).append("=\"").append(timeBuilder).append("\", ");
builder.append(OAUTH_NONCE).append("=\"").append(nonceBuilder).append("\", ");
builder.append(OAUTH_VERSION).append("=\"").append("1.0").append("\"");
} catch (Exception e) {
throw new RuntimeException(e);
}
return builder;
}
private void doFinalHMAC(A builder, String upperVerb, String hostAndPath, Mac mac) {
int size = Pipe.addMsgIdx(workingPipe, RawDataSchema.MSG_CHUNKEDSTREAM_1);
DataOutputBlobWriter normalizedBuilder = Pipe.openOutputStream(workingPipe);
normalizedBuilder.append(upperVerb);
normalizedBuilder.append('&');
normalizedBuilder.append(hostAndPath); //hostAndPath already URL encoded.
normalizedBuilder.append('&');
//NOTE: the params are already URL encoded.
if (!macParams.isEmpty()) {
boolean isFirst = true;
for (int i=0; i0) {
if (!isFirst) {
normalizedBuilder.append("%26");
}
normalizedBuilder.append(key);
normalizedBuilder.append("%3D");
normalizedBuilder.append(value);
isFirst = false;
}
}
}
DataOutputBlobWriter.closeLowLevelField(normalizedBuilder);
Pipe.confirmLowLevelWrite(workingPipe,size);
Pipe.publishWrites(workingPipe);
/////////////////////////////////////////
/////////////////////////////////////////
Pipe.takeMsgIdx(workingPipe);
int meta = Pipe.takeByteArrayMetaData(workingPipe);
int len = Pipe.takeByteArrayLength(workingPipe);
int mask = blobMask(workingPipe);
int pos = bytePosition(meta, workingPipe, len)&mask;
byte[] backing = byteBackingArray(meta, workingPipe);
if ((pos+len) < workingPipe.sizeOfBlobRing) {
//single block
mac.update(backing, pos, len);
} else {
//two blocks, this assumes the mac.update and doFinal can work "in place" which is true.
Pipe.copyBytesFromToRing(backing, pos, mask,
workingMacSpace, 0, Integer.MAX_VALUE,
len);
mac.update(workingMacSpace, 0, len);
}
Pipe.confirmLowLevelRead(workingPipe, size);
Pipe.releaseReadLock(workingPipe);
try {
int macLen = mac.getMacLength();
mac.doFinal(workingMacSpace, 0);
Appendables.appendBase64Encoded(builder, workingMacSpace, 0, macLen, Integer.MAX_VALUE);
} catch (ShortBufferException e) {
throw new RuntimeException(e);
} catch (IllegalStateException e) {
throw new RuntimeException(e);
}
}
//temp mutable structure for building the mac upon request.
private final byte[] workingMacSpace = new byte[1000];
private String buildFormalPath(int port, String scheme, String host, String path) {
StringBuilder requestUrlBuilder = new StringBuilder(512);
requestUrlBuilder.append(scheme.toLowerCase());
requestUrlBuilder.append("://");
requestUrlBuilder.append(host.toLowerCase());
if (includePortString(port, scheme)) {
requestUrlBuilder.append(":").append(port);
}
requestUrlBuilder.append(path);
String hostAndPath = requestUrlBuilder.toString();
//Pre-URL encoded
try {
return URLEncoder.encode(hostAndPath,"UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
/**
* The OAuth 1.0a spec says that the port should not be included in the normalized string
* when (1) it is port 80 and the scheme is HTTP or (2) it is port 443 and the scheme is HTTPS
*/
boolean includePortString(int port, String scheme) {
return !((port == 80 && "HTTP".equalsIgnoreCase(scheme)) || (port == 443 && "HTTPS".equalsIgnoreCase(scheme)));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy