/**
*/
public function generateKey($opts)
{
$skey = $this->_generateSecretKeyPacket($opts['keylength'], 'OpenPGP_SecretKeyPacket');
$id = new Horde_Mail_Rfc822_Address($opts['email']);
if (strlen($opts['comment'])) {
$id->comment[] = $opts['comment'];
}
if (strlen($opts['name'])) {
$id->personal = $opts['name'];
}
/* This is the private key we are creating. */
$key = new OpenPGP_Message(array($skey, new OpenPGP_UserIDPacket($id->writeAddress(array('comment' => true)))));
$rsa = OpenPGP_Crypt_RSA::convert_private_key($skey);
$rsa->setHash(Horde_String::lower($opts['hash']));
$rsa_sign_func = array('RSA' => array($opts['hash'] => function ($data) use($rsa) {
return array($rsa->sign($data));
}));
/* Create signature packet. */
$sig = new OpenPGP_SignaturePacket($key, 'RSA', $opts['hash']);
/* "Generic certification of a User ID and Public-Key packet." */
$sig->signature_type = 0x10;
/* Add subpacket information. */
$sig->hashed_subpackets[] = new OpenPGP_SignaturePacket_KeyFlagsPacket(array(0x3));
$sig->hashed_subpackets[] = new OpenPGP_SignaturePacket_PreferredSymmetricAlgorithmsPacket(array(0x9, 0x8, 0x7, 0x2));
$sig->hashed_subpackets[] = new OpenPGP_SignaturePacket_PreferredHashAlgorithmsPacket(array(0x8, 0x9, 0xa, 0xb, 0x2));
$sig->hashed_subpackets[] = new OpenPGP_SignaturePacket_PreferredCompressionAlgorithmsPacket(array(0x2, 0x1));
$ks_prefs = new OpenPGP_SignaturePacket_KeyServerPreferencesPacket();
$ks_prefs->no_modify = true;
$sig->hashed_subpackets[] = $ks_prefs;
$sig->hashed_subpackets[] = new OpenPGP_SignaturePacket_FeaturesPacket(array(0x1));
if (isset($opts['expire'])) {
$sig->hashed_subpackets[] = new OpenPGP_SignaturePacket_KeyExpirationTimePacket($opts['expire'] - time());
}
$sig->unhashed_subpackets[] = new OpenPGP_SignaturePacket_IssuerPacket(substr($skey->fingerprint, -16));
$key[] = $sig;
/* Create self-signature. */
$sig->sign_data($rsa_sign_func);
/* OpenPGP currently (as of April 2015) encrypts passphrases w/
* AES-128 & SHA-1, so use this strategy. */
if (strlen($opts['passphrase'])) {
$cipher = new Crypt_AES(CRYPT_AES_MODE_CFB);
$cipher->setKeyLength(128);
$s2k = new OpenPGP_S2K(crypt_random_string(8), 2);
$cipher->setKey($s2k->make_key($opts['passphrase'], 16));
$iv = crypt_random_string(16);
$this->_encryptPrivateKey($skey, $cipher, $s2k, $iv);
}
/* Encryption subkey. See RFC 4880 [5.5.1.2] (by convention, top-level
* key is used for signing and subkeys are used for encryption) */
$ekey = $this->_generateSecretKeyPacket($opts['keylength'], 'OpenPGP_SecretSubkeyPacket');
/* Computing signature: RFC 4880 [5.2.4] */
$sig = new OpenPGP_SignaturePacket(implode('', $skey->fingerprint_material()) . implode('', $ekey->fingerprint_material()), 'RSA', $opts['hash']);
/* This is a "Subkey Binding Signature". */
$sig->signature_type = 0x18;
$sig->hashed_subpackets[] = new OpenPGP_SignaturePacket_KeyFlagsPacket(array(0xc));
$sig->unhashed_subpackets[] = new OpenPGP_SignaturePacket_IssuerPacket(substr($skey->fingerprint, -16));
$sig->sign_data($rsa_sign_func);
if (strlen($opts['passphrase'])) {
$this->_encryptPrivateKey($ekey, $cipher, $s2k, $iv);
}
$key[] = $ekey;
$key[] = $sig;
return new Horde_Pgp_Element_PrivateKey($key);
}