I'm trying to do the following setup for signing pdfs, broken down into asynchronous steps between a client and a server:
A server receives a pdf and computes it's digest.
Server sends the digest to a client.
Client signs the hash at a later time.
Client sends the signature to server.
Server embeds the signature into the pdf.
I'm basing myself mainly in PDF Signature digest and Create pkcs7 signature from file digest
The second question allowed me to write most of the code, however I'm getting that the integrity of the file has been compromised. I can't seem to serialize the intermediary pdf for embedding the signature later (to make sure no timestamps are altered, etc). But from the first SO question, it seems to be a harder problem than I thought. Can it actually be done?
I'm using pdfbox.
Server code:
PDDocument document = PDDocument.load(documentFile);
PDSignature signature = new PDSignature();
signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
signature.setName("Example User");
signature.setLocation("Los Angeles, CA");
signature.setReason("Testing");
Calendar date = Calendar.getInstance();
signature.setSignDate(date);
document.addSignature(signature);
ExternalSigningSupport externalSigningSupport = document.saveIncrementalForExternalSigning(null);
byte[] content = IOUtils.toByteArray(externalSigningSupport.getContent());
MessageDigest md = MessageDigest.getInstance("SHA256", new BouncyCastleProvider());
byte[] digest = md.digest(content); // this is sent to client
What I'm basically doing is sending that digest to the client to sign and then on the server redoing the above steps and setting the client signature:
ExternalSigningSupport externalSigning = document.saveIncrementalForExternalSigning(fos);
externalSigning.setSignature(encodedSignature); // encodedSignature is received from client and computed based on the digest sent by the server
This setup ends up with the integrity of the file being corrupted, since I'm creating a new PDSignature once I have the encodedSignature on the server to embed it. Is there a way to serialize the PDDocument created after calling addSignature, so I can later deserialize it on the server and add the client's signature?
What I'm basically doing is sending that digest to the client to sign and then on the server redoing the above steps and setting the client signature
If you want those above steps to generate identical documents, you need to
make sure the inputs to those steps are identical and
provide the same revision id seed value.
If you do so, the outputs of the above steps are identical as is required for your task.
Making sure the inputs are identical
One step of your above steps is prone to result in different inputs:
Calendar date = Calendar.getInstance();
signature.setSignDate(date);
To guarantee identical inputs, you have to determine date only once and use that single value every time you execute those steps for the same signing transaction.
Providing the same revision id seed value
As recommended by the specification, PDFBox attempts to give each PDF revision its unique ID. In the case at hand, though, we need the same revision ID both times the above steps are executed.
Fortunately, PDFBox allows us to provide the seed value it uses to make the revision ID unique enough.
As we don't want to same revision ID all the time we sign the same document but merely during the current signing transaction, we should use the same seed value only in the same transaction. As the seed value is a long, we can simply use the time in milliseconds corresponding to the date already discussed above, i.e.:
pdDocument.setDocumentId(date.getTimeInMillis());
Related
I need to check JWT token before sending it in request. But I not generated this token, I just reseived it by authorization, than, I have no secret for it.
I use io.jsonwebtoken.jjwt library.
How to check token expiration time with this library?
You do not need the secret to read the token. The secret is only required to ensure that the token was not modified. From what it looks like, however, the library, ensure you can not skip the signature check. So we need to trick it.
Accessing the Expiration while ignoring the Signature
A JWT consists of three parts, base64 encoded and separated by dots: Header.Body.Signature
If we remove the signature, there is nothing the library can check against.
We must, however, also access the raw body , as signatureless claims are not supported.
var signedToken = "eyJhbGciOiJIUzM4NCIsInR5cCI6IkpXVCJ9...";
var tokenOnly = signedToken.substring(0, signedToken.lastIndexOf('.') + 1);
var expiration = ((Claims)Jwts.parser().parse(tokenOnly).getBody()).getExpiration();
However, without verifying the signature, you will not know if someone modified the token. I can not stress this enough: Never rely on any token information if you can not verify the signature.
How to do it better
Have a look at asymmetric algorithms (the RS, ES and PS family). This allows an issuer to generate JWTs with a private key and anyone with the corresponding public key can verify that the token is valid. This way you can validate and access the claims you want with the assurance that they where issued by an issuer you trust and that they where not modified in any way.
I have implemented an OpenID 1.1 provider in Java but I am having trouble with smart clients using an assoc_handle from associate coming up with different signatures. Dumb clients relying on check_authentication work fine. Specifically, I am testing against LiveJournal and it keeps returning:
signature_mismatch: Prior association invalidated ID provider response.
The body of my HMAC() function is:
public static byte[] HMAC(byte[] secret, String token_contents) {
SecretKey sk = new SecretKeySpec(secret, "HMACSHA1");
Mac m = Mac.getInstance(sk.getAlgorithm());
m.init(sk);
return m.doFinal(token_contents.getBytes("UTF-8"));
}
The token_contents for calling HMAC() comes from the following code during the handling for checkid_setup. That is, the signing is being done on mode,identity,return_to and this is also the value of the signed response parameter.
String token_contents = String.format(
"mode:id_res\nidentity:%s\nreturn_to:%s\n",
identity, return_to);
And finally, the secret is the base64-decoded version of mac_key returned by the initial associate call (e.g. retrieved via secret(assoc_handle) as per the spec). I've done a fair amount of testing to make sure the enc_mac_key can be decrypted properly.
Any thoughts? Is there anything glaringly wrong with this?
Or ... is there a simple, stand-alone client that anyone knows of which would do OpenID 1.1 and trace out its steps. Given that I may be able to figure out where I'm calculating things differently.
The problem in my case was using base64url encoding on output of key values (mac_key, enc_mac_key, dh_server_public) instead of standard base64. In Apache Commons I was using encodeBase64URLSafeString instead of simply encodeBase64String. This was an unfortunate carry over from having worked in Open ID Connect previously and I misunderstood the nature of the function.
Anyway, something that helped me discover the answer was using the simply excellent OpenID4Java and its simple-openid JSP sample. Immediately it barfed out errors on my signature, complaining that it was 168 bits (instead of 160).
I recently got to know about Json Web Token (JWT). Since I liked how it works I have started to implement it on my project. My project involves two apps to communicate. One is an android app and the other is Laravel web application.
The mobile app logs in after the user credential is authenticated from the server side.
I have sent the username and password to server from the mobile app and I have got the JWT in string format. But from this point onward I couldn't find a way to collect the JWT content.
I have gone through almost all possible shown (googled results) but I couldn't manage to get the contents, signature and header.
One of the method I have got a little bit further with, was using the following code, notice I have removed the setSigningKey():
try {
Claims claims = Jwts.parser().parseClaimsJwt(jwtHeaderAndClaim).getBody();
System.out.println("ID of the claims: " + claims.getId().toString());
}catch (Exception e){
Log.e("Exception: ", e.toString());
}
The above code generates the following error:
Exception: io.jsonwebtoken.PrematureJwtException: JWT must not be accepted before 2016-06-14T10:20:09+0300. Current time: 2016-06-14T10:19:37+0300´
the jwtHeaderAndClaim is the JWT String after removing the signature part only (i.e: "xxxxxx.yyyyyyyy."). if i put the jwtString (xxxxxxx.yyyyyyyy.ccccccc) instead of jwtHeaderAndClaim the following error will occur:
Exception: io.jsonwebtoken.UnsupportedJwtException: Signed JWSs are not supported
If I put the setSigningKey as shown in stormpath example:
Claims claims = Jwts.parser().setSigningKey(DatatypeConverter.parseBase64Binary(apiKey.getSecret())).parseClaimsJwt(jwtString).getBody();.
The above code will not work for two reasons:
1. I don't have the library
import javax.xml.bind.DatatypeConverter;
2. I don't know how to get the key.
But know that I don't need the key since this time I am trying to login and collect the user information's (like firstname, lastname, phone, etc), and the signature (token) so that the next time I send data to be stored to the server side I have the token to get access to the backend.
Can anyone please help me?
You have many questions. I try to answer some of them
io.jsonwebtoken.PrematureJwtException: JWT must not be accepted before
2016-06-14T10:20:09+0300. Current time: 2016-06-14T10:19:37+0300´
You are using nbf (not before) attribute in JWT. Do not use it (it is optional) or sets a range of validity given that the clocks of the devices will not be synchronized
From RFC 7519
The "nbf" (not before) claim identifies the time before which the JWT
MUST NOT be accepted for processing. The processing of the "nbf" claim requires that the current date/time MUST be after or equal to the not-before date/time listed in the "nbf" claim. Implementers MAY provide for some small leeway, usually no more than a few minutes, to account for clock skew. Its value MUST be a number containing a NumericDate value. Use of this claim is OPTIONAL.
Signed JWS
Exception: io.jsonwebtoken.UnsupportedJwtException: Signed JWSs are
not supported
Do you want to validate the signing key at client side or at server side?
If you use the JWT for authentication replacing user & password, and you are sending token in each request, you can validate the signature at server side.
If you want to validate the key on the app, do not use a symmetric key, because it could be a big vulnerability if it fell into the wrong hands. See. You can use and asymmetric key pair. Sign the JWT in server with the private key and validate on device with public key.
I don't have the library import javax.xml.bind.DatatypeConverter
String base64 = Base64.encodeToString(data, Base64.DEFAULT);
byte[] data = Base64.decode(base64, Base64.DEFAULT);
I don't know how to get the key.
Your key probably was generated on server side in this way
Key key = MacProvider.generateKey(SignatureAlgorithm.HS256);
byte data[] = key.getEncoded();
Make available the key data[] to client in the way you prefer. Using assymetric keys, you only need to make available the public key.
KeyPair keyPair = RsaProvider.generateKeyPair();
byte data[] = keyPair.getPublic().getEncoded();
A common SO question, but no specific solid answers.
My setup:
I have a website running on Classic ASP with backed DB. Unfortunately, no SSL Certs are available
I have an Android application that will send a Google Volley to request data from the site using a bespoke but simple API
Currently:
I am still in testing, privately, so currently I just access the site as such:
On the app, the user enters a UserId and Password once.
User navigates to a Fragment which is associated with a specific ASP Page which will return some data
A Volley is sent to /mysite.com/_api/apage.asp?m=md5hashhereabcdefghijk
The server searches user records for a matching hash (built on UserID+SALT+pass). On matching record, it uses the found userid as the User's ID
apage.asp does some sql queries and returns a JSON object
app receives this JSON response and parses.
The problem:
Anyone packet sniffing, or MITM, would be able to plainly see the URLs being accessed (and server responses) and be able to replicate the query via their browser. This is what I'm trying to stop. Any SALTs or secret keys in the app would be easily seen by decompiling the APK.
Issues:
I've read all sorts of different solutions, but none of which really fit my environment. I can't use ASP session variables (RESTful being stateless), I cant use HTTPS(SSL/TLS) as there are no Certs on the Server. I can't use an App-based password as this can be decompiled and easily seen.
I appreciate that you will never get something 100% secure, but can only make people disinterested in hacking a system, or not make it worth while.
Proposed solution:
I want some feedback/thoughts on the following proposed method:
Each request will have its own handshake to authenticate the app
This will go as such:
User opens app for the first time and enters UserID/Password. This will remain with the app until it is uninstalled (or logged out), but I intend to keep the user's app logged in
User navigates in the app to a Fragment that corresponds with a specific page on the server
Volley is sent with :
UserAgent HTTP header 'some value'
generate the same authentication hash for (userid+salt+pass)
encrypt this hash with a public key
one query string /apage.asp?q=abcdefghijk.... sent to server
server decrypts using its private key
server checks this hash as I do currently.
page returns plaintext JSON values (not encrypted)
The same problem happens here whereby a MITM or sniffer could replicate the URL and get the same information back
A Second Proposed Solution:
Would it be better with every request actually starting with a whole handshake?
App sends volley to server requesting a handshake (HELO)
Server gross error check with UserAgent HTTP Header
Server logs the timestamp and IP of the request and generates a unique random code
App receives this code and builds a new hash using that unique code as a Salt
App sends second volley to /apage.asp?m=MD5(UserID+UniqueCode+Password)
Server Gross error check with originating IP, timestamp+-tolerance (30 seconds between the two requests?), UserAgent Request Header.
APage.asp uses this hash to authenticate the request, providing previous steps have successfully passed.
APage.asp returns a JSON object, as requested.
Server flags the log of originating IP/timestamp as EXPIRED, or, just remove the record.
This initial step would make it a lot harder for a sniffer or MITM to replicate the URL calls, as A) Each request would have a randomly returned code B) each code/hash combo can only be used once.
The only thing I can think of is someone decompiles the App, and sees the method of handshake, so could try to replicate this. However, the username/password hash would never match as that is the only thing they cannot get from the hash (as it is salted with the random code)
Thoughts? Could it be improved with some RSA public/private key cryptography? Could I generate my querystring apage.asp?m=abcdeghi..., Generate an MD5 Hash of that, append onto the end, then encrypt before sending?
Many thanks in advance
I am trying to generate an Oauth signature in order to authenticate an user in flickr.com from my android app.
According to the article in flickr.com, I have to send a signing request in order to get the signature. The hyperlink to the flickr.com guide page is:
http://www.flickr.com/services/api/auth.oauth.html#request_token
According to the post, I have to send a request like this to the flickr server in order to receive the signature key:
http://www.flickr.com/services/oauth/request_token
?oauth_nonce=89601180
&oauth_timestamp=1305583298
&oauth_consumer_key=653e7a6ecc1d528c516cc8f92cf98611
&oauth_signature_method=HMAC-SHA1
&oauth_version=1.0
&oauth_callback=http%3A%2F%2Fwww.example.com
I have send a request from my app, in the above mentioned format, but all I received is an error saying oauth_problem=parameter_absent&oauth_parameter_absent=oauth_signature.
My request code is:
HttpGet get = new HttpGet("http://www.flickr.com/services/oauth/request_token?oauth_nonce="+nonce+"&oauth_timestamp="+ts+"&oauth_consumer_key=****&oauth_signature_method=HMAC-SHA1&oauth_version=1.0");
Actually the problem is that, the url through which I am requesting for the signature is responding in a wrong way. Where it should return the signature, its asking for the signature.
The signing step is no request. You take the URI you have so far and transform it into the base string as seen in the documentation (URL encoding its parts and the like). The you use the HMAC-SHA1 algorithm witch takes the 2 parameters key and data. Use the base string as data and key
is the concatenated values of the Consumer Secret and Token Secret, separated by an '&'.
The value you get back from the algorithm (the signature) is then appended to your URI with
&oauth_signature={ALGORITHM_OUTPUT}
Using this new URI you can then request tokens.
If you think this is too much work, check out some Java OAuth library, e.g. scribe-java.