/** * Creates a <code>EmailIdentity</code> from a Base64-encoded string. * The format can be any format supported by one of the {@link CryptoImplementation}s; * the length of the string must match {@link CryptoImplementation#getBase64CompleteKeySetLength()}. * @param base64Key * @throws GeneralSecurityException */ public EmailIdentity(String base64Key) throws GeneralSecurityException { // find the crypto implementation for this key length for (CryptoImplementation cryptoImpl: CryptoFactory.getInstances()) { int base64Length = cryptoImpl.getBase64CompleteKeySetLength(); // length of an email identity with this CryptoImplementation if (base64Key.length() == base64Length) { this.cryptoImpl = cryptoImpl; break; } } if (cryptoImpl == null) throw new InvalidKeyException("Not a valid Email Identity, no CryptoImplementation (out of " + CryptoFactory.getInstances().size() + ") matches length " + base64Key.length() + ": <" + base64Key + ">"); String base64PublicKeys = base64Key.substring(0, cryptoImpl.getBase64PublicKeyPairLength()); // the two private keys start after the two public keys PublicKeyPair publicKeys = cryptoImpl.createPublicKeyPair(base64PublicKeys); String base64PrivateKeys = base64Key.substring(cryptoImpl.getBase64PublicKeyPairLength()); // the two private keys start after the two public keys PrivateKeyPair privateKeys = cryptoImpl.createPrivateKeyPair(base64PrivateKeys); publicEncryptionKey = publicKeys.encryptionKey; privateEncryptionKey = privateKeys.encryptionKey; publicSigningKey = publicKeys.signingKey; privateSigningKey = privateKeys.signingKey; identityConfig = new IdentityConfigImpl(); }
@Test public void encryptAndDecrypt() throws GeneralSecurityException, InvalidCipherTextException { for (TestIdentity testData: testIdentities) for (byte[] original: testMessages) { CryptoImplementation cryptoImpl = testData.cryptoImpl; KeyPair encryptionKeys = testData.encryptionKeys; byte[] encrypted = cryptoImpl.encrypt(original, encryptionKeys.getPublic()); byte[] decrypted = cryptoImpl.decrypt(encrypted, encryptionKeys.getPublic(), encryptionKeys.getPrivate()); assertArrayEquals("encrypted data != decrypted data for crypto implementation <" + cryptoImpl.getName() + ">", original, decrypted); } }
@Test public void testToByteArray() throws GeneralSecurityException { for (TestIdentity testData: testIdentities) { CryptoImplementation cryptoImpl = testData.cryptoImpl; // test public key pair PublicKeyPair originalPublicKeyPair = cryptoImpl.createPublicKeyPair(testData.base64PublicKeyPair); byte[] originalPublic = cryptoImpl.toByteArray(originalPublicKeyPair); PublicKeyPair keyPairPublic = cryptoImpl.createPublicKeyPair(testData.base64PublicKeyPair); byte[] reencodedPublic = cryptoImpl.toByteArray(keyPairPublic); assertArrayEquals(originalPublic, reencodedPublic); assertEquals(cryptoImpl.getByteArrayPublicKeyPairLength(), reencodedPublic.length); // test private key pair PrivateKeyPair originalPrivateKeyPair = cryptoImpl.createPrivateKeyPair(testData.base64PrivateKeyPair); byte[] originalPrivate = cryptoImpl.toByteArray(originalPrivateKeyPair); PrivateKeyPair keyPairPrivate = cryptoImpl.createPrivateKeyPair(testData.base64PrivateKeyPair); byte[] reencodedPrivate = cryptoImpl.toByteArray(keyPairPrivate); assertArrayEquals(originalPrivate, reencodedPrivate); } }
public EmailDestination(byte[] bytes) throws GeneralSecurityException { // find the crypto implementation for this key length for (CryptoImplementation cryptoImpl: CryptoFactory.getInstances()) { int byteArrayLength = cryptoImpl.getByteArrayPublicKeyPairLength(); // length of an email destination that uses this CryptoImplementation if (bytes.length == byteArrayLength) this.cryptoImpl = cryptoImpl; } if (cryptoImpl == null) throw new InvalidKeyException("Not a valid Email Destination: " + Arrays.toString(bytes)); PublicKeyPair keyPair = cryptoImpl.createPublicKeyPair(bytes); publicEncryptionKey = keyPair.encryptionKey; publicSigningKey = keyPair.signingKey; }
/** * Decrypts the encrypted part of the packet with the private key of an <code>EmailIdentity</code>. * The {@link CryptoImplementation} in the <code>EmailIdentity</code> must be the same as the one * in this <code>EncryptedEmailPacket</code>. * @param identity * @throws GeneralSecurityException * @throws InvalidCipherTextException */ public UnencryptedEmailPacket decrypt(EmailIdentity identity) throws GeneralSecurityException { if (cryptoImpl != identity.getCryptoImpl()) throw new IllegalArgumentException("CryptoImplementations don't match. Email Packet: <" + cryptoImpl.getName() + ">, Email Identity: <" + identity.getCryptoImpl().getName() + ">."); byte[] decryptedData = cryptoImpl.decrypt(encryptedData, identity.getPublicEncryptionKey(), identity.getPrivateEncryptionKey()); return new UnencryptedEmailPacket(decryptedData); }
private TestIdentity(String base64PublicKeyPair, String base64PrivateKeyPair, CryptoImplementation cryptoImpl) throws GeneralSecurityException { this.base64PublicKeyPair = base64PublicKeyPair; this.base64PrivateKeyPair = base64PrivateKeyPair; this.cryptoImpl = cryptoImpl; publicKeys = cryptoImpl.createPublicKeyPair(base64PublicKeyPair); privateKeys = cryptoImpl.createPrivateKeyPair(base64PrivateKeyPair); signingKeys = new KeyPair(publicKeys.signingKey, privateKeys.signingKey); encryptionKeys = new KeyPair(publicKeys.encryptionKey, privateKeys.encryptionKey); identity = new EmailIdentity(base64PublicKeyPair + base64PrivateKeyPair); identity.setPublicName(cryptoImpl.getName()); } }
@Test public void signAndVerify() throws GeneralSecurityException, IOException, PasswordException { for (TestIdentity testIdentity: testIdentities) { KeyUpdateHandler keyUpdateHandler; if (testIdentity.cryptoImpl instanceof NTRUEncrypt1087_GMSS512) keyUpdateHandler = TestUtil.createVerifyingKeyUpdateHandler(testMessages.size()); // verify that KeyUpdateHandler is called once for each signed message else keyUpdateHandler = TestUtil.createDummyKeyUpdateHandler(); for (byte[] message: testMessages) { CryptoImplementation cryptoImpl = testIdentity.cryptoImpl; KeyPair signingKeys = testIdentity.signingKeys; byte[] signature = cryptoImpl.sign(message, signingKeys.getPrivate(), keyUpdateHandler); assertTrue("Invalid signature for crypto implementation <" + cryptoImpl.getName() + ">", cryptoImpl.verify(message, signature, signingKeys.getPublic())); } } } }
/** * @param address A string containing a valid base64-encoded Email Destination * @throws GeneralSecurityException If <code>address</code> doesn't contain a valid Email Destination */ public EmailDestination(String address) throws GeneralSecurityException { String base64Key = extractBase64Dest(address); if (base64Key == null) throw new GeneralSecurityException("No Email Destination found in string: <" + address + ">"); // find the crypto implementation for this key length for (CryptoImplementation cryptoImpl: CryptoFactory.getInstances()) { int base64Length = cryptoImpl.getBase64PublicKeyPairLength(); // length of an email destination that uses this CryptoImplementation if (base64Key.length() == base64Length) this.cryptoImpl = cryptoImpl; } if (cryptoImpl == null) throw new InvalidKeyException("Not a valid Email Destination: <" + base64Key + ">"); PublicKeyPair keyPair = cryptoImpl.createPublicKeyPair(base64Key); publicEncryptionKey = keyPair.encryptionKey; publicSigningKey = keyPair.signingKey; }
@Override public String toString() { return super.toString() + ", DHTkey=" + dhtKey + ", tstamp=" + storeTime + ", alg=" + cryptoImpl.getName() + ", delVerifHash=" + delVerificationHash + ", encrLen=" + encryptedData.length; } }
/** * Creates a digital signature of the email and stores it in the * <code>SIGNATURE_HEADER</code> header field. It also removes the * <code>SIGNATURE_VALID_HEADER</code> header. If there is a signature * already, it is replaced.<br/> * The signature is computed over the stream representation of the * email, minus the signature header if it is present.<br/> * The signature includes the ID number of the {@link CryptoImplementation} * used (signature lengths can be different for the same algorithm). * @param senderIdentity * @param keyUpdateHandler Needed for updating the signature key after signing (see {@link CryptoImplementation#sign(byte[], PrivateKey, KeyUpdateHandler)}) * @throws MessagingException * @throws GeneralSecurityException * @throws PasswordException */ public void sign(EmailIdentity senderIdentity, KeyUpdateHandler keyUpdateHandler) throws MessagingException, GeneralSecurityException, PasswordException { removeHeader(SIGNATURE_HEADER); // make sure there is no existing signature which would make the new signature invalid removeHeader(SIGNATURE_VALID_HEADER); // remove the signature validity flag before signing CryptoImplementation cryptoImpl = senderIdentity.getCryptoImpl(); PrivateKey privateSigningKey = senderIdentity.getPrivateSigningKey(); byte[] signature = cryptoImpl.sign(toByteArray(), privateSigningKey, keyUpdateHandler); String foldedSignature = foldSignature(cryptoImpl.getId() + "_" + Base64.encode(signature)); setHeader(SIGNATURE_HEADER, foldedSignature); }
/** * Returns the two key pairs (public + private) as one Base64-encoded string. * @throws GeneralSecurityException */ public String getFullKey() throws GeneralSecurityException { PublicKeyPair publicKeys = new PublicKeyPair(publicEncryptionKey, publicSigningKey); PrivateKeyPair privateKeys = new PrivateKeyPair(privateEncryptionKey, privateSigningKey); String pubKeys = cryptoImpl.toBase64(publicKeys); String privKeys = cryptoImpl.toBase64(privateKeys); return pubKeys + privKeys; }
/** * Creates a signature over the byte array representation of the packet * @param identity An email identity that matches the destination field * @param keyUpdateHandler * @throws PasswordException * @throws GeneralSecurityException */ private void sign(EmailIdentity identity, KeyUpdateHandler keyUpdateHandler) throws GeneralSecurityException, PasswordException { byte[] data = getDataToSign(); CryptoImplementation cryptoImpl = identity.getCryptoImpl(); PrivateKey privateSigningKey = identity.getPrivateSigningKey(); signature = cryptoImpl.sign(data, privateSigningKey, keyUpdateHandler); }
int base64Length = cryptoImpl.getBase64PublicKeyPairLength(); // length of an email destination with this CryptoImplementation
/** * Creates an <code>EncryptedEmailPacket</code> from an <code>UnencryptedEmailPacket</code>. * The public key of <code>emailDestination</code> is used for encryption. * The store time is set to <code>0</code>. * @param unencryptedPacket * @param emailDestination * @throws GeneralSecurityException If an error occurred during encryption */ public EncryptedEmailPacket(UnencryptedEmailPacket unencryptedPacket, EmailDestination emailDestination) throws GeneralSecurityException { storeTime = 0; byte[] delAuthorizationBytes = unencryptedPacket.getDeleteAuthorization().toByteArray(); delVerificationHash = SHA256Generator.getInstance().calculateHash(delAuthorizationBytes); cryptoImpl = emailDestination.getCryptoImpl(); encryptedData = cryptoImpl.encrypt(unencryptedPacket.toByteArray(), emailDestination.getPublicEncryptionKey()); dhtKey = getDhtKey(); }
@Test public void testEncryptionDecryption() throws Exception { for (int i=0; i<encryptedPackets.length; i++) { EncryptedEmailPacket packet = encryptedPackets[i]; UnencryptedEmailPacket decryptedPacket = packet.decrypt(identities[i]); byte[] arrayA = decryptedPacket.getContent(); byte[] arrayB = message.getBytes(); assertArrayEquals("Email message differs after decryption! CryptoImplementation = " + packet.getCryptoImpl().getName(), arrayA, arrayB); } }
/** * Returns the two public keys in Base64 representation. */ public String getKey() { PublicKeyPair keys = new PublicKeyPair(publicEncryptionKey, publicSigningKey); try { return cryptoImpl.toBase64(keys); } catch (GeneralSecurityException e) { log.error("Can't get email destination keys.", e); return "<Error>: " + e.getLocalizedMessage(); } }
cryptoImpl.sign(message, privateKey, identities);
@Test public void encodeDecodeBase64() throws GeneralSecurityException { for (TestIdentity testData: testIdentities) { CryptoImplementation cryptoImpl = testData.cryptoImpl; // test a public key pair PublicKeyPair publicKeyPair = cryptoImpl.createPublicKeyPair(testData.base64PublicKeyPair); String reencodedPublic = cryptoImpl.toBase64(publicKeyPair); assertEquals(testData.base64PublicKeyPair, reencodedPublic); assertEquals("Wrong public key length!", cryptoImpl.getBase64PublicKeyPairLength(), testData.base64PublicKeyPair.length()); // test a private key pair PrivateKeyPair privateKeyPair = cryptoImpl.createPrivateKeyPair(testData.base64PrivateKeyPair); String reencodedPrivate = cryptoImpl.toBase64(privateKeyPair); assertEquals(testData.base64PrivateKeyPair, reencodedPrivate); assertEquals("Wrong private key length!", cryptoImpl.getBase64CompleteKeySetLength()-cryptoImpl.getBase64PublicKeyPairLength(), testData.base64PrivateKeyPair.length()); } }
@Test public void testHash() throws Exception { for (EncryptedEmailPacket packet: encryptedPackets) { assertTrue("Hash not valid! CryptoImplementation = " + packet.getCryptoImpl().getName(), packet.verifyPacketHash()); alterEncryptedData(packet); assertFalse("Hash is valid, but should be invalid! CryptoImplementation = " + packet.getCryptoImpl().getName(), packet.verifyPacketHash()); } }
@Test public void toByteArrayAndBack() throws Exception { for (EncryptedEmailPacket packet: encryptedPackets) { byte[] arrayA = packet.toByteArray(); byte[] arrayB = new EncryptedEmailPacket(arrayA).toByteArray(); assertArrayEquals("The two arrays differ! CryptoImplementation = " + packet.getCryptoImpl().getName(), arrayA, arrayB); } }