I'm trying to make a post request to one of our clients with certificate as part of their security.
I tried testing it in postman, just like the usual attached the certificate on postman settings and then able to make a post request to their api.
Now my problem is i'm encountering this error when i'm doing the request on our platform built in java
java.base/sun.security.provider.JavaKeyStore.engineLoad(JavaKeyStore.java:664)
java.base/sun.security.util.KeyStoreDelegator.engineLoad(KeyStoreDelegator.java:222)
java.base/java.security.KeyStore.load(KeyStore.java:1479)
On the test environment they don't require the password hence null.
try (InputStream inputStream = new ByteArrayInputStream(p12Cert))
{
KeyStore keyStore = KeyStore.getInstance("jks");
keyStore.load(inputStream, null);
I traced the engineLoad method from javakeystore.
private static final int MAGIC = 0xfeedfeed;
private static final int VERSION_1 = 0x01;
private static final int VERSION_2 = 0x02;
if (xMagic!=MAGIC ||
(xVersion!=VERSION_1 && xVersion!=VERSION_2)) {
throw new IOException("Invalid keystore format");
}
Can someone elaborate the above?
Able to fix the problem above, first is the p12 cert i am trying to encode in base64 is in utf-8 encoding which is wrong and should be converted from HEXADECIMAL to base64.
Related
I'm using passport-oauth2 (passportjs.org and https://github.com/jaredhanson/passport-oauth2/blob/master/lib/strategy.js) for OAuth2+PKCE integration in a nodejs application.
The backend it's authenticating against is written in Java.
The problem is that I can't seem to decode->hash the code_verifier to correctly match the code_challenge that comes from passport-oauth2.
I know that the Base64 encoding that comes from passport has been generated to be URL safe (no padding, no wrapping, replacements for + or /), so I'm using a Url Decoder:
Base64.getUrlDecoder().decode(...)
Then I'm using commons DigestUtils to generate a SHA256 of the decoded verifier and comparing it with the challenge. So the whole thing looks something like this:
java.util.Base64.Decoder decoder = java.util.Base64.getUrlDecoder();
String codeChallenge = // get the code challenge from my cache
byte[] decodedCodeChallenge = decoder.decode(codeChallenge);
byte[] decodedCodeVerifier = decoder.decode(codeVerifier);
if (!Arrays.equals(sha256(decodedCodeVerifier), decodedCodeChallenge)) {
return Response.status(400).entity(ERROR_INVALID_CHALLENGE_VERIFIER).build();
}
Example:
This code verifier: 5CFCAiZC0g0OA-jmBmmjTBZiyPCQsnq_2q5k9fD-aAY
should match this code challenge: Fw7s3XHRVb2m1nT7s646UrYiYLMJ54as0ZIU_injyqw once both have been Base64-url-decoded and the verifier has been SHA256 hashed, but it doesn't.
What am I doing wrong?
Just 5 minutes later I figured it out.
In passport-oauth2, the code verifier is Base64-url-encoded(random bytes):
verifier = base64url(crypto.pseudoRandomBytes(32))
See: https://github.com/jaredhanson/passport-oauth2/blob/master/lib/strategy.js#L236
The challenge is then Base64-url-encoded(sha256(verifier)), which expands to Base64-url-encoded(sha256(Base64-url-encoded(random bytes))):
challenge = base64url(crypto.createHash('sha256').update(verifier).digest());
See: https://github.com/jaredhanson/passport-oauth2/blob/master/lib/strategy.js#L242
So to do the verification, I don't need to decode anything. It was sha256-d in it's encoded state.
This worked in the end:
java.util.Base64.Encoder encoder = java.util.Base64.getUrlEncoder();
String codeChallenge = // get code challenge from my cache;
String encodedVerifier = new String(encoder.encode(sha256(codeVerifier))).split("=")[0]; // Remember to remove padding
if (!encodedVerifier.equals(codeChallenge)) {
return Response.status(400).entity(ERROR_INVALID_CHALLENGE_VERIFIER).build();
}
I'm using wss4j in order to sign a message request.
But when signing the message with this code:
Document d = signer.build(unsignedDoc, crypto, header)
I always get the error
org.apache.ws.security.WSSecurityException: General security error (no certificates for user ... were found for signature)
Searching for the cause I found out, that en empty key store seems to be the reason for this.
This is how I create a crypto instance:
private void createCrypto(){
// reading preferences (keystone-filename, password, etc.) from properties file
Properties prop = new Properties();
FileInputStream fis = new FileInputStream("myprops.properties");
props.load(fis);
// create a crypto instance
Crypto crypto = CryptoFactory.getInstance(props);
// This line always prints "Size:0"
System.out.println("Size:" + crypto.getKeyStore.getSize())
...
But if I load the Keystore the following way, it shows size=1:
private void loadKeystore(){
KeyStore keystore;
try{
// reading and loading keystone file
InputStream is = new FileInputStream("myKeyStore.jks");
keystore.load(is, "password".toCharArray());
// Prints "Size:1"
System.out.println("Size:"+keystore.size());
...
So I wonder, what is wrong with the first example. Why is the keystore empty?
I already checked the properties: keystore and password are set correctly!
But if I remove the keystore-property from the properties-file, the error message is the same.
Can anyone help me with this?
I'm working on this project in which I'm using a Google-App-Engine backend connected to an Android app via Google-Cloud-Endpoints. For Google-Cloud-Datastore access I'm using Objectify and everything works fine.
Now I decided to add the functionality to upload images to Google-Cloud-Storage but I couldn't find a clear explanation on how to do this using the Google-Cloud-Endpoints setup.
I found the following explanation how to use Google-Cloud-Storage with Google-App-Engine:
https://cloud.google.com/appengine/docs/java/googlecloudstorageclient/app-engine-cloud-storage-sample
but instead of adding it to the Endpoints Api the article writes an additional servlet.
Furthermore I found this example of upload/download for Android:
github.com /thorrism/GoogleCloudExample
Sadly this is using the Google Cloud Storage API for direct access to the Google-Cloud-Storage and you need to add a P12-file to the asset folder, which seems unsecure.
My Google-App-Engine code looks like that:
#Api(
name = "example",
version = "v1",
scopes = { Constants.EMAIL_SCOPE },
clientIds = { Constants.WEB_CLIENT_ID, Constants.ANDROID_CLIENT_ID, Constants.API_EXPLORER_CLIENT_ID },
audiences = {Constants.ANDROID_AUDIENCE},
description = "API for the Example Backend application."
)
public class ExampleApi{
#ApiMethod(name = "doSomething", path = "dosomething", httpMethod = HttpMethod.POST)
public String doSomething(#Named("text") String text){
TestEntity test = new TestEntity(text);
ofy().save().entity(test).now();
return test;
}
After I uploaded it I generated the Endpoints Client Library and imported it into my android project.
Then I'm calling Endpoints from Android like explained here:
https://cloud.google.com/appengine/docs/java/endpoints/calling-from-android#creating_the_service_object
public static com.appspot.******.example.Example buildServiceHandler(Context context, String email) {
GoogleAccountCredential credential = GoogleAccountCredential.usingAudience(
context, AppConstants.AUDIENCE);
credential.setSelectedAccountName(email);
com.appspot.******.example.Example.Builder builder = new com.appspot.******.example.Example.Builder(
AppConstants.HTTP_TRANSPORT,
AppConstants.JSON_FACTORY, null);
builder.setApplicationName("example-server");
return builder.build();
}
sApiServiceHandler = buildServiceHandlerWithAuth(context,email);
And each Api-Method I call like this:
com.appspot.******.example.Example.DoSomething doSomething = sApiServiceHandler.doSomething(someString);
doSomething.execute();
All of this works fine, but only for storing/receiving Datastore Entities. How would I go about uploading/downloading files to Google Cloud Storage using the Google Cloud Endpoints setup?
Is it somehow possible to send a POST with my image data via Endpoints to the UploadServlet using the already build ServiceHandler ?
Is it possible to call a servlet from an Endpoints Method? How am I supposed to send the Post to the Servlet and how would I go about the authentication?
Any help or advice would be greatly appreciated!
There are different ways to do this, but the most recommended way is to use Signed URLs, so that your Android app can upload the file securely to Google Cloud Storage directly, without going through your Endpoints backend. The basic process is:
1) Create an Endpoints method that creates a new signed URL and returns it to the Android client. Signing the URL on the server still requires a P12 key but is stored on App Engine, not on the client, so is secure. Try to use a short expiration for the URL, for example no more than 5 minutes.
2) Have the Android client upload the file directly to the signed URL, as you would doing a normal HTTP PUT to the Cloud Storage XML API to upload a file (resumable uploads with the JSON API are also supported, but not covered here).
Your Endpoints method might look like this:
#ApiMethod(name = "getUploadUrl", path = "getuploadurl", httpMethod = HttpMethod.GET)
public MyApiResponse getUploadUrl(#Named("fileName") String fileName
#Named("contentType" String contentType)
{
String stringToSign
= "PUT\n" + contentType
+ "\n" + EXPIRATION_TIMESTAMP_EPOCH_SECONDS + "\n"
+ YOUR_GCS_BUCKET + "/" + fileName;
// Load P12 key
FileInputStream fileInputStream = new FileInputStream(PATH_TO_P12_KEY);
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(fileInputStream, password);
PrivateKey key = keyStore.getKey(privatekey", YOUR_P12_KEY_PASSWORD);
// Get signature
Signature signer = Signature.getInstance("SHA256withRSA");
signer.initSign(key);
signer.update(stringToSign.getBytes("UTF-8"));
byte[] rawSignature = signer.sign();
String signature = new String(Base64.encodeBase64(rawSignature, false), "UTF-8");
// Construct signed url
String url
= "http://storage.googleapis.com/" + YOUR_GCS_BUCKET + fileName
+ "?GoogleAccessId=" + P12_KEY_SERVICE_ACCOUNT_CLIENT_ID
+ "&Expires=" + EXPIRATION_TIMESTAMP_EPOCH_SECONDS
+ "&Signature=" + URLEncoder.encode(signature, "UTF-8");
// Endpoints doesn't let you return 'String' directly
MyApiResponse response = new MyApiResponse();
response.setString(url);
return response;
}
On the Android side, you might use the method like this:
// Get the upload URL from the API
getUploadUrl = sApiServiceHandler.getUploadUrl(fileName, contentType);
MyApiResponse response = getUploadUrl.execute();
String uploadUrl = response.getString();
// Open connection to GCS
URL url = new URL(uploadUrl);
HttpURLConnection httpConnection = (HttpURLConnection) url.openConnection();
httpConnection.setDoOutput(true);
httpConnection.setRequestMethod("PUT");
httpConnection.setRequestProperty("Content-Type", contentType);
// Write file data
OutputStreamWriter out = new OutputStreamWriter(httpConnection.getOutputStream());
out.write(fileData);
out.flush();
// Get response, check status code etc.
InputStreamReader in = new InputStreamReader(httpConnection.getInputStream());
// ...
(Disclaimer: I'm just typing code freely into a text editor but not actually testing it, but it should be enough to give you a general idea.)
How could I express this java code in JRuby:
// Convert the store to a certificate chain
CertStore store = response.getCertStore();
Collection<? extends Certificate> certs = store
.getCertificates(null);
Certificate[] chain = new Certificate[certs.size()];
int i = 0;
for (Certificate certificate : certs) {
chain[i++] = certificate;
}
I have the "store" within JRuby and its recognised as a collection.
e.g.
puts store.type
#Collection
OK so the problem here is that I was passing in a regular expression and not a CertSelector object.
This code now works as expected.
store.get_certificates(nil)
I recently got the book "Pro Paypal E-Commerce" by Damon Williams. Its a 2007 copy, so its to be expected that some things, like the code, would change over time.
I'm trying to get this code below to work. I downloaded the paypal_base.jar file and also the paypal_wpstoolkit.jar and put them into my lib folder under jakarta-tomcat (where all my other jars are). I'm having trouble compiling the code.
This code example comes from the book and also http://en.csharp-online.net/Encrypted_Website_Payments%E2%80%94Using_the_PayPal_Java_SDK
I modified it slightly.
import com.paypal.sdk.profiles.EWPProfile;
import com.paypal.sdk.profiles.ProfileFactory;
import com.paypal.wpstoolkit.services.EWPServices;
import com.paypal.sdk.exceptions.PayPalException;
public class PaypalTest {
// path to your PKCS12 file
public static final String PKCS12 = "./Certs/my_pkcs12.p12";
// path to PayPal's public certificate
public static final String PAYPAL_CERT = "./Certs/paypal_cert_pem.txt";
// use https://www.sandbox.paypal.com if testing
//public static final String URL = "https://www.paypal.com";
public static final String URL = "https://sandbox.paypal.com";
public static void main (String args[]) {
// Check to see if the user provided a password
if (args.length != 1) {
System.out.println("You must provide a password.");
System.exit(0);
}
// password used to encrypt your PKCS12 files
// obtained from the command line
String USER_PASSWORD = args[0];
// First we will create the EWPProfile object
try {
com.paypal.sdk.profiles.EWPProfile ewpProfile = ProfileFactory.createEWPProfile();
ewpProfile.setCertificateFile(PKCS12);
ewpProfile.setPayPalCertificateFile(PAYPAL_CERT);
ewpProfile.setPrivateKeyPassword(USER_PASSWORD);
ewpProfile.setUrl(URL);
String buttonParameters = "cmd=_xclick\nbusiness=buyer#hotmail.com\nitem_name=vase\nitemprice=25.00";
// Next we will create the EWPServices object
// and tell it which EWPProfile object to use
EWPServices ewpServices = new EWPServices();
ewpServices.setEWPProfile(ewpProfile);
// Finally we are ready to call the method to perform the button encryption
String encryptedButton = ewpServices.encryptButton(buttonParameters.getBytes());
System.out.println(encryptedButton);
} catch (PayPalException ppe) {
System.out.println("An exception occurred when creating the button.");
ppe.printStackTrace();
}
}
}//class
The errors I'm getting during compilation are as follows -
java:51: cannot find symbol
symbol: method setEWPProfile(com.paypal.sdk.profiles.EWPProfile)
location: class com.paypal.wpstoolkit.services.EWPServices
ewpServices.setEWPProfile(ewpProfile);
java:55: encryptButton(byte[],java.lang.String,java.lang.String.,java.lang.String.,java.lang.String) in com.paypal.wpstoolkit.services.EWPServices cannot be applied to (byte[])
ewpServices.encryptButton(buttonParameters.getBytes());
The paypal_base jar only has NVPCallerServices.class in it, and not EWPServices. EWPServices is in the wpstoolkit jar.
How do I fix my errors? I'm having trouble finding documentation on the paypal classes.
The updated Java SDK + API documenatation can be found here:
https://cms.paypal.com/cms_content/US/en_US/files/developer/PP_Java_NVP_SDK.zip
Extract that .zip and open docs/index.html
That is where you can find all of the API documentation. It looks like you are trying to make calls to methods that no longer exist. Have a look through the new classes and see what will work for you.
Looks like with the newer API's Paypal want you to have all button code generated from their web service, as they seem to have removed the EWPService class from the SDK. But then I noticed they still provide the client side utility with with you can manually generate the code here. After a little tweaking, I got the code in there to do what I needed (encrypt an upload cart button locally).
Assuming you use Java 5+, just make sure you this and this in your classpath. Now the code isn't perfect, as it contains a bunch of deprecated methods, but for such a trivial task as encrypting the button code, it works just fine.
public String getButtonEncryptionValue(String _data,
String _privateKeyPath, String _certPath, String _payPalCertPath,
String _keyPass) throws IOException, CertificateException,
KeyStoreException, UnrecoverableKeyException,
InvalidAlgorithmParameterException, NoSuchAlgorithmException,
NoSuchProviderException, CertStoreException, CMSException {
_data = _data.replace(',', '\n');
CertificateFactory cf = CertificateFactory.getInstance("X509", "BC");
// Read the Private Key
KeyStore ks = KeyStore.getInstance("PKCS12", "BC");
ks.load(new FileInputStream(_privateKeyPath), _keyPass.toCharArray());
String keyAlias = null;
Enumeration<String> aliases = ks.aliases();
while (aliases.hasMoreElements()) {
keyAlias = aliases.nextElement();
}
PrivateKey privateKey = (PrivateKey) ks.getKey(keyAlias,
_keyPass.toCharArray());
// Read the Certificate
X509Certificate certificate = (X509Certificate) cf
.generateCertificate(new FileInputStream(_certPath));
// Read the PayPal Cert
X509Certificate payPalCert = (X509Certificate) cf
.generateCertificate(new FileInputStream(_payPalCertPath));
// Create the Data
byte[] data = _data.getBytes();
// Sign the Data with my signing only key pair
CMSSignedDataGenerator signedGenerator = new CMSSignedDataGenerator();
signedGenerator.addSigner(privateKey, certificate,
CMSSignedDataGenerator.DIGEST_SHA1);
ArrayList<X509Certificate> certList = new ArrayList<X509Certificate>();
certList.add(certificate);
CertStore certStore = CertStore.getInstance("Collection",
new CollectionCertStoreParameters(certList));
signedGenerator.addCertificatesAndCRLs(certStore);
CMSProcessableByteArray cmsByteArray = new CMSProcessableByteArray(data);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
cmsByteArray.write(baos);
System.out.println("CMSProcessableByteArray contains ["
+ baos.toString() + "]");
CMSSignedData signedData = signedGenerator.generate(cmsByteArray, true,
"BC");
byte[] signed = signedData.getEncoded();
CMSEnvelopedDataGenerator envGenerator = new CMSEnvelopedDataGenerator();
envGenerator.addKeyTransRecipient(payPalCert);
CMSEnvelopedData envData = envGenerator.generate(
new CMSProcessableByteArray(signed),
CMSEnvelopedDataGenerator.DES_EDE3_CBC, "BC");
byte[] pkcs7Bytes = envData.getEncoded();
return new String(DERtoPEM(pkcs7Bytes, "PKCS7"));
}
public static byte[] DERtoPEM(byte[] bytes, String headfoot) {
ByteArrayOutputStream pemStream = new ByteArrayOutputStream();
PrintWriter writer = new PrintWriter(pemStream);
byte[] stringBytes = Base64.encode(bytes);
System.out.println("Converting " + stringBytes.length + " bytes");
String encoded = new String(stringBytes);
if (headfoot != null) {
writer.print("-----BEGIN " + headfoot + "-----\n");
}
// write 64 chars per line till done
int i = 0;
while ((i + 1) * 64 < encoded.length()) {
writer.print(encoded.substring(i * 64, (i + 1) * 64));
writer.print("\n");
i++;
}
if (encoded.length() % 64 != 0) {
writer.print(encoded.substring(i * 64)); // write remainder
writer.print("\n");
}
if (headfoot != null) {
writer.print("-----END " + headfoot + "-----\n");
}
writer.flush();
return pemStream.toByteArray();
}
An easier way to do this is not to encrypt, but use an unencrypted button and then a hash trick to detect tampering. I explain this here with PHP, but you can translate to Java.
How do I make a PayPal encrypted buy now button with custom fields?