Introduction
Note: If you don't have Netbeans installed on your workstation, please install it from the link above. You don't have administrator rights so install it to the folder where you are able to write. E.g., C:\PV181\Netbeans.
Basic JCA architecture:There are multiple default providers installed.
Engine classes
SecureRandom
: used to generate random or pseudo-random numbers.MessageDigest
: used to calculate the message digest (hash) of specified data.Signature
: initialized with keys, these are used to sign data and verify digital signatures.Cipher
: initialized with keys, these used for encrypting/decrypting data. There are various types of algorithms: symmetric bulk encryption (e.g. AES, DES, DESede, Blowfish, IDEA), stream encryption (e.g. RC4), asymmetric encryption (e.g. RSA), and password-based encryption (PBE).- Message Authentication Codes (MAC): like
MessageDigest
s, these also generate hash values, but are first initialized with keys to protect the integrity of messages. KeyFactory
: used to convert existing opaque cryptographic keys of typeKey
into key specifications (transparent representations of the underlying key material), and vice versa.SecretKeyFactory
: used to convert existing opaque cryptographic keys of typeSecretKey
into key specifications (transparent representations of the underlying key material), and vice versa.SecretKeyFactory
s are specializedKeyFactory
s that create secret (symmetric) keys only.KeyPairGenerator
: used to generate a new pair of public and private keys suitable for use with a specified algorithm.KeyGenerator
: used to generate new secret keys for use with a specified algorithm.KeyAgreement
: used by two or more parties to agree upon and establish a specific key to use for a particular cryptographic operation.AlgorithmParameters
: used to store the parameters for a particular algorithm, including parameter encoding and decoding.AlgorithmParameterGenerator
: used to generate a set of AlgorithmParameters suitable for a specified algorithm.KeyStore
: used to create and manage a keystore. A keystore is a database of keys. Private keys in a keystore have a certificate chain associated with them, which authenticates the corresponding public key. A keystore also contains certificates from trusted entities.CertificateFactory
: used to create public key certificates and Certificate Revocation Lists (CRLs).CertPathBuilder
: used to build certificate chains (also known as certification paths).CertPathValidator
: used to validate certificate chains.CertStore
: used to retrieveCertificate
s andCRL
s from a repository.
Strong cryptography
- By default, JRE is restricted to particular Encryption Algorithms and Key Lengths (Strong cryptography).
- The reason is the JRE and Java applications can be used in countries with restricted use of Cryptography by default.
- In Czech Republic you can download Unlimited Strength Policy Files from Oracle website in order to unlock strong cryptography. e.g., Java 7
- Test the maximum key length allowed for AES: int maxKeyLen = Cipher.getMaxAllowedKeyLength("AES");
1. SecureRandom
- SecureRandom uses PRNG implementations from Cryptography Service Providers. It may be:
- SHA1PRNG default on Windows.
- NativePRNG which reads /dev/urandom on Linux & Solaris.
- Something completely different, depending on the architecture, provider, configuration, ...
- SecureRandom is automatically seeded by OS means before a first call for a random data.
- This seeding mechanism is bypassed if java.security.SecureRandom.setSeed(byte[]) is called.
- You should not explicitly seed SecureRandom with your own data unless you are sure seed is not predictable.
- For long term SecureRandom object is is recommended to re-seed it from time to time: rand.setSeed(java.security.SecureRandom.generateSeed(int)).
2. MessageDigest
Filename | SHA-256 checksum | MD5 checksum |
file_a.bin | 230cb8e5f966c9d4618040fee7e010f8350794d0029df32c40fe8796d872bf29 | e64db39c582fe33b35df742e8c23bd55 |
file_b.bin | c1627b1968253cbc8595b1b4c951f949acbd1d6001ae366e108c20cfbb5232f3 | 3bf834b2853fbbace062cfe1f93f3776 |
file_c.bin | aeedd172bcbc5c16a161844b689a465b96739a554d85b96138423aefec701a18 | bec261a2d2a8921cb4cf78cc87c3d565 |
file_d.bin | 73cf8ba20aa05ba3c81387669e9c4b300742cfc5297569157712b4d6e2658638 | 79f2807a930062c358ecb65a484bd4d1 |
Hint: You may use Globals.bytesToHex(buffer, false); to encode
byte[]
byte array to a hex-coded string.Hint 2: You may use InputStream to read from URL directly: InputStream is01 = new URL("http://www.fi.muni.cz/~xklinec/java/file_a.bin").openStream();
Hint 3: Boiler plate code for InputStream processing is here.
Hint 4: Getting different hashes? Pay attention to URL and count number of bytes already hashed vs. file size.
3. AES Encryption
Question: How many bits does IV have? How many bits does key have?IV | KEY | Ciphertext |
AAAAAAAAAAAAAAAAAAAAAA== | AAAAAAAAAAAAAAAAAAAAAA== | 6VMSY9xFduwNsiyn8mGZdLG6/NXb3ziw81MBSfaKozs= |
FiikDkkW+k+oW2biRnC1zQ== | eUaq9at/s29swOs5EEWv8Q== | vDoRZgpnJ2/yCnW7ogatKoBlR3XBsViSz5Dfj2ExLl8= |
tPIljLHaDSa8vXwrnDZiCg== | 0y4bBloL0Ppbuy3o8AK6Vw== | N2HNL2GCfEahFJ+9ieUuKzns4zp10nsWqN3SKN5s0x1uOn2BNn1s7bkqbQuTSYLFf/ow3kUQL7kk6HRWTOf/aGmvkD61udKlN70Eby1Zgik= |
Hint1: You may use javax.xml.bind.DatatypeConverter class for Base64 encoding & decoding. Java 10+ may need JVM param: --add-modules java.xml.bind
Hint2: To construct AES encryption key you may use: new SecretKeySpec(key0, "AES");
Hint2: To construct IV you may use: new IvParameterSpec(iv0);
4. Signature verification
Two signatures are swapped, find them and verify them.File | Signature |
file_a.bin | file_a.sig |
file_b.bin | file_b.sig |
file_c.bin | file_c.sig |
You will need a certificate: PEM encoded X509 certificate and DER encoded X509 certificate.
Note: Signature files are base64 encoded. You have to perform base64 decoding before signature verification.
Hint 1: You will need to construct X509Certificate object. Here is how.
Hint 2: You may use InputStrem to read from URL directly: InputStream is01 = new URL("http://www.fi.muni.cz/~xklinec/java/file_a.bin").openStream();
Hint 3: In order to convert a stream to a byte array you may use the following snippet.
Hint 4: This snippet was used to generate a signature. By changing it you can produce a signature verifier.
Homework assignment #1
- In this assignment you are supposed to create a Java application which computes a message digest of a specific form.
- The principle is very similar to the Bitcoin hash computation.
- Use SHA-256 hash function.
- Let denote UCO your university number identifier. Suppose mine is 987654.
- Task is to find a string of a form "UCO:number", where "number" is an arbitrary number in a
decimal representation, such that byte[] digest = md.digest("UCO:number".toBytes()); hashes to a
byte array for which holds:
- digest[0] == (byte)0x98, thus first byte of the digest is 1st part of your UCO.
- digest[1] == (byte)0x76, thus second byte of the digest is 2nd part of your UCO.
- digest[2] == (byte)0x54, thus third byte of the digest is 3rd part of your UCO.
- (digest[3] & ((byte)0xF0)) == 0, thus fourth byte has upper half byte zero.
- BONUS: digest[3] == 0. For each additional full zero byte you receive 0.25 points up to 1 extra points.
- BONUS: Write your reasoning to uco_reasoning.txt to explain why the given task worked (i.e., such hash exists) and why it took given amount of time (mathematically, benchmark hashes per second, compare to search space and match probability). Also explain why 0x98 != (byte)0x98. 0.25 Extra point.
- For generating a string I would suggest to use BigInteger, integer with
arbitrary size. Hints:
- Start with BigInteger c = BigInteger.ZERO;
- In each iteration increment counter with c = c.add(BigInteger.ONE);.
- Use String.format("987654:%d", c.toString); to generate a string that you will hash. Use you UCO, not 987654!
- Don't forget to reset message digest internal state in each iteration
- Your result has to be reproducible! So when you manage to find a given number, verify your approach by hashing the particular text you've generated separately. For example use provided verify() method.
- Computing a hash of a given value takes some time so better don't leave this assignment to the last day.
- Furthermore, in order to practice JCA/JCE the are the following tasks
prepared for you:
- You now have digest byte array that contains hash you have produced.
- In the source HashCollisions.java you can find prepared code skeletons for required tasks.
- 1. encrypt digest with a random AES key and IV, AES mode: AES/CBC/PKCS5Padding. Store IV, Key, AES ciphertext to the protocol buffer message (demonstrated in HashCollisions.java), so we are able to verify your computation.
- 2. encrypt digest with a random RSA key. RSA mode: RSA/ECB/PKCS1PADDING. In the file I have prepared a RSA key pair generation for you. Both private and public key are stored to the message for you. You have to store RSA ciphertext to the message so we are able to verify your result.
- 3. sign digest with RSA. RSA signature mode: SHA256WithRSA. Use same key as generated in a previous step. (Choose the key typically used for digital signatures). Put generated signature to the message.
- 4. HMAC digest. HMAC: HmacSHA256. You have to also generate a random 32B HMAC key and store it to the message together with generated HMAC.
- Note verify() method does not check neither of this.
- Submit your assignment using ProtocolBuffers.
There is a prepared Messages.HashMessage message for you in the Netbeans Java project.
- Fill in UCO in the uco field, as a string.
- Set hashType field to 1.
- Set hashInput field to the input string you found.
- Set hash field to the final hash you computed. It has to have a format specified above.
- Build a protocol buffers message Messages.HashMessage, convert it to base64 and add as a text file to the resulting zip. Text file has to be of a name "uco_hash.txt" in the root directory of a ZIP file.
- Structure of a zip file you submit to IS: ./uco_hash.txt, ./uco_reasoning.txt (may be empty), uco_sources directory with the Netbeans project with your solution. Has to be without errors and working. Mine would have a structure 987654_hash.txt, 987654_reasoning.txt, 987654_sources directory with Netbeans Java project.
- Cheating will be reported to the course administrator (M. Sys).
- If you don't know how to do it (protocol buffers), please refer to the next section.
- Example of a valid solution for hash collision task is: 987654:476346608 for UCO: 987654. It took 35 minutes to compute on my laptop. You can verify it here.
Protocol Buffers
- At first, look at the official pages of the project.
- What is it?
- It is a tool that helps us to build a binary protocol for sending a structured data in a binary form and parse it back to a structured data on the other side.
- You have pv181.jca.res.messages.proto file in the Netbeans Java project which specifies messages. It is only for demonstration purposes for you to see messages generated for you. You are not supposed to edit it or do anything with it. You can see the message structure (i.e., fields and its type).
- Protocol buffers compiler generates a Java file from the proto file. The one corresponding to a pv181.jca.res.messages.proto is pv181.jca.protobuf.entities.Messages. I have generated the file for you. Do not edit it. You can use it's methods in order to build new protocol buffers messages.
- There is a demo class pv181.jca.protobuf.ProtobufDemo which shows how to build a demo protocol buffer message, fill it with data and export to base64.
- In the homework assignments you are required to use appropriate ProtocolBuffer message to submit your solution.
- Why do I need it?
- This is a good and widely used tool for building a protocol.
- I will mark your assignments automatically with this. I will take your submission, parse the message you've submitted and verify its correctness (e.g., if hash matches).
- If there are problems with parsing your message you will receive a point penalty (negative points).