Skip to content

Instantly share code, notes, and snippets.

@alopresto
Created December 16, 2015 22:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alopresto/15477cf7bf12f74359c5 to your computer and use it in GitHub Desktop.
Save alopresto/15477cf7bf12f74359c5 to your computer and use it in GitHub Desktop.
Test code and output using BcPEMDecryptorProvider to try to decrypt an OpenSSL-encrypted file
@Test
public void testShouldDecryptOpenSSLWithBcPEMDecryptor() throws Exception {
// Arrange
if (!isUnlimitedStrengthCrypto()) {
logger.info("Running in limited encryption mode. Overriding...")
setJCEUnlimitedStrength()
logger.info("Now running with unlimited strength crypto")
}
logger.info("Plaintext: ${plaintext}")
File file = new File("src/test/resources/salted_raw.enc")
byte[] encryptedBytes = file.bytes
logger.info("Read encrypted bytes: ${Hex.encodeHexString(encryptedBytes)}")
String saltHex = OpenSSLPBEEncryptor.extractHexSaltFromHeader(encryptedBytes)
logger.info("Extracted salt hex: ${saltHex.padLeft(saltHex.length() * 2)}")
final String PASSWORD = "thisIsABadPassword"
byte[] salt = Hex.decodeHex(saltHex.toCharArray())
// Sanity check against known values
final String EXPECTED_KEY_HEX = "9C876867E3E914DE8E6249D1C5B4EC21DBCEB8A3F1A09579C8C5AE08B73DCB2E"
logger.info("Expected key: ${EXPECTED_KEY_HEX.toLowerCase()}")
final String EXPECTED_IV_HEX = "C3A9B16EA80F76CEDA1DB445A62F1BC2"
logger.info("Expected IV: ${EXPECTED_IV_HEX.toLowerCase()}")
final String EXPECTED_SALT_HEX = "4D41C78350EE0CE8"
logger.info("Expected salt: ${EXPECTED_SALT_HEX.toLowerCase()}")
final byte[] EXPECTED_KEY = Hex.decodeHex(EXPECTED_KEY_HEX.toCharArray())
final byte[] EXPECTED_IV = Hex.decodeHex(EXPECTED_IV_HEX.toCharArray())
final byte[] EXPECTED_SALT = Hex.decodeHex(EXPECTED_SALT_HEX.toCharArray())
final int EXPECTED_KEY_SIZE = 256
final int EXPECTED_IV_SIZE = 128
String algorithm = "PBEWITHMD5AND256BITAES-CBC-OPENSSL"
String provider = "BC"
logger.info("Password: ${PASSWORD}")
logger.info("Salt : ${fb(salt)}")
// Demonstrate that the existing code works correctly
PBEKeySpec pbeSpec = new PBEKeySpec(PASSWORD.toCharArray());
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(algorithm, provider);
SecretKey secretKey = secretKeyFactory.generateSecret(pbeSpec);
PBEParameterSpec saltParams = new PBEParameterSpec(salt, 0);
Cipher cipher = Cipher.getInstance(algorithm, "BC");
cipher.init(Cipher.DECRYPT_MODE, secretKey, saltParams);
logger.info("Derived IV: ${Hex.encodeHexString(cipher.IV)}")
assert cipher.IV == EXPECTED_IV
final byte[] cipherBytes = OpenSSLPBEEncryptor.extractCipherTextFromBody(encryptedBytes)
byte[] sanityRecovered = cipher.doFinal(cipherBytes)
logger.info("Recovered (hex): ${Hex.encodeHexString(sanityRecovered)}")
logger.info("Recovered: ${new String(sanityRecovered)}")
assert sanityRecovered == plaintext.bytes
// // Manually derive the IV because the decryptor requires it as a parameter
// OpenSSLPBEParametersGenerator gen = new OpenSSLPBEParametersGenerator();
// gen.init(PASSWORD.bytes, salt);
// ParametersWithIV parametersWithIV = (ParametersWithIV) gen.generateDerivedParameters(EXPECTED_KEY_SIZE, EXPECTED_IV_SIZE);
// byte[] iv = parametersWithIV.IV
// logger.info("Derived IV (PEM): ${Hex.encodeHexString(iv)}")
// assert iv == EXPECTED_IV
// Act
BcPEMDecryptorProvider decryptorProvider = new BcPEMDecryptorProvider(PASSWORD.toCharArray())
PEMDecryptor decryptor = decryptorProvider.get(algorithm)
byte[] recovered = decryptor.decrypt(cipherBytes, salt)
// Assert
assert new String(recovered) == plaintext
}
Output:
[main] INFO com.hortonworks.crypto.OpenSSLPBEEncryptorTest - Running in limited encryption mode. Overriding...
[main] INFO com.hortonworks.crypto.OpenSSLPBEEncryptorTest - Now running with unlimited strength crypto
[main] INFO com.hortonworks.crypto.OpenSSLPBEEncryptorTest - Plaintext: This is a plaintext message.
[main] INFO com.hortonworks.crypto.OpenSSLPBEEncryptorTest - Read encrypted bytes: 53616c7465645f5f4d41c78350ee0ce8e0ebb9a85bd0cbef4ff06849135817413fc73c6f3bcdab247fa7fb446166108f
[main] INFO com.hortonworks.crypto.OpenSSLPBEEncryptorTest - Extracted salt hex: 4d41c78350ee0ce8
[main] INFO com.hortonworks.crypto.OpenSSLPBEEncryptorTest - Expected key: 9c876867e3e914de8e6249d1c5b4ec21dbceb8a3f1a09579c8c5ae08b73dcb2e
[main] INFO com.hortonworks.crypto.OpenSSLPBEEncryptorTest - Expected IV: c3a9b16ea80f76ceda1db445a62f1bc2
[main] INFO com.hortonworks.crypto.OpenSSLPBEEncryptorTest - Expected salt: 4d41c78350ee0ce8
[main] INFO com.hortonworks.crypto.OpenSSLPBEEncryptorTest - Password: thisIsABadPassword
[main] INFO com.hortonworks.crypto.OpenSSLPBEEncryptorTest - Salt : 77 65 -57 -125 80 -18 12 -24
[main] INFO com.hortonworks.crypto.OpenSSLPBEEncryptorTest - Derived IV: c3a9b16ea80f76ceda1db445a62f1bc2
[main] INFO com.hortonworks.crypto.OpenSSLPBEEncryptorTest - Recovered (hex): 54686973206973206120706c61696e74657874206d6573736167652e
[main] INFO com.hortonworks.crypto.OpenSSLPBEEncryptorTest - Recovered: This is a plaintext message.
org.bouncycastle.openssl.EncryptionException: unknown encryption with private key
at org.bouncycastle.openssl.bc.PEMUtilities.crypt(Unknown Source)
at org.bouncycastle.openssl.bc.BcPEMDecryptorProvider$1.decrypt(Unknown Source)
at org.bouncycastle.openssl.PEMDecryptor$decrypt.call(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:110)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:130)
at com.hortonworks.crypto.OpenSSLPBEEncryptorTest.testShouldDecryptOpenSSLWithBcPEMDecryptor(OpenSSLPBEEncryptorTest.groovy:1312)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:117)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment