I'm using retrofit for calling my services. One of my services has a header parameter that it signed by SHA256-RSA, but when I invoke my request, I've got the error like this :
java.lang.IllegalArgumentException: Unexpected char 0x1d at 4 in signature value: "|._趿s<�ˠ"
my request method is :
Call<ResponseBody> getToken(#Header("Signature") String Signature,#Header("signature") String signature,#Header("headers") String headers, #Field("username") String username, #Field("password") String password, #Field("grant_type") String grantType);
and my signing method is :
String pkcs8Pem = pkcs8Lines.toString();
pkcs8Pem = pkcs8Pem.replace("-----BEGIN PRIVATE KEY-----", "");
pkcs8Pem = pkcs8Pem.replace("-----END PRIVATE KEY-----", "");
pkcs8Pem = pkcs8Pem.replaceAll("\\s+", "");
// Base64 decode the result
byte[] pkcs8EncodedBytes = Base64.decode(pkcs8Pem, Base64.DEFAULT);
// extract the private key
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(pkcs8EncodedBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey privKey = kf.generatePrivate(keySpec);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, privKey);
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(privKey);
signature.update(message.getBytes());
return signature.sign();
could you help with that?
there are several characters, that could cause this error, for example an unvisible newline character, as stated in this github issue:
https://github.com/square/retrofit/issues/1153
This is forbidden by okhttp Client, which is utilized by retrofit to make http calls.
I would recommend you log the lower case signaturer esult and check directly which character is causing the trouble.
Best of luck!
Related
I need some help with JWS Signing with RSA 256 privatekey -RSASSA-PKCS1-v1.5 SHA-256
I m working on SAP PI/PO.
I am unable to retrieve the RSA privatekey saved in server's OS folder, so I am trying to pass the pem(base64 encoded) key as a string.
My requirement is to generate Header & payload & signed it.
Sample input Header:
{"alg": "RS256","kid": "asff1233dd"}
sample Json Payload:
{"CompInvoiceId": "0009699521","IssueDtm": "20220623"}<br />
Error: I am able to generate Header.payload in base64 url encode but the signature part is getting corrupted when I convert the privatekey to PKCS8encoding.
The generated JWS looks like:
eyJhbGciOiJSUzI1NiIsImtpZCI6Imh5d3kzaHIifQ.eyJDb21waW52b2ljZSI6IjAwOTk5MzMzIiwic3VibWl0SWQiOiIxMjM0NSJ9.[B#42ace6ba
This is signature part which is getting corrupted - [B#42ace6ba
Kindly help with below code:
This is because of this declaration byte[] signed = null, when I remove
that it just throwserror as cannot find variable for signed.
Please help me with passing privatekey & signature.
The Java code I am working on:
I am passing :
Json data= data,
header = header
Privatekey in base64 = key
String jwsToken(String key, String data, String header, Container container) throws
StreamTransformationException{
String tok = null;
byte[] signed = null;
try {
StringBuffer token = new StringBuffer();
//Encode the JWT Header and add it to our string to sign
token.append(Base64.getUrlEncoder().withoutPadding().encodeToString(header.getBytes("UTF-
8")));
token.append(".");
//Encode the Json payload
token.append(Base64.getUrlEncoder().withoutPadding().encodeToString(data.getBytes("UTF-8")));
//Separate with a period
token.append(".");
//String signedPayload =
Base64.getUrlEncoder().withoutPadding().encodeToString(signature.sign());
PrivateKey privatekey = null;
String privateKeyPEM = key;
//String key = new String(Files.readAllBytes(file.toPath()), Charset.defaultCharset());
byte[] decodePrivateKey = Base64.getDecoder().decode(key);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decodePrivateKey);
privatekey = (PrivateKey) keyFactory.generatePrivate(keySpec);
Signature sig = Signature.getInstance( "SHA256withRSA" );
sig.initSign( ( PrivateKey ) privatekey );
sig.update(token.toString().getBytes("UTF-8"));
signed=sig.sign();
tok = (token.toString());
}
catch (Exception e) {
e.printStackTrace();
}
return tok;
}
Instead of appending byte array, encode it in base64 then append it
signed = sig.sign();
token.append(Base64.getUrlEncoder().withoutPadding().encodeToString(signed));
We want to Hash a data using clients digital signature using java sha 256 bit hashing algorithm.
How can we add digital signature while hashing in java.
If I'm understanding correctly, you want to sign some data. Here is a sample method:
public static String encode(String dataToEncode, String secret) throws InvalidKeyException, NoSuchAlgorithmException {
byte[] decodedSecret = Base64.getDecoder().decode(secret);
SecretKeySpec keySpec = new SecretKeySpec(decodedSecret, "HmacSHA256");
Mac sha256 = Mac.getInstance("HmacSHA256");
sha256.init(keySpec);
return Base64.getEncoder().encodeToString(sha256.doFinal(dataToEncode.getBytes()));
}
The secret is the Base64 encoded secret key. The method returns the Base64 encoded hash of the data. The Base64 part is optional, you can remove it if you don't need that encoding. This is a method I use when signing REST API calls to crypto exchanges.
The following solution signs a String by applying an RSA PKCS#8 formatted private key. If your code has read the private key as a text from a pem file that looks like the following example:
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDRxFWXGYDG8zKw
ihIS+Ydh/nWX9NwkFTKMRjH8BQ78ZEnXrnGJHvd+dI+zEiRo7rLuDXMOjsnhIR/O
....
+wqssDAApq+CiPcBnn0x2Vw=
-----END PRIVATE KEY-----
Then you need to strip out the first and last lines and all the new line characters ('\n'). If your privateKey is read (from a java keystore for example) you can remove the lines of code that convert the String of private key into java.security.PrivateKey object.
private static String signSHA256RSA(String inputStr, String inputKey) throws Exception {
String key = inputKey.replaceAll("-----END PRIVATE KEY-----", "")
.replaceAll("-----BEGIN PRIVATE KEY-----", "")
.replaceAll("\n", "");
byte[] keyBytes = Base64.getDecoder().decode(key);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey privateKey = kf.generatePrivate(spec);
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(privateKey);
signature.update(inputStr.getBytes("UTF-8"));
byte[] s = signature.sign();
return Base64.getEncoder().encodeToString(s);
}
I have a PrivateKey and a PublicKey and use the privateKey to init Signature and publicKey to verify the Signature:
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
// decode public key
X509EncodedKeySpec publicSpec = new X509EncodedKeySpec(pubKeyBytes);
RSAPublicKey pubKey = (RSAPublicKey)
keyFactory.generatePublic(publicSpec);
//decode private key
PKCS8EncodedKeySpec privSpec = new PKCS8EncodedKeySpec(prvKeyBytes);
RSAPrivateKey privKey = (RSAPrivateKey)
keyFactory.generatePrivate(privSpec);
//header and body Base64 decoded value
String headerString = mapper.writeValueAsString(header);
String headerEncoded =
Base64.getUrlEncoder().encodeToString(headerString.getBytes());
String payloadString = mapper.writeValueAsString(payload);
String payloadEncoded =
Base64.getUrlEncoder().encodeToString(payloadString.getBytes());
String tempAccessToken = hearderEncoded+"."+payloadString;
Signature sign = Signature.getInstance("SHA256withRSA");
sign.initSign(privKey);
sign.update(tempAccessToken.getBytes("UTF-8"));
byte[] signatureBytes = sign.sign();
sign.initVerify(pubKey);
sign.update(tempAccessToken.getBytes("UTF-8"));
String jsonToken = Base64.getUrlEncoder().encodeToString(signatureBytes);
String JWTtoken = tempAccessToken + "." + jsonToken;
finally I get the JWT token below (dummy value):
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI4MzliN2NhNC02MDE1LTQ0OWQtOGZiNi02jkyYzk1Ny1jYTNjLTQyMWQtOTk2zY2VkNTAzZGViNWYiLCJleHAiOjE1MzIwMDQwMTcsImF1ZCI6ImFjY291bnQtZC5kb2N1c2lnbi5jb20iLzY29wZSI6InNpZ25hdHVyZSBpbXBlcnNvbmF0aW9uIn0=.YNZzR0HgPWFm4dXMMHn3czEDRukYUB0Hyw7YP_BGY2dniIMqpH4Ctz5EEHcHrNT_mGImcp026w-isYgv56TTugYnkcAs8HH0YXa1Ey0skdjVYoZHdeyPC8Ci9xeOQF9wf4VjrBboaRxWkVXfngOHHewpV42NcaXxwmGlresWukHVmmjbFokd1Cm5BJKVhVlsZWuftcJlC3XoO9REjaZX78YTY9S5bpXmsZBYr20wHsWJrkP9EWGf7WSGHVXGBJfCpvOTZDC8_4B12ToD_RKjk6n5s8-F7X54KY4kx6Gg7xnk55vDw==
I'm not able to get the accessToken using this JWT Token. I will get the response instead:
"error" :"invalid_request";
NOTE: I am using this JWT token to get the access token for DocuSign Application.
This is the code am using on the Java side
private String encodedHexString(String secretKey, String payload)
throws NoSuchAlgorithmException, InvalidKeyException {
SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(),"HmacSHA1");
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(keySpec);
byte[] payloadDigest = mac.doFinal(payload.getBytes());
String encodedDigest = DatatypeConverter.printHexBinary(payloadDigest);
return encodedDigest;
}
where secretKey is the Secret token entered on GitHub side as well on my side and the payload is what I am getting in the request.getParameter("payload") for application/x-www-form-urlencoded Content-type.
matching this with request.getHeader("X-Hub-Signature") don't match unfortunately, even after appending "sha1=" to it on Java side.
I had success with simply encoding the whole request body, without any kind of parameter parsing, unescaping, etc:
String secret = "73fcce1ecaeeb4136f27854eaaacb785929bb9a3";
String payload = "payload=%7B%22zen%22%3A%22It%27s+not+fully+shipped+until+it%27s+fast.%22%2C%22hook_id%2...";
System.out.println(encodedHexString(secret, payload));
The result was:
0F026F9E5107F4BF377CA032C1431BFED687B6E9
This matched the X-Hub-Signature header very well:
X-Hub-Signature: sha1=0f026f9e5107f4bf377ca032c1431bfed687b6e9
Background:
I have created an applet to extract public key of a certificate extracted from a smart card.
This public key is then stored in database.
The private key of certificate is used to sign data and the public key is then used to verify the signature.
Code for extracting public key from certificate:
private byte[] getPublicKey(KeyStore paramKeyStore)
throws GeneralSecurityException {
Enumeration localEnumeration = paramKeyStore.aliases();
if (localEnumeration.hasMoreElements()) {
String element = (String) localEnumeration.nextElement();
Certificate[] arrayOfCertificate =
paramKeyStore.getCertificateChain(element);
byte[] publicKeyByteArray =
arrayOfCertificate[0].getPublicKey().getEncoded();
return publicKeyByteArray;
}
throw new KeyStoreException("The keystore is empty!");
}
This publicKeyByteArray is then storeed in database as BLOB after converting to string using bytes2String method:
private static String bytes2String(byte[] bytes) {
StringBuilder string = new StringBuilder();
for (byte b : bytes) {
String hexString = Integer.toHexString(0x00FF & b);
string.append(hexString.length() == 1 ? "0" + hexString : hexString);
}
return string.toString();
}
The content of the BLOB(key) saved in database is:
30820122300d06092a864886f70d01010105000382010f003082010a02820101009bd307e4fc38adae43b93ba1152a4d6dbf82689336bb4e3af5160d16bf1599fe070f7acbfefd93e866e52043de1620bd57d9a3f244fb4e6ef758d70d19e0be86e1b12595af748fbc00aad9009bd61120d3348079b00af8462de46e254f6d2b092cbc85c7f6194c6c37f8955ef7b9b8937a7e9999541dbbea8c1b2349c712565482dbd573cd9b7ec56a59e7683b4c246620cf0d8148ed38da937f1e4e930eb05d5b4c6054712928fa59870763468c07e71265525e1e40839b51c833579f5742d3c8e0588766e3ed6deef1593b10baad0a2abea34734de1505d37710e1cfaa4225b562b96a6a4e87fecb1d627d4c61916e543eba87054ee9212e8183125cdb49750203010001
After reading the stored public key byte[] from database, I try to convert it back to Public Key using following code:
Cipher rsa;
rsa = Cipher.getInstance("RSA");
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(pkey.getBytes());
PublicKey pk = keyFactory.generatePublic(publicKeySpec);
rsa.init(Cipher.DECRYPT_MODE, pk);
byte[] cipherDecrypt = rsa.doFinal(encryptedText.getBytes());
but it gives following error:
Caused by: java.security.InvalidKeyException: invalid key format
at sun.security.x509.X509Key.decode(X509Key.java:387)
at sun.security.x509.X509Key.decode(X509Key.java:403)
at sun.security.rsa.RSAPublicKeyImpl.<init>(RSAPublicKeyImpl.java:83)
at sun.security.rsa.RSAKeyFactory.generatePublic(RSAKeyFactory.java:298)
at sun.security.rsa.RSAKeyFactory.engineGeneratePublic(RSAKeyFactory.java:201)
Please suggest the reason and resolution for this issue.
You must have an error in the way you read the key back from the database. The following code works just fine for me:
String key = "3082012230..."; // full key omitted for brevity
byte[] derPublicKey = DatatypeConverter.parseHexBinary(key);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(derPublicKey);
keyFactory.generatePublic(publicKeySpec);
I would guess, based on the use of pkey.getBytes(), that you've simply tried to get the bytes from the string rather than hex-decoding it.