DSAEngine: Add sign/verify methods using Java keys

SU3File: Use Java keys to sign and verify so we don't
           lose the key parameters in the conversion to I2P keys
This commit is contained in:
zzz
2013-09-14 15:53:08 +00:00
parent 68aa1aea8e
commit b5dc9bc0ba
4 changed files with 98 additions and 19 deletions

View File

@@ -38,6 +38,9 @@ import java.security.KeyFactory;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.PrivateKey; import java.security.PrivateKey;
import java.security.PublicKey; import java.security.PublicKey;
import java.security.interfaces.DSAPrivateKey;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.RSAPrivateKey;
import net.i2p.I2PAppContext; import net.i2p.I2PAppContext;
import net.i2p.data.Hash; import net.i2p.data.Hash;
@@ -176,6 +179,25 @@ public class DSAEngine {
} }
} }
/**
* Generic signature type.
* If you have a Java pubkey, use this, so you don't lose the key parameters,
* which may be different than the ones defined in SigType.
*
* @param hash SHA1Hash, Hash, Hash384, or Hash512
* @param pubKey Java key
* @since 0.9.9
*/
public boolean verifySignature(Signature signature, SimpleDataStructure hash, PublicKey pubKey) {
try {
return altVerifySigRaw(signature, hash, pubKey);
} catch (GeneralSecurityException gse) {
if (_log.shouldLog(Log.WARN))
_log.warn(signature.getType() + " Sig Verify Fail", gse);
return false;
}
}
/** /**
* Verify using DSA-SHA1 or Syndie DSA-SHA256 ONLY. * Verify using DSA-SHA1 or Syndie DSA-SHA256 ONLY.
* @param hash either a Hash or a SHA1Hash * @param hash either a Hash or a SHA1Hash
@@ -330,6 +352,28 @@ public class DSAEngine {
} }
} }
/**
* Generic signature type.
* If you have a Java privkey, use this, so you don't lose the key parameters,
* which may be different than the ones defined in SigType.
*
* @param hash SHA1Hash, Hash, Hash384, or Hash512
* @param pubKey Java key
* @param type returns a Signature of this type
* @return null on error
* @since 0.9.9
*/
public Signature sign(SimpleDataStructure hash, PrivateKey privKey, SigType type) {
try {
String algo = getRawAlgo(privKey);
return altSignRaw(algo, hash, privKey, type);
} catch (GeneralSecurityException gse) {
if (_log.shouldLog(Log.WARN))
_log.warn(type + " Sign Fail", gse);
return null;
}
}
/** /**
* Sign using DSA-SHA1 or Syndie DSA-SHA256 ONLY. * Sign using DSA-SHA1 or Syndie DSA-SHA256 ONLY.
* *
@@ -477,13 +521,29 @@ public class DSAEngine {
SigType type = signature.getType(); SigType type = signature.getType();
if (type != verifyingKey.getType()) if (type != verifyingKey.getType())
throw new IllegalArgumentException("type mismatch sig=" + type + " key=" + verifyingKey.getType()); throw new IllegalArgumentException("type mismatch sig=" + type + " key=" + verifyingKey.getType());
PublicKey pubKey = SigUtil.toJavaKey(verifyingKey);
return verifySignature(signature, hash, pubKey);
}
/**
* Generic raw verify any type.
* If you have a Java pubkey, use this, so you don't lose the key parameters,
* which may be different than the ones defined in SigType.
*
* @throws GeneralSecurityException if algorithm unvailable or on other errors
* @param verifyingKey Java key
* @since 0.9.9
*/
private boolean altVerifySigRaw(Signature signature, SimpleDataStructure hash, PublicKey pubKey)
throws GeneralSecurityException {
SigType type = signature.getType();
int hashlen = hash.length(); int hashlen = hash.length();
if (type.getHashLen() != hashlen) if (type.getHashLen() != hashlen)
throw new IllegalArgumentException("type mismatch hash=" + hash.getClass() + " key=" + type); throw new IllegalArgumentException("type mismatch hash=" + hash.getClass() + " key=" + type);
String algo = getRawAlgo(type); String algo = getRawAlgo(type);
java.security.Signature jsig = java.security.Signature.getInstance(algo); java.security.Signature jsig = java.security.Signature.getInstance(algo);
PublicKey pubKey = SigUtil.toJavaKey(verifyingKey);
jsig.initVerify(pubKey); jsig.initVerify(pubKey);
jsig.update(hash.getData()); jsig.update(hash.getData());
boolean rv = jsig.verify(SigUtil.toJavaSig(signature)); boolean rv = jsig.verify(SigUtil.toJavaSig(signature));
@@ -533,13 +593,26 @@ public class DSAEngine {
*/ */
private Signature altSignRaw(SimpleDataStructure hash, SigningPrivateKey privateKey) throws GeneralSecurityException { private Signature altSignRaw(SimpleDataStructure hash, SigningPrivateKey privateKey) throws GeneralSecurityException {
SigType type = privateKey.getType(); SigType type = privateKey.getType();
String algo = getRawAlgo(type);
java.security.Signature jsig = java.security.Signature.getInstance(algo);
PrivateKey privKey = SigUtil.toJavaKey(privateKey);
return altSignRaw(algo, hash, privKey, type);
}
/**
* Generic raw verify any type
* @param hash SHA1Hash, Hash, Hash384, or Hash512
* @param type returns a Signature of this type
* @throws GeneralSecurityException if algorithm unvailable or on other errors
* @since 0.9.9
*/
private Signature altSignRaw(String algo, SimpleDataStructure hash, PrivateKey privKey, SigType type)
throws GeneralSecurityException {
int hashlen = hash.length(); int hashlen = hash.length();
if (type.getHashLen() != hashlen) if (type.getHashLen() != hashlen)
throw new IllegalArgumentException("type mismatch hash=" + hash.getClass() + " key=" + type); throw new IllegalArgumentException("type mismatch hash=" + hash.getClass() + " key=" + type);
String algo = getRawAlgo(type);
java.security.Signature jsig = java.security.Signature.getInstance(algo); java.security.Signature jsig = java.security.Signature.getInstance(algo);
PrivateKey privKey = SigUtil.toJavaKey(privateKey);
jsig.initSign(privKey, _context.random()); jsig.initSign(privKey, _context.random());
jsig.update(hash.getData()); jsig.update(hash.getData());
return SigUtil.fromJavaSig(jsig.sign(), type); return SigUtil.fromJavaSig(jsig.sign(), type);
@@ -558,6 +631,7 @@ public class DSAEngine {
return SigUtil.fromJavaSig(jsig.sign(), SigType.DSA_SHA1); return SigUtil.fromJavaSig(jsig.sign(), SigType.DSA_SHA1);
} }
/** @since 0.9.9 */
private static String getRawAlgo(SigType type) { private static String getRawAlgo(SigType type) {
switch (type.getBaseAlgorithm()) { switch (type.getBaseAlgorithm()) {
case DSA: case DSA:
@@ -571,6 +645,17 @@ public class DSAEngine {
} }
} }
/** @since 0.9.9 */
private static String getRawAlgo(PrivateKey privKey) {
if (privKey instanceof DSAPrivateKey)
return "NONEwithDSA";
if (privKey instanceof ECPrivateKey)
return "NONEwithECDSA";
if (privKey instanceof RSAPrivateKey)
return "NONEwithRSA";
throw new IllegalArgumentException();
}
//private static final int RUNS = 1000; //private static final int RUNS = 1000;
/** /**

View File

@@ -14,8 +14,6 @@ import java.security.PublicKey;
import java.security.cert.CertificateFactory; import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import net.i2p.data.SigningPublicKey;
/** /**
* Dumb storage in a directory for testing. * Dumb storage in a directory for testing.
* No sanitization of filenames, unsafe. * No sanitization of filenames, unsafe.
@@ -30,7 +28,7 @@ class DirKeyRing implements KeyRing {
_base = baseDir; _base = baseDir;
} }
public SigningPublicKey getKey(String keyName, String scope, SigType type) public PublicKey getKey(String keyName, String scope, SigType type)
throws GeneralSecurityException, IOException { throws GeneralSecurityException, IOException {
keyName = keyName.replace("@", "_at_"); keyName = keyName.replace("@", "_at_");
File test = new File(keyName); File test = new File(keyName);
@@ -46,12 +44,11 @@ class DirKeyRing implements KeyRing {
fis = new FileInputStream(kd); fis = new FileInputStream(kd);
CertificateFactory cf = CertificateFactory.getInstance("X.509"); CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate)cf.generateCertificate(fis); X509Certificate cert = (X509Certificate)cf.generateCertificate(fis);
PublicKey pk = cert.getPublicKey(); return cert.getPublicKey();
return SigUtil.fromJavaKey(pk, type);
} finally { } finally {
try { if (fis != null) fis.close(); } catch (IOException foo) {} try { if (fis != null) fis.close(); } catch (IOException foo) {}
} }
} }
public void setKey(String keyName, String scope, SigningPublicKey key) {} public void setKey(String keyName, String scope, PublicKey key) {}
} }

View File

@@ -7,8 +7,7 @@ package net.i2p.crypto;
import java.io.IOException; import java.io.IOException;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.security.PublicKey;
import net.i2p.data.SigningPublicKey;
/** /**
* A backend for storing and retrieving SigningPublicKeys * A backend for storing and retrieving SigningPublicKeys
@@ -24,7 +23,7 @@ public interface KeyRing {
* @param scope a domain identifier, indicating router update, reseed, etc. * @param scope a domain identifier, indicating router update, reseed, etc.
* @return null if none * @return null if none
*/ */
public SigningPublicKey getKey(String keyName, String scope, SigType type) public PublicKey getKey(String keyName, String scope, SigType type)
throws GeneralSecurityException, IOException; throws GeneralSecurityException, IOException;
/** /**
@@ -32,6 +31,6 @@ public interface KeyRing {
* Throws on all errors. * Throws on all errors.
* @param scope a domain identifier, indicating router update, reseed, etc. * @param scope a domain identifier, indicating router update, reseed, etc.
*/ */
public void setKey(String keyName, String scope, SigningPublicKey key) public void setKey(String keyName, String scope, PublicKey key)
throws GeneralSecurityException, IOException; throws GeneralSecurityException, IOException;
} }

View File

@@ -47,7 +47,7 @@ public class SU3File {
private int _signerLength; private int _signerLength;
private ContentType _contentType; private ContentType _contentType;
private long _contentLength; private long _contentLength;
private SigningPublicKey _signerPubkey; private PublicKey _signerPubkey;
private boolean _headerVerified; private boolean _headerVerified;
private SigType _sigType; private SigType _sigType;
@@ -330,13 +330,12 @@ public class SU3File {
* @param signer ID of the public key, 1-255 bytes when converted to UTF-8 * @param signer ID of the public key, 1-255 bytes when converted to UTF-8
*/ */
public void write(File content, int contentType, String version, public void write(File content, int contentType, String version,
String signer, SigningPrivateKey privkey) throws IOException { String signer, PrivateKey privkey, SigType sigType) throws IOException {
InputStream in = null; InputStream in = null;
DigestOutputStream out = null; DigestOutputStream out = null;
boolean ok = false; boolean ok = false;
try { try {
in = new BufferedInputStream(new FileInputStream(content)); in = new BufferedInputStream(new FileInputStream(content));
SigType sigType = privkey.getType();
MessageDigest md = sigType.getDigestInstance(); MessageDigest md = sigType.getDigestInstance();
out = new DigestOutputStream(new BufferedOutputStream(new FileOutputStream(_file)), md); out = new DigestOutputStream(new BufferedOutputStream(new FileOutputStream(_file)), md);
out.write(MAGIC); out.write(MAGIC);
@@ -385,7 +384,7 @@ public class SU3File {
out.on(false); out.on(false);
SimpleDataStructure hash = sigType.getHashInstance(); SimpleDataStructure hash = sigType.getHashInstance();
hash.setData(sha); hash.setData(sha);
Signature signature = _context.dsa().sign(hash, privkey); Signature signature = _context.dsa().sign(hash, privkey, sigType);
//System.out.println("hash\n" + HexDump.dump(sha)); //System.out.println("hash\n" + HexDump.dump(sha));
//System.out.println("sig\n" + HexDump.dump(signature.getData())); //System.out.println("sig\n" + HexDump.dump(signature.getData()));
signature.writeBytes(out); signature.writeBytes(out);
@@ -548,9 +547,8 @@ public class SU3File {
System.out.println("Private key for " + signerName + " not found in keystore " + privateKeyFile); System.out.println("Private key for " + signerName + " not found in keystore " + privateKeyFile);
return false; return false;
} }
SigningPrivateKey spk = SigUtil.fromJavaKey(pk, type);
SU3File file = new SU3File(signedFile); SU3File file = new SU3File(signedFile);
file.write(new File(inputFile), CONTENT_ROUTER, version, signerName, spk); file.write(new File(inputFile), CONTENT_ROUTER, version, signerName, pk, type);
System.out.println("Input file '" + inputFile + "' signed and written to '" + signedFile + "'"); System.out.println("Input file '" + inputFile + "' signed and written to '" + signedFile + "'");
return true; return true;
} catch (GeneralSecurityException gse) { } catch (GeneralSecurityException gse) {