├── .gitignore ├── src └── test │ ├── resources │ ├── keystore.p12 │ ├── sample.pdf │ └── soap-unsigned.xml │ └── java │ └── nl │ └── craftsmen │ └── crypto │ ├── HMACDemo.java │ ├── BitcoinMiningDemo.java │ ├── HashingDemo.java │ ├── SymmetricEncryptionECBDemo.java │ ├── SimpleSigningDemo.java │ ├── SymmetricEncryptionCBCDemo.java │ ├── WalletEncryptionDemo.java │ ├── ASymmetricEncryptionDemo.java │ ├── BitcoinTransactionSigningDemo.java │ ├── HttpsDemo.java │ ├── CmsSigner.java │ ├── util │ ├── Utils.java │ ├── CryptoToolboxCrypto.java │ └── SampleKeyStoreFactory.java │ ├── KeystoreTest.java │ └── PdfSignDemo.java ├── justright.txt ├── pdf_digital_signature_timestamp.pdf └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | myStore.pkcs12 2 | signed.pdf 3 | *.iml 4 | .idea 5 | target -------------------------------------------------------------------------------- /src/test/resources/keystore.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichelSchudel/crypto-demo/HEAD/src/test/resources/keystore.p12 -------------------------------------------------------------------------------- /src/test/resources/sample.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichelSchudel/crypto-demo/HEAD/src/test/resources/sample.pdf -------------------------------------------------------------------------------- /justright.txt: -------------------------------------------------------------------------------- 1 | The Lord of the Rings has been read by many people since it finally appeared in print; and I should like to say it. -------------------------------------------------------------------------------- /pdf_digital_signature_timestamp.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichelSchudel/crypto-demo/HEAD/pdf_digital_signature_timestamp.pdf -------------------------------------------------------------------------------- /src/test/resources/soap-unsigned.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/test/java/nl/craftsmen/crypto/HMACDemo.java: -------------------------------------------------------------------------------- 1 | package nl.craftsmen.crypto; 2 | 3 | import nl.craftsmen.crypto.util.Utils; 4 | import org.junit.Test; 5 | 6 | import javax.crypto.KeyGenerator; 7 | import javax.crypto.Mac; 8 | import java.security.InvalidKeyException; 9 | import java.security.Key; 10 | import java.security.NoSuchAlgorithmException; 11 | 12 | import static org.junit.Assert.assertEquals; 13 | 14 | public class HMACDemo { 15 | 16 | @Test 17 | public void testHMac() throws NoSuchAlgorithmException, InvalidKeyException { 18 | 19 | // make key 20 | KeyGenerator generator = KeyGenerator.getInstance("HMACSha256"); 21 | Key key = generator.generateKey(); 22 | Utils.printByteArray("hmac key", key.getEncoded()); 23 | 24 | // create signature 25 | Mac mac = Mac.getInstance("HMACSha256"); 26 | mac.init(key); 27 | byte[] input = "Hello, world!".getBytes(); 28 | byte[] signature = mac.doFinal(input); 29 | Utils.printByteArray("Signature", signature); 30 | 31 | // validation of signature on the other end 32 | mac.init(key); 33 | byte[] newSignature = mac.doFinal(input); 34 | Utils.printByteArray("Recomputed signature", newSignature); 35 | 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/nl/craftsmen/crypto/BitcoinMiningDemo.java: -------------------------------------------------------------------------------- 1 | package nl.craftsmen.crypto; 2 | 3 | import org.apache.commons.codec.binary.Hex; 4 | import org.junit.Test; 5 | 6 | import java.security.MessageDigest; 7 | import java.security.NoSuchAlgorithmException; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | /** 12 | * A small message digest demo. 13 | */ 14 | public class BitcoinMiningDemo { 15 | 16 | @Test 17 | public void miningDemo() throws NoSuchAlgorithmException { 18 | 19 | //setup block 20 | String blockData = "whole bunch of transactions"; 21 | 22 | MessageDigest digester = MessageDigest.getInstance("SHA-256"); 23 | 24 | int number = 0; 25 | String blockWithRandomValue = blockData + number; 26 | while (!(digester.digest(blockWithRandomValue.toString().getBytes())[0] == 0)) { 27 | System.out.println("block" + blockWithRandomValue + " has hash " + Hex.encodeHexString(digester.digest(blockWithRandomValue.toString().getBytes())) + ", not good enough!"); 28 | number++; 29 | blockWithRandomValue = blockData + number; 30 | } 31 | 32 | System.out.println("block " + blockWithRandomValue + " has hash " + Hex.encodeHexString(digester.digest(blockWithRandomValue.toString().getBytes()))); 33 | System.out.println("block took " + number + " iterations to seal."); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/test/java/nl/craftsmen/crypto/HashingDemo.java: -------------------------------------------------------------------------------- 1 | package nl.craftsmen.crypto; 2 | 3 | import nl.craftsmen.crypto.util.Utils; 4 | 5 | import org.junit.Test; 6 | 7 | import java.security.MessageDigest; 8 | import java.security.NoSuchAlgorithmException; 9 | 10 | /** 11 | * A small message digest demo. 12 | */ 13 | public class HashingDemo { 14 | 15 | @Test 16 | public void hashingDemo() throws NoSuchAlgorithmException { 17 | 18 | // get a message digest 19 | System.out.println("one way only!"); 20 | hashText("The quick brown fox jumped over the lazy dog."); 21 | 22 | //hash it again, deterministic 23 | System.out.println("deterministic"); 24 | hashText("The quick brown fox jumped over the lazy dog."); 25 | 26 | // psuedorandom, new digest looks nothing like old digest 27 | System.out.println("psuedorandom"); 28 | hashText("The quick brown fox jumped ower the lazy dog."); 29 | 30 | // hash is always fixed length. 31 | System.out.println("fixedlength"); 32 | hashText("The quick brown fox jumped ower the lazy dog and a lot more stuff happened after that."); 33 | } 34 | 35 | private void hashText(String data) throws NoSuchAlgorithmException { 36 | System.out.println("Input: " + data); 37 | MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); 38 | byte[] digest = messageDigest.digest(data.getBytes()); 39 | Utils.printByteArray("Digest", digest); 40 | } 41 | 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/test/java/nl/craftsmen/crypto/SymmetricEncryptionECBDemo.java: -------------------------------------------------------------------------------- 1 | package nl.craftsmen.crypto; 2 | 3 | import nl.craftsmen.crypto.util.Utils; 4 | import org.junit.Test; 5 | 6 | import javax.crypto.Cipher; 7 | import javax.crypto.KeyGenerator; 8 | import java.security.GeneralSecurityException; 9 | import java.security.Key; 10 | 11 | public class SymmetricEncryptionECBDemo { 12 | 13 | //2. show key generation 14 | //3. create string of 128 bytes, explaining block size of AES is 128 15 | //4. Encrypt, show it's 128 bytes 16 | //5. Decrypt, show end result 17 | //6. Notice pattern in ECB 18 | 19 | @Test 20 | public void testSymmetricEncryption() throws GeneralSecurityException { 21 | 22 | KeyGenerator generator = KeyGenerator.getInstance("AES"); 23 | // specify we want a key length of 192 bits, allowed for AES 24 | generator.init(192); 25 | Key key = generator.generateKey(); 26 | Utils.printByteArray("key", key.getEncoded()); 27 | 28 | byte[] input = "JavaLand".repeat(16).getBytes(); 29 | Utils.printText("input", input); 30 | 31 | Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); 32 | cipher.init(Cipher.ENCRYPT_MODE, key); 33 | byte[] encryptedOutput = cipher.doFinal(input); 34 | Utils.printByteArray("ciphertext", encryptedOutput); 35 | 36 | //decryption on the other end 37 | cipher.init(Cipher.DECRYPT_MODE, key); 38 | byte[] decryptedOutput = cipher.doFinal(encryptedOutput); 39 | Utils.printText("decoded input", decryptedOutput); 40 | 41 | 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/test/java/nl/craftsmen/crypto/SimpleSigningDemo.java: -------------------------------------------------------------------------------- 1 | package nl.craftsmen.crypto; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | import static org.junit.Assert.assertTrue; 5 | 6 | import nl.craftsmen.crypto.util.Utils; 7 | import org.apache.commons.codec.DecoderException; 8 | import org.junit.Test; 9 | 10 | import java.security.*; 11 | 12 | public class SimpleSigningDemo { 13 | 14 | @Test 15 | public void testAsymmetricSigningWithSignatureClasses() throws GeneralSecurityException, DecoderException { 16 | 17 | KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA"); 18 | kpGen.initialize(1024); 19 | KeyPair keyPair = kpGen.generateKeyPair(); 20 | Utils.printByteArray("private key", keyPair.getPrivate().getEncoded()); 21 | Utils.printByteArray("public key", keyPair.getPublic().getEncoded()); 22 | 23 | String data = "JavaLand is the best!!!"; 24 | 25 | Signature signatureAlgorithm = Signature.getInstance("SHA256WithRSA"); 26 | signatureAlgorithm.initSign(keyPair.getPrivate()); 27 | signatureAlgorithm.update(data.getBytes()); 28 | 29 | byte[] signature = signatureAlgorithm.sign(); 30 | 31 | Utils.printByteArray("signature", signature); 32 | 33 | 34 | 35 | //verification on the other end 36 | String receivedData = "JavaLand is the worst!!!"; 37 | 38 | Signature verificationAlgorithm = Signature.getInstance("SHA256WithRSA"); 39 | verificationAlgorithm.initVerify(keyPair.getPublic()); 40 | verificationAlgorithm.update(receivedData.getBytes()); 41 | 42 | boolean matches = verificationAlgorithm.verify(signature); 43 | 44 | System.out.println("signature matches: " + matches); 45 | assertThat(matches).isTrue(); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/nl/craftsmen/crypto/SymmetricEncryptionCBCDemo.java: -------------------------------------------------------------------------------- 1 | package nl.craftsmen.crypto; 2 | 3 | import nl.craftsmen.crypto.util.Utils; 4 | import org.junit.Test; 5 | 6 | import javax.crypto.Cipher; 7 | import javax.crypto.KeyGenerator; 8 | import javax.crypto.spec.IvParameterSpec; 9 | import java.security.GeneralSecurityException; 10 | import java.security.Key; 11 | import java.security.SecureRandom; 12 | 13 | public class SymmetricEncryptionCBCDemo { 14 | 15 | //2. show key generation 16 | //3. create string of 128 bytes, explaining block size of AES is 128 17 | //4. Encrypt, show it's 128 bytes 18 | //5. Decrypt, show end result 19 | //6. Notice absence of pattern in CBC 20 | 21 | @Test 22 | public void testSymmetricEncryption() throws GeneralSecurityException { 23 | 24 | 25 | //make key 26 | KeyGenerator generator = KeyGenerator.getInstance("AES"); 27 | // specify we want a key length of 192 bits, allowed for AES 28 | generator.init(192); 29 | Key key = generator.generateKey(); 30 | Utils.printByteArray("key", key.getEncoded()); 31 | 32 | //get IV 33 | Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 34 | SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG"); 35 | byte[] random = new byte[16]; 36 | secureRandom.nextBytes(random); 37 | IvParameterSpec ivSpec = new IvParameterSpec(random); 38 | Utils.printByteArray("ivSpec", random); 39 | 40 | //input 41 | byte[] input = "JavaLand".repeat(16).getBytes(); 42 | Utils.printText("input", input); 43 | 44 | //encryption 45 | cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec); 46 | byte[] encryptedOutput = cipher.doFinal(input); 47 | Utils.printByteArray("ciphertext", encryptedOutput); 48 | 49 | //decryption 50 | cipher.init(Cipher.DECRYPT_MODE, key, ivSpec); 51 | byte[] decryptedOutput = cipher.doFinal(encryptedOutput); 52 | Utils.printText("decoded input", decryptedOutput); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/test/java/nl/craftsmen/crypto/WalletEncryptionDemo.java: -------------------------------------------------------------------------------- 1 | package nl.craftsmen.crypto; 2 | 3 | import nl.craftsmen.crypto.util.Utils; 4 | 5 | import javax.crypto.Cipher; 6 | import javax.crypto.SecretKeyFactory; 7 | import javax.crypto.spec.IvParameterSpec; 8 | import javax.crypto.spec.PBEKeySpec; 9 | import java.security.Key; 10 | import java.security.NoSuchAlgorithmException; 11 | import java.security.NoSuchProviderException; 12 | import java.security.SecureRandom; 13 | import java.security.spec.InvalidKeySpecException; 14 | 15 | public class WalletEncryptionDemo { 16 | 17 | public static void main(String[] args) throws Exception { 18 | Utils.loadProvider(); 19 | 20 | Key passwordKey = getPasswordKey("myPassword"); 21 | 22 | 23 | byte[] wallet = "{\"myAccountBalance\": 500}".getBytes(); 24 | Utils.printText("Decrypted wallet: ", wallet); 25 | 26 | Cipher encryptingCipher = Cipher.getInstance("AES/CBC/PKCS5Padding", "BC"); 27 | encryptingCipher.init(Cipher.ENCRYPT_MODE, passwordKey); 28 | byte[] cipherText = encryptingCipher.doFinal(wallet); 29 | Utils.printByteArray("Encrypted wallet", cipherText); 30 | 31 | Cipher decryptingCipher = Cipher.getInstance("AES/CBC/PKCS5Padding", "BC"); 32 | byte[] iv = encryptingCipher.getIV(); 33 | 34 | decryptingCipher.init(Cipher.DECRYPT_MODE, passwordKey, new IvParameterSpec(iv)); 35 | byte[] plainText2 = decryptingCipher.doFinal(cipherText); 36 | Utils.printText("Decrypted wallet: ", plainText2); 37 | 38 | 39 | } 40 | 41 | private static Key getPasswordKey(String password) throws InvalidKeySpecException, NoSuchProviderException, NoSuchAlgorithmException { 42 | int keyLength = 256; 43 | int saltLength = keyLength / 8; // It's bytes, not bits. 44 | int iterations = 65536; 45 | byte[] salt = new SecureRandom().generateSeed(saltLength); 46 | PBEKeySpec passwordKeySpec = new PBEKeySpec(password.toCharArray(), salt, iterations, keyLength); 47 | SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256", "BC"); 48 | Key passwordKey = secretKeyFactory.generateSecret(passwordKeySpec); 49 | return passwordKey; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/test/java/nl/craftsmen/crypto/ASymmetricEncryptionDemo.java: -------------------------------------------------------------------------------- 1 | package nl.craftsmen.crypto; 2 | 3 | import nl.craftsmen.crypto.util.Utils; 4 | 5 | import org.junit.Test; 6 | 7 | import javax.crypto.BadPaddingException; 8 | import javax.crypto.Cipher; 9 | import javax.crypto.IllegalBlockSizeException; 10 | import javax.crypto.NoSuchPaddingException; 11 | import java.io.IOException; 12 | import java.nio.file.Files; 13 | import java.nio.file.Path; 14 | import java.security.*; 15 | 16 | public class ASymmetricEncryptionDemo { 17 | 18 | @Test 19 | public void encryptSomeShortTextWithRsa() throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, IOException, BadPaddingException, IllegalBlockSizeException, NoSuchProviderException { 20 | 21 | KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA"); 22 | kpGen.initialize(1024); 23 | KeyPair keyPair = kpGen.generateKeyPair(); 24 | Utils.printByteArray("private key", keyPair.getPrivate().getEncoded()); 25 | Utils.printByteArray("public key", keyPair.getPublic().getEncoded()); 26 | 27 | 28 | 29 | byte[] text = "The Lord of the Rings has been read by many people".getBytes(); 30 | Utils.printText("plain text", text); 31 | 32 | //encrypt 33 | Cipher cipher = Cipher.getInstance("RSA"); 34 | 35 | cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPrivate()); 36 | 37 | byte[] encryptedText = cipher.doFinal(text); 38 | Utils.printByteArray("ciphertext", encryptedText); 39 | 40 | 41 | 42 | 43 | //decrypt 44 | cipher.init(Cipher.DECRYPT_MODE, keyPair.getPublic()); 45 | 46 | byte[] plainText = cipher.doFinal(encryptedText); 47 | Utils.printText("decoded text", plainText); 48 | 49 | } 50 | 51 | @Test 52 | public void encryptTheFellowshipOfTheRingWithRsa() throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, IOException, BadPaddingException, IllegalBlockSizeException, NoSuchProviderException { 53 | 54 | KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA"); 55 | kpGen.initialize(1024); 56 | KeyPair keyPair = kpGen.generateKeyPair(); 57 | 58 | 59 | Cipher cipher = Cipher.getInstance("RSA"); 60 | cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPrivate()); 61 | 62 | byte[] text = Files.readAllBytes(Path.of("largefile.txt")); 63 | byte[] encryptedText = cipher.doFinal(text); 64 | } 65 | 66 | 67 | } 68 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | nl.ordina.crypto 5 | crypto-demo 6 | 0.0.1-SNAPSHOT 7 | 8 | 9 | 11 10 | 11 11 | 12 | 13 | 14 | 15 | org.bouncycastle 16 | bcprov-jdk15on 17 | 1.67 18 | 19 | 20 | org.mockito 21 | mockito-core 22 | 3.0.0 23 | 24 | 25 | org.bouncycastle 26 | bcpkix-jdk15on 27 | 1.63 28 | 29 | 30 | junit 31 | junit 32 | 4.13.1 33 | jar 34 | test 35 | 36 | 37 | org.assertj 38 | assertj-core 39 | 3.13.2 40 | test 41 | 42 | 43 | 44 | org.apache.pdfbox 45 | pdfbox 46 | 2.0.24 47 | 48 | 49 | 50 | org.apache.logging.log4j 51 | log4j-core 52 | 2.11.1 53 | 54 | 55 | wsdl4j 56 | wsdl4j 57 | 1.6.3 58 | jar 59 | compile 60 | 61 | 62 | org.apache.ws.security 63 | wss4j 64 | 1.6.19 65 | compile 66 | 67 | 68 | 69 | commons-codec 70 | commons-codec 71 | 1.11 72 | 73 | 74 | -------------------------------------------------------------------------------- /src/test/java/nl/craftsmen/crypto/BitcoinTransactionSigningDemo.java: -------------------------------------------------------------------------------- 1 | package nl.craftsmen.crypto; 2 | 3 | import org.apache.commons.codec.DecoderException; 4 | import org.apache.commons.codec.binary.Hex; 5 | import org.junit.Test; 6 | 7 | import java.security.*; 8 | import java.security.spec.InvalidKeySpecException; 9 | import java.security.spec.X509EncodedKeySpec; 10 | 11 | public class BitcoinTransactionSigningDemo { 12 | 13 | class Transaction { 14 | 15 | public Transaction(String from, String to, int amount) { 16 | this.from = from; 17 | this.to = to; 18 | this.amount = amount; 19 | 20 | } 21 | 22 | String from; 23 | String to; 24 | int amount; 25 | String signature; 26 | 27 | @Override 28 | public String toString() { 29 | return "Transaction{" + 30 | "\nfrom='" + from + '\'' + 31 | "\n, to='" + to + '\'' + 32 | "\n, amount=" + amount + 33 | "\n, signature=" + signature + 34 | '}'; 35 | } 36 | } 37 | 38 | @Test 39 | public void testAsymmetricSigningWithSignatureClasses() throws GeneralSecurityException, DecoderException { 40 | 41 | KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA"); 42 | kpGen.initialize(1024); 43 | KeyPair michelKeyPair = kpGen.generateKeyPair(); 44 | KeyPair nlJugKeyPair = kpGen.generateKeyPair(); 45 | 46 | Transaction transaction = new Transaction( 47 | Hex.encodeHexString(michelKeyPair.getPublic().getEncoded()), 48 | Hex.encodeHexString(nlJugKeyPair.getPublic().getEncoded()), 49 | 5); 50 | 51 | String data = transaction.from + transaction.to + transaction.amount; 52 | 53 | // create signature 54 | Signature signatureAlgorithm = Signature.getInstance("SHA256WithRSA"); 55 | signatureAlgorithm.initSign(michelKeyPair.getPrivate()); 56 | signatureAlgorithm.update(data.getBytes()); 57 | transaction.signature = Hex.encodeHexString(signatureAlgorithm.sign()); 58 | 59 | System.out.println(transaction); 60 | 61 | Hex.decodeHex(transaction.from); 62 | // validation of signature 63 | 64 | Signature verificationAlgorithm = Signature.getInstance("SHA256WithRSA"); 65 | verificationAlgorithm.initVerify(constructkey(transaction.from)); 66 | verificationAlgorithm.update(data.getBytes()); 67 | boolean matches = verificationAlgorithm.verify(Hex.decodeHex(transaction.signature)); 68 | System.out.println("signature matches: " + matches); 69 | 70 | } 71 | 72 | private PublicKey constructkey(String keyString) throws NoSuchAlgorithmException, InvalidKeySpecException, DecoderException { 73 | byte[] publicKeyArray = Hex.decodeHex(keyString); 74 | KeyFactory kf = KeyFactory.getInstance("RSA"); 75 | X509EncodedKeySpec X509publicKey = new X509EncodedKeySpec(publicKeyArray); 76 | return kf.generatePublic(X509publicKey); 77 | 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/test/java/nl/craftsmen/crypto/HttpsDemo.java: -------------------------------------------------------------------------------- 1 | package nl.craftsmen.crypto; 2 | 3 | import javax.net.ssl.HttpsURLConnection; 4 | import javax.net.ssl.SSLPeerUnverifiedException; 5 | import java.io.BufferedReader; 6 | import java.io.IOException; 7 | import java.io.InputStreamReader; 8 | import java.net.MalformedURLException; 9 | import java.net.URL; 10 | import java.security.cert.Certificate; 11 | 12 | public class HttpsDemo { 13 | 14 | 15 | public static void main(String[] args) 16 | { 17 | new HttpsDemo().testIt(); 18 | } 19 | 20 | private void testIt(){ 21 | 22 | String https_url = "https://www.google.com/"; 23 | URL url; 24 | try { 25 | 26 | url = new URL(https_url); 27 | HttpsURLConnection con = (HttpsURLConnection)url.openConnection(); 28 | 29 | //dumpl all cert info 30 | print_https_cert(con); 31 | 32 | //dump all the content 33 | print_content(con); 34 | 35 | } catch (MalformedURLException e) { 36 | e.printStackTrace(); 37 | } catch (IOException e) { 38 | e.printStackTrace(); 39 | } 40 | 41 | } 42 | 43 | private void print_https_cert(HttpsURLConnection con){ 44 | 45 | if(con!=null){ 46 | 47 | try { 48 | 49 | System.out.println("Response Code : " + con.getResponseCode()); 50 | System.out.println("Cipher Suite : " + con.getCipherSuite()); 51 | System.out.println("\n"); 52 | 53 | Certificate[] certs = con.getServerCertificates(); 54 | for(Certificate cert : certs){ 55 | System.out.println("Cert Type : " + cert.getType()); 56 | System.out.println("Cert Hash Code : " + cert.hashCode()); 57 | System.out.println("Cert Public Key Algorithm : " 58 | + cert.getPublicKey().getAlgorithm()); 59 | System.out.println("Cert Public Key Format : " 60 | + cert.getPublicKey().getFormat()); 61 | System.out.println("\n"); 62 | } 63 | 64 | } catch (SSLPeerUnverifiedException e) { 65 | e.printStackTrace(); 66 | } catch (IOException e){ 67 | e.printStackTrace(); 68 | } 69 | 70 | } 71 | 72 | } 73 | 74 | private void print_content(HttpsURLConnection con){ 75 | if(con!=null){ 76 | 77 | try { 78 | 79 | System.out.println("****** Content of the URL ********"); 80 | BufferedReader br = 81 | new BufferedReader( 82 | new InputStreamReader(con.getInputStream())); 83 | 84 | String input; 85 | 86 | while ((input = br.readLine()) != null){ 87 | System.out.println(input); 88 | } 89 | br.close(); 90 | 91 | } catch (IOException e) { 92 | e.printStackTrace(); 93 | } 94 | 95 | } 96 | 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/test/java/nl/craftsmen/crypto/CmsSigner.java: -------------------------------------------------------------------------------- 1 | package nl.craftsmen.crypto; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.OutputStream; 6 | import java.security.PrivateKey; 7 | import java.security.Provider; 8 | import java.security.Security; 9 | import java.security.cert.CertStore; 10 | import java.security.cert.Certificate; 11 | import java.security.cert.CollectionCertStoreParameters; 12 | import java.security.cert.X509Certificate; 13 | import java.util.Arrays; 14 | import java.util.List; 15 | 16 | import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureInterface; 17 | import org.bouncycastle.cert.jcajce.JcaCertStore; 18 | import org.bouncycastle.cms.CMSException; 19 | import org.bouncycastle.cms.CMSProcessable; 20 | import org.bouncycastle.cms.CMSProcessableByteArray; 21 | import org.bouncycastle.cms.CMSSignedData; 22 | import org.bouncycastle.cms.CMSSignedDataGenerator; 23 | import org.bouncycastle.cms.CMSTypedData; 24 | import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder; 25 | import org.bouncycastle.operator.ContentSigner; 26 | import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; 27 | import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; 28 | 29 | public class CmsSigner implements SignatureInterface { 30 | 31 | private Provider provider; 32 | 33 | private PrivateKey privKey; 34 | 35 | private Certificate[] cert; 36 | 37 | public CmsSigner(Provider provider, PrivateKey privateKey, Certificate[] certificates) { 38 | this.privKey = privateKey; 39 | this.cert = certificates; 40 | this.provider = provider; 41 | } 42 | 43 | @Override 44 | public byte[] sign(InputStream content) throws IOException { 45 | CMSProcessableInputStream input = new CMSProcessableInputStream(content); 46 | CMSSignedDataGenerator gen = new CMSSignedDataGenerator(); 47 | // CertificateChain 48 | List certList = Arrays.asList(cert); 49 | 50 | if (Security.getProvider(provider.getName()) == null) { 51 | Security.addProvider(provider); 52 | } 53 | 54 | CertStore certStore = null; 55 | try { 56 | certStore = CertStore.getInstance("Collection", new CollectionCertStoreParameters(certList), provider); 57 | ContentSigner sha256Signer = new JcaContentSignerBuilder("SHA256withRSA").setProvider(provider) 58 | .build(privKey); 59 | 60 | JcaCertStore jcaCertStore = new JcaCertStore(certList); 61 | gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider(provider) 62 | .build()).build(sha256Signer, (X509Certificate) certList.get(0))); 63 | gen.addCertificates(jcaCertStore); 64 | gen.addCRLs(jcaCertStore); 65 | 66 | CMSTypedData chainMessage = new CMSProcessableByteArray(content.readAllBytes()); 67 | CMSSignedData signedData = gen.generate(chainMessage); 68 | 69 | return signedData.getEncoded(); 70 | } catch (Exception e) { 71 | // should be handled 72 | e.printStackTrace(); 73 | } 74 | throw new RuntimeException("Problem while preparing signature"); 75 | } 76 | } 77 | 78 | class CMSProcessableInputStream implements CMSProcessable { 79 | 80 | InputStream in; 81 | 82 | public CMSProcessableInputStream(InputStream is) { 83 | in = is; 84 | } 85 | 86 | public Object getContent() { 87 | return null; 88 | } 89 | 90 | public void write(OutputStream out) throws IOException, CMSException { 91 | // read the content only one time 92 | byte[] buffer = new byte[8 * 1024]; 93 | int read; 94 | while ((read = in.read(buffer)) != -1) { 95 | out.write(buffer, 0, read); 96 | } 97 | in.close(); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/test/java/nl/craftsmen/crypto/util/Utils.java: -------------------------------------------------------------------------------- 1 | package nl.craftsmen.crypto.util; 2 | 3 | import org.apache.commons.codec.binary.Hex; 4 | import org.apache.ws.security.util.DOM2Writer; 5 | import org.bouncycastle.jce.provider.BouncyCastleProvider; 6 | import org.w3c.dom.Document; 7 | import org.w3c.dom.Node; 8 | import org.xml.sax.SAXException; 9 | 10 | import javax.xml.parsers.DocumentBuilder; 11 | import javax.xml.parsers.DocumentBuilderFactory; 12 | import javax.xml.parsers.ParserConfigurationException; 13 | import java.io.ByteArrayInputStream; 14 | import java.io.IOException; 15 | import java.io.StringWriter; 16 | import java.security.Key; 17 | import java.security.Security; 18 | 19 | /** 20 | * Utility class for parsing DOM documents. 21 | */ 22 | public class Utils { 23 | 24 | /** 25 | * Hide default constructor. Constructs a new DOMUtils. 26 | */ 27 | private Utils() { 28 | } 29 | 30 | public static String getDocumentAsString(Node node) { 31 | StringWriter stringWriter = new StringWriter(); 32 | // omit xml declaration 33 | DOM2Writer.serializeAsXML(node, stringWriter, true); 34 | return stringWriter.getBuffer().toString(); 35 | 36 | } 37 | 38 | /** 39 | * Constructs a DOM document from an xml message in string format. 40 | * 41 | * @param xml the xml message. 42 | * @return the xml message as a DOM document. 43 | */ 44 | public static Document getDocumentFromString(String xml) { 45 | DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 46 | factory.setValidating(false); 47 | factory.setNamespaceAware(true); 48 | // Create the builder and parse the xml 49 | DocumentBuilder builder; 50 | try { 51 | builder = factory.newDocumentBuilder(); 52 | return builder.parse(new ByteArrayInputStream(xml.getBytes("UTF-8"))); 53 | } catch (ParserConfigurationException e) { 54 | throw new RuntimeException(e); 55 | } catch (SAXException e) { 56 | throw new RuntimeException(e); 57 | } catch (IOException e) { 58 | throw new RuntimeException(e); 59 | } 60 | 61 | } 62 | 63 | public static Document getDocument(String filename) throws Exception { 64 | DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 65 | factory.setValidating(false); 66 | factory.setNamespaceAware(true); 67 | // Create the builder and parse the file 68 | DocumentBuilder builder = factory.newDocumentBuilder(); 69 | Document doc = builder.parse(Utils.class.getResourceAsStream(filename)); 70 | return doc; 71 | 72 | } 73 | 74 | 75 | public static void printText(String name, byte[] bytes) { 76 | System.out.println(name + ": "+ new String(bytes)); 77 | System.out.println(name + "length: " + bytes.length + " bytes, " + bytes.length * 8 + " bits."); 78 | System.out.println("\r\n"); 79 | } 80 | 81 | public static void printByteArray(String name, byte[] bytes) { 82 | System.out.println(name + ": "+ Hex.encodeHexString(bytes)); 83 | System.out.println(name + " length: " + bytes.length + " bytes, " + bytes.length * 8 + " bits."); 84 | System.out.println("\r\n"); 85 | } 86 | 87 | public static String byteArrayToHexString(Key key) { 88 | return byteArrayToHexString(key.getEncoded()); 89 | } 90 | 91 | public static String byteArrayToHexString(byte[] b) { 92 | StringBuffer sb = new StringBuffer(b.length * 2); 93 | for (int i = 0; i < b.length; i++) { 94 | int v = b[i] & 0xff; 95 | if (v < 16) { 96 | sb.append('0'); 97 | } 98 | sb.append(Integer.toHexString(v)); 99 | } 100 | return sb.toString().toUpperCase(); 101 | } 102 | 103 | public static void loadProvider() { 104 | Security.addProvider(new BouncyCastleProvider()); 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /src/test/java/nl/craftsmen/crypto/KeystoreTest.java: -------------------------------------------------------------------------------- 1 | package nl.craftsmen.crypto; 2 | 3 | import org.bouncycastle.asn1.DERNull; 4 | import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; 5 | import org.bouncycastle.asn1.x500.X500Name; 6 | import org.bouncycastle.asn1.x509.AlgorithmIdentifier; 7 | import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; 8 | import org.bouncycastle.cert.X509CertificateHolder; 9 | import org.bouncycastle.cert.X509v3CertificateBuilder; 10 | import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; 11 | import org.bouncycastle.crypto.params.AsymmetricKeyParameter; 12 | import org.bouncycastle.crypto.util.PrivateKeyFactory; 13 | import org.bouncycastle.operator.ContentSigner; 14 | import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder; 15 | import org.bouncycastle.operator.OperatorCreationException; 16 | import org.bouncycastle.operator.bc.BcContentSignerBuilder; 17 | import org.bouncycastle.operator.bc.BcRSAContentSignerBuilder; 18 | import org.junit.Test; 19 | 20 | import java.io.FileOutputStream; 21 | import java.io.IOException; 22 | import java.math.BigInteger; 23 | import java.nio.file.Files; 24 | import java.nio.file.Path; 25 | import java.security.*; 26 | import java.security.cert.Certificate; 27 | import java.security.cert.CertificateException; 28 | import java.security.cert.X509Certificate; 29 | import java.util.*; 30 | 31 | public class KeystoreTest { 32 | 33 | 34 | @Test 35 | public void createKeyStore() throws GeneralSecurityException, IOException, OperatorCreationException { 36 | if (Files.exists(Path.of("myStore.pkcs12"))) { 37 | Files.delete(Path.of("myStore.pkcs12")); 38 | } 39 | KeyStore keyStore = KeyStore.getInstance("PKCS12"); 40 | keyStore.load(null, null); 41 | //create keypair 42 | KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); 43 | kpg.initialize(2048); 44 | KeyPair kp = kpg.generateKeyPair(); 45 | Key pub = kp.getPublic(); 46 | Key pvt = kp.getPrivate(); 47 | X509Certificate x509Certificate = generateRootCert(kp); 48 | keyStore.setCertificateEntry("myRootCertificate", x509Certificate); 49 | Certificate[] chain = {x509Certificate}; 50 | keyStore.setKeyEntry("myPrivateKey", pvt, "password".toCharArray(), chain); 51 | keyStore.store(new FileOutputStream("myStore.pkcs12"), "password".toCharArray()); 52 | Enumeration enums = keyStore.aliases(); 53 | System.out.println("aliases:"); 54 | enums.asIterator().forEachRemaining(System.out::println); 55 | Key key = keyStore.getKey("myPrivateKey", "password".toCharArray()); 56 | System.out.println(key.toString()); 57 | Certificate certificate = keyStore.getCertificate("myRootCertificate"); 58 | System.out.println(certificate.toString()); 59 | } 60 | 61 | public X509Certificate generateRootCert(KeyPair pair) throws IOException, OperatorCreationException, CertificateException { 62 | Calendar expiry = Calendar.getInstance(); 63 | expiry.add(Calendar.DAY_OF_YEAR, 365); 64 | 65 | //final GeneralNames subjectAltNames = new GeneralNames(new GeneralName(GeneralName.iPAddress, "127.0.0.1")); 66 | //certificateBuilder.addExtension(org.bouncycastle.asn1.x509.Extension.subjectAlternativeName, false, subjectAltNames); 67 | 68 | AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PKCSObjectIdentifiers.sha256WithRSAEncryption); 69 | AlgorithmIdentifier sigAlgId = new AlgorithmIdentifier(algorithmIdentifier.getAlgorithm(), DERNull.INSTANCE); 70 | final AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId); 71 | final BcContentSignerBuilder signerBuilder = new BcRSAContentSignerBuilder(sigAlgId, digAlgId); 72 | final AsymmetricKeyParameter keyp = PrivateKeyFactory.createKey(pair.getPrivate().getEncoded()); 73 | final ContentSigner signer = signerBuilder.build(keyp); 74 | 75 | X509v3CertificateBuilder x509v3CertificateBuilder = new X509v3CertificateBuilder( 76 | new X500Name("CN=Root Certificate"), 77 | BigInteger.ONE, 78 | new Date(), 79 | expiry.getTime(), 80 | Locale.ENGLISH, 81 | new X500Name("CN=Root Certificate"), 82 | SubjectPublicKeyInfo.getInstance(pair.getPublic().getEncoded()) 83 | ); 84 | final X509CertificateHolder x509CertificateHolder = x509v3CertificateBuilder.build(signer); 85 | final X509Certificate certificate = new JcaX509CertificateConverter() 86 | .getCertificate(x509CertificateHolder); 87 | return certificate; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/test/java/nl/craftsmen/crypto/util/CryptoToolboxCrypto.java: -------------------------------------------------------------------------------- 1 | package nl.craftsmen.crypto.util; 2 | 3 | import org.apache.logging.log4j.LogManager; 4 | import org.apache.logging.log4j.Logger; 5 | import org.apache.ws.security.WSSecurityException; 6 | import org.apache.ws.security.components.crypto.CredentialException; 7 | import org.apache.ws.security.components.crypto.Merlin; 8 | 9 | import javax.security.auth.x500.X500Principal; 10 | import java.io.IOException; 11 | import java.lang.reflect.Constructor; 12 | import java.lang.reflect.InvocationTargetException; 13 | import java.math.BigInteger; 14 | import java.security.KeyStoreException; 15 | import java.security.cert.Certificate; 16 | import java.security.cert.X509Certificate; 17 | import java.util.Enumeration; 18 | import java.util.Properties; 19 | 20 | /** 21 | * Extension of Merlin class to fix bug WS-218 of WSS4J where the alias lookup based on a serial and issuer fails if 22 | * there are other objects in the keystore besides certificates. TODO remove this class and replace it with Merlin in 23 | * the SOAPSignerImpl when WSS4J 1.5.9 is released, which fixes this bug. 24 | */ 25 | public class CryptoToolboxCrypto extends Merlin { 26 | 27 | /** Logger. */ 28 | private static final Logger LOG = LogManager.getLogger(CryptoToolboxCrypto.class); 29 | 30 | /** Constant. */ 31 | private static final Constructor BC_509CLASS_CONS; 32 | 33 | /** 34 | * Load the x509name 35 | */ 36 | static { 37 | Constructor cons = null; 38 | Class c; 39 | try { 40 | c = Class.forName("org.bouncycastle.asn1.x509.X509Name"); 41 | cons = c.getConstructor(new Class[] {String.class}); 42 | } catch (ClassNotFoundException e) { 43 | LOG.error(e); 44 | } catch (SecurityException e) { 45 | LOG.error(e); 46 | } catch (NoSuchMethodException e) { 47 | LOG.error(e); 48 | } 49 | BC_509CLASS_CONS = cons; 50 | } 51 | 52 | /** 53 | * Constructs a new CryptoToolboxCrypto. 54 | * @param properties the properties. 55 | * @throws CredentialException if an error occurs. 56 | * @throws IOException if an error occurs. 57 | */ 58 | public CryptoToolboxCrypto(Properties properties) throws CredentialException, IOException { 59 | super(properties); 60 | } 61 | 62 | /** 63 | * Constructs a new CryptoToolboxCrypto. 64 | * @param properties the properties. 65 | * @param loader the loader. 66 | * @throws CredentialException if an error occurs. 67 | * @throws IOException if an error occurs. 68 | */ 69 | public CryptoToolboxCrypto(Properties properties, ClassLoader loader) throws CredentialException, IOException { 70 | super(properties, loader); 71 | } 72 | 73 | /** 74 | * Create the x509 name. 75 | * @param s the name. 76 | * @return an x500principal. 77 | */ 78 | protected Object createBCX509Name(String s) { 79 | if (BC_509CLASS_CONS != null) { 80 | try { 81 | return BC_509CLASS_CONS.newInstance(new Object[] {s}); 82 | } catch (IllegalArgumentException e) { 83 | LOG.error(e); 84 | } catch (InstantiationException e) { 85 | LOG.error(e); 86 | } catch (IllegalAccessException e) { 87 | LOG.error(e); 88 | } catch (InvocationTargetException e) { 89 | LOG.error(e); 90 | } 91 | } 92 | return new X500Principal(s); 93 | } 94 | 95 | /** 96 | * {@inheritDoc} 97 | */ 98 | public final String getAliasForX509Cert(String issuer, BigInteger serialNumber) throws WSSecurityException { 99 | return getAliasForX509Cert(issuer, serialNumber, true); 100 | } 101 | 102 | /** 103 | * Get the alias. 104 | * @param issuer the issuer. 105 | * @param serialNumber the serial number. 106 | * @param useSerialNumber indicates if the serial number should be used in the lookup. 107 | * @return the alias if found, or null. 108 | * @throws WSSecurityException if an error occurs. 109 | */ 110 | protected final String getAliasForX509Cert(String issuer, BigInteger serialNumber, boolean useSerialNumber) 111 | throws WSSecurityException { 112 | Object issuerName = null; 113 | Certificate cert = null; 114 | 115 | // 116 | // Convert the issuer DN to a java X500Principal object first. This is to ensure 117 | // interop with a DN constructed from .NET, where e.g. it uses "S" instead of "ST". 118 | // Then convert it to a BouncyCastle X509Name, which will order the attributes of 119 | // the DN in a particular way (see WSS-168). If the conversion to an X500Principal 120 | // object fails (e.g. if the DN contains "E" instead of "EMAILADDRESS"), then fall 121 | // back on a direct conversion to a BC X509Name 122 | // 123 | try { 124 | X500Principal issuerRDN = new X500Principal(issuer); 125 | issuerName = createBCX509Name(issuerRDN.getName()); 126 | } catch (java.lang.IllegalArgumentException ex) { 127 | issuerName = createBCX509Name(issuer); 128 | } 129 | 130 | try { 131 | for (Enumeration e = keystore.aliases(); e.hasMoreElements();) { 132 | String alias = (String) e.nextElement(); 133 | // BEGIN fix 134 | if (keystore.isCertificateEntry(alias)) { 135 | // END fix 136 | Certificate[] certs = keystore.getCertificateChain(alias); 137 | 138 | if (certs == null || certs.length == 0) { 139 | // no cert chain, so lets check if getCertificate gives us a result. 140 | cert = keystore.getCertificate(alias); 141 | if (cert == null) { 142 | return null; 143 | } 144 | } else { 145 | cert = certs[0]; 146 | } 147 | if (cert instanceof X509Certificate) { 148 | X509Certificate x509cert = (X509Certificate) cert; 149 | if (!useSerialNumber || x509cert.getSerialNumber().compareTo(serialNumber) == 0) { 150 | Object certName = createBCX509Name(x509cert.getIssuerDN().getName()); 151 | if (certName.equals(issuerName)) { 152 | return alias; 153 | } 154 | } 155 | } 156 | // BEGIN fix 157 | } 158 | // END fix 159 | } 160 | } catch (KeyStoreException e) { 161 | throw new WSSecurityException(WSSecurityException.FAILURE, "keystore", null, e); 162 | } 163 | return null; 164 | } 165 | 166 | } 167 | -------------------------------------------------------------------------------- /src/test/java/nl/craftsmen/crypto/PdfSignDemo.java: -------------------------------------------------------------------------------- 1 | package nl.craftsmen.crypto; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.File; 5 | import java.io.FileInputStream; 6 | import java.io.FileNotFoundException; 7 | import java.io.FileOutputStream; 8 | import java.io.IOException; 9 | import java.io.InputStream; 10 | import java.security.KeyStore; 11 | import java.security.KeyStoreException; 12 | import java.security.NoSuchAlgorithmException; 13 | import java.security.PrivateKey; 14 | import java.security.Provider; 15 | import java.security.Signature; 16 | import java.security.SignatureException; 17 | import java.security.UnrecoverableKeyException; 18 | import java.security.cert.CertPathValidator; 19 | import java.security.cert.Certificate; 20 | import java.security.cert.CertificateException; 21 | import java.security.cert.CertificateFactory; 22 | import java.util.Calendar; 23 | import java.util.Collection; 24 | import java.util.Enumeration; 25 | import java.util.Iterator; 26 | import java.util.List; 27 | 28 | import org.apache.pdfbox.pdmodel.PDDocument; 29 | import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature; 30 | import org.bouncycastle.cert.X509CRLHolder; 31 | import org.bouncycastle.cert.X509CertificateHolder; 32 | import org.bouncycastle.cms.CMSProcessable; 33 | import org.bouncycastle.cms.CMSProcessableByteArray; 34 | import org.bouncycastle.cms.CMSSignedData; 35 | import org.bouncycastle.cms.SignerInformation; 36 | import org.bouncycastle.cms.SignerInformationStore; 37 | import org.bouncycastle.cms.SignerInformationVerifier; 38 | import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder; 39 | import org.bouncycastle.jce.provider.BouncyCastleProvider; 40 | import org.bouncycastle.util.Store; 41 | 42 | public class PdfSignDemo { 43 | 44 | private static Provider provider = new BouncyCastleProvider(); 45 | 46 | private static PrivateKey privKey; 47 | 48 | private static Certificate[] cert; 49 | 50 | public static void main(String[] args) 51 | throws KeyStoreException, NoSuchAlgorithmException, CertificateException, FileNotFoundException, IOException, SignatureException, Exception { 52 | 53 | verifySignature(); 54 | InputStream documentStream = PdfSignDemo.class.getResourceAsStream("/sample.pdf"); 55 | PDDocument pdDocument = PDDocument.load(documentStream); 56 | 57 | final String filePath = PdfSignDemo.class.getResource("/keystore.p12") 58 | .getFile(); 59 | 60 | addSignature(pdDocument, filePath, "password"); 61 | FileOutputStream fos = new FileOutputStream("signed.pdf"); 62 | pdDocument.saveIncremental(fos); 63 | pdDocument.close(); 64 | 65 | verifySignature(); 66 | } 67 | 68 | public static void verifySignature() throws Exception { 69 | //File signedFile = new File("test_uwv_pdf.pdf"); 70 | //File signedFile = new File("signed.pdf"); 71 | File signedFile = new File("pdf_digital_signature_timestamp.pdf"); 72 | // We load the signed document. 73 | PDDocument document = PDDocument.load(signedFile); 74 | List signatureDictionaries = document.getSignatureDictionaries(); 75 | // Then we validate signatures one at the time. 76 | for (PDSignature signatureDictionary : signatureDictionaries) { 77 | // NOTE that this code currently supports only "adbe.pkcs7.detached", the most common signature /SubFilter anyway. 78 | byte[] signatureContent = signatureDictionary.getContents(new FileInputStream(signedFile)); 79 | byte[] signedContent = signatureDictionary.getSignedContent(new FileInputStream(signedFile)); 80 | // Now we construct a PKCS #7 or CMS. 81 | CMSProcessable cmsProcessableInputStream = new CMSProcessableByteArray(signedContent); 82 | CMSSignedData cmsSignedData = new CMSSignedData(cmsProcessableInputStream, signatureContent); 83 | SignerInformationStore signerInformationStore = cmsSignedData.getSignerInfos(); 84 | Collection signerInformationList = signerInformationStore.getSigners(); 85 | Store certificateHolderStore = cmsSignedData.getCertificates(); 86 | Store crlHolderStore = cmsSignedData.getCRLs(); 87 | Iterator signerInformationIterator = signerInformationList.iterator(); 88 | while (signerInformationIterator.hasNext()) { 89 | SignerInformation signerInformation = signerInformationIterator.next(); 90 | Collection certificateHolders = certificateHolderStore.getMatches(signerInformation.getSID()); 91 | Iterator certificateHolderIterator = certificateHolders.iterator(); 92 | X509CertificateHolder signerCertificate = certificateHolderIterator.next(); 93 | // And here we validate the document signature. 94 | SignerInformationVerifier siv = new JcaSimpleSignerInfoVerifierBuilder().setProvider(provider) 95 | .build(signerCertificate); 96 | 97 | //alternate 98 | CertificateFactory certificateFactory = CertificateFactory.getInstance("X509"); 99 | Certificate certificate = certificateFactory.generateCertificate(new ByteArrayInputStream(signerCertificate.getEncoded())); 100 | 101 | Signature signature = Signature.getInstance("SHA256withRSA", provider); 102 | signature.initVerify(certificate.getPublicKey()); 103 | signature.update(signedContent); 104 | 105 | boolean result = signature.verify(signerInformation.getSignature()); 106 | 107 | System.out.println(result); 108 | 109 | if (signerInformation.verify(siv)) { 110 | System.out.println("PDF signature verification is correct."); 111 | // IMPORTANT: Note that you should usually validate the signing certificate in this phase, e.g. trust, validity, revocation, etc. See http://www.nakov.com/blog/2009/12/01/x509-certificate-validation-in-java-build-and-verify-chain-and-verify-clr-with-bouncy-castle/. 112 | CertPathValidator certPathValidator = CertPathValidator.getInstance("PKIX"); 113 | } else { 114 | System.out.println("PDF signature verification failed."); 115 | } 116 | } 117 | } 118 | } 119 | 120 | static void addSignature(PDDocument pdDocument, String filePath, String pwd) throws Exception { 121 | File ksFile = new File(filePath); 122 | KeyStore keystore = KeyStore.getInstance("PKCS12"); 123 | char[] pin = pwd.toCharArray(); 124 | keystore.load(new FileInputStream(ksFile), pin); 125 | loadKeystore(keystore, pin); 126 | CmsSigner cmsSigner = new CmsSigner(provider, privKey, cert); 127 | //signing.signPDF(document); 128 | 129 | // create signature dictionary 130 | PDSignature pdSignature = new PDSignature(); 131 | pdSignature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE); // default filter 132 | // subfilter for basic and PAdES Part 2 signatures 133 | pdSignature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED); 134 | pdSignature.setName("signer name"); 135 | pdSignature.setLocation("signer location"); 136 | pdSignature.setReason("reason for signature"); 137 | 138 | // the signing date, needed for valid signature 139 | pdSignature.setSignDate(Calendar.getInstance()); 140 | // SignatureOptions signatureOptions= new SignatureOptions(); 141 | // signatureOptions.setVisualSignature(); 142 | // register signature dictionary and sign interface 143 | pdDocument.addSignature(pdSignature, cmsSigner); 144 | 145 | } 146 | 147 | static void loadKeystore(KeyStore keystore, char[] pin) throws KeyStoreException, NoSuchAlgorithmException { 148 | try { 149 | Enumeration aliases = keystore.aliases(); 150 | String alias = null; 151 | if (aliases.hasMoreElements()) { 152 | alias = aliases.nextElement(); 153 | } else { 154 | throw new RuntimeException("Could not find Key"); 155 | } 156 | privKey = (PrivateKey) keystore.getKey(alias, pin); 157 | cert = keystore.getCertificateChain(alias); 158 | } catch (KeyStoreException e) { 159 | // TODO Auto-generated catch block 160 | e.printStackTrace(); 161 | } catch (UnrecoverableKeyException e) { 162 | // TODO Auto-generated catch block 163 | e.printStackTrace(); 164 | } catch (NoSuchAlgorithmException e) { 165 | // TODO Auto-generated catch block 166 | e.printStackTrace(); 167 | } 168 | } 169 | 170 | } 171 | 172 | -------------------------------------------------------------------------------- /src/test/java/nl/craftsmen/crypto/util/SampleKeyStoreFactory.java: -------------------------------------------------------------------------------- 1 | package nl.craftsmen.crypto.util; 2 | 3 | import org.apache.logging.log4j.LogManager; 4 | import org.apache.logging.log4j.Logger; 5 | import org.bouncycastle.asn1.DERIA5String; 6 | import org.bouncycastle.asn1.x509.BasicConstraints; 7 | import org.bouncycastle.asn1.x509.GeneralName; 8 | import org.bouncycastle.asn1.x509.KeyUsage; 9 | import org.bouncycastle.asn1.x509.X509Extensions; 10 | import org.bouncycastle.x509.X509V1CertificateGenerator; 11 | import org.bouncycastle.x509.X509V3CertificateGenerator; 12 | import org.bouncycastle.x509.extension.AuthorityKeyIdentifierStructure; 13 | 14 | import javax.security.auth.x500.X500Principal; 15 | import javax.security.auth.x500.X500PrivateCredential; 16 | import java.io.IOException; 17 | import java.math.BigInteger; 18 | import java.security.*; 19 | import java.security.cert.Certificate; 20 | import java.security.cert.X509Certificate; 21 | import java.util.Calendar; 22 | import java.util.Date; 23 | 24 | /** 25 | * Helper class to generate an in-memory keystore filled with sample keys and 26 | * certificates for health checking. 27 | */ 28 | public final class SampleKeyStoreFactory { 29 | 30 | /** 31 | * Logger. 32 | */ 33 | private static final Logger LOG = LogManager.getLogger(SampleKeyStoreFactory.class); 34 | 35 | /** 36 | * root cert alias for looking up the certificate in the keystore. 37 | */ 38 | public static final String ROOT_ALIAS = "root"; 39 | 40 | /** 41 | * intermediate cert alias for looking up the certificate in the keystore. 42 | */ 43 | public static final String INTERMEDIATE_ALIAS = "intermediate"; 44 | 45 | /** 46 | * Certificate key alias for looking up the edn certificate in the keystore. 47 | */ 48 | public static final String END_ENTITY_ALIAS = "end"; 49 | 50 | /** 51 | * Private key alias for looking up the key in the keystore. 52 | */ 53 | public static final String PRIVATE_KEY_ALIAS = "privateKey"; 54 | 55 | /** 56 | * Secret key alias for looking up the key in the keystore. 57 | */ 58 | public static final String SECRET_KEY_ALIAS = "secretKeyAlias"; 59 | 60 | /** 61 | * HMAC key alias for looking up the key in the keystore. 62 | */ 63 | public static final String HMAC_KEY_ALIAS = "hmacKeyAlias"; 64 | 65 | /** 66 | * AES key alias for looking up the key in the keystore. 67 | */ 68 | public static final String AES_KEY_ALIAS = "aesKeyAlias"; 69 | 70 | /** 71 | * 3DES key alias for looking up the key in the keystore. 72 | */ 73 | public static final String TDES_KEY_ALIAS = "tdesKeyAlias"; 74 | 75 | /** 76 | * Pregenerated 3DES key for use in testing. 77 | */ 78 | public static final String HARD_TRIPLEDES_KEY = "bfE4QzSkV7lk2q3Lm8deCAuoq39bm5vW"; 79 | 80 | /** 81 | * 3DES key alias for looking up the hard 3des key in the keystore. 82 | */ 83 | public static final String SECRET_KEY_ALIAS_HARD = "secretKeyAliasHard"; 84 | 85 | /** 86 | * Serial number to give to the certificates. 87 | */ 88 | private static final int SERIAL_NUMBER = 5; 89 | 90 | /** 91 | * Provider to use. Default is BouncyCastle (BC). 92 | */ 93 | private String provider = "BC"; 94 | 95 | /** 96 | * Keystore type to generate. Default is JCEKS, which can also hold secret 97 | * key entries, whereas JKS cannot. 98 | */ 99 | private String keyStoreType = "JCEKS"; 100 | 101 | /** 102 | * The generated keystore. 103 | */ 104 | private KeyStore store = null; 105 | 106 | /** 107 | * The generated root keystore. 108 | */ 109 | private KeyStore rootStore = null; 110 | 111 | /** 112 | * The keystore password. Default is an empty string. 113 | */ 114 | private String keyPassword = null; 115 | 116 | /** 117 | * Default signature algorithm when generating certificates. 118 | */ 119 | private String signatureAlgoritm = "SHA1WithRSAEncryption"; 120 | 121 | /** 122 | * Default AES key size. 123 | */ 124 | private static final int AES_DEFAULT_KEYSIZE = 128; 125 | 126 | /** 127 | * Default 3DES key size. 128 | */ 129 | private static final int TDES_DEFAULT_KEYSIZE = 192; 130 | 131 | /** 132 | * Default RSA key size. 133 | */ 134 | private static final int RSA_DEFAULT_KEYSIZE = 1024; 135 | 136 | /** 137 | * AES key size. 138 | */ 139 | private int aesKeySize = AES_DEFAULT_KEYSIZE; 140 | 141 | /** 142 | * 3DES key size. 143 | */ 144 | private int tdesKeySize = TDES_DEFAULT_KEYSIZE; 145 | 146 | /** 147 | * Give each test certificate a validity of 10 years. 148 | */ 149 | private static final int VALID_YEARS = 10; 150 | 151 | /** 152 | * Validity of the certificates. Default is forever. 153 | */ 154 | private long validUntil = 0; 155 | 156 | /** 157 | * Constructs a new HealthCheckKeyStoreFactory. The default is a key factory 158 | * in software. 159 | */ 160 | public SampleKeyStoreFactory() { 161 | 162 | // setup validty of certificates 163 | Calendar cal = Calendar.getInstance(); 164 | // set validty of certificates 10 years 165 | cal.roll(Calendar.YEAR, VALID_YEARS); 166 | validUntil = cal.getTimeInMillis(); 167 | keyPassword = ""; 168 | } 169 | 170 | /** 171 | * Gets the generated keystore. 172 | * 173 | * @return the keystore. 174 | */ 175 | public KeyStore getRootKeyStore() { 176 | if (rootStore == null) { 177 | createCredentials(); 178 | } 179 | return rootStore; 180 | } 181 | 182 | /** 183 | * Gets the generated keystore. 184 | * 185 | * @return the keystore. 186 | */ 187 | public KeyStore getKeyStore() { 188 | if (store == null) { 189 | createCredentials(); 190 | } 191 | return store; 192 | } 193 | 194 | /** 195 | * Create a KeyStore containing the a private credential with certificate 196 | * chain and a trust anchor. 197 | */ 198 | private void createCredentials() { 199 | try { 200 | 201 | LOG.info("Using provider '" + provider + "' to generate in-memory keys and certificates."); 202 | LOG.info("Generating keystore of type: " + keyStoreType); 203 | rootStore = KeyStore.getInstance(keyStoreType); 204 | store = KeyStore.getInstance(keyStoreType); 205 | rootStore.load(null, keyPassword.toCharArray()); 206 | store.load(null, keyPassword.toCharArray()); 207 | // try { 208 | // KeyGenerator aesGenerator = KeyGenerator.getInstance("AES", 209 | // provider); 210 | // aesGenerator.init(aesKeySize); 211 | // SecretKey aesKey = aesGenerator.generateKey(); 212 | // store.setKeyEntry(AES_KEY_ALIAS, aesKey, 213 | // keyPassword.toCharArray(), null); 214 | // LOG.info("Generated and stored AES key."); 215 | // } catch (GeneralSecurityException e) { 216 | // LOG.error("could not generate AES key, perhaps your provider does not support this. Skipping."); 217 | // } 218 | // 219 | // try { 220 | // KeyGenerator tdesGenerator = KeyGenerator.getInstance("DESEDE", 221 | // provider); 222 | // tdesGenerator.init(tdesKeySize); 223 | // SecretKey tdesKey = tdesGenerator.generateKey(); 224 | // store.setKeyEntry(TDES_KEY_ALIAS, tdesKey, 225 | // keyPassword.toCharArray(), null); 226 | // LOG.info("Generated and stored 3DES key."); 227 | // } catch (GeneralSecurityException e) { 228 | // LOG.error("could not generate 3DES key, perhaps your provider does not support this. Skipping."); 229 | // } 230 | // 231 | // SecretKeySpec spec = new 232 | // SecretKeySpec(Base64.decode(HARD_TRIPLEDES_KEY), "DESEDE"); 233 | // LOG.info("Built and stored predefined 3DES key for use as master key for decrypting secret key."); 234 | // store.setKeyEntry(SECRET_KEY_ALIAS_HARD, spec, 235 | // keyPassword.toCharArray(), null); 236 | // try { 237 | // KeyGenerator g = KeyGenerator.getInstance("HmacSHA256", 238 | // provider); 239 | // SecretKey secretKey = g.generateKey(); 240 | // store.setKeyEntry(SECRET_KEY_ALIAS, secretKey, 241 | // keyPassword.toCharArray(), null); 242 | // LOG.info("Generated and stored HMACSHA256 key."); 243 | // } catch (GeneralSecurityException e) { 244 | // LOG.error("could not generate HMAC key, perhaps your provider does not support this. Skipping."); 245 | // } 246 | // String hmacKeyString = "hmac secret key"; 247 | // try { 248 | // SecretKey hmacKey = new 249 | // SecretKeySpec(hmacKeyString.getBytes("UTF-8"), "HmacSHA256"); 250 | // store.setKeyEntry(HMAC_KEY_ALIAS, hmacKey, 251 | // keyPassword.toCharArray(), null); 252 | // } catch (GeneralSecurityException e) { 253 | // LOG.error("could not store predefined HMAC key '" + hmacKeyString 254 | // + "' , perhaps your provider does not support this. Skipping."); 255 | // } 256 | 257 | X500PrivateCredential rootCredential = createRootCredential(); 258 | X500PrivateCredential interCredential = createIntermediateCredential(rootCredential.getPrivateKey(), 259 | rootCredential.getCertificate()); 260 | X500PrivateCredential endCredential = createEndEntityCredential(interCredential.getPrivateKey(), 261 | interCredential.getCertificate()); 262 | rootStore.setCertificateEntry(rootCredential.getAlias(), rootCredential.getCertificate()); 263 | store.setCertificateEntry(rootCredential.getAlias(), rootCredential.getCertificate()); 264 | store.setCertificateEntry(interCredential.getAlias(), interCredential.getCertificate()); 265 | 266 | // store the public key 267 | store.setCertificateEntry(endCredential.getAlias(), endCredential.getCertificate()); 268 | LOG.info("Generated and stored certificate."); 269 | 270 | // store the private key 271 | store.setKeyEntry(PRIVATE_KEY_ALIAS, endCredential.getPrivateKey(), keyPassword.toCharArray(), 272 | 273 | new Certificate[]{endCredential.getCertificate(), interCredential.getCertificate(), 274 | rootCredential.getCertificate()}); 275 | LOG.info("Generated and stored private key."); 276 | } catch (GeneralSecurityException e) { 277 | throw new RuntimeException(e); 278 | } catch (IOException e) { 279 | throw new RuntimeException(e); 280 | } 281 | } 282 | 283 | /** 284 | * Create a random RSA key pair. 285 | * 286 | * @return the key pair. 287 | * @throws GeneralSecurityException if an error occurs. 288 | */ 289 | public KeyPair generateRSAKeyPair() throws GeneralSecurityException { 290 | KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA", provider); 291 | 292 | kpGen.initialize(RSA_DEFAULT_KEYSIZE, new SecureRandom()); 293 | 294 | return kpGen.generateKeyPair(); 295 | } 296 | 297 | /** 298 | * Generate a X500PrivateCredential for the root entity. 299 | * 300 | * @return a root certificate. 301 | * @throws GeneralSecurityException if an error occurs. 302 | */ 303 | public X500PrivateCredential createRootCredential() throws GeneralSecurityException { 304 | KeyPair rootPair = generateRSAKeyPair(); 305 | X509Certificate rootCert = generateRootCert(rootPair); 306 | return new X500PrivateCredential(rootCert, rootPair.getPrivate(), ROOT_ALIAS); 307 | } 308 | 309 | /** 310 | * Generate a X500PrivateCredential for the intermediate entity. 311 | * 312 | * @param caKey the ca key. 313 | * @param caCert the caCert. 314 | * @return an intermediate certificate. 315 | * @throws GeneralSecurityException if an error occurs. 316 | */ 317 | public X500PrivateCredential createIntermediateCredential(PrivateKey caKey, X509Certificate caCert) 318 | throws GeneralSecurityException { 319 | KeyPair interPair = generateRSAKeyPair(); 320 | X509Certificate interCert = generateIntermediateCert(interPair.getPublic(), caKey, caCert); 321 | 322 | return new X500PrivateCredential( 323 | 324 | interCert, interPair.getPrivate(), INTERMEDIATE_ALIAS); 325 | } 326 | 327 | /** 328 | * Generate a sample V1 certificate to use as a CA root certificate. 329 | * 330 | * @param pair the keypair to base the certificate on. 331 | * @return the x509 certificate. 332 | * @throws GeneralSecurityException if an error occurs during certificate generation. 333 | */ 334 | public X509Certificate generateRootCert(KeyPair pair) throws GeneralSecurityException { 335 | X509V1CertificateGenerator certGen = new X509V1CertificateGenerator(); 336 | certGen.setSerialNumber(BigInteger.valueOf(SERIAL_NUMBER)); 337 | certGen.setIssuerDN(new X500Principal("CN=Test CA Certificate")); 338 | certGen.setNotBefore(new Date()); 339 | certGen.setNotAfter(new Date(validUntil)); 340 | certGen.setSubjectDN(new X500Principal("CN=Test CA Certificate")); 341 | certGen.setPublicKey(pair.getPublic()); 342 | certGen.setSignatureAlgorithm(signatureAlgoritm); 343 | 344 | return certGen.generate(pair.getPrivate(), provider); 345 | } 346 | 347 | /** 348 | * Generate a sample V3 certificate to use as an intermediate CA 349 | * certificate. 350 | * 351 | * @param intKey the private key for this certificate. 352 | * @param caKey the public key of the root certificate. 353 | * @param caCert the root certificate with which to sign the intermediate 354 | * certificate. 355 | * @return the intermediate certificate. 356 | * @throws GeneralSecurityException if an error occurs during certificate generation. 357 | */ 358 | public X509Certificate generateIntermediateCert(PublicKey intKey, PrivateKey caKey, X509Certificate caCert) 359 | throws GeneralSecurityException { 360 | X509V3CertificateGenerator certGen = new X509V3CertificateGenerator(); 361 | certGen.setSerialNumber(BigInteger.valueOf(SERIAL_NUMBER)); 362 | certGen.setIssuerDN(caCert.getSubjectX500Principal()); 363 | certGen.setNotBefore(new Date()); 364 | certGen.setNotAfter(new Date(validUntil)); 365 | certGen.setSubjectDN(new X500Principal("CN=Test Intermediate Certificate")); 366 | certGen.setPublicKey(intKey); 367 | certGen.setSignatureAlgorithm(signatureAlgoritm); 368 | certGen.addExtension(X509Extensions.AuthorityKeyIdentifier, false, new AuthorityKeyIdentifierStructure(caCert)); 369 | // certGen.addExtension(X509Extensions.SubjectKeyIdentifier, false, new SubjectKeyIdentifierStructure(intKey)); 370 | certGen.addExtension(X509Extensions.BasicConstraints, true, new BasicConstraints(0)); 371 | certGen.addExtension(X509Extensions.KeyUsage, true, new KeyUsage(KeyUsage.digitalSignature 372 | | KeyUsage.keyCertSign | KeyUsage.cRLSign)); 373 | 374 | GeneralName gn = new GeneralName(GeneralName.uniformResourceIdentifier, new DERIA5String( 375 | "http://test-hsmwin.rabobank.nl/crl/ryptotoolbox.crl")); 376 | // GeneralNames gns = new GeneralNames(new DERSequence(gn)); 377 | // DistributionPointName dpn = new DistributionPointName(0, gns); 378 | // DistributionPoint distp = new DistributionPoint(dpn, null, null); 379 | //certGen.addExtension(X509Extensions.CRLDistributionPoints, false, new DERSequence(distp)); 380 | return certGen.generate(caKey, provider); 381 | } 382 | 383 | /** 384 | * Generate a sample V3 certificate to use as an end entity certificate. 385 | * 386 | * @param entityKey the private key for this certificate. 387 | * @param caKey the public key of the intermediate certificate. 388 | * @param caCert the intermediate certificate with which to sign the end entity 389 | * certificate. 390 | * @return the end entity certificate. 391 | * @throws GeneralSecurityException if an error occurs during certificate generation. 392 | */ 393 | public X509Certificate generateEndEntityCert(PublicKey entityKey, PrivateKey caKey, X509Certificate caCert) 394 | throws GeneralSecurityException { 395 | X509V3CertificateGenerator certGen = new X509V3CertificateGenerator(); 396 | 397 | certGen.setSerialNumber(BigInteger.valueOf(SERIAL_NUMBER)); 398 | certGen.setIssuerDN(caCert.getSubjectX500Principal()); 399 | certGen.setNotBefore(new Date()); 400 | certGen.setNotAfter(new Date(validUntil)); 401 | certGen.setSubjectDN(new X500Principal("CN=Test End Certificate")); 402 | certGen.setPublicKey(entityKey); 403 | certGen.setSignatureAlgorithm(signatureAlgoritm); 404 | 405 | certGen.addExtension(X509Extensions.AuthorityKeyIdentifier, false, new AuthorityKeyIdentifierStructure(caCert)); 406 | // certGen.addExtension(X509Extensions.SubjectKeyIdentifier, false, new SubjectKeyIdentifierStructure(entityKey)); 407 | certGen.addExtension(X509Extensions.BasicConstraints, true, new BasicConstraints(false)); 408 | certGen.addExtension(X509Extensions.KeyUsage, true, new KeyUsage(KeyUsage.digitalSignature 409 | | KeyUsage.keyEncipherment)); 410 | 411 | GeneralName gn = new GeneralName(GeneralName.uniformResourceIdentifier, new DERIA5String( 412 | "http://test-hsmwin.rabobank.nl/crl/ryptotoolbox.crl")); 413 | // GeneralNames gns = new GeneralNames(new DERSequence(gn)); 414 | // DistributionPointName dpn = new DistributionPointName(0, gns); 415 | // DistributionPoint distp = new DistributionPoint(dpn, null, null); 416 | // certGen.addExtension(X509Extensions.CRLDistributionPoints, false, new DERSequence(distp)); 417 | 418 | return certGen.generate(caKey, provider); 419 | } 420 | 421 | /** 422 | * Generate a X500PrivateCredential for the end entity. 423 | * 424 | * @param caKey the private key for the end entity certificate. 425 | * @param caCert the end certificate. 426 | * @return the credential. 427 | * @throws GeneralSecurityException if an error occurs. 428 | */ 429 | public X500PrivateCredential createEndEntityCredential(PrivateKey caKey, X509Certificate caCert) 430 | throws GeneralSecurityException { 431 | KeyPair endPair = generateRSAKeyPair(); 432 | X509Certificate endCert = generateEndEntityCert(endPair.getPublic(), caKey, caCert); 433 | 434 | return new X500PrivateCredential(endCert, endPair.getPrivate(), END_ENTITY_ALIAS); 435 | } 436 | 437 | } 438 | --------------------------------------------------------------------------------