I am trying to create a simple form to POST an object to a bucket in S3 directly from my browser using AWS Signature V4.
I use Java to generate the policy and signature values for the pre-signed form. For now, I just want to test that it works so I don't mind that this is a manual signature generation process.
My Java code is the following
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
public class Lala {
static String policy_document = "{ \"expiration\": \"2015-12-30T12:00:00.000Z\"," +
" \"conditions\": [" +
" {\"bucket\": \"sigv4examplebucket\"}," +
" [\"starts-with\", \"$key\", \"user/user1/\"]," +
" {\"acl\": \"public-read\"}," +
" {\"success_action_redirect\": \"http://sigv4examplebucket.s3.amazonaws.com/successful_upload.html\"}," +
" [\"starts-with\", \"$Content-Type\", \"image/\"]," +
" {\"x-amz-meta-uuid\": \"14365123651274\"}," +
" {\"x-amz-server-side-encryption\": \"AES256\"}," +
" [\"starts-with\", \"$x-amz-meta-tag\", \"\"]," +
" {\"x-amz-credential\": \"AKIAIOSFODNN7EXAMPLE/20151229/us-east-1/s3/aws4_request\"}," +
" {\"x-amz-algorithm\": \"AWS4-HMAC-SHA256\"}," +
" {\"x-amz-date\": \"20151229T000000Z\" }" +
" ]" +
"}";
static String secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY";
public static void main(String[] args) throws Exception {
// Create a policy using UTF-8 encoding.
byte[] utf8_policy = policy_document.getBytes("UTF-8");
// Convert the UTF-8-encoded policy bytes to Base64. The result is the StringToSign.
String base64_policy = new String(Base64.encodeBase64(utf8_policy));
// Create a signing key.
byte[] signing_key = getSignatureKey(secret_key , "20151229", "us-east-1", "s3");
// Use the signing key to sign the StringToSign using HMAC-SHA256 signing algorithm.
byte[] signature_bytes = HmacSHA256(base64_policy, signing_key);
String signature = Hex.encodeHexString(signature_bytes);
System.out.println(base64_policy);
System.out.println();
System.out.println(signature);
}
static byte[] getSignatureKey(String key, String dateStamp, String regionName, String serviceName) throws Exception {
byte[] kSecret = ("AWS4" + key).getBytes("UTF-8");
byte[] kDate = HmacSHA256(dateStamp, kSecret);
byte[] kRegion = HmacSHA256(regionName, kDate);
byte[] kService = HmacSHA256(serviceName, kRegion);
byte[] kSigning = HmacSHA256("aws4_request", kService);
return kSigning;
}
static byte[] HmacSHA256(String data, byte[] key) throws Exception {
String algorithm="HmacSHA256";
Mac mac = Mac.getInstance(algorithm);
mac.init(new SecretKeySpec(key, algorithm));
return mac.doFinal(data.getBytes("UTF-8"));
}
}
The policy document and access/secret keys pair are all taken from the Amazon's example here.
My code gives back the following Base64 encoded policy:
eyAiZXhwaXJhdGlvbiI6ICIyMDE1LTEyLTMwVDEyOjAwOjAwLjAwMFoiLCAgImNvbmRpdGlvbnMiOiBbICAgIHsiYnVja2V0IjogInNpZ3Y0ZXhhbXBsZWJ1Y2tldCJ9LCAgICBbInN0YXJ0cy13aXRoIiwgIiRrZXkiLCAidXNlci91c2VyMS8iXSwgICAgeyJhY2wiOiAicHVibGljLXJlYWQifSwgICAgeyJzdWNjZXNzX2FjdGlvbl9yZWRpcmVjdCI6ICJodHRwOi8vc2lndjRleGFtcGxlYnVja2V0LnMzLmFtYXpvbmF3cy5jb20vc3VjY2Vzc2Z1bF91cGxvYWQuaHRtbCJ9LCAgICBbInN0YXJ0cy13aXRoIiwgIiRDb250ZW50LVR5cGUiLCAiaW1hZ2UvIl0sICAgIHsieC1hbXotbWV0YS11dWlkIjogIjE0MzY1MTIzNjUxMjc0In0sICAgIHsieC1hbXotc2VydmVyLXNpZGUtZW5jcnlwdGlvbiI6ICJBRVMyNTYifSwgICAgWyJzdGFydHMtd2l0aCIsICIkeC1hbXotbWV0YS10YWciLCAiIl0sICAgIHsieC1hbXotY3JlZGVudGlhbCI6ICJBS0lBSU9TRk9ETk43RVhBTVBMRS8yMDE1MTIyOS91cy1lYXN0LTEvczMvYXdzNF9yZXF1ZXN0In0sICAgIHsieC1hbXotYWxnb3JpdGhtIjogIkFXUzQtSE1BQy1TSEEyNTYifSwgICAgeyJ4LWFtei1kYXRlIjogIjIwMTUxMjI5VDAwMDAwMFoiIH0gIF19
and the following signature for it
1df5972015a56d4fdef92944436b91ce1f39b5cc684dcce9f4dab74b82734e84
which are different from the ones provided by Amazon in the above link.
Am I missing something? I have a feeling that the String format in the policy_document variable might be messing things up, but no matter how I format it (i.e. new lines, escaping etc.) I can't seem to get it working.
Decode Amazon's and yours, they are not the same, mainly in formatting. Simple formatting differences will result in a different signing hash.
In your case you need to add newline characters at the end of each line.
Amazon
{ "expiration": "2015-12-30T12:00:00.000Z",
"conditions": [
{"bucket": "sigv4examplebucket"},
["starts-with", "$key", "user/user1/"],
{"acl": "public-read"},
{"success_action_redirect": "http://sigv4examplebucket.s3.amazonaws.com/successful_upload.html"},
["starts-with", "$Content-Type", "image/"],
{"x-amz-meta-uuid": "14365123651274"},
{"x-amz-server-side-encryption": "AES256"},
["starts-with", "$x-amz-meta-tag", ""],
{"x-amz-credential": "AKIAIOSFODNN7EXAMPLE/20151229/us-east-1/s3/aws4_request"},
{"x-amz-algorithm": "AWS4-HMAC-SHA256"},
{"x-amz-date": "20151229T000000Z" }
]
}
Yours:
{ "expiration": "2015-12-30T12:00:00.000Z", "conditions": [ {"bucket": "sigv4examplebucket"}, ["starts-with", "$key", "user/user1/"], {"acl": "public-read"}, {"success_action_redirect": "http://sigv4examplebucket.s3.amazonaws.com/successful_upload.html"}, ["starts-with", "$Content-Type", "image/"], {"x-amz-meta-uuid": "14365123651274"}, {"x-amz-server-side-encryption": "AES256"}, ["starts-with", "$x-amz-meta-tag", ""], {"x-amz-credential": "AKIAIOSFODNN7EXAMPLE/20151229/us-east-1/s3/aws4_request"}, {"x-amz-algorithm": "AWS4-HMAC-SHA256"}, {"x-amz-date": "20151229T000000Z" } ]}
'
Related
I have a java script and want to use it in SQL oracle but don't know how and where to add the 3rd java library in the server.
I found one folder name lib
Is it the right folder or there is another folder I can Idd? which I have the .jar file need to add
I wrote a java code to sign a file from Starkbank library and in order to be able to us the java code I have to add starkbank library java file
import com.starkbank.ellipticcurve.PrivateKey;
import com.starkbank.ellipticcurve.PublicKey;
import com.starkbank.ellipticcurve.Signature;
import com.starkbank.ellipticcurve.Ecdsa;
import com.starkbank.ellipticcurve.Ecdsa;
import com.starkbank.ellipticcurve.PublicKey;
import com.starkbank.ellipticcurve.Signature;
import com.starkbank.ellipticcurve.utils.ByteString;
import com.starkbank.ellipticcurve.utils.File;
import java.util.*;
public class NewClass3{
public static void main(String[] args){
// Read files
// String publicKeyPem = File.read("publicKey.pem");
String PrivKeyPem = File.read("ec-secp256k1-priv-key.pem");
// PublicKey publicKey = PublicKey.fromPem(publicKeyPem);
PrivateKey privateKey = PrivateKey.fromPem(PrivKeyPem);
Base64.Encoder enc = Base64.getEncoder();
Base64.Decoder dec = Base64.getDecoder();
String str = "LcHeQ9b2bhNW291aOgeXhBHKa1uHmNoKf/ATfX+AJbo=";
// Decode data
String decoded = new String(dec.decode(str));
System.out.println("decoded value is \t" + decoded);
System.out.println("original value is \t" + str);
// Generate Signature
Signature signature = Ecdsa.sign(decoded, privateKey);
// Return the signature verification status
System.out.println("signature: " + signature);
// Encode into Base64 format - This value should add in QR code
String base64 = signature.toBase64();
// print encoded String
System.out.println("Encoded String:\n" + base64);
}
}
Basically I have this
if (args.length != 1) {
System.err.println("Usage: java MyMd5 <message>");
System.exit(1);
}
try {
// get message
byte[] message = args[0].getBytes("UTF8");
// create message digest object for MD5
MessageDigest messageDigest = MessageDigest.getInstance("SHA");
// System.out.println("Provider: " +
messageDigest.getProvider().getInfo();
// create message digest
messageDigest.update(message);
byte[] md = messageDigest.digest();
// print result
System.out.println("Message Digest Algorithm: MD5");
System.out.println("Message: " + new String(message));
System.out.println("Message Digest: \"" + new String(md, "UTF8") + "\"");
String HashPassword=new String(md, "UTF8");
System.out.println(HashPassword);
System.out.println(HashPassword.equals("???8p???W?B:??N?~"));
but somehow when I try to compare it is returning false. Is it some problem with my encoding? Thanks!
Message Digest Algorithm: MD5
Message: pass123
Message Digest: "???8p???W?B:??N?~"
???8p???W?B:??N?~
false
The problem is with unnecessary encoding, with md5 we have to encode the String in the correct way instead of using UTF8 encoding
public static void main(String[] args) throws Exception {
String pass = "pass123";
byte[] message = pass.getBytes();
MessageDigest messageDigest = MessageDigest.getInstance("SHA");
messageDigest.update(message);
byte[] md = messageDigest.digest();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < md.length; i++) {
sb.append(Integer.toString((md[i] & 0xff) + 0x100, 16).substring(1));
}
System.out.println("encoded pass:" + sb.toString());
System.out.println(sb.toString().equals("aafdc23870ecbcd3d557b6423a8982134e17927e"));
}
If I understood your question correctly you want to compare newly generated hash string with existing one (stored in a data base or somewhere else). It's possible with provided above code and you will get true if you store generated value (not copy it from console) to the data base for example, and then compare it with newly generated.
But when you generate hash it contains symbols (characters), that doesn't have correct representation in any String encoding. So, nobody don't store hashes as a plain strings. Usually they are stored in hex string representation or Base64 representation.
Implementation with the use of hex strings provided below:
public static void main(String[] args) throws Exception { // args = ["Hello world"]
if (args.length != 1) {
System.err.println("Usage: java MyMd5 <message>");
System.exit(1);
}
// get message
byte[] message = args[0].getBytes("UTF8"); //You may have problems with encoding here. In Windows, for example, with symbols that are out of English alphabet range
// create message digest object for MD5
MessageDigest messageDigest = MessageDigest.getInstance("SHA");
// System.out.println("Provider: " +
messageDigest.getProvider().getInfo();
// create message digest
messageDigest.update(message);
byte[] md = messageDigest.digest();
System.out.println("Digest: " + byteArrayToHexString(md));
System.out.println("Is equals? " + byteArrayToHexString(md).equals("7b502c3a1f48c8609ae212cdfb639dee39673f5e"));
}
private static String byteArrayToHexString(byte[] array) {
StringBuilder result = new StringBuilder(array.length);
IntStream.range(0, array.length)
.forEach(i -> result.append(Integer.toString( (array[i] & 0xff) + 0x100, 16).substring(1)));
return result.toString();
}
Also, in provided example mentioned that you are using MD5, but actually it's SHA. MessageDigest messageDigest = MessageDigest.getInstance("SHA");
I need to create two simple methods for string DES encryption/decruption. The goal is to have these two methods in the following form
public static String desEcnrypt(String key, String clearMessage)
{
.....
}
public static String desDecrypt(String key, String encryptedMessage)
{
.....
}
I haven't found yet any example in this form.
Use the "not-yet-commons-ssl.jar" from http://juliusdavies.ca/commons-ssl/.
http://juliusdavies.ca/commons-ssl/pbe.html
PBE code example (DES-3):*
char[] password = {'c','h','a','n','g','e','i','t'};
byte[] data = "Hello World!".getBytes();
// Encrypt!
byte[] encrypted = OpenSSL.encrypt("des3", password, data);
System.out.println("ENCRYPTED: [" + new String(encrypted) + "]");
// Decrypt results of previous!
data = OpenSSL.decrypt("des3", password, encrypted);
System.out.println("DECRYPTED: [" + new String(data) + "]");
OUTPUT:
=======================
ENCRYPTED: [U2FsdGVkX19qplb9qVDVVEYxH8wjJDGpMS+F4/2pS2c=]
DECRYPTED: [Hello World!]
This question already has answers here:
Encoding as Base64 in Java
(19 answers)
Closed 7 years ago.
I want to encode a string into base64 and transfer it through a socket and decode it back.
But after decoding it gives different answer.
Following is my code and result is "77+9x6s="
import javax.xml.bind.DatatypeConverter;
public class f{
public static void main(String a[]){
String str = new String(DatatypeConverter.parseBase64Binary("user:123"));
String res = DatatypeConverter.printBase64Binary(str.getBytes());
System.out.println(res);
}
}
Any idea about how to implement this?
You can use following approach:
import org.apache.commons.codec.binary.Base64;
// Encode data on your side using BASE64
byte[] bytesEncoded = Base64.encodeBase64(str.getBytes());
System.out.println("encoded value is " + new String(bytesEncoded));
// Decode data on other side, by processing encoded data
byte[] valueDecoded = Base64.decodeBase64(bytesEncoded);
System.out.println("Decoded value is " + new String(valueDecoded));
Hope this answers your doubt.
Java 8 now supports BASE64 Encoding and Decoding. You can use the following classes:
java.util.Base64, java.util.Base64.Encoder and java.util.Base64.Decoder.
Example usage:
// encode with padding
String encoded = Base64.getEncoder().encodeToString(someByteArray);
// encode without padding
String encoded = Base64.getEncoder().withoutPadding().encodeToString(someByteArray);
// decode a String
byte [] barr = Base64.getDecoder().decode(encoded);
The accepted answer uses the Apache Commons package but this is how I did it using Java's native libraries
Java 11 and up
import java.util.Base64;
public class Base64Encoding {
public static void main(String[] args) {
Base64.Encoder enc = Base64.getEncoder();
Base64.Decoder dec = Base64.getDecoder();
String str = "77+9x6s=";
// encode data using BASE64
String encoded = enc.encodeToString(str.getBytes());
System.out.println("encoded value is \t" + encoded);
// Decode data
String decoded = new String(dec.decode(encoded));
System.out.println("decoded value is \t" + decoded);
System.out.println("original value is \t" + str);
}
}
Java 6 - 10
import java.io.UnsupportedEncodingException;
import javax.xml.bind.DatatypeConverter;
public class EncodeString64 {
public static void main(String[] args) throws UnsupportedEncodingException {
String str = "77+9x6s=";
// encode data using BASE64
String encoded = DatatypeConverter.printBase64Binary(str.getBytes());
System.out.println("encoded value is \t" + encoded);
// Decode data
String decoded = new String(DatatypeConverter.parseBase64Binary(encoded));
System.out.println("decoded value is \t" + decoded);
System.out.println("original value is \t" + str);
}
}
The better way would be to try/catch the encoding/decoding steps but hopefully you get the idea.
For Spring Users , Spring Security has a Base64 class in the org.springframework.security.crypto.codec package that can also be used for encoding and decoding of Base64.
Ex.
public static String base64Encode(String token) {
byte[] encodedBytes = Base64.encode(token.getBytes());
return new String(encodedBytes, Charset.forName("UTF-8"));
}
public static String base64Decode(String token) {
byte[] decodedBytes = Base64.decode(token.getBytes());
return new String(decodedBytes, Charset.forName("UTF-8"));
}
The following is a good solution -
import android.util.Base64;
String converted = Base64.encodeToString(toConvert.toString().getBytes(), Base64.DEFAULT);
String stringFromBase = new String(Base64.decode(converted, Base64.DEFAULT));
That's it. A single line encoding and decoding.
import javax.xml.bind.DatatypeConverter;
public class f{
public static void main(String a[]){
String str = new String(DatatypeConverter.printBase64Binary(new String("user:123").getBytes()));
String res = DatatypeConverter.parseBase64Binary(str);
System.out.println(res);
}
}
I'm implementing a simple password store using Blowfish. All was fine until I tried out a few different password/key combinations and came across numerous instances where the decrypted values were still garbage.
Below is a standalone class that demonstrates the issue. I get the following output:
'Aaaaaaa7' encrypted: 'r?—èLèdÓ,·Ã¸ÍÒ'*
'Aaaaaaa7' decrypted: 'ñü=€¼(T'*
Any idea what I need to do to guarantee it always decrypts correctly.
(Using jce.jar in JDK 1.6.0_26)
Thanks,
David
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
public class BlowfishTwoWayHashImpl {
static {
test();
}
public static void test() {
String key = "wibble";
String passwordToEnrypt = "Aaaaaaa7";
String enc = BlowfishTwoWayHashImpl.encryptBlowfish(passwordToEnrypt, key);
System.out.println("'" + passwordToEnrypt + "' encrypted: '" + enc + "'");
String dec = BlowfishTwoWayHashImpl.decryptBlowfish(enc, key);
System.out.println("'" + passwordToEnrypt + "' decrypted: '" + dec + "'");
}
private static final String CIPHER_NAME = "Blowfish";
public static String encryptBlowfish(String toEncrypt, String key) {
return processString(toEncrypt, key, Cipher.ENCRYPT_MODE);
}
public static String decryptBlowfish(String toDecrypt, String key) {
return processString(toDecrypt, key, Cipher.DECRYPT_MODE);
}
private static String processString(String toEncrypt, String key, int encryptDecryptMode) {
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), CIPHER_NAME);
Cipher cipher;
try {
cipher = Cipher.getInstance(CIPHER_NAME);
cipher.init(encryptDecryptMode, secretKeySpec);
return new String(cipher.doFinal(toEncrypt.getBytes()));
}
catch (Exception e) {
throw new RuntimeException(e.toString());
}
}
}
Don't do this:
return new String(cipher.doFinal(toEncrypt.getBytes()));
You're using the platform default encoding all over the place in your code. Don't do this. It will lose data.
When you're converting genuine text to bytes (e.g. on encryption) use a specific charset - UTF-8 is a good choice. Use the same charset to decode from "encoded text" to String.
When you're converting arbitrary binary data to text, use base64 encoding, e.g. via this public domain Base64 encoding library.
Basically, when you create a new string with the String(byte[]) or String(byte[], String) constructors, you're saying, "This is genuine text data - please just decode it as a string." When the data is actually the result of encryption, it's not text data... it's an arbitrary bunch of bytes.