I've been using the Java security api and was following the tutorial on how to generate digital signatures: http://docs.oracle.com/javase/tutorial/security/apisign/gensig.html
The problem is when I run the code, the output signature is always different even when the private key and data which is to be signed stays constant. I have provided the code below which I used to run. "priv" is a file which holds the private key and is used to sign the data: "hello".
Basically the two outputs show the same data: "hello" signed with the same key but it gives a different output. I was expecting the same output to be given. Also when I run the program again, the signed data is different again from the initial run. Any help would be much appreciated. Thanks in advance
public static void main(String[] args) {
try {
FileInputStream privfis;
privfis = new FileInputStream("priv");
byte[] encKey = new byte[privfis.available()];
// obtain the encoded key in the file
privfis.read(encKey);
privfis.close();
PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec( encKey);
KeyFactory keyFactory = KeyFactory.getInstance("DSA", "SUN");
PrivateKey priv = keyFactory.generatePrivate(privKeySpec);
Signature dsa = Signature.getInstance("SHA1withDSA", "SUN");
dsa.initSign(priv);
String message = "hello";
System.out.println(getHexString(message.getBytes()));
dsa.update(message.getBytes());
byte[] realSig = dsa.sign();
System.out.println(getHexString(realSig));
dsa.update(message.getBytes());
System.out.println(getHexString(message.getBytes()));
byte[] realSig2 = dsa.sign();
System.out.println(getHexString(realSig2));
} catch (Exception e) {
System.err.println("Caught exception " + e.toString());
e.printStackTrace();
}
}
private static String getHexString(byte[] b) {
String result = "";
for (int i = 0; i < b.length; i++) {
result += Integer.toString((b[i] & 0xff) + 0x100, 16).substring(1);
}
return result;
}
}
Related
I use this code in PHP to encrypt the password:
return sha1("kD0a1".md5("xA4".$password)."f4A");
Does anyone know an effective replacement for use in Android? I tried different functions for MD5 and SHA1 but in Java it always generates a different HASH than in PHP.
For example, as follows:
public static String passwordHash(String password) {
return sha1("kD0a1"+md5("xA4"+password)+"f4A");
}
public static String md5(String s) {
try {
MessageDigest digest = java.security.MessageDigest.getInstance("MD5");
digest.update(s.getBytes());
byte messageDigest[] = digest.digest();
StringBuffer hexString = new StringBuffer();
for (int i=0; i<messageDigest.length; i++)
hexString.append(Integer.toHexString(0xFF & messageDigest[i]));
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return "";
}
public static String sha1(String clearString) {
try {
MessageDigest messageDigest = MessageDigest.getInstance("SHA-1");
messageDigest.update(clearString.getBytes("UTF-8"));
byte[] bytes = messageDigest.digest();
StringBuilder buffer = new StringBuilder();
for (byte b : bytes) {
buffer.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));
}
return buffer.toString();
}
catch (Exception ignored) {
ignored.printStackTrace();
return null;
}
}
However, PHP and Java will return a different HASH string to me.
PASS: test
PHP: 17bf2c08f4b9447cf8316736e13833316d3edc23
JAVA: 8434696e252b89af0db033eb255c88a91a42ce14
However, if I enter "passTest" for example, it will generate a hash correctly
PASS: passTest
PHP: db4aedf1d4072b7b645996394aa74743f14eeb7a
JAVA: db4aedf1d4072b7b645996394aa74743f14eeb7a
And "passwordTest" is wrong again.
PASS: passwordTest
PHP: 1ad47c24d556187f1de5db66ff623bbe08a27f33
JAVA: 0f058b3aea48e69c028a7ee2693a98d6074b10a8
I can't explain that sometimes it works and sometimes it doesn't, and at the same time it just changes the String for the password.
Do you think there could be a problem with coding or something? I thought TextView did it, but even if I enter a String outside of TextView, it behaves the same way.
Thank you in advance for any advice.
I would add that I am testing on SDK 31
M.
following code may help you to achieve what you want, it's a method named hashing and salt password, furthermore, the salt method prevents the collision of passwords that have been registered on your database
public static String hashPassword(String password) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("SHA-512");
md.reset();
md.update(password.getBytes());
byte[] mdArray = md.digest();
StringBuilder sb = new StringBuilder(mdArray.length * 2);
for(byte b : mdArray) {
int v = b & 0xff;
if(v < 16)
sb.append('0');
sb.append(Integer.toHexString(v));
}
return sb.toString();
}
here is the salt method
public static String getSalt() throws NoSuchAlgorithmException {
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
byte[] salt = new byte[32];
sr.nextBytes(salt);
return Base64.getEncoder().encodeToString(salt);
}
for further reading about different hashing&salting password, consult the below link, it might help you to solid your understands
https://howtodoinjava.com/java/java-security/how-to-generate-secure-password-hash-md5-sha-pbkdf2-bcrypt-examples/
NB: you should use a strong implementation to hash your password to prevent cracking
I used below code for encryption using AES/ECB/PKCS5Padding algorith, function:
public static void setKey(String myKey) {
MessageDigest sha = null;
try {
key = myKey.getBytes("UTF-8");
sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
key = Arrays.copyOf(key, 16);
secretKey = new SecretKeySpec(key, "AES");
} catch (NoSuchAlgorithmException e) {
// e.printStackTrace();
} catch (UnsupportedEncodingException e) {
// e.printStackTrace();
}
}
public static String encrypt(String strToEncrypt, String secret) {
try {
setKey(secret);
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
return Base64.encodeToString(cipher.doFinal(strToEncrypt.getBytes("UTF-8")), Base64.DEFAULT);
} catch (Exception e) {
// System.out.println("Error while encrypting: " + e.toString());
}
return null;
}
Output:
Meeting ID : 20201228165764
Passcode: 769762
Encrypted values:
meetingid: LfD3obeP45AAGXYxPiByvQ==
passcode: fcJSdMArO3vUOL/WxwzRfw==
I used below AESEncryption code in iOS, but get a different encryption key, function:
func AESEncryption(key: String) -> String? {
let keyData: NSData! = (key as NSString).data(using: String.Encoding.utf8.rawValue) as NSData!
let data: NSData! = (self as NSString).data(using: String.Encoding.utf8.rawValue) as NSData!
let cryptData = NSMutableData(length: Int(data.length) + kCCBlockSizeAES128)!
let keyLength = size_t(kCCKeySizeAES128)
let operation: CCOperation = UInt32(kCCEncrypt)
let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES128)
let options: CCOptions = UInt32(kCCOptionECBMode + kCCOptionPKCS5Padding)
var numBytesEncrypted :size_t = 0
let cryptStatus = CCCrypt(operation,
algoritm,
options,
keyData.bytes, keyLength,
nil,
data.bytes, data.length,
cryptData.mutableBytes, cryptData.length,
&numBytesEncrypted)
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
cryptData.length = Int(numBytesEncrypted)
var bytes = [UInt8](repeating: 0, count: cryptData.length)
cryptData.getBytes(&bytes, length: cryptData.length)
var hexString = ""
for byte in bytes {
hexString += String(format:"%02x", UInt8(byte))
}
return hexString}
return nil //Check condition nil
}
In android code secret key is generated is different than iOS. Can you please help to get same encrypted key from AES/ECB/PKS5padding algorithm? I am checking with https://8gwifi.org/CipherFunctions.jsp link.
In your Swift code, you're just using the string bytes as-is without hashing.
Note that you shouldn't be using SHA-1 for this; you should be using a KDF instead, or (most ideally) a prepackaged library that does all of this error-prone operation for you.
I'm trying to setup a secure hash key in java (android). It's not getting the same result as that of php side (which I use as a reference and it works).
I've gone through many similar questions, but (only one, I tried it but doesn't work) none doesn't solved it clearly. Here's the codes I've tested.
// php code
$secureHash = 'ABCD';
$secret = '123AE45F';
echo '<br> using pack--';
echo hash_hmac('sha256', $secureHash, pack('H*', $secret));
echo '<br> without using pack--';
echo hash_hmac('sha256', $secureHash, $secret, false);
result with pack : f7a009f2c3e654fa48296917ab6372ecb7aa2a24c43fccb70af743f66b6dba55
result without pack : fc602f0f6faf2072be9c0b995ee3d603f61414c4beb027b678c90946db6903a2
// Java code
private String getHashCode(String message, String secretKey) {
Mac mac;
String result = null;
try {
byte[] byteKey = secretKey.getBytes(StandardCharsets.UTF_8);
final String hmacSHA256 = "HmacSHA256";
mac = Mac.getInstance(hmacSHA256);
SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(), hmacSHA256);
sha256HMAC.init(keySpec);
byte[] mac_data = sha256HMAC.doFinal(message.getBytes(StandardCharsets.UTF_8));
result = bytesToHex(mac_data);
System.out.println("getHashCode: result " + result);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
}
return result;
}
In the Java code I'm getting the output as
fc602f0f6faf2072be9c0b995ee3d603f61414c4beb027b678c90946db6903a2
same as php code without pack. How can I achieve the same output as PHP, ie using the pack('H*', $secret) in Java code ?
Thanks to this stackoverflow answer by #rolfl, instead of string.getBytes java function on the secret key, I used his function to get the bytes,
public byte[] hexToString(String hex) {
// hexToString that works at a byte level, not at character level
byte[] output = new byte[(hex.length() + 1) / 2];
for (int i = hex.length() - 1; i >= 0; i -= 2) {
int from = i - 1;
if (from < 0) {
from = 0;
}
String str = hex.substring(from, i + 1);
output[i/2] = (byte)Integer.parseInt(str, 16);
}
return output;
}
Now I get the same as php side for hex type secret key.
I am asked to verify if the encryption is working ok. I am given input hex text of 238 bytes. Asked to use DESede/CBC/NoPadding algorithm. I am also given encrypted value. Of course, I am given key also ( Given two bytes. Added third byte as copy of first byte to make it three bytes)
(238 bytes+ 2 bytes padding)
Problem is: Encrypted value from my code does not match completely with the given encrypted value ( only First 56 bytes are matching).
What I did is: Decrypted the given encrypted value and encrypted value produced from my code. Both of these decrypted values are matching with the given input.
That means, I have two encrypted values for which the decrypted value is the same.
Using InitialVector of Zeros.(8 zero bytes).
Can somebody throw some light? I am sure I am missing something. Thanks for any help.
Using javax.crypto.Cipher.getInstance for getting Cipher instance. Using SecretKeyFactory and DESedeKeySpec classes to generate key
Edited:
public String encrypt(byte[] sourceDataInBytes, String keyInHex, String cryptoMode, String cryptoPadding)
{
Cipher des3cipher = null;
IvParameterSpec ivParamSpec = null;
String transformation = "DESede"+"/"+ cryptoMode+"/"+cryptoPadding;
byte[] encryptedDataInBytes = null;
try{
des3cipher = Cipher.getInstance(transformation);
Key key = generate3DESKey(keyInHex);
if(cryptoMode.equalsIgnoreCase("CBC"))
{
ivParamSpec = new IvParameterSpec(INITIAL_VECTOR_SALT);
des3cipher.init(CYPHER_ENCRYPT_MODE, key, (AlgorithmParameterSpec)ivParamSpec );
}else{
des3cipher.init(CYPHER_ENCRYPT_MODE, key);
}
if(cryptoPadding.equals("NoPadding")){'
sourceDataInBytes = addPadding(sourceDataInBytes, 8);
}
encryptedDataInBytes = des3cipher.doFinal(sourceDataInBytes);
}catch(Exception e){
e.printStackTrace();
}
return encryptedDataInBytes ;
}
public Key generate3DESKey(String srcInHex)
{
Key key = null;
if(src != null)
{
byte[] bk = null;
try{
bk = hexStringToByte(src + src.length==32 ? src.substring(0,16) : ""));
DESedeKeySpec des3KeySpec = new DESedeKeySpec(bk);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DESede");
key = keyFactory.generateSecret(des3KeySpec);
}catch(Exception e){
e,printStyackTrace();
}
}
return key;
}
public static void main(String[] args) {
try{
String mod = "q0AwozeUj0VVkoksDQSCTj3QEgODomq4sAr02xMyIrWldZrNHhWfZAIcWt2MuAY3X6S3ZVUfOFXOrVbltRrO3F9Z6R8/jJIMv7wjkeVBFC5gncwGR0C3aV9gmF6II19jTKfF1sxb26iMEMAlMEOSnAAceNaJH91zBoaW7ZIh+qk=";
String exp = "AQAB";
byte[] modulusBytes = Base64.decodeBase64(mod.getBytes("UTF-8"));
byte[] exponentBytes = Base64.decodeBase64(exp.getBytes("UTF-8"));
String signedMessage = "3753e672cfb21e3c182ef2df51f19edeffb63432ed338a47251326ccc14aa63883e910a140cf313754ebc6425aad434e309307cc882da6cd4a4f9f40bd14a9823aca145e5ffc97cd63dbb5925c049282416bdfd7d74ddeef7055065210a841793fe315dff5a44af19c1522daafdc2f7e61ce5a2b42ebf79dfb086e6d210168dd";
BigInteger modulus = new BigInteger(1, modulusBytes );
BigInteger exponent = new BigInteger(1, exponentBytes);
RSAPublicKeySpec rsaPubKey = new RSAPublicKeySpec(modulus, exponent);
KeyFactory fact = KeyFactory.getInstance("RSA");
PublicKey pubKey = fact.generatePublic(rsaPubKey);
Signature signature = Signature.getInstance("SHA1withRSA");
byte[] sigBytes = hexStringToByteArray(signedMessage);
signature.initVerify(pubKey);
System.out.println(signature.verify(sigBytes));
}catch(Exception e){
System.out.println("Error: " + e.toString());
}
}
private static byte[] hexStringToByteArray(final String encoded) {
if ((encoded.length() % 2) != 0)
throw new IllegalArgumentException("Input string must contain an even number of characters");
final byte result[] = new byte[encoded.length()/2];
final char enc[] = encoded.toCharArray();
for (int i = 0; i < enc.length; i += 2) {
StringBuilder curr = new StringBuilder(2);
curr.append(enc[i]).append(enc[i + 1]);
result[i/2] = (byte) Integer.parseInt(curr.toString(), 16);
}
return result;
}
This code always returns false. I'm not sure where to go from here.
Where you sign the message you should have some code like this:
Signature signature = Signature.getInstance("SHA1withRSA");
signature.initSign(privKey);
signature.update(message);
byte[] signatureValue = signature.sign();
Note the byte-array named signatureValue. That is the actual signature on the data. That is what you should provide to the verify()-method. The message that is signed should be provided in a call to the update()-method. I.e.:
Signature signature = Signature.getInstance("SHA1withRSA");
signature.initVerify(pubKey);
signature.update(message);
bool ok = signature.verify(signatureValue);
I think the problem is that you are not actually giving it a message to verify.
An RSA signature works by first hashing the message (that's the "SHA1" in "SHA1withRSA"), and then performing an trapdoor operation to it. This is an operation which is easy to do in one direction and hard in the other direction, unless you know some secret information (the RSA private key).
To verify, you first invert the mathematical transformation (because it's easy in one direction), and then compare the hash that is embedded in the signature with the hash of the message you just computed. The signature does not in itself contain the message; to verify a signature you need both the signature and the message that was signed.
At an API level, it looks like the Signature class is expecting you to call update with the contents of the message that this signature was for. Without this, it probably is comparing the hash with the hash of an empty string, so unless your originally signed message was also an empty string, the signature is in fact not valid.
You were right, thanks Jack. The below method works perfectly (even with a key created in .NET)! I hope this helps others.
public static void main(String[] args) {
try{
String userID = "189711";
String companyCode = "ILIKEPIZZA";
String combine = userID + "." + companyCode;
String mod = "q0AwozeUj0VVkoksDQSCTj3QEgODomq4sAr02xMyIrWldZrNHhWfZAIcWt2MuAY3X6S3ZVUfOFXOrVbltRrO3F9Z6R8/jJIMv7wjkeVBFC5gncwGR0C3aV9gmF6II19jTKfF1sxb26iMEMAlMEOSnAAceNaJH91zBoaW7ZIh+qk=";
String exp = "AQAB";
byte[] modulusBytes = Base64.decodeBase64(mod.getBytes("UTF-8"));
byte[] exponentBytes = Base64.decodeBase64(exp.getBytes("UTF-8"));
String sign = "3753e672cfb21e3c182ef2df51f19edeffb63432ed338a47251326ccc14aa63883e910a140cf313754ebc6425aad434e309307cc882da6cd4a4f9f40bd14a9823aca145e5ffc97cd63dbb5925c049282416bdfd7d74ddeef7055065210a841793fe315dff5a44af19c1522daafdc2f7e61ce5a2b42ebf79dfb086e6d210168dd";
BigInteger modulus = new BigInteger(1, modulusBytes );
BigInteger exponent = new BigInteger(1, exponentBytes);
RSAPublicKeySpec rsaPubKey = new RSAPublicKeySpec(modulus, exponent);
KeyFactory fact = KeyFactory.getInstance("RSA");
PublicKey pubKey = fact.generatePublic(rsaPubKey);
Signature signature = Signature.getInstance("SHA1withRSA");
byte[] sigBytes = hexStringToByteArray(sign);
signature.initVerify(pubKey);
signature.update(combine.getBytes("UTF-8"));
System.out.println(signature.verify(sigBytes));
}catch(Exception e){
System.out.println("Error: " + e.toString());
}
}