All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bouncycastle.openpgp.PGPSecretKey Maven / Gradle / Ivy

Go to download

The Bouncy Castle Java API for handling the OpenPGP protocol. This jar contains the OpenPGP API for JDK 1.5. The APIs can be used in conjunction with a JCE/JCA provider such as the one provided with the Bouncy Castle Cryptography APIs.

There is a newer version: 1.46
Show newest version
package org.bouncycastle.openpgp;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.security.KeyFactory;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Provider;
import java.security.interfaces.DSAPrivateKey;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.spec.DSAPrivateKeySpec;
import java.security.spec.RSAPrivateCrtKeySpec;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;

import org.bouncycastle.bcpg.BCPGInputStream;
import org.bouncycastle.bcpg.BCPGObject;
import org.bouncycastle.bcpg.BCPGOutputStream;
import org.bouncycastle.bcpg.ContainedPacket;
import org.bouncycastle.bcpg.DSAPublicBCPGKey;
import org.bouncycastle.bcpg.DSASecretBCPGKey;
import org.bouncycastle.bcpg.ElGamalPublicBCPGKey;
import org.bouncycastle.bcpg.ElGamalSecretBCPGKey;
import org.bouncycastle.bcpg.HashAlgorithmTags;
import org.bouncycastle.bcpg.PublicKeyPacket;
import org.bouncycastle.bcpg.RSAPublicBCPGKey;
import org.bouncycastle.bcpg.RSASecretBCPGKey;
import org.bouncycastle.bcpg.S2K;
import org.bouncycastle.bcpg.SecretKeyPacket;
import org.bouncycastle.bcpg.SecretSubkeyPacket;
import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
import org.bouncycastle.bcpg.UserAttributePacket;
import org.bouncycastle.bcpg.UserIDPacket;
import org.bouncycastle.jce.interfaces.ElGamalPrivateKey;
import org.bouncycastle.jce.spec.ElGamalParameterSpec;
import org.bouncycastle.jce.spec.ElGamalPrivateKeySpec;

/**
 * general class to handle a PGP secret key object.
 */
public class PGPSecretKey
{    
    final SecretKeyPacket secret;
    final PGPPublicKey    pub;

    PGPSecretKey(
        SecretKeyPacket secret,
        PGPPublicKey    pub)
    {
        this.secret = secret;
        this.pub = pub;
    }
    
    PGPSecretKey(
        PGPPrivateKey   privKey,
        PGPPublicKey    pubKey,
        int             encAlgorithm,
        char[]          passPhrase,
        boolean         useSHA1,
        SecureRandom    rand,
        Provider        provider)
        throws PGPException
    {
        this(privKey, pubKey, encAlgorithm, passPhrase, useSHA1, rand, false, provider);
    }
    
    PGPSecretKey(
        PGPPrivateKey   privKey,
        PGPPublicKey    pubKey,
        int             encAlgorithm,
        char[]          passPhrase,
        boolean         useSHA1,
        SecureRandom    rand,
        boolean         isMasterKey,
        Provider        provider) 
        throws PGPException
    {
        BCPGObject      secKey;

        this.pub = pubKey;
        
        switch (pubKey.getAlgorithm())
        {
        case PGPPublicKey.RSA_ENCRYPT:
        case PGPPublicKey.RSA_SIGN:
        case PGPPublicKey.RSA_GENERAL:
            RSAPrivateCrtKey    rsK = (RSAPrivateCrtKey)privKey.getKey();
            
            secKey = new RSASecretBCPGKey(rsK.getPrivateExponent(), rsK.getPrimeP(), rsK.getPrimeQ());
            break;
        case PGPPublicKey.DSA:
            DSAPrivateKey       dsK = (DSAPrivateKey)privKey.getKey();
            
            secKey = new DSASecretBCPGKey(dsK.getX());
            break;
        case PGPPublicKey.ELGAMAL_ENCRYPT:
        case PGPPublicKey.ELGAMAL_GENERAL:
            ElGamalPrivateKey   esK = (ElGamalPrivateKey)privKey.getKey();
            
            secKey = new ElGamalSecretBCPGKey(esK.getX());
            break;
        default:
            throw new PGPException("unknown key class");
        }

        String    cName = PGPUtil.getSymmetricCipherName(encAlgorithm);
        Cipher    c = null;
        
        if (cName != null)
        {
            try
            {
                c = Cipher.getInstance(cName + "/CFB/NoPadding", provider);
            }
            catch (Exception e)
            {
                throw new PGPException("Exception creating cipher", e);
            }
        }
        
        try
        {
            ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
            BCPGOutputStream        pOut = new BCPGOutputStream(bOut);
            
            pOut.writeObject(secKey);
            
            byte[]    keyData = bOut.toByteArray();

            pOut.write(checksum(useSHA1, keyData, keyData.length));
            
            if (c != null)
            {
                byte[]       iv = new byte[8];
                
                rand.nextBytes(iv);
                
                S2K          s2k = new S2K(HashAlgorithmTags.SHA1, iv, 0x60);
                SecretKey    key = PGPUtil.makeKeyFromPassPhrase(encAlgorithm, s2k, passPhrase, provider);
    
                c.init(Cipher.ENCRYPT_MODE, key, rand);
            
                iv = c.getIV();
    
                byte[]    encData = c.doFinal(bOut.toByteArray());

                int s2kUsage;

                if (useSHA1)
                {
                    s2kUsage = SecretKeyPacket.USAGE_SHA1;
                }
                else
                {
                    s2kUsage = SecretKeyPacket.USAGE_CHECKSUM;
                }

                if (isMasterKey)
                {
                    this.secret = new SecretKeyPacket(pub.publicPk, encAlgorithm, s2kUsage, s2k, iv, encData);
                }
                else
                {
                    this.secret = new SecretSubkeyPacket(pub.publicPk, encAlgorithm, s2kUsage, s2k, iv, encData);
                }
            }
            else
            {
                if (isMasterKey)
                {
                    this.secret = new SecretKeyPacket(pub.publicPk, encAlgorithm, null, null, bOut.toByteArray());
                }
                else
                {
                    this.secret = new SecretSubkeyPacket(pub.publicPk, encAlgorithm, null, null, bOut.toByteArray());
                }
            }
        }
        catch (PGPException e)
        {
            throw e;
        }
        catch (Exception e)
        {
            throw new PGPException("Exception encrypting key", e);
        }
    }
    
    public PGPSecretKey(
        int                         certificationLevel,
        PGPKeyPair                  keyPair,
        String                      id,
        int                         encAlgorithm,
        char[]                      passPhrase,
        PGPSignatureSubpacketVector hashedPcks,
        PGPSignatureSubpacketVector unhashedPcks,
        SecureRandom                rand,
        String                      provider)
        throws PGPException, NoSuchProviderException
    {
        this(certificationLevel, keyPair, id, encAlgorithm, passPhrase, false, hashedPcks, unhashedPcks, rand, provider);
    }

    public PGPSecretKey(
        int                         certificationLevel,
        PGPKeyPair                  keyPair,
        String                      id,
        int                         encAlgorithm,
        char[]                      passPhrase,
        boolean                     useSHA1,
        PGPSignatureSubpacketVector hashedPcks,
        PGPSignatureSubpacketVector unhashedPcks,
        SecureRandom                rand,
        String                      provider)
        throws PGPException, NoSuchProviderException
    {
        this(certificationLevel, keyPair, id, encAlgorithm, passPhrase, useSHA1, hashedPcks, unhashedPcks, rand, PGPUtil.getProvider(provider));
    }
    
    public PGPSecretKey(
        int                         certificationLevel,
        PGPKeyPair                  keyPair,
        String                      id,
        int                         encAlgorithm,
        char[]                      passPhrase,
        boolean                     useSHA1,
        PGPSignatureSubpacketVector hashedPcks,
        PGPSignatureSubpacketVector unhashedPcks,
        SecureRandom                rand,
        Provider                    provider)
        throws PGPException
    {
        this(keyPair.getPrivateKey(), certifiedPublicKey(certificationLevel, keyPair, id, hashedPcks, unhashedPcks, provider), encAlgorithm, passPhrase, useSHA1, rand, true, provider);
    }

    private static PGPPublicKey certifiedPublicKey(
        int certificationLevel,
        PGPKeyPair keyPair,
        String id,
        PGPSignatureSubpacketVector hashedPcks,
        PGPSignatureSubpacketVector unhashedPcks,
        Provider provider)
        throws PGPException
    {
        PGPSignatureGenerator    sGen;

        try
        {
            sGen = new PGPSignatureGenerator(keyPair.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1, provider);
        }
        catch (Exception e)
        {
            throw new PGPException("creating signature generator: " + e, e);
        }

        //
        // generate the certification
        //
        sGen.initSign(certificationLevel, keyPair.getPrivateKey());

        sGen.setHashedSubpackets(hashedPcks);
        sGen.setUnhashedSubpackets(unhashedPcks);

        try
        {
            PGPSignature    certification = sGen.generateCertification(id, keyPair.getPublicKey());

            return PGPPublicKey.addCertification(keyPair.getPublicKey(), id, certification);
        }
        catch (Exception e)
        {
            throw new PGPException("exception doing certification: " + e, e);
        }
    }

    public PGPSecretKey(
        int                         certificationLevel,
        int                         algorithm,
        PublicKey                   pubKey,
        PrivateKey                  privKey,
        Date                        time,
        String                      id,
        int                         encAlgorithm,
        char[]                      passPhrase,
        PGPSignatureSubpacketVector hashedPcks,
        PGPSignatureSubpacketVector unhashedPcks,
        SecureRandom                rand,
        String                      provider)
        throws PGPException, NoSuchProviderException
    {
        this(certificationLevel, new PGPKeyPair(algorithm,pubKey, privKey, time), id, encAlgorithm, passPhrase, hashedPcks, unhashedPcks, rand, provider);
    }

    public PGPSecretKey(
        int                         certificationLevel,
        int                         algorithm,
        PublicKey                   pubKey,
        PrivateKey                  privKey,
        Date                        time,
        String                      id,
        int                         encAlgorithm,
        char[]                      passPhrase,
        boolean                     useSHA1,
        PGPSignatureSubpacketVector hashedPcks,
        PGPSignatureSubpacketVector unhashedPcks,
        SecureRandom                rand,
        String                      provider)
        throws PGPException, NoSuchProviderException
    {
        this(certificationLevel, new PGPKeyPair(algorithm,pubKey, privKey, time), id, encAlgorithm, passPhrase, useSHA1, hashedPcks, unhashedPcks, rand, provider);
    }

    /**
     * Return true if this key has an algorithm type that makes it suitable to use for signing.
     * 

* Note: with version 4 keys KeyFlags subpackets should also be considered when present for * determining the preferred use of the key. * * @return true if this key algorithm is suitable for use with signing. */ public boolean isSigningKey() { int algorithm = pub.getAlgorithm(); return ((algorithm == PGPPublicKey.RSA_GENERAL) || (algorithm == PGPPublicKey.RSA_SIGN) || (algorithm == PGPPublicKey.DSA) || (algorithm == PGPPublicKey.ECDSA) || (algorithm == PGPPublicKey.ELGAMAL_GENERAL)); } /** * Return true if this is a master key. * @return true if a master key. */ public boolean isMasterKey() { return pub.isMasterKey(); } /** * return the algorithm the key is encrypted with. * * @return the algorithm used to encrypt the secret key. */ public int getKeyEncryptionAlgorithm() { return secret.getEncAlgorithm(); } /** * Return the keyID of the public key associated with this key. * * @return the keyID associated with this key. */ public long getKeyID() { return pub.getKeyID(); } /** * Return the public key associated with this key. * * @return the public key for this key. */ public PGPPublicKey getPublicKey() { return pub; } /** * Return any userIDs associated with the key. * * @return an iterator of Strings. */ public Iterator getUserIDs() { return pub.getUserIDs(); } /** * Return any user attribute vectors associated with the key. * * @return an iterator of Strings. */ public Iterator getUserAttributes() { return pub.getUserAttributes(); } private byte[] extractKeyData( char[] passPhrase, Provider provider) throws PGPException { String cName = PGPUtil.getSymmetricCipherName(secret.getEncAlgorithm()); Cipher c = null; if (cName != null) { try { c = Cipher.getInstance(cName + "/CFB/NoPadding", provider); } catch (Exception e) { throw new PGPException("Exception creating cipher", e); } } byte[] encData = secret.getSecretKeyData(); byte[] data = null; try { if (c != null) { try { if (secret.getPublicKeyPacket().getVersion() == 4) { IvParameterSpec ivSpec = new IvParameterSpec(secret.getIV()); SecretKey key = PGPUtil.makeKeyFromPassPhrase(secret.getEncAlgorithm(), secret.getS2K(), passPhrase, provider); c.init(Cipher.DECRYPT_MODE, key, ivSpec); data = c.doFinal(encData, 0, encData.length); boolean useSHA1 = secret.getS2KUsage() == SecretKeyPacket.USAGE_SHA1; byte[] check = checksum(useSHA1, data, (useSHA1) ? data.length - 20 : data.length - 2); for (int i = 0; i != check.length; i++) { if (check[i] != data[data.length - check.length + i]) { throw new PGPException("checksum mismatch at " + i + " of " + check.length); } } } else // version 2 or 3, RSA only. { SecretKey key = PGPUtil.makeKeyFromPassPhrase(secret.getEncAlgorithm(), secret.getS2K(), passPhrase, provider); data = new byte[encData.length]; byte[] iv = new byte[secret.getIV().length]; System.arraycopy(secret.getIV(), 0, iv, 0, iv.length); // // read in the four numbers // int pos = 0; for (int i = 0; i != 4; i++) { c.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv)); int encLen = (((encData[pos] << 8) | (encData[pos + 1] & 0xff)) + 7) / 8; data[pos] = encData[pos]; data[pos + 1] = encData[pos + 1]; c.doFinal(encData, pos + 2, encLen, data, pos + 2); pos += 2 + encLen; if (i != 3) { System.arraycopy(encData, pos - iv.length, iv, 0, iv.length); } } // // verify checksum // int cs = ((encData[pos] << 8) & 0xff00) | (encData[pos + 1] & 0xff); int calcCs = 0; for (int j=0; j < data.length-2; j++) { calcCs += data[j] & 0xff; } calcCs &= 0xffff; if (calcCs != cs) { throw new PGPException("checksum mismatch: passphrase wrong, expected " + Integer.toHexString(cs) + " found " + Integer.toHexString(calcCs)); } } } catch (PGPException e) { throw e; } catch (Exception e) { throw new PGPException("Exception decrypting key", e); } } else { data = encData; } return data; } catch (PGPException e) { throw e; } catch (Exception e) { throw new PGPException("Exception constructing key", e); } } /** * Extract a PGPPrivate key from the SecretKey's encrypted contents. * * @param passPhrase * @param provider * @return PGPPrivateKey * @throws PGPException * @throws NoSuchProviderException */ public PGPPrivateKey extractPrivateKey( char[] passPhrase, String provider) throws PGPException, NoSuchProviderException { return extractPrivateKey(passPhrase, PGPUtil.getProvider(provider)); } /** * Extract a PGPPrivate key from the SecretKey's encrypted contents. * * @param passPhrase * @param provider * @return PGPPrivateKey * @throws PGPException */ public PGPPrivateKey extractPrivateKey( char[] passPhrase, Provider provider) throws PGPException { byte[] secKeyData = secret.getSecretKeyData(); if (secKeyData == null || secKeyData.length < 1) { return null; } PublicKeyPacket pubPk = secret.getPublicKeyPacket(); try { KeyFactory fact; byte[] data = extractKeyData(passPhrase, provider); BCPGInputStream in = new BCPGInputStream(new ByteArrayInputStream(data)); switch (pubPk.getAlgorithm()) { case PGPPublicKey.RSA_ENCRYPT: case PGPPublicKey.RSA_GENERAL: case PGPPublicKey.RSA_SIGN: RSAPublicBCPGKey rsaPub = (RSAPublicBCPGKey)pubPk.getKey(); RSASecretBCPGKey rsaPriv = new RSASecretBCPGKey(in); RSAPrivateCrtKeySpec rsaPrivSpec = new RSAPrivateCrtKeySpec( rsaPriv.getModulus(), rsaPub.getPublicExponent(), rsaPriv.getPrivateExponent(), rsaPriv.getPrimeP(), rsaPriv.getPrimeQ(), rsaPriv.getPrimeExponentP(), rsaPriv.getPrimeExponentQ(), rsaPriv.getCrtCoefficient()); fact = KeyFactory.getInstance("RSA", provider); return new PGPPrivateKey(fact.generatePrivate(rsaPrivSpec), this.getKeyID()); case PGPPublicKey.DSA: DSAPublicBCPGKey dsaPub = (DSAPublicBCPGKey)pubPk.getKey(); DSASecretBCPGKey dsaPriv = new DSASecretBCPGKey(in); DSAPrivateKeySpec dsaPrivSpec = new DSAPrivateKeySpec(dsaPriv.getX(), dsaPub.getP(), dsaPub.getQ(), dsaPub.getG()); fact = KeyFactory.getInstance("DSA", provider); return new PGPPrivateKey(fact.generatePrivate(dsaPrivSpec), this.getKeyID()); case PGPPublicKey.ELGAMAL_ENCRYPT: case PGPPublicKey.ELGAMAL_GENERAL: ElGamalPublicBCPGKey elPub = (ElGamalPublicBCPGKey)pubPk.getKey(); ElGamalSecretBCPGKey elPriv = new ElGamalSecretBCPGKey(in); ElGamalPrivateKeySpec elSpec = new ElGamalPrivateKeySpec(elPriv.getX(), new ElGamalParameterSpec(elPub.getP(), elPub.getG())); fact = KeyFactory.getInstance("ElGamal", provider); return new PGPPrivateKey(fact.generatePrivate(elSpec), this.getKeyID()); default: throw new PGPException("unknown public key algorithm encountered"); } } catch (PGPException e) { throw e; } catch (Exception e) { throw new PGPException("Exception constructing key", e); } } private static byte[] checksum(boolean useSHA1, byte[] bytes, int length) throws PGPException { if (useSHA1) { try { MessageDigest dig = MessageDigest.getInstance("SHA1"); dig.update(bytes, 0, length); return dig.digest(); } catch (NoSuchAlgorithmException e) { throw new PGPException("Can't find SHA-1", e); } } else { int checksum = 0; for (int i = 0; i != length; i++) { checksum += bytes[i] & 0xff; } byte[] check = new byte[2]; check[0] = (byte)(checksum >> 8); check[1] = (byte)checksum; return check; } } public byte[] getEncoded() throws IOException { ByteArrayOutputStream bOut = new ByteArrayOutputStream(); this.encode(bOut); return bOut.toByteArray(); } public void encode( OutputStream outStream) throws IOException { BCPGOutputStream out; if (outStream instanceof BCPGOutputStream) { out = (BCPGOutputStream)outStream; } else { out = new BCPGOutputStream(outStream); } out.writePacket(secret); if (pub.trustPk != null) { out.writePacket(pub.trustPk); } if (pub.subSigs == null) // is not a sub key { for (int i = 0; i != pub.keySigs.size(); i++) { ((PGPSignature)pub.keySigs.get(i)).encode(out); } for (int i = 0; i != pub.ids.size(); i++) { if (pub.ids.get(i) instanceof String) { String id = (String)pub.ids.get(i); out.writePacket(new UserIDPacket(id)); } else { PGPUserAttributeSubpacketVector v = (PGPUserAttributeSubpacketVector)pub.ids.get(i); out.writePacket(new UserAttributePacket(v.toSubpacketArray())); } if (pub.idTrusts.get(i) != null) { out.writePacket((ContainedPacket)pub.idTrusts.get(i)); } List sigs = (ArrayList)pub.idSigs.get(i); for (int j = 0; j != sigs.size(); j++) { ((PGPSignature)sigs.get(j)).encode(out); } } } else { for (int j = 0; j != pub.subSigs.size(); j++) { ((PGPSignature)pub.subSigs.get(j)).encode(out); } } } /** * Return a copy of the passed in secret key, encrypted using a new * password and the passed in algorithm. * * @param key the PGPSecretKey to be copied. * @param oldPassPhrase the current password for key. * @param newPassPhrase the new password for the key. * @param newEncAlgorithm the algorithm to be used for the encryption. * @param rand source of randomness. * @param provider name of the provider to use */ public static PGPSecretKey copyWithNewPassword( PGPSecretKey key, char[] oldPassPhrase, char[] newPassPhrase, int newEncAlgorithm, SecureRandom rand, String provider) throws PGPException, NoSuchProviderException { return copyWithNewPassword(key, oldPassPhrase, newPassPhrase, newEncAlgorithm, rand, PGPUtil.getProvider(provider)); } /** * Return a copy of the passed in secret key, encrypted using a new * password and the passed in algorithm. * * @param key the PGPSecretKey to be copied. * @param oldPassPhrase the current password for key. * @param newPassPhrase the new password for the key. * @param newEncAlgorithm the algorithm to be used for the encryption. * @param rand source of randomness. * @param provider the provider to use */ public static PGPSecretKey copyWithNewPassword( PGPSecretKey key, char[] oldPassPhrase, char[] newPassPhrase, int newEncAlgorithm, SecureRandom rand, Provider provider) throws PGPException { byte[] rawKeyData = key.extractKeyData(oldPassPhrase, provider); int s2kUsage = key.secret.getS2KUsage(); byte[] iv = null; S2K s2k = null; byte[] keyData; if (newEncAlgorithm == SymmetricKeyAlgorithmTags.NULL) { s2kUsage = SecretKeyPacket.USAGE_NONE; if (key.secret.getS2KUsage() == SecretKeyPacket.USAGE_SHA1) // SHA-1 hash, need to rewrite checksum { keyData = new byte[rawKeyData.length - 18]; System.arraycopy(rawKeyData, 0, keyData, 0, keyData.length - 2); byte[] check = checksum(false, keyData, keyData.length - 2); keyData[keyData.length - 2] = check[0]; keyData[keyData.length - 1] = check[1]; } else { keyData = rawKeyData; } } else { Cipher c = null; String cName = PGPUtil.getSymmetricCipherName(newEncAlgorithm); try { c = Cipher.getInstance(cName + "/CFB/NoPadding", provider); } catch (Exception e) { throw new PGPException("Exception creating cipher", e); } iv = new byte[8]; rand.nextBytes(iv); s2k = new S2K(HashAlgorithmTags.SHA1, iv, 0x60); try { SecretKey sKey = PGPUtil.makeKeyFromPassPhrase(newEncAlgorithm, s2k, newPassPhrase, provider); c.init(Cipher.ENCRYPT_MODE, sKey, rand); iv = c.getIV(); keyData = c.doFinal(rawKeyData); } catch (PGPException e) { throw e; } catch (Exception e) { throw new PGPException("Exception encrypting key", e); } } SecretKeyPacket secret = null; if (key.secret instanceof SecretSubkeyPacket) { secret = new SecretSubkeyPacket(key.secret.getPublicKeyPacket(), newEncAlgorithm, s2kUsage, s2k, iv, keyData); } else { secret = new SecretKeyPacket(key.secret.getPublicKeyPacket(), newEncAlgorithm, s2kUsage, s2k, iv, keyData); } return new PGPSecretKey(secret, key.pub); } /** * Replace the passed the public key on the passed in secret key. * * @param secretKey secret key to change * @param publicKey new public key. * @return a new secret key. * @throws IllegalArgumentException if keyIDs do not match. */ public static PGPSecretKey replacePublicKey(PGPSecretKey secretKey, PGPPublicKey publicKey) { if (publicKey.getKeyID() != secretKey.getKeyID()) { throw new IllegalArgumentException("keyIDs do not match"); } return new PGPSecretKey(secretKey.secret, publicKey); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy