package com.subgraph.orchid.crypto; import java.security.GeneralSecurityException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.interfaces.RSAPublicKey; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import com.subgraph.orchid.TorException; import com.subgraph.orchid.data.HexDigest; import com.subgraph.orchid.misc.Utils; /** * This class wraps the RSA public keys used in the Tor protocol. */ public class TorPublicKey { static public TorPublicKey createFromPEMBuffer(String buffer) { return new TorPublicKey(buffer); } private final String pemBuffer; private RSAPublicKey key; private byte[] rawKeyBytes = null; private HexDigest keyFingerprint = null; private TorPublicKey(String pemBuffer) { this.pemBuffer = pemBuffer; this.key = null; } public TorPublicKey(RSAPublicKey key) { this.pemBuffer = null; this.key = key; } private synchronized RSAPublicKey getKey() { if(key != null) { return key; } else if(pemBuffer != null) { final RSAKeyEncoder encoder = new RSAKeyEncoder(); try { key = encoder.parsePEMPublicKey(pemBuffer); } catch (GeneralSecurityException e) { throw new IllegalArgumentException("Failed to parse PEM encoded key: "+ e); } } return key; } public synchronized byte[] getRawBytes() { if(rawKeyBytes == null) { final RSAKeyEncoder encoder = new RSAKeyEncoder(); rawKeyBytes = encoder.getPKCS1Encoded(getKey()); } return rawKeyBytes; } public synchronized HexDigest getFingerprint() { if(keyFingerprint == null) { keyFingerprint = HexDigest.createDigestForData(getRawBytes()); } return keyFingerprint; } public boolean verifySignature(TorSignature signature, HexDigest digest) { return verifySignatureFromDigestBytes(signature, digest.getRawBytes()); } public boolean verifySignature(TorSignature signature, TorMessageDigest digest) { return verifySignatureFromDigestBytes(signature, digest.getDigestBytes()); } public boolean verifySignatureFromDigestBytes(TorSignature signature, byte[] digestBytes) { final Cipher cipher = createCipherInstance(); try { byte[] decrypted = cipher.doFinal(signature.getSignatureBytes()); return Utils.constantTimeArrayEquals(decrypted, digestBytes); } catch (IllegalBlockSizeException e) { throw new TorException(e); } catch (BadPaddingException e) { throw new TorException(e); } } private Cipher createCipherInstance() { try { Cipher cipher = getCipherInstance(); cipher.init(Cipher.DECRYPT_MODE, getKey()); return cipher; } catch (InvalidKeyException e) { throw new TorException(e); } } private Cipher getCipherInstance() { try { try { return Cipher.getInstance("RSA/ECB/PKCS1Padding", "SunJCE"); } catch (NoSuchProviderException e) { return Cipher.getInstance("RSA/ECB/PKCS1Padding"); } } catch (NoSuchAlgorithmException e) { throw new TorException(e); } catch (NoSuchPaddingException e) { throw new TorException(e); } } public RSAPublicKey getRSAPublicKey() { return getKey(); } public String toString() { return "Tor Public Key: " + getFingerprint(); } public boolean equals(Object o) { if(!(o instanceof TorPublicKey)) return false; final TorPublicKey other = (TorPublicKey) o; return other.getFingerprint().equals(getFingerprint()); } public int hashCode() { return getFingerprint().hashCode(); } }