Skip to content

Commit

Permalink
Fixes for encrypted PEM input / output
Browse files Browse the repository at this point in the history
  • Loading branch information
mikera committed Jan 29, 2024
1 parent 947cc92 commit 688173f
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 32 deletions.
38 changes: 18 additions & 20 deletions convex-core/src/main/java/convex/core/crypto/PEMTools.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,27 @@

import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PKCS8Generator;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPKCS8Generator;
import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder;
import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8EncryptorBuilder;
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
import org.bouncycastle.openssl.PKCS8Generator;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
import org.bouncycastle.openssl.jcajce.JcaPKCS8Generator;
import org.bouncycastle.operator.InputDecryptorProvider;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.OutputEncryptor;
import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
import org.bouncycastle.pkcs.PKCSException;
import org.bouncycastle.pkcs.jcajce.JcePKCSPBEInputDecryptorProviderBuilder;
import org.bouncycastle.pkcs.jcajce.JcePKCSPBEOutputEncryptorBuilder;
import org.bouncycastle.util.io.pem.PemObject;

public class PEMTools {
// private static String encryptionAlgorithm="AES-128-CBC";

static {
// Ensure we have BC provider initialised etc.
Providers.init();
}

/**
* Writes a key pair to a String
Expand Down Expand Up @@ -79,46 +84,39 @@ public static AKeyPair readPEM(String pem) throws GeneralSecurityException {
* Encrypt a priavte key into a PEM formated text
*
* @param privateKey Private key to encrypt
*
* @param password Password to use for encryption
*
* @return PEM text that can be saved or sent to another keystore
*
* @throws Error Any encryption error that occurs
*/
public static String encryptPrivateKeyToPEM(PrivateKey privateKey, char[] password) throws Error {
StringWriter stringWriter = new StringWriter();
JcaPEMWriter writer = new JcaPEMWriter(stringWriter);
JceOpenSSLPKCS8EncryptorBuilder builder = new JceOpenSSLPKCS8EncryptorBuilder(PKCS8Generator.AES_256_CBC);
builder.setPassword(password);

JcePKCSPBEOutputEncryptorBuilder builder = new JcePKCSPBEOutputEncryptorBuilder(PKCS8Generator.PBE_SHA1_RC2_128);
try {
OutputEncryptor encryptor = builder.build();
OutputEncryptor encryptor = builder.build(password);
JcaPKCS8Generator generator = new JcaPKCS8Generator(privateKey, encryptor);
writer.writeObject(generator);
writer.close();
} catch (IOException | OperatorCreationException e) {
throw new Error("cannot encrypt private key to PEM: " + e);
}
}
return stringWriter.toString();
}

/**
* Decrypt a PEM string to a private key. The PEM string must contain the "ENCRYPTED PRIVATE KEY" type.
*
* @param pemText PEM string to decode
*
* @param password Password that was used to encrypt the private key
*
* @return PrivateKey stored in the PEM
*
* @throws Error on reading the PEM, decryption and decoding the private key
*/
public static PrivateKey decryptPrivateKeyFromPEM(String pemText, char[] password) throws Error {
PrivateKey privateKey = null;
StringReader stringReader = new StringReader(pemText);
PEMParser pemParser = new PEMParser(stringReader);
PemObject pemObject = null;
Security.addProvider(new BouncyCastleProvider());
JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
try {
pemObject = pemParser.readPemObject();
Expand All @@ -130,7 +128,7 @@ public static PrivateKey decryptPrivateKeyFromPEM(String pemText, char[] passwor
}

} catch (IOException e) {
throw new Error("cannot read PEM " + e);
throw new Error("cannot read PEM",e);
}

if (pemObject == null) {
Expand All @@ -139,14 +137,14 @@ public static PrivateKey decryptPrivateKeyFromPEM(String pemText, char[] passwor
try {
PKCS8EncryptedPrivateKeyInfo encryptedInfo = new PKCS8EncryptedPrivateKeyInfo(pemObject.getContent());

JceOpenSSLPKCS8DecryptorProviderBuilder inputBuilder = new JceOpenSSLPKCS8DecryptorProviderBuilder();
JcePKCSPBEInputDecryptorProviderBuilder inputBuilder = new JcePKCSPBEInputDecryptorProviderBuilder();
inputBuilder.setProvider("BC");
InputDecryptorProvider decryptor = inputBuilder.build(password);

PrivateKeyInfo privateKeyInfo = encryptedInfo.decryptPrivateKeyInfo(decryptor);
privateKey = converter.getPrivateKey(privateKeyInfo);
} catch (IOException | OperatorCreationException | PKCSException e) {
throw new Error("cannot decrypt password from PEM " + e);
} catch (IOException | PKCSException e) {
throw new Error("cannot decrypt password from PEM ", e);
}
return privateKey;
}
Expand Down
24 changes: 12 additions & 12 deletions convex-core/src/test/java/convex/core/crypto/PEMToolsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,38 +19,38 @@ String generateRandomHex(int size) {
random.nextBytes(password);
return Utils.toHexString(password);
}

static AKeyPair KP = AKeyPair.createSeeded(156858);

@Test
public void testPEMPrivateKey() {
AKeyPair keyPair = AKeyPair.generate();
AKeyPair keyPair = KP;

String testPassword = generateRandomHex(32);
String pemText = null;
try {
pemText = PEMTools.encryptPrivateKeyToPEM(keyPair.getPrivate(), testPassword.toCharArray());
} catch (Error e) {
throw e;
}
pemText = PEMTools.encryptPrivateKeyToPEM(keyPair.getPrivate(), testPassword.toCharArray());

assertTrue(pemText != null);
PrivateKey privateKey = null;
try {
privateKey = PEMTools.decryptPrivateKeyFromPEM(pemText, testPassword.toCharArray());
} catch (Error e) {
throw e;
}

privateKey = PEMTools.decryptPrivateKeyFromPEM(pemText, testPassword.toCharArray());

AKeyPair importKeyPair = AKeyPair.create(privateKey);
AString data = Strings.create(generateRandomHex(1024));
ASignature leftSignature = keyPair.sign(data.getHash());
ASignature rightSignature = importKeyPair.sign(data.getHash());
assertTrue(leftSignature.equals(rightSignature));


// TODO: fix equality testing
// Blob key1 = keyPair.getEncodedPrivateKey();
// Blob key2 = importKeyPair.getEncodedPrivateKey();
//assertEquals(key1,key2);
//(keyPair,importKeyPair);
}

public static void main(String... args) {
AKeyPair kp = KP;
System.out.println(kp.getSeed());
System.out.println(PEMTools.encryptPrivateKeyToPEM(kp.getPrivate(),"foo".toCharArray()));
}
}

0 comments on commit 688173f

Please sign in to comment.