isInsideSecureHardware() return false for Android <= 7.1.1. How to force the key to be stored inside hardware? - android-keystore

I'm working on using KeyStore to store my app secrets securely in Android. However, I've found that for devices with Android 7.1.1 and below, the stored key using KeyStore is not 'hardware-backed?'i.e. whenever I called isInsideSecureHardware() method to the KeyInfo, it will always return me 'false'.
As far as I tested, this happen even if the device has the 'Storage type = Hardware-backed' in Settings->Security->Credential Storage->Storage type. But this is not the case for Android 7.1.2 and above which always return me 'true' for isInsideSecureHardware() as long as the Credential Storage type is hardware-backed; never tried with other types.
Is there anyway to force the key to be stored inside secure hardware for Android 7.1.1 down to 4.3 (per my understanding this is where HSM in KeyStore is introduced; please correct me if I'm wrong)?
Can anyone clarify what happen if the stored key is not inside the secure hardware; where is the key stored? how secure it is? To make thing easy, what are the differences between isInsideSecureHardware == true vs isInsideSecureHardware == false?
Here is snippet of my code to inject the key and check the KeyInfo (yes I'm asking down to Android 4.3...please ignore the version check in the code):
private void injectKey(Context context, String keyName){
KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
Calendar start = Calendar.getInstance();
Calendar end = Calendar.getInstance();
end.add(Calendar.YEAR, 1);
keyGenerator.init(new KeyGenParameterSpec.Builder(keyName, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setRandomizedEncryptionRequired(true)
.setKeyValidityStart(start.getTime())
.setKeyValidityEnd(end.getTime())
//.setUserAuthenticationRequired(true) //need PIN to get key
//.setUserAuthenticationValidityDurationSeconds(86400) //1-day time can use key from entering PIN
//.setUnlockedDeviceRequired(true) API level 28
//.setIsStrongBoxBacked(true) API level 28
.build()
);
}
SecretKey secretKey = keyGenerator.generateKey();
//check key info
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
SecretKeyFactory factory = SecretKeyFactory.getInstance(secretKey.getAlgorithm(), "AndroidKeyStore");
KeyInfo keyInfo;
try {
keyInfo = (KeyInfo) factory.getKeySpec(secretKey, KeyInfo.class);
boolean insideHW = keyInfo.isInsideSecureHardware();
boolean authReq = keyInfo.isUserAuthenticationRequired();
boolean authHW = keyInfo.isUserAuthenticationRequirementEnforcedBySecureHardware();
String[] blockModes = keyInfo.getBlockModes();
String[] digests = keyInfo.getDigests();
String[] encryptionPaddings = keyInfo.getEncryptionPaddings();
int keySize = keyInfo.getKeySize();
Date keyValidityStart = keyInfo.getKeyValidityStart();
Date keyEndValid = keyInfo.getKeyValidityForConsumptionEnd();
String keyAlias = keyInfo.getKeystoreAlias();
int keyPurpose = keyInfo.getPurposes();
String [] signaturePaddings = keyInfo.getSignaturePaddings();
int authType = keyInfo.getUserAuthenticationValidityDurationSeconds();
MainActivity.showMessage(context, "Key Info",
"inside HW = " + insideHW + "\n" +
"auth Req = " + authReq + "\n" +
"auth HW = " + authHW + "\n" +
"keySize = " + keySize + "\n" +
"keyValidityStart = " + keyValidityStart + "\n" +
"keyEndValid = " + keyEndValid + "\n" +
"keyAlias = " + keyAlias + "\n" +
"keyPurpose = " + keyPurpose + "\n" +
"authType = " + authType + "\n");
String checkKeyInfo = "";
} catch (InvalidKeySpecException e) {
String checkKeyInfo = "";
}
}
}

The versions don't quite align with your findings, though the Android hardware keystore only supported storing of AES keys from API 23 onwards - you might have more success using RSA keys, as these were supported from API 18 onwards.
Link to relevant docs on developer.android.com
This is why examples exist on the internet of "How to encrypt using the hardware keystore" recommend creating an RSA keypair in the keystore, using SecureRandom or similar to create your 256bit AES key, and then using the private RSA key to encrypt the AES key, and store the result in SharedPreferences. This works on API 18+ without changes.
So try with RSA keys and you'll have more luck, but still not pre API 18.

Related

can't get ADLS Gen2 REST continuation token to work

I'm trying to retrieve list of files and folders form ADLS Gen2. I can get the first 5000 items, but when I use continuation to get the rest (about 17,000 items or so), I get Error 403 (Forbidden). According to documentation, I add the continuation token to URI and to Canonicalized Resource in signature string. However, I cannot get it to work.
I've read the documentation on ADLS Gen2 REST calls and whatever I could find on this, and I can't figure out the issue.
var date = System.DateTime.UtcNow.ToString("R");
string toSign = DefaultSignatureString(date);
toSign +=
$"/{storageaccountname}/{filesystemname}" + "\n" +
$"directory:{dir}" +"\n" +
"recursive:true" + "\n" +
"resource:filesystem";
var signedSignature = SignData(accessKey, toSign);
var uri = $"https://{storageaccountname}.dfs.core.windows.net/{filesystemname}?directory={dir}&recursive=true&resource=filesystem";
HttpWebResponse response = GetWebResponse(storageaccountname, date, signedSignature, uri);
var token_continuation = response.Headers["x-ms-continuation"];
//I get the token_continuation and repeat the previous steps, adding the continuation part:
while (token_continuation != null)
{
date = System.DateTime.UtcNow.ToString("R");
toSign = DefaultSignatureString(date);
toSign +=
$"/{storageaccountname}/{filesystemname}" + "\n" +
$"continuation:{token_continuation}" + "\n" +
$"directory:{dir}" + "\n" +
"recursive:true" + "\n" +
"resource:filesystem";
signedSignature = SignData(accessKey, toSign);
uri = $"https://{storageaccountname}.dfs.core.windows.net/{filesystemname}?directory={dir}&recursive=true&resource=filesystem&continuation={token_continuation}";
response = GetWebResponse(storageaccountname, date, signedSignature, uri);
token_continuation = response.Headers["x-ms-continuation"];
}
//this is my GetWebResponse method
private static HttpWebResponse GetWebResponse(string storageaccountname, string date, string signedSignature, string uri, string continuation = null)
{
WebRequest request = WebRequest.Create(uri);
if (continuation != null)
{
request.Headers.Add($"x-ms-continuation:{continuation}");
}
request.Headers.Add($"x-ms-date:{date}");
request.Headers.Add($"x-ms-version:2018-11-09");
request.Headers.Add($"Authorization:SharedKey {storageaccountname}:{signedSignature}");
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
return response;
}
As I said, I get the first response OK; when I get into while loop, I get the error. What am I doing wrong?
In most of the cases (if not all) continuation token returns with == at the end, which messes up the URI.
For URIs, escape them by replacing == with %3D%3D.
For canonicalized resources, leave the string as is.

Bukkit - Displaying null when getting a string from the config file

So I've been working on a custom feature for my minecraft server, one of the things that I need to do is get an integer from the config file that is specific to each player to display how many Packages(keys) they have (Virtual items)
The issue that I am having is that in the GUI it is displaying 'null' instead of how many they have... Could anyone help me please?
Item in the gui
Code for creating the player's instance in the config (Using a custom file class that was provided to me by a friend of mine.)
#EventHandler
public void playerJoin(PlayerJoinEvent event) {
Main main = Main.getPlugin(Main.class);
Player player = event.getPlayer();
UUID uuid = player.getUniqueId();
if (!main.getDataFolder().exists())
main.getDataFolder().mkdirs();
File file = new File(main.getDataFolder(), "players.yml");
FileConfiguration config = YamlConfiguration.loadConfiguration(file);
if (!config.contains("Users." + uuid + ".Username")) {
try {
System.out.println("Creating entry for " + player + " (" + uuid + ")");
config.set("Users." + uuid + ".Username", player);
config.set("Users." + uuid + ".Packages.Common", 0);
config.set("Users." + uuid + ".Packages.Rare", 0);
config.set("Users." + uuid + ".Packages.Epic", 0);
config.set("Users." + uuid + ".Packages.Legendary", 0);
config.set("Users." + uuid + ".Packages.Exotic", 0);
config.save(file);
System.out.println("Successfully created the entry for " + " (" + uuid + ")");
} catch (Exception e) {
}
}
}
Code for the creation of the item in the gui:
public static String inventoryname = Utils.chat("&fWhite Backpack");
public static Inventory WhiteBackpack(Player player) {
UUID uuid = player.getUniqueId();
Inventory inv = Bukkit.createInventory(null, 27, (inventoryname));
ItemStack common = new ItemStack(Material.INK_SACK);
common.setDurability((byte) 8);
ItemMeta commonMeta = common.getItemMeta();
commonMeta.setDisplayName(Utils.chat("&fCommon Packages &8» &f&l" + Main.pl.getFileControl().getConfig().getString("Users." + uuid + ".Packages.Common")));
common.setItemMeta(commonMeta);
inv.setItem(10, common);
return inv;
}
There are a couple things wrong with your code.
First, you never account for what happens if the config you are loading does not exist. When you do main.getDataFolder().mkdirs(), you account for if the folder is missing, but not the file.
Second, you are doing the following operation:
config.set("Users." + uuid + ".Username", player);
This is incorrect because the player variable is of the type Player, not of the type String. To fix this, you need to instead do the following:
config.set("Users." + uuid + ".Username", player.getName());
Third, you are attempting to write to a file that might not exist. When you initialize you file, you need to also make sure it exists, and if it does not, you need to create it. Right now you have the following:
File file = new File(main.getDataFolder(), "players.yml");
It must be changed to this block of code:
File file = new File(main.getDataFolder(), "players.yml");
if (!file.exists()) {
try {
file.createNewFile();
} catch (IOException ex) {
ex.printStackTrace();
}
}
You could just have it be created when you attempt to save the file later on, but that is not ideal since it's safer to let Bukkit write to a file that already exists.
Fourth, and I'm not necessarily sure that this is a problem per se, but you are trying to access an Integer value from the config file as if it were a String. Try to replace the following:
commonMeta.setDisplayName(Utils.chat("&fCommon Packages &8» &f&l"
+ Main.pl.getFileControl().getConfig().getString("Users." + uuid + ".Packages.Common")));
with this instead:
commonMeta.setDisplayName(Utils.chat("&fCommon Packages &8» &f&l"
+ Main.pl.getFileControl().getConfig().getInt("Users." + uuid + ".Packages.Common")));
Hope this gets you moving in the right direction!

Calculating an oauth signature

I am trying something a little specific, namely trying to call a REST API. I have been following these instructions.
I have been very careful to ensure that I am creating the "Signature base string" correctly. They define it to be created like this:
(HTTP Method)&(Request URL)&(Normalized Parameters)
You can double check if need be in my code, but I am very sure that it is fine.
The problem that I am having is creating what they call the "oauth signature" and mine isn't matching theirs. They it should be created like this:
Use the HMAC-SHA1 signature algorithm as defined by the [RFC2104] to sign the request where text is the Signature Base String and key is the concatenated values of the Consumer Secret and Access Secret separated by an '&' character (show '&' even if Access Secret is empty as some methods do not require an Access Token).
The calculated digest octet string, first base64-encoded per [RFC2045], then escaped using the [RFC3986] percent-encoding (%xx) mechanism is the oauth_signature.
I express this in my code like so:
var oauthSignature = CryptoJS.HmacSHA1(signatureBaseString, sharedSecret+"&");
var oauthSignature64 = encodeURIComponent(CryptoJS.enc.Base64.stringify(oauthSignature));
console.log("hash in 64: " + oauthSignature64);
I am using Google's CryptoJS library. I take the signature base string as the text, I then take my consumer secret as the key concatenated with "&", I have no Access key and it isn't required but that is OK. I then base 64 encode the result of that hash, after which I URI encode it, please could some guys sanity check my understanding of that and my usage/expressing of it in code using this library, I think this is where my problem is.
Here is my full code:
var fatSecretRestUrl = "http://platform.fatsecret.com/rest/server.api";
var d = new Date();
var sharedSecret = "xxxx";
var consumerKey = "xxxx";
//this is yet another test tyring to make this thing work
var baseUrl = "http://platform.fatsecret.com/rest/server.api?";
var parameters = "method=food.search&oauth_consumer_key="+consumerKey+"&oauth_nonce=123&oauth_signature_method=HMAC-SHA1&oauth_timestamp="+getTimeInSeconds()+"&oauth_version=1.0&search_expression=banana";
var signatureBaseString = "POST&" + encodeURIComponent(baseUrl) + "&" + encodeURIComponent(parameters);
console.log("signature base string: " + signatureBaseString);
var oauthSignature = CryptoJS.HmacSHA1(signatureBaseString, sharedSecret+"&");
var oauthSignature64 = encodeURIComponent(CryptoJS.enc.Base64.stringify(oauthSignature));
console.log("hash in 64: " + oauthSignature64);
var testUrl = baseUrl+"method=food.search&oauth_consumer_key=xxxx&oauth_nonce=123&oauth_signature="+oauthSignature64+"&oauth_signature_method=HMAC-SHA1&oauth_timestamp="+getTimeInSeconds()+"&oauth_version=1.0&search_expression=banana";
console.log("final URL: " + testUrl);
var request = $http({
method :"POST",
url: testUrl
});
I have taken care to ensure that the parameters that I am posting are in lexicographical order and I am very sure that it is correct.
The response that I am getting back is:
Invalid signature: oauth_signature 'RWeFME4w2Obzn2x50xsXujAs1yI='
So clearly either
I haven't understood the instructions provided in the API
Or I have understood them but I haven't expressed them in that way in my code
Or both of the above
Or I have made some subtle mistake somewhere that I can't see
I would really appreciate a sanity check, this has taken a while.
well...I did it, but not the way that I thought I would end up doing it, I spent hours trying it out with angular then JQuery, then finally I tried Node JS and it worked, here are two working examples, one with food.get and another with foods.search
food.get example
var rest = require('restler'),
crypto = require('crypto'),
apiKey = 'xxxx',
fatSecretRestUrl = 'http://platform.fatsecret.com/rest/server.api',
sharedSecret = 'xxxx',
date = new Date;
// keys in lexicographical order
var reqObj = {
food_id: '2395843', // test query
method: 'food.get',
oauth_consumer_key: apiKey,
oauth_nonce: Math.random().toString(36).replace(/[^a-z]/, '').substr(2),
oauth_signature_method: 'HMAC-SHA1',
oauth_timestamp: Math.floor(date.getTime() / 1000),
oauth_version: '1.0'
};
// make the string...got tired of writing that long thing
var paramsStr = '';
for (var i in reqObj) {
paramsStr += "&" + i + "=" + reqObj[i];
}
// had an extra '&' at the front
paramsStr = paramsStr.substr(1);
var sigBaseStr = "GET&"
+ encodeURIComponent(fatSecretRestUrl)
+ "&"
+ encodeURIComponent(paramsStr);
// no access token but we still have to append '&' according to the instructions
sharedSecret += "&";
var hashedBaseStr = crypto.createHmac('sha1', sharedSecret).update(sigBaseStr).digest('base64');
// Add oauth_signature to the request object
reqObj.oauth_signature = hashedBaseStr;
rest.get(fatSecretRestUrl, {
data: reqObj,
}).on('complete', function(data, response) {
console.log(response);
console.log("DATA: " + data + "\n");
});
foods.search example
var rest = require('restler'),
crypto = require('crypto'),
apiKey = 'xxxx',
fatSecretRestUrl = 'http://platform.fatsecret.com/rest/server.api',
sharedSecret = 'xxxx',
date = new Date;
// keys in lexicographical order
var reqObj = {
method: 'foods.search',
oauth_consumer_key: apiKey,
oauth_nonce: Math.random().toString(36).replace(/[^a-z]/, '').substr(2),
oauth_signature_method: 'HMAC-SHA1',
oauth_timestamp: Math.floor(date.getTime() / 1000),
oauth_version: '1.0',
search_expression: 'mcdonalds' // test query
};
// make the string...got tired of writing that long thing
var paramsStr = '';
for (var i in reqObj) {
paramsStr += "&" + i + "=" + reqObj[i];
}
// had an extra '&' at the front
paramsStr = paramsStr.substr(1);
var sigBaseStr = "POST&"
+ encodeURIComponent(fatSecretRestUrl)
+ "&"
+ encodeURIComponent(paramsStr);
// again there is no need for an access token, but we need an '&' according to the instructions
sharedSecret += "&";
var hashedBaseStr = crypto.createHmac('sha1', sharedSecret).update(sigBaseStr).digest('base64');
// Add oauth_signature to the request object
reqObj.oauth_signature = hashedBaseStr;
rest.post(fatSecretRestUrl, {
data: reqObj,
}).on('complete', function(data, response) {
console.log(response);
console.log("DATA: " + data + "\n");
});
really sorry to anyone using Angular or JQuery, if I ever have a spare minute or two I will try it with angular, also anyone using angular if you get CORS related errors just start chrome like this:
chromium-browser --disable-web-security - I do this on terminal
or add that extension to some chrome shortcut on windows, just as a quick work around, hope it helps anybody out there.

Create Alias using barclaycard payment epdq - unknown order/1/s/

Below is the code used for creating the Alias which does not work and throw an error
"unknown order/1/s/". The same code works for payment if I remove the code for Alias.
Not sure what am I missing?
I could log into the epdq barclaycard account and see the error which is "unknown order/1/s/". I can also create Alias manually through the epdq account, but just cant get to the orderstandard.asp page without error (when alias hidden fields and code used).
I would be glad if someone could help me.
<body>
<form id="OrderForm" action="https://payments.epdq.co.uk/ncol/prod/orderstandard.asp" method="post" runat="server">
<div>
<asp:HiddenField ID="AMOUNT" runat="server" />
<asp:HiddenField ID="CN" runat="server" />
...
<asp:HiddenField ID="ALIAS" runat="server" />
<asp:HiddenField ID="ALIASUSAGE" runat="server" />
<asp:HiddenField ID="ALIASOPERATION" runat="server" />
<asp:HiddenField ID="SHASign" runat="server" />
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Text; //for Encoding
using System.Security.Cryptography; //for SHA1
public partial class _DefaultAliasTest : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
//-- Set Values (these would be pulled from DB or a previous page). -- //
bool isAlias = true;
//- Customer/Order Details - //
string strUserTitle = "Mr"; // Customer details
string strUserFirstname = "Edward";
string strUserSurname = "Shopper";
string strBillHouseNumber = "123"; // Address Details
string strAd1 = "Penny Lane";
string strAd2 = "Central Areas";
string strBillTown = "Middlehampton"; // Bill Town
string strBillCountry = "England"; // Bill Country
string strPcde = "NN4 7SG"; // Postcode
string strContactTel = "01604 567 890"; // Contact Telephone number
string strShopperEmail = "test#test.com"; // shopper Email
string strShopperLocale = "en_GB"; // shopper locale
string strCurrencyCode = "GBP"; // CurrecncyCode
string strAddressline1n2 = strBillHouseNumber + " " + strAd1 + ", " + strAd2; // Concatenated Address eg 123 Penny Lane Central Areas
string strCustomerName = strUserTitle + " " + strUserFirstname + " " + strUserSurname; // Concatenated Customer Name eg Mr Edward Shopper
string strPaymentAmount = "100"; // This is 1 pound (100p)
string strOrderDataRaw = "HDTV - AVTV3000"; // Order description
string strOrderID = "ORD1234556Y"; // Order Id - **needs to be unique**
//- integration user details - //
string strPW = "xxxxxx!?"; // Update with the details you entered into back office
string strPSPID = "epdqxxxxxxx"; // update with the details of the PSPID you were supplied with
//- payment design options - '//
string strTXTCOLOR = "#005588"; // Page Text Colour
string strTBLTXTCOLOR = "#005588"; // Table Text Colour
string strFONTTYPE = "Helvetica, Arial"; // fonttype
string strBUTTONTXTCOLOR = "#005588"; // Button Text Colour
string strBGCOLOR = "#d1ecf3"; // Page Background Colour
string strTBLBGCOLOR = "#ffffff"; // Table BG Colour
string strBUTTONBGCOLOR = "#cccccc"; // Button Colour
string strTITLE = "Testing - Secure Payment Page"; // Title
string strLOGO = "https://www.site.com/logo.png"; // logo location
string strPMLISTTYPE = "1"; // Payment Method List type
string strALIAS = System.Guid.NewGuid().ToString();
string strALIASUSAGE = "usage"; // ALIAS USAGE
string strALIASOPERATION = "BYMERCHANT"; // ALIAS Operation
//= create string to hash (digest) using values of options/details above. MUST be in field alphabetical order!
string plainDigest =
"AMOUNT=" + strPaymentAmount + strPW +
"BGCOLOR=" + strBGCOLOR + strPW +
"BUTTONBGCOLOR=" + strBUTTONBGCOLOR + strPW +
"BUTTONTXTCOLOR=" + strBUTTONTXTCOLOR + strPW +
"CN=" + strCustomerName + strPW +
"COM=" + strOrderDataRaw + strPW +
"CURRENCY=" + strCurrencyCode + strPW +
"EMAIL=" + strShopperEmail + strPW +
"FONTTYPE=" + strFONTTYPE + strPW +
"LANGUAGE=" + strShopperLocale + strPW +
"LOGO=" + strLOGO + strPW +
"ORDERID=" + strOrderID + strPW +
"OWNERADDRESS=" + strAddressline1n2 + strPW +
"OWNERCTY=" + strBillCountry + strPW +
"OWNERTELNO=" + strContactTel + strPW +
"OWNERTOWN=" + strBillTown + strPW +
"OWNERZIP=" + strPcde + strPW +
"PMLISTTYPE=" + strPMLISTTYPE + strPW +
"PSPID=" + strPSPID + strPW +
"TBLBGCOLOR=" + strTBLBGCOLOR + strPW +
"TBLTXTCOLOR=" + strTBLTXTCOLOR + strPW +
"TITLE=" + strTITLE + strPW +
"TXTCOLOR=" + strTXTCOLOR + strPW +
"";
if (isAlias)
{
plainDigest =
plainDigest +
"ALIAS=" + strALIAS + strPW +
"ALIASUSAGE=" + strALIASUSAGE + strPW +
"ALIASOPERATION=" + strALIASOPERATION + strPW +
"";
}
//Payment
//-- insert payment details into hidden fields -- //
AMOUNT.Value = strPaymentAmount; // PaymentAmmount : (100 pence)
CN.Value = strCustomerName; // Customer Name
COM.Value = strOrderDataRaw; // OrderDataRaw (order description)
CURRENCY.Value = strCurrencyCode; // CurrecncyCode
EMAIL.Value = strShopperEmail; // shopper Email
FONTTYPE.Value = strFONTTYPE; // fonttype
LANGUAGE.Value = strShopperLocale; // shopper locale
LOGO.Value = strLOGO; // logo location
ORDERID.Value = strOrderID; // *this ORDER ID*
OWNERADDRESS.Value = strAddressline1n2; // AddressLine2
OWNERCTY.Value = strBillCountry; // Bill Country
OWNERTELNO.Value = strContactTel; // Contact Telephone number
OWNERTOWN.Value = strBillTown; // Bill Town
OWNERZIP.Value = strPcde; // Postcode
PMLISTTYPE.Value = strPMLISTTYPE; // Payment Method List type
PSPID.Value = strPSPID; // *Your PSPID*
BGCOLOR.Value = strBGCOLOR; // Page Background Colour
BUTTONBGCOLOR.Value = strBUTTONBGCOLOR; // Button Colour
BUTTONTXTCOLOR.Value = strBUTTONTXTCOLOR; // Button Text Colour
TBLBGCOLOR.Value = strTBLBGCOLOR; // Table BG Colour
TBLTXTCOLOR.Value = strTBLTXTCOLOR; // Table Text Colour
TITLE.Value = strTITLE; // Title
TXTCOLOR.Value = strTXTCOLOR; // Page Text Colour
if (isAlias)
{
ALIAS.Value = strALIAS;
ALIASUSAGE.Value = strALIASUSAGE;
ALIASOPERATION.Value = strALIASOPERATION;
}
SHASign.Value = SHA1HashData(plainDigest); // Hashed String of plain digest put into sha sign using SHA1HashData function
}
}
I found the answer to my question via customer service to Barclaycard epdq. I hope this helps others. For me the answer is the point selected in bold below.
Please see below details on how to rectify the error ‘unknown order/1/s/’:
This error indicates that ePDQ has been unable to decrypt the SHASIGN HTML Form value sent with the customer when you redirect them from your website to the ePDQ Hosted Payment Page.
The typical reasons for this error are:
• The SHA-IN Passphrase value configured in the ePDQ back office does not match the value you used to encrypt the transaction data used to create the SHASIGN parameter (please also ensure you are sending transactions to the correct ePDQ environment – TEST or PRODUCTION)
• You have not arranged the parameters in alphabetical order when calculating the SHASIGN in your server-side code
• You have not correctly declared some of the parameters – all parameters and values are case sensitive (all parameter names must be upper case)
• You have set a HASH ALGORITHM value that is different to the SHA method used in your server side script (for example, you have configured SHA-256 in the ePDQ Back Office Technical Information settings, but are using a SHA-1 method in your encryption process).
• You have passed additional parameter/value pairs in the HTML Form that have not been included in the SHA-IN calculation
For more information please refer to the Basic & Advanced e-Commerce integration guides located in the ePDQ Back Office under Support –> Integration & User Manuals.

Getting Error constructing implementation (algorithm: Collection, provider: BC, class: org.bouncycastle.jce.provider.CertStoreCollectionSpi)

I'm facing a problem on one project Im working on, when trying to create a digital signature with BouncyCastle.
Here's the code I'm running:
Statement stmt_cert = conn.createStatement();
ResultSet rs_cert= stmt_cert.executeQuery("select c.ca, c.privk from certs c where num_tab="+stat_cert);
rs_cert.next();
castr = rs_cert.getString("ca") + "\n";
strPriv = rs_cert.getString("privk") + "\n" ;
rs_cert.close();
stmt_cert.close();
byte[] encKey = castr.getBytes();
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate caCert = (X509Certificate)cf.generateCertificate(new ByteArrayInputStream(encKey));
PEMReader pr = new PEMReader(new StringReader(strPriv));
Object obj = pr.readObject();
KeyPair kp = (KeyPair) obj;
PrivateKey privateKey = kp.getPrivate();
Certificate[] chain =new Certificate[]{caCert};
byte[] plainText = digest.getBytes("UTF8");
CertStore certs =null;
ArrayList certList = new ArrayList();
try{
for ( int i = 0; i < chain.length;i++)
{
result += chain[i];
certList.add(chain[i]);
}
certs = CertStore.getInstance("Collection", new CollectionCertStoreParameters(certList), "BC");
}
catch(Exception exc){
result += "Problem with keystore access: " + exc.toString() ;
InsErr_log.Insert_error(1000,"Error when generate Signature of Statements",result);
return result;
}
// --- Use Bouncy Castle provider to create CSM/PKCS#7 signed message ---
try{
CMSSignedDataGenerator signGen = new CMSSignedDataGenerator();
signGen.addSigner(privateKey, (X509Certificate)caCert, CMSSignedDataGenerator.DIGEST_SHA1);
signGen.addCertificatesAndCRLs(certs);
CMSProcessable content = new CMSProcessableByteArray(plainText);
CMSSignedData signedData = signGen.generate(content,"BC");
byte[] signeddata = signedData.getEncoded();
result += "Created signed message: " + signeddata.length + " bytes" ;
result += new String(signeddata,"UTF8");
}
catch(Exception ex){
result = "Couldn't generate CMS signed message\n" + ex.toString() ;
}
The problem comes from this line of code:
certs = CertStore.getInstance("Collection", new CollectionCertStoreParameters(certList), "BC");
and here is the error:
Problem with keystore access: java.security.NoSuchAlgorithmException:
Error constructing implementation (algorithm: Collection, provider:
BC, class: org.bouncycastle.jce.provider.CertStoreCollectionSpi)
I'm a newbie so please bear with me, any information will be highly appreciated!
I managed to solve this one on my own! It turned out that while I was deploying bcmail-jdk14-146.jar and bcprov-jdk14-146.jar there was an old version of jce-jdk13-131.jar which had to be removed and after that all worked and I managed to place the signature!
However I am unable to verify it using bcmail-jdk14-146.jar and bcprov-jdk14-146.jar combination!
It only gets verified with the bcmail-jdk13-131.jar and jce-jdk13-131.jar combination.
I use the following code, Pls note the comments in the code itself:
public static boolean verify (byte[] bytes, byte[] bytessig, long userID, int stat_sign) throws Exception
{
boolean result = false;
boolean bcert = false;
boolean bsign=false;
try {
CMSSignedData s;
ByteArrayInputStream bIn = new ByteArrayInputStream(bytessig);
ASN1InputStream aIn = new ASN1InputStream(bIn);
s = new CMSSignedData(new CMSProcessableByteArray(bytes),ContentInfo.getInstance(aIn.readObject()));
//CertStore certs = s.getCertificatesAndCRLs("Collection", "BC");
//Im not using the above line but if I uncomment it with bcmail-jdk14-146.jar and bcprov-jdk14-146.jar
//cert is correctly filled with
//the public key of the signer however verification fails with
//message-digest attribute value does not match calculated value
SignerInformationStore signers = s.getSignerInfos();
Collection c = signers.getSigners();
CollectionCertStoreParameters ccsp = new CollectionCertStoreParameters(c);
CertStore certs = CertStore.getInstance("Collection", ccsp, "BC");
Iterator it = c.iterator();
if (it.hasNext())
{
SignerInformation signer = (SignerInformation)it.next();
Collection certCollection = certs.getCertificates(signer.getSID());
//This is the point where Empty Collection is returned in 1.4
Iterator certIt = certCollection.iterator();
X509Certificate cert = (X509Certificate)certIt.next();
//with bcmail-jdk14-146.jar and bcprov-jdk14-146.jar cert is empty
//and throws : java.util.NoSuchElementException on (X509Certificate)certIt.next();
//while in bcmail-jdk13-131.jar and jce-jdk13-131.jar it verifies correctly
bsign=signer.verify(cert, "BC");
}
return bsign;
}
catch( Exception e) {
e.printStackTrace();
return false;
}
}
I hope I make sense and really would appreciate if you could help me out to verify the message with bcmail-jdk14-146.jar and bcprov-jdk14-146.jar as the above signing code uses these libraries to sign the message!
PS:I found out here that some one else has the same problem
http://www.ibm.com/developerworks/forums/thread.jspa?messageID=14124014
probably its an environment configuration problem?