PGPKeyConverter.java
/*
* Copyright 2023 Emmanuel Bourg
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.jsign;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.KeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.util.Date;
import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.bcpg.sig.KeyFlags;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jcajce.provider.asymmetric.rsa.BCRSAPrivateCrtKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECPublicKeySpec;
import org.bouncycastle.openpgp.PGPEncryptedData;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPKeyPair;
import org.bouncycastle.openpgp.PGPKeyRingGenerator;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator;
import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor;
import org.bouncycastle.openpgp.operator.PGPContentSignerBuilder;
import org.bouncycastle.openpgp.operator.PGPDigestCalculator;
import org.bouncycastle.openpgp.operator.bc.BcPBESecretKeyEncryptorBuilder;
import org.bouncycastle.openpgp.operator.bc.BcPGPContentSignerBuilder;
import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider;
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyPair;
import static org.bouncycastle.bcpg.HashAlgorithmTags.*;
/**
* Tool to generate a PGP key from the same key material used for the tests.
*
* <pre>
* $ gpg --import privatekey.asc
* $ gpg --edit-key jsign@example.com
*
* gpg> keytocard
* Really move the primary key? (y/N) y
* Please select where to store the key:
* (1) Signature key
* (2) Encryption key
* (3) Authentication key
* Your selection? 1
*
* Replace existing key? (y/N) y
* </pre>
*/
public class PGPKeyConverter {
public static void main(String[] args) throws Exception {
File rsaPrivateKeyFile = new File("jsign-core/src/test/resources/keystores/privatekey.pkcs1.pem");
PGPKeyPair rsaKeyPair = getKeyPair(rsaPrivateKeyFile);
writeKeyring(rsaKeyPair, "Jsign Test Key (RSA) <jsign-rsa@example.com>", new File(rsaPrivateKeyFile.getParentFile(), "privatekey.asc"));
File ecPrivateKeyFile = new File("jsign-core/src/test/resources/keystores/privatekey-ec-p384.pkcs1.pem");
PGPKeyPair ecKeyPair = getKeyPair(ecPrivateKeyFile);
writeKeyring(ecKeyPair, "Jsign Test Key (EC) <jsign-ec@example.com>", new File(ecPrivateKeyFile.getParentFile(), "privatekey-ec-p384.asc"));
}
private static PGPKeyPair getKeyPair(File privateKeyFile) throws PGPException, GeneralSecurityException {
PrivateKey key = PrivateKeyUtils.load(privateKeyFile, null);
Date creationDate = new Date(1668510000000L);
if (key instanceof BCRSAPrivateCrtKey) {
BCRSAPrivateCrtKey privateKey = (BCRSAPrivateCrtKey) key;
KeySpec keySpec = new RSAPublicKeySpec(privateKey.getModulus(), privateKey.getPublicExponent());
PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(keySpec);
KeyPair keyPair = new KeyPair(publicKey, privateKey);
return new JcaPGPKeyPair(PGPPublicKey.RSA_GENERAL, keyPair, creationDate);
} else {
BCECPrivateKey privateKey = (BCECPrivateKey) key;
ECPublicKeySpec pubSpec = new ECPublicKeySpec(privateKey.getParameters().getG().multiply(privateKey.getD()), privateKey.getParameters());
BCECPublicKey publicKey = new BCECPublicKey(privateKey.getAlgorithm(), pubSpec, BouncyCastleProvider.CONFIGURATION);
KeyPair keyPair = new KeyPair(publicKey, privateKey);
return new JcaPGPKeyPair(PGPPublicKey.ECDSA, keyPair, creationDate);
}
}
private static void writeKeyring(PGPKeyPair pgpKeyPair, String id, File targetFile) throws IOException, PGPException {
PGPSignatureSubpacketGenerator subpacketGenerator = new PGPSignatureSubpacketGenerator();
subpacketGenerator.setKeyFlags(false, KeyFlags.SIGN_DATA | KeyFlags.AUTHENTICATION | KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE | KeyFlags.CERTIFY_OTHER);
PGPDigestCalculator sha1DigestCalculator = new BcPGPDigestCalculatorProvider().get(SHA1);
PGPDigestCalculator sha2DigestCalculator = new BcPGPDigestCalculatorProvider().get(SHA256);
PBESecretKeyEncryptor secretKeyEncryptor = new BcPBESecretKeyEncryptorBuilder(PGPEncryptedData.AES_256, sha2DigestCalculator).build("password".toCharArray());
PGPContentSignerBuilder contentSignerBuilder = new BcPGPContentSignerBuilder(pgpKeyPair.getPublicKey().getAlgorithm(), SHA512);
PGPKeyRingGenerator keyringGenerator = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, pgpKeyPair,
id, sha1DigestCalculator, subpacketGenerator.generate(), null, contentSignerBuilder, secretKeyEncryptor);
PGPSecretKeyRing keyring = keyringGenerator.generateSecretKeyRing();
keyring.getSecretKeys().forEachRemaining(key -> {
System.out.println("Key ID : " + Long.toHexString(key.getKeyID()).toUpperCase());
System.out.println("Size : " + key.getPublicKey().getBitStrength());
System.out.println("Master Key : " + key.isMasterKey());
System.out.println("Signing Key : " + key.isSigningKey());
System.out.print("User IDs : ");
key.getUserIDs().forEachRemaining(System.out::print);
System.out.println();
System.out.println();
});
try (ArmoredOutputStream out = new ArmoredOutputStream(new BufferedOutputStream(Files.newOutputStream(targetFile.toPath())))) {
keyring.encode(out);
}
}
}