/*
* Kontalk Android client
* Copyright (C) 2017 Kontalk Devteam <devteam@kontalk.org>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.kontalk.crypto;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.PublicKey;
import java.security.SignatureException;
import java.security.spec.ECGenParameterSpec;
import java.util.Date;
import java.util.Iterator;
import java.util.Locale;
import org.jxmpp.util.XmppStringUtils;
import org.spongycastle.bcpg.ArmoredInputStream;
import org.spongycastle.bcpg.BCPGInputStream;
import org.spongycastle.bcpg.BCPGKey;
import org.spongycastle.bcpg.DSASecretBCPGKey;
import org.spongycastle.bcpg.ECSecretBCPGKey;
import org.spongycastle.bcpg.ElGamalSecretBCPGKey;
import org.spongycastle.bcpg.HashAlgorithmTags;
import org.spongycastle.bcpg.PublicKeyPacket;
import org.spongycastle.bcpg.RSASecretBCPGKey;
import org.spongycastle.jce.provider.BouncyCastleProvider;
import org.spongycastle.openpgp.PGPEncryptedData;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPKeyPair;
import org.spongycastle.openpgp.PGPKeyRingGenerator;
import org.spongycastle.openpgp.PGPObjectFactory;
import org.spongycastle.openpgp.PGPPrivateKey;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureGenerator;
import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
import org.spongycastle.openpgp.PGPUtil;
import org.spongycastle.openpgp.operator.KeyFingerPrintCalculator;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.PBESecretKeyEncryptor;
import org.spongycastle.openpgp.operator.PGPDigestCalculator;
import org.spongycastle.openpgp.operator.PGPDigestCalculatorProvider;
import org.spongycastle.openpgp.operator.bc.BcKeyFingerprintCalculator;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPKeyConverter;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPKeyPair;
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder;
import android.os.Parcel;
import org.kontalk.util.MessageUtils;
/** Some PGP utility method, mainly for use by {@link PersonalKey}. */
public class PGP {
/** Security provider: Spongy Castle. */
public static Provider PROVIDER;
/** Default EC curve used. */
private static final String EC_CURVE = "P-256";
/** Default RSA key length used. */
private static final int RSA_KEY_LENGTH = 2048;
/** Singleton for converting a PGP key to a JCA key. */
private static JcaPGPKeyConverter sKeyConverter;
static KeyFingerPrintCalculator sFingerprintCalculator =
new BcKeyFingerprintCalculator();
private PGP() {
}
public static final class PGPDecryptedKeyPairRing {
/* Authentication key. */
PGPKeyPair authKey;
/* Signing key. */
PGPKeyPair signKey;
/* Encryption key. */
PGPKeyPair encryptKey;
public PGPDecryptedKeyPairRing(PGPKeyPair auth, PGPKeyPair sign, PGPKeyPair encrypt) {
this.authKey = auth;
this.signKey = sign;
this.encryptKey = encrypt;
}
}
public static final class PGPKeyPairRing {
public PGPPublicKeyRing publicKey;
public PGPSecretKeyRing secretKey;
PGPKeyPairRing(PGPPublicKeyRing publicKey, PGPSecretKeyRing secretKey) {
this.publicKey = publicKey;
this.secretKey = secretKey;
}
public static PGPKeyPairRing load(byte[] privateKeyData, byte[] publicKeyData)
throws IOException, PGPException {
ArmoredInputStream inPublic = new ArmoredInputStream(new ByteArrayInputStream(publicKeyData));
PGPPublicKeyRing publicKey = new PGPPublicKeyRing(inPublic, sFingerprintCalculator);
ArmoredInputStream inPrivate = new ArmoredInputStream(new ByteArrayInputStream(privateKeyData));
PGPSecretKeyRing secretKey = new PGPSecretKeyRing(inPrivate, sFingerprintCalculator);
return new PGPKeyPairRing(publicKey, secretKey);
}
}
public static void registerProvider() {
// create spongy castle provider
// do not register it as can cause issues on some devices
PROVIDER = new BouncyCastleProvider();
try {
// apply RNG fixes
PRNGFixes.apply();
}
catch (Exception e) {
throw new PRNGFixException("Unable to apply PRNG fix", e);
}
}
public static final class PRNGFixException extends SecurityException {
PRNGFixException(String message, Throwable cause) {
super(message, cause);
}
}
/** Creates an ECDSA/ECDH key pair. */
public static PGPDecryptedKeyPairRing create(Date timestamp)
throws NoSuchAlgorithmException, NoSuchProviderException, PGPException, InvalidAlgorithmParameterException {
KeyPairGenerator gen;
PGPKeyPair authKp, encryptKp, signKp;
gen = KeyPairGenerator.getInstance("RSA", PROVIDER);
gen.initialize(RSA_KEY_LENGTH);
authKp = new JcaPGPKeyPair(PGPPublicKey.RSA_GENERAL, gen.generateKeyPair(), timestamp);
gen = KeyPairGenerator.getInstance("ECDH", PROVIDER);
gen.initialize(new ECGenParameterSpec(EC_CURVE));
encryptKp = new JcaPGPKeyPair(PGPPublicKey.ECDH, gen.generateKeyPair(), timestamp);
gen = KeyPairGenerator.getInstance("ECDSA", PROVIDER);
gen.initialize(new ECGenParameterSpec(EC_CURVE));
signKp = new JcaPGPKeyPair(PGPPublicKey.ECDSA, gen.generateKeyPair(), timestamp);
return new PGPDecryptedKeyPairRing(authKp, signKp, encryptKp);
}
/** Creates public and secret keyring for a given keypair. */
public static PGPKeyPairRing store(PGPDecryptedKeyPairRing pair,
String id,
String passphrase)
throws PGPException, IOException {
PGPSignatureSubpacketGenerator sbpktGen;
// some hashed subpackets for the key
sbpktGen = new PGPSignatureSubpacketGenerator();
// the master key is used for authentication and certification
sbpktGen.setKeyFlags(false, PGPKeyFlags.CAN_AUTHENTICATE | PGPKeyFlags.CAN_CERTIFY);
sbpktGen.setPrimaryUserID(false, true);
PGPDigestCalculator digestCalc = new JcaPGPDigestCalculatorProviderBuilder().build().get(HashAlgorithmTags.SHA1);
PGPKeyRingGenerator keyRingGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, pair.authKey,
id, digestCalc, sbpktGen.generate(), null,
new JcaPGPContentSignerBuilder(pair.authKey.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA256),
new JcePBESecretKeyEncryptorBuilder(PGPEncryptedData.AES_256, digestCalc)
.setProvider(PROVIDER).build(passphrase.toCharArray()));
// add signing subkey
sbpktGen = new PGPSignatureSubpacketGenerator();
sbpktGen.setKeyFlags(false, PGPKeyFlags.CAN_SIGN);
sbpktGen.setEmbeddedSignature(false, crossCertify(pair.signKey, pair.authKey.getPublicKey()));
keyRingGen.addSubKey(pair.signKey, sbpktGen.generate(), null);
// add encryption subkey
sbpktGen = new PGPSignatureSubpacketGenerator();
sbpktGen.setKeyFlags(false, PGPKeyFlags.CAN_ENCRYPT_COMMS);
keyRingGen.addSubKey(pair.encryptKey, sbpktGen.generate(), null);
PGPSecretKeyRing secRing = keyRingGen.generateSecretKeyRing();
PGPPublicKeyRing pubRing = keyRingGen.generatePublicKeyRing();
return new PGPKeyPairRing(pubRing, secRing);
}
/** Generates a cross-certification for a subkey. */
private static PGPSignature crossCertify(PGPKeyPair signer, PGPPublicKey key) throws PGPException {
PGPSignatureGenerator sGen = new PGPSignatureGenerator(
new JcaPGPContentSignerBuilder(signer.getPublicKey().getAlgorithm(),
PGPUtil.SHA256).setProvider(PROVIDER));
sGen.init(PGPSignature.PRIMARYKEY_BINDING, signer.getPrivateKey());
return sGen.generateCertification(key);
}
/** Revokes the given key. */
public static PGPPublicKey revokeKey(PGPKeyPair secret)
throws PGPException, IOException, SignatureException {
PGPPrivateKey pgpPrivKey = secret.getPrivateKey();
PGPPublicKey pgpPubKey = secret.getPublicKey();
PGPSignatureGenerator sGen = new PGPSignatureGenerator(
new JcaPGPContentSignerBuilder(secret.getPublicKey().getAlgorithm(),
PGPUtil.SHA256).setProvider(PROVIDER));
sGen.init(PGPSignature.KEY_REVOCATION, pgpPrivKey);
return PGPPublicKey.addCertification(pgpPubKey, sGen.generateCertification(pgpPubKey));
}
public static PGPDecryptedKeyPairRing fromParcel(Parcel in) throws PGPException, IOException {
PGPKeyPair authKp = readKeyPairFromParcel(in);
PGPKeyPair signKp = readKeyPairFromParcel(in);
PGPKeyPair encryptKp = readKeyPairFromParcel(in);
return new PGPDecryptedKeyPairRing(authKp, signKp, encryptKp);
}
public static void toParcel(PGPDecryptedKeyPairRing pair, Parcel dest)
throws NoSuchProviderException, PGPException, IOException {
writeKeyPairToParcel(pair.authKey, dest);
writeKeyPairToParcel(pair.signKey, dest);
writeKeyPairToParcel(pair.encryptKey, dest);
}
private static void writeKeyPairToParcel(PGPKeyPair keypair, Parcel dest) throws IOException {
// write private key
PGPPrivateKey priv = keypair.getPrivateKey();
// key ID
dest.writeLong(priv.getKeyID());
// private key data packet
byte[] privData = priv.getPrivateKeyDataPacket().getEncoded();
dest.writeInt(privData.length);
dest.writeByteArray(privData);
// public key packet
byte[] privPubKey = priv.getPublicKeyPacket().getEncoded();
dest.writeInt(privPubKey.length);
dest.writeByteArray(privPubKey);
// write public key
PGPPublicKey pub = keypair.getPublicKey();
// whole public key
byte[] pubPacket = pub.getEncoded();
dest.writeInt(pubPacket.length);
dest.writeByteArray(pubPacket);
}
private static PGPKeyPair readKeyPairFromParcel(Parcel in) throws IOException, PGPException {
BCPGInputStream reader = null;
long privID;
BCPGKey privData;
PublicKeyPacket privPubKey;
// rebuild private key
try {
// key ID
privID = in.readLong();
// private key data packet
int privDataLength = in.readInt();
byte[] privDataData = new byte[privDataLength];
in.readByteArray(privDataData);
// object will be read later
// public key packet
int privPubKeyLength = in.readInt();
byte[] privPubKeyData = new byte[privPubKeyLength];
in.readByteArray(privPubKeyData);
reader = new BCPGInputStream(new ByteArrayInputStream(privPubKeyData));
privPubKey = (PublicKeyPacket) reader.readPacket();
reader.close();
reader = new BCPGInputStream(new ByteArrayInputStream(privDataData));
// from PGPSecretKey
// apparently there is no way to make this dynamic
switch (privPubKey.getAlgorithm())
{
case PGPPublicKey.RSA_ENCRYPT:
case PGPPublicKey.RSA_GENERAL:
case PGPPublicKey.RSA_SIGN:
privData = new RSASecretBCPGKey(reader);
break;
case PGPPublicKey.DSA:
privData = new DSASecretBCPGKey(reader);
break;
case PGPPublicKey.ELGAMAL_ENCRYPT:
case PGPPublicKey.ELGAMAL_GENERAL:
privData = new ElGamalSecretBCPGKey(reader);
break;
case PGPPublicKey.ECDH:
case PGPPublicKey.ECDSA:
privData = new ECSecretBCPGKey(reader);
break;
default:
throw new PGPException("unknown public key algorithm encountered");
}
reader.close();
}
finally {
try {
if (reader != null)
reader.close();
}
catch (IOException ignored) {
}
}
PGPPrivateKey priv = new PGPPrivateKey(privID, privPubKey, privData);
// rebuild public key
// public key data
int pubKeyLength = in.readInt();
byte[] pubKeyData = new byte[pubKeyLength];
in.readByteArray(pubKeyData);
PGPObjectFactory f = new PGPObjectFactory(pubKeyData, sFingerprintCalculator);
PGPPublicKeyRing pubring = (PGPPublicKeyRing) f.nextObject();
PGPPublicKey pub = pubring.getPublicKey();
return new PGPKeyPair(pub, priv);
}
public static void serialize(PGPDecryptedKeyPairRing pair, ObjectOutputStream dest)
throws PGPException, IOException {
PrivateKey privAuth = convertPrivateKey(pair.authKey.getPrivateKey());
PublicKey pubAuth = convertPublicKey(pair.authKey.getPublicKey());
int algoAuth = pair.authKey.getPrivateKey().getPublicKeyPacket().getAlgorithm();
Date dateAuth = pair.authKey.getPrivateKey().getPublicKeyPacket().getTime();
PrivateKey privSign = convertPrivateKey(pair.signKey.getPrivateKey());
PublicKey pubSign = convertPublicKey(pair.signKey.getPublicKey());
int algoSign = pair.signKey.getPrivateKey().getPublicKeyPacket().getAlgorithm();
Date dateSign = pair.signKey.getPrivateKey().getPublicKeyPacket().getTime();
PrivateKey privEnc = convertPrivateKey(pair.encryptKey.getPrivateKey());
PublicKey pubEnc = convertPublicKey(pair.encryptKey.getPublicKey());
int algoEnc = pair.encryptKey.getPrivateKey().getPublicKeyPacket().getAlgorithm();
Date dateEnc = pair.encryptKey.getPrivateKey().getPublicKeyPacket().getTime();
dest.writeObject(privAuth);
dest.writeObject(pubAuth);
dest.writeInt(algoAuth);
dest.writeLong(dateAuth.getTime());
dest.writeObject(privSign);
dest.writeObject(pubSign);
dest.writeInt(algoSign);
dest.writeLong(dateSign.getTime());
dest.writeObject(privEnc);
dest.writeObject(pubEnc);
dest.writeInt(algoEnc);
dest.writeLong(dateEnc.getTime());
}
public static PGPDecryptedKeyPairRing unserialize(ObjectInputStream in)
throws IOException, ClassNotFoundException, PGPException {
ensureKeyConverter();
// TODO read byte data
PrivateKey privAuth = (PrivateKey) in.readObject();
PublicKey pubAuth = (PublicKey) in.readObject();
int algoAuth = in.readInt();
Date dateAuth = new Date(in.readLong());
PGPPublicKey pubKeyAuth = sKeyConverter.getPGPPublicKey(algoAuth, pubAuth, dateAuth);
PGPPrivateKey privKeyAuth = sKeyConverter.getPGPPrivateKey(pubKeyAuth, privAuth);
PGPKeyPair authKp = new PGPKeyPair(pubKeyAuth, privKeyAuth);
PrivateKey privSign = (PrivateKey) in.readObject();
PublicKey pubSign = (PublicKey) in.readObject();
int algoSign = in.readInt();
Date dateSign = new Date(in.readLong());
PGPPublicKey pubKeySign = sKeyConverter.getPGPPublicKey(algoSign, pubSign, dateSign);
PGPPrivateKey privKeySign = sKeyConverter.getPGPPrivateKey(pubKeySign, privSign);
PGPKeyPair signKp = new PGPKeyPair(pubKeySign, privKeySign);
PrivateKey privEnc = (PrivateKey) in.readObject();
PublicKey pubEnc = (PublicKey) in.readObject();
int algoEnc = in.readInt();
Date dateEnc = new Date(in.readLong());
PGPPublicKey pubKeyEnc = sKeyConverter.getPGPPublicKey(algoEnc, pubEnc, dateEnc);
PGPPrivateKey privKeyEnc = sKeyConverter.getPGPPrivateKey(pubKeyEnc, privEnc);
PGPKeyPair encryptKp = new PGPKeyPair(pubKeyEnc, privKeyEnc);
return new PGPDecryptedKeyPairRing(authKp, signKp, encryptKp);
}
public static String getFingerprint(PGPPublicKey publicKey) {
return MessageUtils.bytesToHex(publicKey.getFingerprint()).toUpperCase(Locale.US);
}
public static String getFingerprint(byte[] publicKeyring) throws IOException, PGPException {
return getFingerprint(getMasterKey(publicKeyring));
}
// FIXME very ugly method
public static String formatFingerprint(String fingerprint) {
StringBuilder fpr = new StringBuilder();
int length = fingerprint.length();
for (int i = 0; i < length; i += 4) {
fpr.append(fingerprint.substring(i, i + 4));
if (i < (length - 4)) {
fpr.append(' ');
if (i == (length / 2 - 4))
fpr.append(' ');
}
}
return fpr.toString();
}
public static String createFingerprintURI(String fingerprint) {
return "openpgp4fpr:" + fingerprint;
}
/** Returns the first user ID on the key that matches the given hostname. */
// TODO return type should be PGPUserID
public static String getUserId(PGPPublicKey key, String host) {
String first = null;
@SuppressWarnings("unchecked")
Iterator<String> uids = key.getUserIDs();
while (uids.hasNext()) {
String uid = uids.next();
// save the first if everything else fails
if (first == null) {
first = uid;
// no host to verify, exit now
if (host == null)
break;
}
if (uid != null) {
// parse uid
PGPUserID parsed = PGPUserID.parse(uid);
if (parsed != null) {
String email = parsed.getEmail();
if (email != null) {
// check if email host name matches
if (host.equalsIgnoreCase(XmppStringUtils.parseDomain(email))) {
return uid;
}
}
}
}
}
return first;
}
/** Returns the first user ID on the key that matches the given hostname. */
// TODO return type should be PGPUserID
public static String getUserId(byte[] publicKeyring, String host) throws IOException, PGPException {
PGPPublicKey pk = getMasterKey(publicKeyring);
return getUserId(pk, host);
}
public static PGPUserID parseUserId(byte[] publicKeyring, String host) throws IOException, PGPException {
return parseUserId(getMasterKey(publicKeyring), host);
}
public static PGPUserID parseUserId(PGPPublicKey key, String host) throws IOException, PGPException {
String uid = getUserId(key, host);
return PGPUserID.parse(uid);
}
public static int getKeyFlags(PGPPublicKey key) {
@SuppressWarnings("unchecked")
Iterator<PGPSignature> sigs = key.getSignatures();
while (sigs.hasNext()) {
PGPSignature sig = sigs.next();
if (sig != null) {
PGPSignatureSubpacketVector subpackets = sig.getHashedSubPackets();
if (subpackets != null) {
return subpackets.getKeyFlags();
}
}
}
return 0;
}
/** Returns the first master key found in the given public keyring. */
public static PGPPublicKey getMasterKey(PGPPublicKeyRing publicKeyring) {
@SuppressWarnings("unchecked")
Iterator<PGPPublicKey> iter = publicKeyring.getPublicKeys();
while (iter.hasNext()) {
PGPPublicKey pk = iter.next();
if (pk.isMasterKey())
return pk;
}
return null;
}
/** Returns the first master key found in the given public keyring. */
public static PGPPublicKey getMasterKey(byte[] publicKeyring) throws IOException, PGPException {
return getMasterKey(readPublicKeyring(publicKeyring));
}
public static PGPPublicKey getSigningKey(PGPPublicKeyRing publicKeyring) {
@SuppressWarnings("unchecked")
Iterator<PGPPublicKey> iter = publicKeyring.getPublicKeys();
while (iter.hasNext()) {
PGPPublicKey pk = iter.next();
if (!pk.isMasterKey()) {
int keyFlags = getKeyFlags(pk);
if ((keyFlags & PGPKeyFlags.CAN_SIGN) == PGPKeyFlags.CAN_SIGN)
return pk;
}
}
// legacy key format support
return getLegacySigningKey(publicKeyring);
}
public static PGPPublicKey getEncryptionKey(PGPPublicKeyRing publicKeyring) {
@SuppressWarnings("unchecked")
Iterator<PGPPublicKey> iter = publicKeyring.getPublicKeys();
while (iter.hasNext()) {
PGPPublicKey pk = iter.next();
if (!pk.isMasterKey()) {
int keyFlags = getKeyFlags(pk);
if ((keyFlags & PGPKeyFlags.CAN_ENCRYPT_COMMS) == PGPKeyFlags.CAN_ENCRYPT_COMMS)
return pk;
}
}
// legacy key format support
return getLegacyEncryptionKey(publicKeyring);
}
private static PGPPublicKey getLegacyEncryptionKey(PGPPublicKeyRing publicKeyring) {
@SuppressWarnings("unchecked")
Iterator<PGPPublicKey> iter = publicKeyring.getPublicKeys();
while (iter.hasNext()) {
PGPPublicKey pk = iter.next();
if (!pk.isMasterKey() && pk.isEncryptionKey())
return pk;
}
return null;
}
private static PGPPublicKey getLegacySigningKey(PGPPublicKeyRing publicKeyring) {
@SuppressWarnings("unchecked")
Iterator<PGPPublicKey> iter = publicKeyring.getPublicKeys();
while (iter.hasNext()) {
PGPPublicKey pk = iter.next();
if (pk.isMasterKey())
return pk;
}
return null;
}
public static PGPPublicKeyRing readPublicKeyring(byte[] publicKeyring) throws IOException, PGPException {
PGPObjectFactory reader = new PGPObjectFactory(publicKeyring, sFingerprintCalculator);
Object o = reader.nextObject();
while (o != null) {
if (o instanceof PGPPublicKeyRing)
return (PGPPublicKeyRing) o;
o = reader.nextObject();
}
throw new PGPException("invalid keyring data.");
}
private static void ensureKeyConverter() {
if (sKeyConverter == null)
sKeyConverter = new JcaPGPKeyConverter().setProvider(PGP.PROVIDER);
}
public static PrivateKey convertPrivateKey(PGPPrivateKey key) throws PGPException {
ensureKeyConverter();
return sKeyConverter.getPrivateKey(key);
}
@SuppressWarnings("unchecked")
public static PrivateKey convertPrivateKey(byte[] privateKeyData, String passphrase)
throws PGPException, IOException {
PGPDigestCalculatorProvider digestCalc = new JcaPGPDigestCalculatorProviderBuilder().build();
PBESecretKeyDecryptor decryptor = new JcePBESecretKeyDecryptorBuilder(digestCalc)
.setProvider(PGP.PROVIDER)
.build(passphrase.toCharArray());
// load the secret key ring
PGPSecretKeyRing secRing = new PGPSecretKeyRing(privateKeyData, sFingerprintCalculator);
// search and decrypt the master (signing key)
// secret keys
Iterator<PGPSecretKey> skeys = secRing.getSecretKeys();
while (skeys.hasNext()) {
PGPSecretKey key = skeys.next();
PGPSecretKey sec = secRing.getSecretKey();
if (key.isMasterKey())
return convertPrivateKey(sec.extractPrivateKey(decryptor));
}
throw new PGPException("no suitable private key found.");
}
public static PublicKey convertPublicKey(PGPPublicKey key) throws PGPException {
ensureKeyConverter();
return sKeyConverter.getPublicKey(key);
}
public static PGPSecretKeyRing copySecretKeyRingWithNewPassword(byte[] privateKeyData,
String oldPassphrase, String newPassphrase) throws PGPException, IOException {
// load the secret key ring
PGPSecretKeyRing secRing = new PGPSecretKeyRing(privateKeyData, sFingerprintCalculator);
return copySecretKeyRingWithNewPassword(secRing, oldPassphrase, newPassphrase);
}
public static PGPSecretKeyRing copySecretKeyRingWithNewPassword(PGPSecretKeyRing secRing,
String oldPassphrase, String newPassphrase) throws PGPException {
PGPDigestCalculatorProvider digestCalcProv = new JcaPGPDigestCalculatorProviderBuilder().build();
PBESecretKeyDecryptor decryptor = new JcePBESecretKeyDecryptorBuilder(digestCalcProv)
.setProvider(PGP.PROVIDER)
.build(oldPassphrase.toCharArray());
PGPDigestCalculator digestCalc = new JcaPGPDigestCalculatorProviderBuilder().build().get(HashAlgorithmTags.SHA256);
PBESecretKeyEncryptor encryptor = new JcePBESecretKeyEncryptorBuilder(PGPEncryptedData.AES_256, digestCalc)
.setProvider(PROVIDER).build(newPassphrase.toCharArray());
return PGPSecretKeyRing.copyWithNewPassword(secRing, decryptor, encryptor);
}
}