Skip to content

Instantly share code, notes, and snippets.

@cristiancrazy
Last active December 6, 2023 09:37
Show Gist options
  • Save cristiancrazy/94a2a403859886f1f2c463f700a99429 to your computer and use it in GitHub Desktop.
Save cristiancrazy/94a2a403859886f1f2c463f700a99429 to your computer and use it in GitHub Desktop.
BouncyCastle: get instance of EC private key / CMS from a string, and decrypt CMS (PEM) files.
plugins {
application
}
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(17))
}
}
repositories {
// Use Maven Central for resolving dependencies.
mavenCentral()
}
dependencies {
// Bouncy Castle
implementation("org.bouncycastle:bcprov-jdk18on:1.76")
implementation("org.bouncycastle:bcpkix-jdk18on:1.76")
}
application {
// Define the main class for the application.
mainClass.set("cmsoperations.WorkingCMS")
}
/* ==========================================
* Author: Cristian Capraro
* Date: 03-09-2023
* Working with: BouncyCastle & Java Security
* :: Cryptographic Message Syntax
* ========================================== */
package cmsoperations;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.time.Duration;
import java.time.Instant;
import java.util.Base64;
import java.util.Collection;
import java.util.Iterator;
import org.bouncycastle.cms.CMSEnvelopedDataParser;
import org.bouncycastle.cms.Recipient;
import org.bouncycastle.cms.RecipientInformation;
import org.bouncycastle.cms.RecipientInformationStore;
import org.bouncycastle.cms.jcajce.JceKeyAgreeEnvelopedRecipient;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
public class WorkingCMS {
/**
* Decrypt Cryptographic Message Syntax (PEM (= S/MIME Without the header))
* @param message single line string in Base64
* @param key the private key used to decrypt the message
* @return the clear message on success | null on failure
*/
private String cmsDecrypt(String message, PrivateKey key){
byte[] byteMessage = Base64.getDecoder().decode(message);
try{
CMSEnvelopedDataParser ep = new CMSEnvelopedDataParser(byteMessage);
RecipientInformationStore recipients = ep.getRecipientInfos();
Collection<RecipientInformation> c = recipients.getRecipients();
Iterator<RecipientInformation> iter = c.iterator();
RecipientInformation recipient = (RecipientInformation) iter.next();
BouncyCastleProvider provide = new BouncyCastleProvider();
Recipient rec = new JceKeyAgreeEnvelopedRecipient(key).setProvider(provide);
return new String(recipient.getContent(rec));
}catch (Exception e){
e.printStackTrace();
}
return null;
}
/**
* Clean CMS Message before using it raw
* @param message Pure CMS file
* @return the clean single line message
*/
private String cleanMessage(String message){
return message
.replace("\n", "")
.replace("message", message)
.replaceAll("-+(BEGIN|END) CMS-+", "")
.replaceAll(" +", "");
}
/**
* Get the instance of PrivateKey using a string
* @param privateKeyString single line string in Base64 which represents EC Key
* @return the EC Private key instance on success | null on failure
*/
private PrivateKey convertKey(String privateKeyString){
byte [] encodedBytes = Base64.getDecoder().decode(privateKeyString);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encodedBytes);
KeyFactory keyFactory = null;
PrivateKey privateKey = null;
try{
BouncyCastleProvider provide = new BouncyCastleProvider();
// Use the BouncyCastle provider (expecially if you're using Android)
keyFactory = KeyFactory.getInstance("EC", provide);
privateKey = keyFactory.generatePrivate(keySpec);
return privateKey;
}catch (Exception e){
e.printStackTrace();
}
return null;
}
/**
* Clean PrivateKEY before using it raw
* @param message Pure PrivateKEY file
* @return the clean single line key
*/
private String cleanPrivateKey(String key){
return key
.replace("\n", "")
.replace("message", key)
.replaceAll("-+(BEGIN|END) PRIVATE KEY-+", "")
.replaceAll(" +", "");
}
/**
* Testing private key & CMS Decryption
*/
public static void main(String[] args) {
/* Input: Private EC Key and CMS Message */
String ECKey =
"""
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgzzQVEWfowpbGpJHu
JdVJ1ByCN740IgosyaBXIIUfaoahRANCAATdbBXgcbpWrAbz0nYdsiWd+WMD8blZ
9+yAnyoavuhEpgPSGOV/uxjtFM2N8A/iM2pgU5r7cgVwTUWRUeaE/bMs
-----END PRIVATE KEY-----
""";
String encryptedMessage =
"""
-----BEGIN CMS-----
MIIBNwYJKoZIhvcNAQcDoIIBKDCCASQCAQIxgbGhga4CAQOgUaFPMAkGByqGSM49
AgEDQgAEK/ikFCT6lmrEPtg1m27yG0mbtACUaablaxarK61U3RMKxC9QoCcCyIJE
LEOvpTxBcmOJQqZS65ME5Xx2CG7tSDAYBgkrgQUQhkg/AAIwCwYJYIZIAWUDBAEt
MDwwOqAWBBSSS0Gyj/WxE7rK/MmJUYZr5RPaNQQgIE9DCPuiTZBCE3U2SWMByLo6
NZjrY6tAV/i1vBk1IhowawYJKoZIhvcNAQcBMBQGCCqGSIb3DQMHBAiKrE0zZY0C
IoBIAjtkH9+1WcURTEvr9L2F7s2rgkVNJQLDYYBPBtz5YN4yJJFEP8FJ+eomq5JA
r8AeAMRtrxPy97O0aEXibFZ8ckEIsMrvrBFX
-----END CMS-----
""";
/* Clean and Decrypt message */
App app = new App();
Instant t1 = Instant.now(), t2;
String clearMessage = app.cmsDecrypt(app.cleanMessage(encryptedMessage), app.convertKey(app.cleanPrivateKey(ECKey)));
t2 = Instant.now();
System.out.println(clearMessage + System.lineSeparator() + "Execution time (ms): " + Duration.between(t1, t2).toMillis());
System.exit(0);
}
}
@cristiancrazy
Copy link
Author

There's a timer which count (in ms) the time required for message decryption

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment