forked from I2P_Developers/i2p.i2p
propagate from branch 'i2p.i2p' (head 6667c4a9f9fcc0705e407006d1933dd31942ffb2)
to branch 'i2p.i2p.zzz.ecdsa' (head bf746450400fc5ffa0e727ed0cd6a3966f6ce51f)
This commit is contained in:
@ -30,6 +30,7 @@ package net.i2p.crypto;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
import java.security.spec.DSAParameterSpec;
|
||||||
|
|
||||||
import net.i2p.util.NativeBigInteger;
|
import net.i2p.util.NativeBigInteger;
|
||||||
|
|
||||||
@ -63,4 +64,9 @@ public class CryptoConstants {
|
|||||||
+ "DE2BCBF6955817183995497CEA956AE515D2261898FA0510"
|
+ "DE2BCBF6955817183995497CEA956AE515D2261898FA0510"
|
||||||
+ "15728E5A8AACAA68FFFFFFFFFFFFFFFF", 16);
|
+ "15728E5A8AACAA68FFFFFFFFFFFFFFFF", 16);
|
||||||
public static final BigInteger elgg = new NativeBigInteger("2");
|
public static final BigInteger elgg = new NativeBigInteger("2");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 0.9.9
|
||||||
|
*/
|
||||||
|
public static final DSAParameterSpec DSA_SHA1_SPEC = new DSAParameterSpec(dsap, dsaq, dsag);
|
||||||
}
|
}
|
||||||
|
@ -38,9 +38,6 @@ 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.spec.DSAPrivateKeySpec;
|
|
||||||
import java.security.spec.DSAPublicKeySpec;
|
|
||||||
import java.security.spec.KeySpec;
|
|
||||||
|
|
||||||
import net.i2p.I2PAppContext;
|
import net.i2p.I2PAppContext;
|
||||||
import net.i2p.data.Hash;
|
import net.i2p.data.Hash;
|
||||||
@ -62,6 +59,12 @@ import net.i2p.util.NativeBigInteger;
|
|||||||
*
|
*
|
||||||
* Params and rv's changed from Hash to SHA1Hash for version 0.8.1
|
* Params and rv's changed from Hash to SHA1Hash for version 0.8.1
|
||||||
* Hash variants of sign() and verifySignature() restored in 0.8.3, required by Syndie.
|
* Hash variants of sign() and verifySignature() restored in 0.8.3, required by Syndie.
|
||||||
|
*
|
||||||
|
* As of 0.9.9, certain methods support ECDSA keys and signatures, i.e. all types
|
||||||
|
* specified in SigType. The type is specified by the getType() method in
|
||||||
|
* Signature, SigningPublicKey, and SigningPrivateKey. See Javadocs for individual
|
||||||
|
* methods for the supported types. Methods encountering an unsupported type
|
||||||
|
* will throw an IllegalArgumentException.
|
||||||
*/
|
*/
|
||||||
public class DSAEngine {
|
public class DSAEngine {
|
||||||
private final Log _log;
|
private final Log _log;
|
||||||
@ -80,11 +83,26 @@ public class DSAEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verify using DSA-SHA1.
|
* Verify using DSA-SHA1 or ECDSA.
|
||||||
* Uses TheCrypto code unless configured to use the java.security libraries.
|
* Uses TheCrypto code for DSA-SHA1 unless configured to use the java.security libraries.
|
||||||
*/
|
*/
|
||||||
public boolean verifySignature(Signature signature, byte signedData[], SigningPublicKey verifyingKey) {
|
public boolean verifySignature(Signature signature, byte signedData[], SigningPublicKey verifyingKey) {
|
||||||
boolean rv;
|
boolean rv;
|
||||||
|
SigType type = signature.getType();
|
||||||
|
if (type != verifyingKey.getType())
|
||||||
|
throw new IllegalArgumentException("type mismatch sig=" + signature.getType() + " key=" + verifyingKey.getType());
|
||||||
|
if (type != SigType.DSA_SHA1) {
|
||||||
|
try {
|
||||||
|
rv = altVerifySig(signature, signedData, verifyingKey);
|
||||||
|
if ((!rv) && _log.shouldLog(Log.WARN))
|
||||||
|
_log.warn(type + " Sig Verify Fail");
|
||||||
|
return rv;
|
||||||
|
} catch (GeneralSecurityException gse) {
|
||||||
|
if (_log.shouldLog(Log.WARN))
|
||||||
|
_log.warn(type + " Sig Verify Fail", gse);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (_useJavaLibs) {
|
if (_useJavaLibs) {
|
||||||
try {
|
try {
|
||||||
rv = altVerifySigSHA1(signature, signedData, verifyingKey);
|
rv = altVerifySigSHA1(signature, signedData, verifyingKey);
|
||||||
@ -104,25 +122,29 @@ public class DSAEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verify using DSA-SHA1
|
* Verify using DSA-SHA1 ONLY
|
||||||
*/
|
*/
|
||||||
public boolean verifySignature(Signature signature, byte signedData[], int offset, int size, SigningPublicKey verifyingKey) {
|
public boolean verifySignature(Signature signature, byte signedData[], int offset, int size, SigningPublicKey verifyingKey) {
|
||||||
return verifySignature(signature, calculateHash(signedData, offset, size), verifyingKey);
|
return verifySignature(signature, calculateHash(signedData, offset, size), verifyingKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verify using DSA-SHA1
|
* Verify using DSA-SHA1 ONLY
|
||||||
*/
|
*/
|
||||||
public boolean verifySignature(Signature signature, InputStream in, SigningPublicKey verifyingKey) {
|
public boolean verifySignature(Signature signature, InputStream in, SigningPublicKey verifyingKey) {
|
||||||
return verifySignature(signature, calculateHash(in), verifyingKey);
|
return verifySignature(signature, calculateHash(in), verifyingKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @param hash SHA-1 hash, NOT a SHA-256 hash */
|
/**
|
||||||
|
* Verify using DSA-SHA1 ONLY
|
||||||
|
* @param hash SHA-1 hash, NOT a SHA-256 hash
|
||||||
|
*/
|
||||||
public boolean verifySignature(Signature signature, SHA1Hash hash, SigningPublicKey verifyingKey) {
|
public boolean verifySignature(Signature signature, SHA1Hash hash, SigningPublicKey verifyingKey) {
|
||||||
return verifySig(signature, hash, verifyingKey);
|
return verifySig(signature, hash, verifyingKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Nonstandard.
|
||||||
* Used by Syndie.
|
* Used by Syndie.
|
||||||
* @since 0.8.3 (restored, was removed in 0.8.1 and 0.8.2)
|
* @since 0.8.3 (restored, was removed in 0.8.1 and 0.8.2)
|
||||||
*/
|
*/
|
||||||
@ -131,10 +153,39 @@ public class DSAEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Generic signature type.
|
||||||
|
*
|
||||||
|
* @param hash SHA1Hash, Hash, Hash384, or Hash512
|
||||||
|
* @since 0.9.9
|
||||||
|
*/
|
||||||
|
public boolean verifySignature(Signature signature, SimpleDataStructure hash, SigningPublicKey verifyingKey) {
|
||||||
|
SigType type = signature.getType();
|
||||||
|
if (type != verifyingKey.getType())
|
||||||
|
throw new IllegalArgumentException("type mismatch sig=" + type + " key=" + verifyingKey.getType());
|
||||||
|
int hashlen = type.getHashLen();
|
||||||
|
if (hash.length() != hashlen)
|
||||||
|
throw new IllegalArgumentException("type mismatch hash=" + hash.getClass() + " sig=" + type);
|
||||||
|
if (type == SigType.DSA_SHA1)
|
||||||
|
return verifySig(signature, hash, verifyingKey);
|
||||||
|
try {
|
||||||
|
return altVerifySigRaw(signature, hash, verifyingKey);
|
||||||
|
} catch (GeneralSecurityException gse) {
|
||||||
|
if (_log.shouldLog(Log.WARN))
|
||||||
|
_log.warn(type + " Sig Verify Fail", gse);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
* @since 0.8.3
|
* @since 0.8.3
|
||||||
*/
|
*/
|
||||||
private boolean verifySig(Signature signature, SimpleDataStructure hash, SigningPublicKey verifyingKey) {
|
private boolean verifySig(Signature signature, SimpleDataStructure hash, SigningPublicKey verifyingKey) {
|
||||||
|
if (signature.getType() != SigType.DSA_SHA1)
|
||||||
|
throw new IllegalArgumentException("Bad sig type " + signature.getType());
|
||||||
|
if (verifyingKey.getType() != SigType.DSA_SHA1)
|
||||||
|
throw new IllegalArgumentException("Bad key type " + verifyingKey.getType());
|
||||||
long start = _context.clock().now();
|
long start = _context.clock().now();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -184,10 +235,22 @@ public class DSAEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sign using DSA-SHA1.
|
* Sign using DSA-SHA1 or ECDSA.
|
||||||
* Uses TheCrypto code unless configured to use the java.security libraries.
|
* Uses TheCrypto code unless configured to use the java.security libraries.
|
||||||
|
*
|
||||||
|
* @return null on error
|
||||||
*/
|
*/
|
||||||
public Signature sign(byte data[], SigningPrivateKey signingKey) {
|
public Signature sign(byte data[], SigningPrivateKey signingKey) {
|
||||||
|
SigType type = signingKey.getType();
|
||||||
|
if (type != SigType.DSA_SHA1) {
|
||||||
|
try {
|
||||||
|
return altSign(data, signingKey);
|
||||||
|
} catch (GeneralSecurityException gse) {
|
||||||
|
if (_log.shouldLog(Log.WARN))
|
||||||
|
_log.warn(type + " Sign Fail", gse);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (_useJavaLibs) {
|
if (_useJavaLibs) {
|
||||||
try {
|
try {
|
||||||
return altSignSHA1(data, signingKey);
|
return altSignSHA1(data, signingKey);
|
||||||
@ -201,7 +264,9 @@ public class DSAEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sign using DSA-SHA1
|
* Sign using DSA-SHA1 ONLY
|
||||||
|
*
|
||||||
|
* @return null on error
|
||||||
*/
|
*/
|
||||||
public Signature sign(byte data[], int offset, int length, SigningPrivateKey signingKey) {
|
public Signature sign(byte data[], int offset, int length, SigningPrivateKey signingKey) {
|
||||||
if ((signingKey == null) || (data == null) || (data.length <= 0)) return null;
|
if ((signingKey == null) || (data == null) || (data.length <= 0)) return null;
|
||||||
@ -210,8 +275,10 @@ public class DSAEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sign using DSA-SHA1.
|
* Sign using DSA-SHA1 ONLY.
|
||||||
* Reads the stream until EOF. Does not close the stream.
|
* Reads the stream until EOF. Does not close the stream.
|
||||||
|
*
|
||||||
|
* @return null on error
|
||||||
*/
|
*/
|
||||||
public Signature sign(InputStream in, SigningPrivateKey signingKey) {
|
public Signature sign(InputStream in, SigningPrivateKey signingKey) {
|
||||||
if ((signingKey == null) || (in == null) ) return null;
|
if ((signingKey == null) || (in == null) ) return null;
|
||||||
@ -219,13 +286,21 @@ public class DSAEngine {
|
|||||||
return sign(h, signingKey);
|
return sign(h, signingKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @param hash SHA-1 hash, NOT a SHA-256 hash */
|
/**
|
||||||
|
* Sign using DSA-SHA1 ONLY.
|
||||||
|
*
|
||||||
|
* @param hash SHA-1 hash, NOT a SHA-256 hash
|
||||||
|
* @return null on error
|
||||||
|
*/
|
||||||
public Signature sign(SHA1Hash hash, SigningPrivateKey signingKey) {
|
public Signature sign(SHA1Hash hash, SigningPrivateKey signingKey) {
|
||||||
return signIt(hash, signingKey);
|
return signIt(hash, signingKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Nonstandard.
|
||||||
* Used by Syndie.
|
* Used by Syndie.
|
||||||
|
*
|
||||||
|
* @return null on error
|
||||||
* @since 0.8.3 (restored, was removed in 0.8.1 and 0.8.2)
|
* @since 0.8.3 (restored, was removed in 0.8.1 and 0.8.2)
|
||||||
*/
|
*/
|
||||||
public Signature sign(Hash hash, SigningPrivateKey signingKey) {
|
public Signature sign(Hash hash, SigningPrivateKey signingKey) {
|
||||||
@ -233,11 +308,39 @@ public class DSAEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Generic signature type.
|
||||||
|
*
|
||||||
|
* @param hash SHA1Hash, Hash, Hash384, or Hash512
|
||||||
|
* @return null on error
|
||||||
|
* @since 0.9.9
|
||||||
|
*/
|
||||||
|
public Signature sign(SimpleDataStructure hash, SigningPrivateKey signingKey) {
|
||||||
|
SigType type = signingKey.getType();
|
||||||
|
int hashlen = type.getHashLen();
|
||||||
|
if (hash.length() != hashlen)
|
||||||
|
throw new IllegalArgumentException("type mismatch hash=" + hash.getClass() + " key=" + type);
|
||||||
|
if (type == SigType.DSA_SHA1)
|
||||||
|
return signIt(hash, signingKey);
|
||||||
|
try {
|
||||||
|
return altSignRaw(hash, signingKey);
|
||||||
|
} 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.
|
||||||
|
*
|
||||||
* @param hash either a Hash or a SHA1Hash
|
* @param hash either a Hash or a SHA1Hash
|
||||||
|
* @return null on error
|
||||||
* @since 0.8.3
|
* @since 0.8.3
|
||||||
*/
|
*/
|
||||||
private Signature signIt(SimpleDataStructure hash, SigningPrivateKey signingKey) {
|
private Signature signIt(SimpleDataStructure hash, SigningPrivateKey signingKey) {
|
||||||
if ((signingKey == null) || (hash == null)) return null;
|
if ((signingKey == null) || (hash == null)) return null;
|
||||||
|
if (signingKey.getType() != SigType.DSA_SHA1)
|
||||||
|
throw new IllegalArgumentException("Bad key type " + signingKey.getType());
|
||||||
long start = _context.clock().now();
|
long start = _context.clock().now();
|
||||||
|
|
||||||
Signature sig = new Signature();
|
Signature sig = new Signature();
|
||||||
@ -275,6 +378,9 @@ public class DSAEngine {
|
|||||||
for (int i = 0; i < 20; i++) {
|
for (int i = 0; i < 20; i++) {
|
||||||
out[i] = rbytes[i + 1];
|
out[i] = rbytes[i + 1];
|
||||||
}
|
}
|
||||||
|
} else if (rbytes.length > 21) {
|
||||||
|
_log.error("Bad R length " + rbytes.length);
|
||||||
|
return null;
|
||||||
} else {
|
} else {
|
||||||
if (_log.shouldLog(Log.DEBUG)) _log.debug("Using short rbytes.length [" + rbytes.length + "]");
|
if (_log.shouldLog(Log.DEBUG)) _log.debug("Using short rbytes.length [" + rbytes.length + "]");
|
||||||
//System.arraycopy(rbytes, 0, out, 20 - rbytes.length, rbytes.length);
|
//System.arraycopy(rbytes, 0, out, 20 - rbytes.length, rbytes.length);
|
||||||
@ -291,6 +397,9 @@ public class DSAEngine {
|
|||||||
for (int i = 0; i < 20; i++) {
|
for (int i = 0; i < 20; i++) {
|
||||||
out[i + 20] = sbytes[i + 1];
|
out[i + 20] = sbytes[i + 1];
|
||||||
}
|
}
|
||||||
|
} else if (sbytes.length > 21) {
|
||||||
|
_log.error("Bad S length " + sbytes.length);
|
||||||
|
return null;
|
||||||
} else {
|
} else {
|
||||||
if (_log.shouldLog(Log.DEBUG)) _log.debug("Using short sbytes.length [" + sbytes.length + "]");
|
if (_log.shouldLog(Log.DEBUG)) _log.debug("Using short sbytes.length [" + sbytes.length + "]");
|
||||||
//System.arraycopy(sbytes, 0, out, 40 - sbytes.length, sbytes.length);
|
//System.arraycopy(sbytes, 0, out, 40 - sbytes.length, sbytes.length);
|
||||||
@ -337,6 +446,51 @@ public class DSAEngine {
|
|||||||
return new SHA1Hash(digested);
|
return new SHA1Hash(digested);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic verify DSA_SHA1 or ECDSA
|
||||||
|
* @throws GeneralSecurityException if algorithm unvailable or on other errors
|
||||||
|
* @since 0.9.9
|
||||||
|
*/
|
||||||
|
private boolean altVerifySig(Signature signature, byte[] data, SigningPublicKey verifyingKey)
|
||||||
|
throws GeneralSecurityException {
|
||||||
|
SigType type = signature.getType();
|
||||||
|
if (type != verifyingKey.getType())
|
||||||
|
throw new IllegalArgumentException("type mismatch sig=" + type + " key=" + verifyingKey.getType());
|
||||||
|
if (type == SigType.DSA_SHA1)
|
||||||
|
return altVerifySigSHA1(signature, data, verifyingKey);
|
||||||
|
|
||||||
|
java.security.Signature jsig = java.security.Signature.getInstance(type.getAlgorithmName());
|
||||||
|
PublicKey pubKey = SigUtil.toJavaECKey(verifyingKey);
|
||||||
|
jsig.initVerify(pubKey);
|
||||||
|
jsig.update(data);
|
||||||
|
boolean rv = jsig.verify(SigUtil.toJavaSig(signature));
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic raw verify ECDSA only
|
||||||
|
* @throws GeneralSecurityException if algorithm unvailable or on other errors
|
||||||
|
* @since 0.9.9
|
||||||
|
*/
|
||||||
|
private boolean altVerifySigRaw(Signature signature, SimpleDataStructure hash, SigningPublicKey verifyingKey)
|
||||||
|
throws GeneralSecurityException {
|
||||||
|
SigType type = signature.getType();
|
||||||
|
if (type != verifyingKey.getType())
|
||||||
|
throw new IllegalArgumentException("type mismatch sig=" + type + " key=" + verifyingKey.getType());
|
||||||
|
int hashlen = hash.length();
|
||||||
|
if (type.getHashLen() != hashlen)
|
||||||
|
throw new IllegalArgumentException("type mismatch hash=" + hash.getClass() + " key=" + type);
|
||||||
|
if (type == SigType.DSA_SHA1)
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
|
||||||
|
java.security.Signature jsig = java.security.Signature.getInstance("NONEwithECDSA");
|
||||||
|
PublicKey pubKey = SigUtil.toJavaECKey(verifyingKey);
|
||||||
|
jsig.initVerify(pubKey);
|
||||||
|
jsig.update(hash.getData());
|
||||||
|
boolean rv = jsig.verify(SigUtil.toJavaSig(signature));
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Alternate to verifySignature() using java.security libraries.
|
* Alternate to verifySignature() using java.security libraries.
|
||||||
* @throws GeneralSecurityException if algorithm unvailable or on other errors
|
* @throws GeneralSecurityException if algorithm unvailable or on other errors
|
||||||
@ -344,16 +498,10 @@ public class DSAEngine {
|
|||||||
*/
|
*/
|
||||||
private boolean altVerifySigSHA1(Signature signature, byte[] data, SigningPublicKey verifyingKey) throws GeneralSecurityException {
|
private boolean altVerifySigSHA1(Signature signature, byte[] data, SigningPublicKey verifyingKey) throws GeneralSecurityException {
|
||||||
java.security.Signature jsig = java.security.Signature.getInstance("SHA1withDSA");
|
java.security.Signature jsig = java.security.Signature.getInstance("SHA1withDSA");
|
||||||
KeyFactory keyFact = KeyFactory.getInstance("DSA");
|
PublicKey pubKey = SigUtil.toJavaDSAKey(verifyingKey);
|
||||||
// y p q g
|
|
||||||
KeySpec spec = new DSAPublicKeySpec(new NativeBigInteger(1, verifyingKey.getData()),
|
|
||||||
CryptoConstants.dsap,
|
|
||||||
CryptoConstants.dsaq,
|
|
||||||
CryptoConstants.dsag);
|
|
||||||
PublicKey pubKey = keyFact.generatePublic(spec);
|
|
||||||
jsig.initVerify(pubKey);
|
jsig.initVerify(pubKey);
|
||||||
jsig.update(data);
|
jsig.update(data);
|
||||||
boolean rv = jsig.verify(sigBytesToASN1(signature.getData()));
|
boolean rv = jsig.verify(SigUtil.toJavaSig(signature));
|
||||||
//if (!rv) {
|
//if (!rv) {
|
||||||
// System.out.println("BAD SIG\n" + net.i2p.util.HexDump.dump(signature.getData()));
|
// System.out.println("BAD SIG\n" + net.i2p.util.HexDump.dump(signature.getData()));
|
||||||
// System.out.println("BAD SIG\n" + net.i2p.util.HexDump.dump(sigBytesToASN1(signature.getData())));
|
// System.out.println("BAD SIG\n" + net.i2p.util.HexDump.dump(sigBytesToASN1(signature.getData())));
|
||||||
@ -361,6 +509,44 @@ public class DSAEngine {
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic sign DSA_SHA1 or ECDSA
|
||||||
|
* @throws GeneralSecurityException if algorithm unvailable or on other errors
|
||||||
|
* @since 0.9.9
|
||||||
|
*/
|
||||||
|
private Signature altSign(byte[] data, SigningPrivateKey privateKey) throws GeneralSecurityException {
|
||||||
|
SigType type = privateKey.getType();
|
||||||
|
if (type == SigType.DSA_SHA1)
|
||||||
|
return altSignSHA1(data, privateKey);
|
||||||
|
|
||||||
|
java.security.Signature jsig = java.security.Signature.getInstance(type.getAlgorithmName());
|
||||||
|
PrivateKey privKey = SigUtil.toJavaECKey(privateKey);
|
||||||
|
jsig.initSign(privKey, _context.random());
|
||||||
|
jsig.update(data);
|
||||||
|
return SigUtil.fromJavaSig(jsig.sign(), type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic raw sign ECDSA only.
|
||||||
|
* @param hash SHA1Hash, Hash, Hash384, or Hash512
|
||||||
|
* @throws GeneralSecurityException if algorithm unvailable or on other errors
|
||||||
|
* @since 0.9.9
|
||||||
|
*/
|
||||||
|
private Signature altSignRaw(SimpleDataStructure hash, SigningPrivateKey privateKey) throws GeneralSecurityException {
|
||||||
|
SigType type = privateKey.getType();
|
||||||
|
if (type == SigType.DSA_SHA1)
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
int hashlen = hash.length();
|
||||||
|
if (type.getHashLen() != hashlen)
|
||||||
|
throw new IllegalArgumentException("type mismatch hash=" + hash.getClass() + " key=" + type);
|
||||||
|
|
||||||
|
java.security.Signature jsig = java.security.Signature.getInstance("NONEwithECDSA");
|
||||||
|
PrivateKey privKey = SigUtil.toJavaECKey(privateKey);
|
||||||
|
jsig.initSign(privKey, _context.random());
|
||||||
|
jsig.update(hash.getData());
|
||||||
|
return SigUtil.fromJavaSig(jsig.sign(), type);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Alternate to sign() using java.security libraries.
|
* Alternate to sign() using java.security libraries.
|
||||||
* @throws GeneralSecurityException if algorithm unvailable or on other errors
|
* @throws GeneralSecurityException if algorithm unvailable or on other errors
|
||||||
@ -368,90 +554,10 @@ public class DSAEngine {
|
|||||||
*/
|
*/
|
||||||
private Signature altSignSHA1(byte[] data, SigningPrivateKey privateKey) throws GeneralSecurityException {
|
private Signature altSignSHA1(byte[] data, SigningPrivateKey privateKey) throws GeneralSecurityException {
|
||||||
java.security.Signature jsig = java.security.Signature.getInstance("SHA1withDSA");
|
java.security.Signature jsig = java.security.Signature.getInstance("SHA1withDSA");
|
||||||
KeyFactory keyFact = KeyFactory.getInstance("DSA");
|
PrivateKey privKey = SigUtil.toJavaDSAKey(privateKey);
|
||||||
// y p q g
|
|
||||||
KeySpec spec = new DSAPrivateKeySpec(new NativeBigInteger(1, privateKey.getData()),
|
|
||||||
CryptoConstants.dsap,
|
|
||||||
CryptoConstants.dsaq,
|
|
||||||
CryptoConstants.dsag);
|
|
||||||
PrivateKey privKey = keyFact.generatePrivate(spec);
|
|
||||||
jsig.initSign(privKey, _context.random());
|
jsig.initSign(privKey, _context.random());
|
||||||
jsig.update(data);
|
jsig.update(data);
|
||||||
return new Signature(aSN1ToSigBytes(jsig.sign()));
|
return SigUtil.fromJavaSig(jsig.sign(), SigType.DSA_SHA1);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* http://download.oracle.com/javase/1.5.0/docs/guide/security/CryptoSpec.html
|
|
||||||
* Signature Format ASN.1 sequence of two INTEGER values: r and s, in that order:
|
|
||||||
* SEQUENCE ::= { r INTEGER, s INTEGER }
|
|
||||||
*
|
|
||||||
* http://en.wikipedia.org/wiki/Abstract_Syntax_Notation_One
|
|
||||||
* 30 -- tag indicating SEQUENCE
|
|
||||||
* xx - length in octets
|
|
||||||
*
|
|
||||||
* 02 -- tag indicating INTEGER
|
|
||||||
* xx - length in octets
|
|
||||||
* xxxxxx - value
|
|
||||||
*
|
|
||||||
* Convert to BigInteger and back so we have the minimum length representation, as required.
|
|
||||||
* r and s are always non-negative.
|
|
||||||
*
|
|
||||||
* @since 0.8.7
|
|
||||||
*/
|
|
||||||
private static byte[] sigBytesToASN1(byte[] sig) {
|
|
||||||
//System.out.println("pre TO asn1\n" + net.i2p.util.HexDump.dump(sig));
|
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(48);
|
|
||||||
baos.write(0x30);
|
|
||||||
baos.write(0); // length to be filled in below
|
|
||||||
|
|
||||||
byte[] tmp = new byte[20];
|
|
||||||
baos.write(2);
|
|
||||||
System.arraycopy(sig, 0, tmp, 0, 20);
|
|
||||||
BigInteger r = new BigInteger(1, tmp);
|
|
||||||
byte[] b = r.toByteArray();
|
|
||||||
baos.write(b.length);
|
|
||||||
baos.write(b, 0, b.length);
|
|
||||||
|
|
||||||
baos.write(2);
|
|
||||||
System.arraycopy(sig, 20, tmp, 0, 20);
|
|
||||||
BigInteger s = new BigInteger(1, tmp);
|
|
||||||
b = s.toByteArray();
|
|
||||||
baos.write(b.length);
|
|
||||||
baos.write(b, 0, b.length);
|
|
||||||
byte[] rv = baos.toByteArray();
|
|
||||||
rv[1] = (byte) (rv.length - 2);
|
|
||||||
//System.out.println("post TO asn1\n" + net.i2p.util.HexDump.dump(rv));
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* See above.
|
|
||||||
* @since 0.8.7
|
|
||||||
*/
|
|
||||||
private static byte[] aSN1ToSigBytes(byte[] asn) {
|
|
||||||
//System.out.println("pre from asn1\n" + net.i2p.util.HexDump.dump(asn));
|
|
||||||
byte[] rv = new byte[40];
|
|
||||||
int rlen = asn[3];
|
|
||||||
if ((asn[4] & 0x80) != 0)
|
|
||||||
throw new IllegalArgumentException("R is negative");
|
|
||||||
if (rlen > 21)
|
|
||||||
throw new IllegalArgumentException("R too big " + rlen);
|
|
||||||
else if (rlen == 21) {
|
|
||||||
System.arraycopy(asn, 5, rv, 0, 20);
|
|
||||||
} else
|
|
||||||
System.arraycopy(asn, 4, rv, 20 - rlen, rlen);
|
|
||||||
int slenloc = 25 + rlen - 20;
|
|
||||||
int slen = asn[slenloc];
|
|
||||||
if ((asn[slenloc + 1] & 0x80) != 0)
|
|
||||||
throw new IllegalArgumentException("S is negative");
|
|
||||||
if (slen > 21)
|
|
||||||
throw new IllegalArgumentException("S too big " + slen);
|
|
||||||
else if (slen == 21) {
|
|
||||||
System.arraycopy(asn, slenloc + 2, rv, 20, 20);
|
|
||||||
} else
|
|
||||||
System.arraycopy(asn, slenloc + 1, rv, 40 - slen, slen);
|
|
||||||
//System.out.println("post from asn1\n" + net.i2p.util.HexDump.dump(rv));
|
|
||||||
return rv;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//private static final int RUNS = 1000;
|
//private static final int RUNS = 1000;
|
||||||
|
41
core/java/src/net/i2p/crypto/DirKeyRing.java
Normal file
41
core/java/src/net/i2p/crypto/DirKeyRing.java
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package net.i2p.crypto;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* free (adj.): unencumbered; not under the control of others
|
||||||
|
* No warranty of any kind, either expressed or implied.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
|
||||||
|
import net.i2p.data.SigningPublicKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dumb storage in a directory for testing.
|
||||||
|
* No sanitization of filenames, unsafe.
|
||||||
|
*
|
||||||
|
* @since 0.9.9
|
||||||
|
*/
|
||||||
|
class DirKeyRing implements KeyRing {
|
||||||
|
|
||||||
|
private final File _base;
|
||||||
|
|
||||||
|
public DirKeyRing(File baseDir) {
|
||||||
|
_base = baseDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SigningPublicKey getKey(String keyName, String scope, SigType type)
|
||||||
|
throws GeneralSecurityException, IOException {
|
||||||
|
File sd = new File(_base, scope);
|
||||||
|
File td = new File(sd, Integer.toString(type.getCode()));
|
||||||
|
File kd = new File(td, keyName + ".key");
|
||||||
|
if (!kd.exists())
|
||||||
|
return null;
|
||||||
|
PublicKey pk = SigUtil.importJavaPublicKey(kd, type);
|
||||||
|
return SigUtil.fromJavaKey(pk, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKey(String keyName, String scope, SigningPublicKey key) {}
|
||||||
|
}
|
327
core/java/src/net/i2p/crypto/ECConstants.java
Normal file
327
core/java/src/net/i2p/crypto/ECConstants.java
Normal file
@ -0,0 +1,327 @@
|
|||||||
|
package net.i2p.crypto;
|
||||||
|
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.security.AlgorithmParameters;
|
||||||
|
import java.security.AlgorithmParameterGenerator;
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.security.Provider;
|
||||||
|
import java.security.Security;
|
||||||
|
import java.security.spec.AlgorithmParameterSpec;
|
||||||
|
import java.security.spec.ECField;
|
||||||
|
import java.security.spec.ECFieldFp;
|
||||||
|
import java.security.spec.ECGenParameterSpec;
|
||||||
|
import java.security.spec.ECParameterSpec;
|
||||||
|
import java.security.spec.ECPoint;
|
||||||
|
import java.security.spec.EllipticCurve;
|
||||||
|
|
||||||
|
import net.i2p.util.NativeBigInteger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constants for elliptic curves, from NIST FIPS 186-4 (2013) / ANSI X9.62
|
||||||
|
*
|
||||||
|
* @since 0.9.9
|
||||||
|
*/
|
||||||
|
class ECConstants {
|
||||||
|
|
||||||
|
private static final boolean DEBUG = true;
|
||||||
|
|
||||||
|
private static void log(String s) {
|
||||||
|
log(s, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void log(String s, Throwable t) {
|
||||||
|
if (DEBUG) {
|
||||||
|
System.out.println("ECConstants: " + s);
|
||||||
|
if (t != null)
|
||||||
|
t.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final boolean BC_AVAILABLE;
|
||||||
|
|
||||||
|
static {
|
||||||
|
boolean loaded;
|
||||||
|
if (Security.getProvider("BC") == null) {
|
||||||
|
try {
|
||||||
|
Class cls = Class.forName("org.bouncycastle.jce.provider.BouncyCastleProvider");
|
||||||
|
Constructor con = cls.getConstructor(new Class[0]);
|
||||||
|
Provider bc = (Provider)con.newInstance(new Object[0]);
|
||||||
|
Security.addProvider(bc);
|
||||||
|
log("Added BC provider");
|
||||||
|
loaded = true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log("Unable to add BC provider", e);
|
||||||
|
loaded = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log("BC provider already loaded");
|
||||||
|
loaded = true;
|
||||||
|
}
|
||||||
|
BC_AVAILABLE = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isBCAvailable() { return BC_AVAILABLE; }
|
||||||
|
|
||||||
|
private static class ECParms {
|
||||||
|
public final String ps, ns, ss, bs, gxs, gys;
|
||||||
|
private static final BigInteger A = new NativeBigInteger("-3");
|
||||||
|
private static final int H = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* P and N in decimal, no spaces;
|
||||||
|
* Seed, B, Gx, Gy in hex, spaces allowed
|
||||||
|
*/
|
||||||
|
public ECParms(String pss, String nss, String sss, String bss, String gxss, String gyss) {
|
||||||
|
ps = pss; ns = nss; ss = sss; bs = bss; gxs = gxss; gys = gyss;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ECParameterSpec genSpec() {
|
||||||
|
BigInteger pb = new NativeBigInteger(ps);
|
||||||
|
BigInteger nb = new NativeBigInteger(ns);
|
||||||
|
BigInteger sb = new NativeBigInteger(ss.replace(" ", ""), 16);
|
||||||
|
BigInteger bb = new NativeBigInteger(bs.replace(" ", ""), 16);
|
||||||
|
BigInteger gxb = new NativeBigInteger(gxs.replace(" ", ""), 16);
|
||||||
|
BigInteger gyb = new NativeBigInteger(gys.replace(" ", ""), 16);
|
||||||
|
BigInteger ab = new NativeBigInteger(A.mod(pb));
|
||||||
|
ECField field = new ECFieldFp(pb);
|
||||||
|
EllipticCurve curve = new EllipticCurve(field, ab, bb, sb.toByteArray());
|
||||||
|
ECPoint g = new ECPoint(gxb, gyb);
|
||||||
|
return new ECParameterSpec(curve, g, nb, H);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
D.1.2 Curves over Prime Fields
|
||||||
|
|
||||||
|
For each prime p, a pseudo-random curve
|
||||||
|
E : y**2 = x**3 -3x +b (mod p)
|
||||||
|
of prime order n is listed 4. (Thus, for these curves, the cofactor is always h = 1.) The following
|
||||||
|
parameters are given:
|
||||||
|
|
||||||
|
The selection a a = -3 for the coefficient of x was made for reasons of efficiency; see IEEE Std 1363-2000.
|
||||||
|
|
||||||
|
* The prime modulus p
|
||||||
|
* The order n
|
||||||
|
* The 160-bit input seed SEED to the SHA-1 based algorithm (i.e., the domain parameter
|
||||||
|
seed)
|
||||||
|
* The output c of the SHA-1 based algorithm
|
||||||
|
* The coefficient b (satisfying b**2 c = -27 (mod p))
|
||||||
|
* The base point x coordinate G x
|
||||||
|
* The base point y coordinate G y
|
||||||
|
The integers p and n are given in decimal form; bit strings and field elements are given in
|
||||||
|
hexadecimal.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
D.1.2.1 Curve P-192
|
||||||
|
|
||||||
|
p= 6277101735386680763835789423207666416083908700390324961279
|
||||||
|
n= 6277101735386680763835789423176059013767194773182842284081
|
||||||
|
SEED = 3045ae6f c8422f64 ed579528 d38120ea e12196d5
|
||||||
|
c= 3099d2bb bfcb2538 542dcd5f b078b6ef 5f3d6fe2 c745de65
|
||||||
|
b= 64210519 e59c80e7 0fa7e9ab 72243049 feb8deec c146b9b1
|
||||||
|
Gx= 188da80e b03090f6 7cbf20eb 43a18800 f4ff0afd 82ff1012
|
||||||
|
Gy= 07192b95 ffc8da78 631011ed 6b24cdd5 73f977a1 1e794811
|
||||||
|
*/
|
||||||
|
|
||||||
|
private static final ECParms PARM_P192 = new ECParms(
|
||||||
|
// P N Seed B Gx Gy
|
||||||
|
"6277101735386680763835789423207666416083908700390324961279",
|
||||||
|
"6277101735386680763835789423176059013767194773182842284081",
|
||||||
|
"3045ae6f c8422f64 ed579528 d38120ea e12196d5",
|
||||||
|
"64210519 e59c80e7 0fa7e9ab 72243049 feb8deec c146b9b1",
|
||||||
|
"188da80e b03090f6 7cbf20eb 43a18800 f4ff0afd 82ff1012",
|
||||||
|
"07192b95 ffc8da78 631011ed 6b24cdd5 73f977a1 1e794811"
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
D.1.2.3 Curve P-256
|
||||||
|
|
||||||
|
p=
|
||||||
|
1157920892103562487626974469494075735300861434152903141955
|
||||||
|
33631308867097853951
|
||||||
|
n=
|
||||||
|
115792089210356248762697446949407573529996955224135760342
|
||||||
|
422259061068512044369
|
||||||
|
SEED = c49d3608 86e70493 6a6678e1 139d26b7 819f7e90
|
||||||
|
c=
|
||||||
|
7efba166 2985be94 03cb055c 75d4f7e0 ce8d84a9 c5114abc
|
||||||
|
af317768 0104fa0d
|
||||||
|
b=
|
||||||
|
5ac635d8 aa3a93e7 b3ebbd55 769886bc 651d06b0 cc53b0f6
|
||||||
|
3bce3c3e 27d2604b
|
||||||
|
Gx=
|
||||||
|
6b17d1f2 e12c4247 f8bce6e5 63a440f2 77037d81 2deb33a0
|
||||||
|
f4a13945 d898c296
|
||||||
|
Gy=
|
||||||
|
4fe342e2 fe1a7f9b 8ee7eb4a 7c0f9e16 2bce3357 6b315ece
|
||||||
|
cbb64068 37bf51f5
|
||||||
|
*/
|
||||||
|
|
||||||
|
private static final ECParms PARM_P256 = new ECParms(
|
||||||
|
// P N Seed B Gx Gy
|
||||||
|
"1157920892103562487626974469494075735300861434152903141955" +
|
||||||
|
"33631308867097853951",
|
||||||
|
"115792089210356248762697446949407573529996955224135760342" +
|
||||||
|
"422259061068512044369",
|
||||||
|
"c49d3608 86e70493 6a6678e1 139d26b7 819f7e90",
|
||||||
|
"5ac635d8 aa3a93e7 b3ebbd55 769886bc 651d06b0 cc53b0f6" +
|
||||||
|
"3bce3c3e 27d2604b",
|
||||||
|
"6b17d1f2 e12c4247 f8bce6e5 63a440f2 77037d81 2deb33a0" +
|
||||||
|
"f4a13945 d898c296",
|
||||||
|
"4fe342e2 fe1a7f9b 8ee7eb4a 7c0f9e16 2bce3357 6b315ece" +
|
||||||
|
"cbb64068 37bf51f5"
|
||||||
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
|
D.1.2.4 Curve P-384
|
||||||
|
|
||||||
|
p=
|
||||||
|
3940200619639447921227904010014361380507973927046544666794
|
||||||
|
8293404245721771496870329047266088258938001861606973112319
|
||||||
|
n=
|
||||||
|
3940200619639447921227904010014361380507973927046544666794
|
||||||
|
6905279627659399113263569398956308152294913554433653942643
|
||||||
|
SEED = a335926a a319a27a 1d00896a 6773a482 7acdac73
|
||||||
|
c=
|
||||||
|
79d1e655 f868f02f ff48dcde e14151dd b80643c1 406d0ca1
|
||||||
|
0dfe6fc5 2009540a 495e8042 ea5f744f 6e184667 cc722483
|
||||||
|
b=
|
||||||
|
b3312fa7 e23ee7e4 988e056b e3f82d19 181d9c6e fe814112
|
||||||
|
0314088f 5013875a c656398d 8a2ed19d 2a85c8ed d3ec2aef
|
||||||
|
Gx=
|
||||||
|
aa87ca22 be8b0537 8eb1c71e f320ad74 6e1d3b62 8ba79b98
|
||||||
|
59f741e0 82542a38 5502f25d bf55296c 3a545e38 72760ab7
|
||||||
|
G y=
|
||||||
|
3617de4a 96262c6f 5d9e98bf 9292dc29 f8f41dbd 289a147c
|
||||||
|
e9da3113 b5f0b8c0 0a60b1ce 1d7e819d 7a431d7c 90ea0e5f
|
||||||
|
*/
|
||||||
|
|
||||||
|
private static final ECParms PARM_P384 = new ECParms(
|
||||||
|
// P N Seed B Gx Gy
|
||||||
|
"3940200619639447921227904010014361380507973927046544666794" +
|
||||||
|
"8293404245721771496870329047266088258938001861606973112319",
|
||||||
|
"3940200619639447921227904010014361380507973927046544666794" +
|
||||||
|
"6905279627659399113263569398956308152294913554433653942643",
|
||||||
|
"a335926a a319a27a 1d00896a 6773a482 7acdac73",
|
||||||
|
"b3312fa7 e23ee7e4 988e056b e3f82d19 181d9c6e fe814112" +
|
||||||
|
"0314088f 5013875a c656398d 8a2ed19d 2a85c8ed d3ec2aef",
|
||||||
|
"aa87ca22 be8b0537 8eb1c71e f320ad74 6e1d3b62 8ba79b98" +
|
||||||
|
"59f741e0 82542a38 5502f25d bf55296c 3a545e38 72760ab7",
|
||||||
|
"3617de4a 96262c6f 5d9e98bf 9292dc29 f8f41dbd 289a147c" +
|
||||||
|
"e9da3113 b5f0b8c0 0a60b1ce 1d7e819d 7a431d7c 90ea0e5f"
|
||||||
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
|
D.1.2.5 Curve P-521
|
||||||
|
|
||||||
|
p=
|
||||||
|
686479766013060971498190079908139321726943530014330540939
|
||||||
|
446345918554318339765605212255964066145455497729631139148
|
||||||
|
0858037121987999716643812574028291115057151
|
||||||
|
n=
|
||||||
|
686479766013060971498190079908139321726943530014330540939
|
||||||
|
446345918554318339765539424505774633321719753296399637136
|
||||||
|
3321113864768612440380340372808892707005449
|
||||||
|
SEED = d09e8800 291cb853 96cc6717 393284aa a0da64ba
|
||||||
|
c=
|
||||||
|
0b4 8bfa5f42 0a349495 39d2bdfc 264eeeeb 077688e4
|
||||||
|
4fbf0ad8 f6d0edb3 7bd6b533 28100051 8e19f1b9 ffbe0fe9
|
||||||
|
ed8a3c22 00b8f875 e523868c 70c1e5bf 55bad637
|
||||||
|
b=
|
||||||
|
051 953eb961 8e1c9a1f 929a21a0 b68540ee a2da725b
|
||||||
|
99b315f3 b8b48991 8ef109e1 56193951 ec7e937b 1652c0bd
|
||||||
|
3bb1bf07 3573df88 3d2c34f1 ef451fd4 6b503f00
|
||||||
|
Gx=
|
||||||
|
c6 858e06b7 0404e9cd 9e3ecb66 2395b442 9c648139
|
||||||
|
053fb521 f828af60 6b4d3dba a14b5e77 efe75928 fe1dc127
|
||||||
|
a2ffa8de 3348b3c1 856a429b f97e7e31 c2e5bd66
|
||||||
|
Gy=
|
||||||
|
118 39296a78 9a3bc004 5c8a5fb4 2c7d1bd9 98f54449
|
||||||
|
579b4468 17afbd17 273e662c 97ee7299 5ef42640 c550b901
|
||||||
|
3fad0761 353c7086 a272c240 88be9476 9fd16650
|
||||||
|
*/
|
||||||
|
|
||||||
|
private static final ECParms PARM_P521 = new ECParms(
|
||||||
|
"686479766013060971498190079908139321726943530014330540939" +
|
||||||
|
"446345918554318339765605212255964066145455497729631139148" +
|
||||||
|
"0858037121987999716643812574028291115057151",
|
||||||
|
"686479766013060971498190079908139321726943530014330540939" +
|
||||||
|
"446345918554318339765539424505774633321719753296399637136" +
|
||||||
|
"3321113864768612440380340372808892707005449",
|
||||||
|
"d09e8800 291cb853 96cc6717 393284aa a0da64ba",
|
||||||
|
"051 953eb961 8e1c9a1f 929a21a0 b68540ee a2da725b" +
|
||||||
|
"99b315f3 b8b48991 8ef109e1 56193951 ec7e937b 1652c0bd" +
|
||||||
|
"3bb1bf07 3573df88 3d2c34f1 ef451fd4 6b503f00",
|
||||||
|
"c6 858e06b7 0404e9cd 9e3ecb66 2395b442 9c648139" +
|
||||||
|
"053fb521 f828af60 6b4d3dba a14b5e77 efe75928 fe1dc127" +
|
||||||
|
"a2ffa8de 3348b3c1 856a429b f97e7e31 c2e5bd66",
|
||||||
|
"118 39296a78 9a3bc004 5c8a5fb4 2c7d1bd9 98f54449" +
|
||||||
|
"579b4468 17afbd17 273e662c 97ee7299 5ef42640 c550b901" +
|
||||||
|
"3fad0761 353c7086 a272c240 88be9476 9fd16650"
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a spec from a curve name
|
||||||
|
* @return null if fail
|
||||||
|
*/
|
||||||
|
private static ECParameterSpec genSpec(String name) {
|
||||||
|
// convert the ECGenParameterSpecs to ECParameterSpecs for several reasons:
|
||||||
|
// 1) to check availability
|
||||||
|
// 2) efficiency
|
||||||
|
// 3) SigUtil must cast the AlgorithmParameterSpec to a ECParameterSpec
|
||||||
|
// to convert a I2P key to a Java key. Sadly, a ECGenParameterSpec
|
||||||
|
// is not a ECParameterSpec.
|
||||||
|
try {
|
||||||
|
AlgorithmParameters ap;
|
||||||
|
try {
|
||||||
|
ap = AlgorithmParameters.getInstance("EC");
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (BC_AVAILABLE) {
|
||||||
|
log("Named curve " + name + " is not available, trying BC", e);
|
||||||
|
ap = AlgorithmParameters.getInstance("EC", "BC");
|
||||||
|
log("Fallback to BC worked for named curve " + name);
|
||||||
|
} else {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ECGenParameterSpec ecgps = new ECGenParameterSpec(name);
|
||||||
|
ap.init(ecgps);
|
||||||
|
ECParameterSpec rv = ap.getParameterSpec(ECParameterSpec.class);
|
||||||
|
log("Named curve " + name + " loaded");
|
||||||
|
return rv;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log("Named curve " + name + " is not available", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries curve name1, then name2, then creates new from parms.
|
||||||
|
* @return null if all fail
|
||||||
|
*/
|
||||||
|
private static ECParameterSpec genSpec(String name1, String name2, ECParms parms) {
|
||||||
|
ECParameterSpec rv = genSpec(name1);
|
||||||
|
if (rv == null) {
|
||||||
|
rv = genSpec(name2);
|
||||||
|
if (rv == null) {
|
||||||
|
rv = parms.genSpec();
|
||||||
|
if (rv != null)
|
||||||
|
log("Curve " + name2 + " created");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
// standard curve names
|
||||||
|
// first is OpenJDK 6/7
|
||||||
|
// second is BC
|
||||||
|
public static final ECParameterSpec P192_SPEC = genSpec("secp192r1", "P-192", PARM_P192);
|
||||||
|
public static final ECParameterSpec P256_SPEC = genSpec("secp256r1", "P-256", PARM_P256);
|
||||||
|
public static final ECParameterSpec P384_SPEC = genSpec("secp384r1", "P-384", PARM_P384);
|
||||||
|
public static final ECParameterSpec P521_SPEC = genSpec("secp521r1", "P-521", PARM_P521);
|
||||||
|
|
||||||
|
}
|
@ -81,6 +81,7 @@ public class ElGamalEngine {
|
|||||||
*/
|
*/
|
||||||
public void shutdown() {
|
public void shutdown() {
|
||||||
_ykgen.shutdown();
|
_ykgen.shutdown();
|
||||||
|
SigUtil.clearCaches();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -10,6 +10,14 @@ package net.i2p.crypto;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.KeyPair;
|
||||||
|
import java.security.KeyPairGenerator;
|
||||||
|
import java.security.ProviderException;
|
||||||
|
import java.security.interfaces.ECPrivateKey;
|
||||||
|
import java.security.interfaces.ECPublicKey;
|
||||||
|
import java.security.spec.ECPoint;
|
||||||
|
|
||||||
import net.i2p.I2PAppContext;
|
import net.i2p.I2PAppContext;
|
||||||
import net.i2p.data.Hash;
|
import net.i2p.data.Hash;
|
||||||
@ -19,9 +27,17 @@ import net.i2p.data.SessionKey;
|
|||||||
import net.i2p.data.SigningPrivateKey;
|
import net.i2p.data.SigningPrivateKey;
|
||||||
import net.i2p.data.SigningPublicKey;
|
import net.i2p.data.SigningPublicKey;
|
||||||
import net.i2p.data.SimpleDataStructure;
|
import net.i2p.data.SimpleDataStructure;
|
||||||
|
import net.i2p.util.Log;
|
||||||
import net.i2p.util.NativeBigInteger;
|
import net.i2p.util.NativeBigInteger;
|
||||||
import net.i2p.util.SystemVersion;
|
import net.i2p.util.SystemVersion;
|
||||||
|
|
||||||
|
|
||||||
|
// main()
|
||||||
|
import net.i2p.data.DataHelper;
|
||||||
|
import net.i2p.data.Signature;
|
||||||
|
import net.i2p.util.Clock;
|
||||||
|
import net.i2p.util.RandomSource;
|
||||||
|
|
||||||
/** Define a way of generating asymmetrical key pairs as well as symmetrical keys
|
/** Define a way of generating asymmetrical key pairs as well as symmetrical keys
|
||||||
* @author jrandom
|
* @author jrandom
|
||||||
*/
|
*/
|
||||||
@ -129,14 +145,16 @@ public class KeyGenerator {
|
|||||||
SimpleDataStructure[] keys = new SimpleDataStructure[2];
|
SimpleDataStructure[] keys = new SimpleDataStructure[2];
|
||||||
keys[0] = new PublicKey();
|
keys[0] = new PublicKey();
|
||||||
keys[1] = new PrivateKey();
|
keys[1] = new PrivateKey();
|
||||||
byte[] k0 = aalpha.toByteArray();
|
|
||||||
byte[] k1 = a.toByteArray();
|
|
||||||
|
|
||||||
// bigInteger.toByteArray returns SIGNED integers, but since they'return positive,
|
// bigInteger.toByteArray returns SIGNED integers, but since they'return positive,
|
||||||
// signed two's complement is the same as unsigned
|
// signed two's complement is the same as unsigned
|
||||||
|
|
||||||
keys[0].setData(padBuffer(k0, PublicKey.KEYSIZE_BYTES));
|
try {
|
||||||
keys[1].setData(padBuffer(k1, PrivateKey.KEYSIZE_BYTES));
|
keys[0].setData(SigUtil.rectify(aalpha, PublicKey.KEYSIZE_BYTES));
|
||||||
|
keys[1].setData(SigUtil.rectify(a, PrivateKey.KEYSIZE_BYTES));
|
||||||
|
} catch (InvalidKeyException ike) {
|
||||||
|
throw new IllegalArgumentException(ike);
|
||||||
|
}
|
||||||
|
|
||||||
return keys;
|
return keys;
|
||||||
}
|
}
|
||||||
@ -149,13 +167,18 @@ public class KeyGenerator {
|
|||||||
BigInteger a = new NativeBigInteger(1, priv.toByteArray());
|
BigInteger a = new NativeBigInteger(1, priv.toByteArray());
|
||||||
BigInteger aalpha = CryptoConstants.elgg.modPow(a, CryptoConstants.elgp);
|
BigInteger aalpha = CryptoConstants.elgg.modPow(a, CryptoConstants.elgp);
|
||||||
PublicKey pub = new PublicKey();
|
PublicKey pub = new PublicKey();
|
||||||
byte [] pubBytes = aalpha.toByteArray();
|
try {
|
||||||
pub.setData(padBuffer(pubBytes, PublicKey.KEYSIZE_BYTES));
|
pub.setData(SigUtil.rectify(aalpha, PublicKey.KEYSIZE_BYTES));
|
||||||
|
} catch (InvalidKeyException ike) {
|
||||||
|
throw new IllegalArgumentException(ike);
|
||||||
|
}
|
||||||
return pub;
|
return pub;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Generate a pair of DSA keys, where index 0 is a SigningPublicKey, and
|
/** Generate a pair of DSA keys, where index 0 is a SigningPublicKey, and
|
||||||
* index 1 is a SigningPrivateKey
|
* index 1 is a SigningPrivateKey.
|
||||||
|
* DSA-SHA1 only.
|
||||||
|
*
|
||||||
* @return pair of keys
|
* @return pair of keys
|
||||||
*/
|
*/
|
||||||
public Object[] generateSigningKeypair() {
|
public Object[] generateSigningKeypair() {
|
||||||
@ -163,6 +186,8 @@ public class KeyGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* DSA-SHA1 only.
|
||||||
|
*
|
||||||
* Same as above but different return type
|
* Same as above but different return type
|
||||||
* @since 0.8.7
|
* @since 0.8.7
|
||||||
*/
|
*/
|
||||||
@ -178,15 +203,69 @@ public class KeyGenerator {
|
|||||||
BigInteger y = CryptoConstants.dsag.modPow(x, CryptoConstants.dsap);
|
BigInteger y = CryptoConstants.dsag.modPow(x, CryptoConstants.dsap);
|
||||||
keys[0] = new SigningPublicKey();
|
keys[0] = new SigningPublicKey();
|
||||||
keys[1] = new SigningPrivateKey();
|
keys[1] = new SigningPrivateKey();
|
||||||
byte k0[] = padBuffer(y.toByteArray(), SigningPublicKey.KEYSIZE_BYTES);
|
try {
|
||||||
byte k1[] = padBuffer(x.toByteArray(), SigningPrivateKey.KEYSIZE_BYTES);
|
keys[0].setData(SigUtil.rectify(y, SigningPublicKey.KEYSIZE_BYTES));
|
||||||
|
keys[1].setData(SigUtil.rectify(x, SigningPrivateKey.KEYSIZE_BYTES));
|
||||||
keys[0].setData(k0);
|
} catch (InvalidKeyException ike) {
|
||||||
keys[1].setData(k1);
|
throw new IllegalStateException(ike);
|
||||||
|
}
|
||||||
return keys;
|
return keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Convert a SigningPrivateKey to a SigningPublicKey
|
/**
|
||||||
|
* Generic signature type, supports DSA and ECDSA
|
||||||
|
* @since 0.9.9
|
||||||
|
*/
|
||||||
|
public SimpleDataStructure[] generateSigningKeys(SigType type) throws GeneralSecurityException {
|
||||||
|
if (type == SigType.DSA_SHA1)
|
||||||
|
return generateSigningKeys();
|
||||||
|
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
|
||||||
|
KeyPair kp;
|
||||||
|
try {
|
||||||
|
kpg.initialize(type.getParams(), _context.random());
|
||||||
|
kp = kpg.generateKeyPair();
|
||||||
|
} catch (ProviderException pe) {
|
||||||
|
// This is a RuntimeException, thx Sun
|
||||||
|
// Fails for P-192 only, on Ubuntu
|
||||||
|
Log log = _context.logManager().getLog(KeyGenerator.class);
|
||||||
|
String pname = kpg.getProvider().getName();
|
||||||
|
if ("BC".equals(pname)) {
|
||||||
|
if (log.shouldLog(Log.WARN))
|
||||||
|
log.warn("BC KPG failed", pe);
|
||||||
|
throw new GeneralSecurityException("BC KPG", pe);
|
||||||
|
}
|
||||||
|
if (!ECConstants.isBCAvailable())
|
||||||
|
throw new GeneralSecurityException(pname + " KPG", pe);
|
||||||
|
if (log.shouldLog(Log.WARN))
|
||||||
|
log.warn(pname + " KPG failed, trying BC", pe);
|
||||||
|
try {
|
||||||
|
kpg = KeyPairGenerator.getInstance("EC", "BC");
|
||||||
|
kpg.initialize(type.getParams(), _context.random());
|
||||||
|
kp = kpg.generateKeyPair();
|
||||||
|
} catch (ProviderException pe2) {
|
||||||
|
if (log.shouldLog(Log.WARN))
|
||||||
|
log.warn("BC KPG failed too", pe2);
|
||||||
|
// throw original exception
|
||||||
|
throw new GeneralSecurityException(pname + " KPG", pe);
|
||||||
|
} catch (GeneralSecurityException gse) {
|
||||||
|
if (log.shouldLog(Log.WARN))
|
||||||
|
log.warn("BC KPG failed too", gse);
|
||||||
|
gse.printStackTrace();
|
||||||
|
// throw original exception
|
||||||
|
throw new GeneralSecurityException(pname + " KPG", pe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ECPublicKey pubkey = (ECPublicKey) kp.getPublic();
|
||||||
|
ECPrivateKey privkey = (ECPrivateKey) kp.getPrivate();
|
||||||
|
SimpleDataStructure[] keys = new SimpleDataStructure[2];
|
||||||
|
keys[0] = SigUtil.fromJavaKey(pubkey, type);
|
||||||
|
keys[1] = SigUtil.fromJavaKey(privkey, type);
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Convert a SigningPrivateKey to a SigningPublicKey.
|
||||||
|
* DSA-SHA1 only.
|
||||||
|
*
|
||||||
* @param priv a SigningPrivateKey object
|
* @param priv a SigningPrivateKey object
|
||||||
* @return a SigningPublicKey object
|
* @return a SigningPublicKey object
|
||||||
*/
|
*/
|
||||||
@ -194,27 +273,67 @@ public class KeyGenerator {
|
|||||||
BigInteger x = new NativeBigInteger(1, priv.toByteArray());
|
BigInteger x = new NativeBigInteger(1, priv.toByteArray());
|
||||||
BigInteger y = CryptoConstants.dsag.modPow(x, CryptoConstants.dsap);
|
BigInteger y = CryptoConstants.dsag.modPow(x, CryptoConstants.dsap);
|
||||||
SigningPublicKey pub = new SigningPublicKey();
|
SigningPublicKey pub = new SigningPublicKey();
|
||||||
byte [] pubBytes = padBuffer(y.toByteArray(), SigningPublicKey.KEYSIZE_BYTES);
|
try {
|
||||||
pub.setData(pubBytes);
|
pub.setData(SigUtil.rectify(y, SigningPublicKey.KEYSIZE_BYTES));
|
||||||
|
} catch (InvalidKeyException ike) {
|
||||||
|
throw new IllegalArgumentException(ike);
|
||||||
|
}
|
||||||
return pub;
|
return pub;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static void main(String args[]) {
|
||||||
* Pad the buffer w/ leading 0s or trim off leading bits so the result is the
|
try {
|
||||||
* given length.
|
main2(args);
|
||||||
*/
|
} catch (Exception e) {
|
||||||
private final static byte[] padBuffer(byte src[], int length) {
|
e.printStackTrace();
|
||||||
byte buf[] = new byte[length];
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (src.length > buf.length) // extra bits, chop leading bits
|
public static void main2(String args[]) {
|
||||||
System.arraycopy(src, src.length - buf.length, buf, 0, buf.length);
|
RandomSource.getInstance().nextBoolean();
|
||||||
else if (src.length < buf.length) // short bits, padd w/ 0s
|
try { Thread.sleep(1000); } catch (InterruptedException ie) {}
|
||||||
System.arraycopy(src, 0, buf, buf.length - src.length, src.length);
|
int runs = 500; // warmup
|
||||||
else
|
for (int j = 0; j < 2; j++) {
|
||||||
// eq
|
for (int i = 0; i <= 100; i++) {
|
||||||
System.arraycopy(src, 0, buf, 0, buf.length);
|
SigType type = SigType.getByCode(i);
|
||||||
|
if (type == null)
|
||||||
|
break;
|
||||||
|
try {
|
||||||
|
System.out.println("Testing " + type);
|
||||||
|
testSig(type, runs);
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.out.println("error testing " + type);
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
runs = 2000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return buf;
|
private static void testSig(SigType type, int runs) throws GeneralSecurityException {
|
||||||
|
byte src[] = new byte[512];
|
||||||
|
long stime = 0;
|
||||||
|
long vtime = 0;
|
||||||
|
SimpleDataStructure keys[] = KeyGenerator.getInstance().generateSigningKeys(type);
|
||||||
|
//System.out.println("pubkey " + keys[0]);
|
||||||
|
//System.out.println("privkey " + keys[1]);
|
||||||
|
for (int i = 0; i < runs; i++) {
|
||||||
|
RandomSource.getInstance().nextBytes(src);
|
||||||
|
long start = System.nanoTime();
|
||||||
|
Signature sig = DSAEngine.getInstance().sign(src, (SigningPrivateKey) keys[1]);
|
||||||
|
long mid = System.nanoTime();
|
||||||
|
boolean ok = DSAEngine.getInstance().verifySignature(sig, src, (SigningPublicKey) keys[0]);
|
||||||
|
long end = System.nanoTime();
|
||||||
|
stime += mid - start;
|
||||||
|
vtime += end - mid;
|
||||||
|
if (!ok)
|
||||||
|
throw new GeneralSecurityException(type + " V(S(data)) fail");
|
||||||
|
}
|
||||||
|
stime /= 1000*1000;
|
||||||
|
vtime /= 1000*1000;
|
||||||
|
System.out.println("Sign/verify " + runs + " times: " + (vtime+stime) + " ms = " +
|
||||||
|
(((double) stime) / runs) + " each sign, " +
|
||||||
|
(((double) vtime) / runs) + " each verify");
|
||||||
}
|
}
|
||||||
|
|
||||||
/******
|
/******
|
||||||
|
37
core/java/src/net/i2p/crypto/KeyRing.java
Normal file
37
core/java/src/net/i2p/crypto/KeyRing.java
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package net.i2p.crypto;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* free (adj.): unencumbered; not under the control of others
|
||||||
|
* No warranty of any kind, either expressed or implied.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
|
||||||
|
import net.i2p.data.SigningPublicKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A backend for storing and retrieving SigningPublicKeys
|
||||||
|
* to be used for verifying signatures.
|
||||||
|
*
|
||||||
|
* @since 0.9.9
|
||||||
|
*/
|
||||||
|
public interface KeyRing {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a key.
|
||||||
|
* Throws on all errors.
|
||||||
|
* @param scope a domain identifier, indicating router update, reseed, etc.
|
||||||
|
* @return null if none
|
||||||
|
*/
|
||||||
|
public SigningPublicKey getKey(String keyName, String scope, SigType type)
|
||||||
|
throws GeneralSecurityException, IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store a key.
|
||||||
|
* Throws on all errors.
|
||||||
|
* @param scope a domain identifier, indicating router update, reseed, etc.
|
||||||
|
*/
|
||||||
|
public void setKey(String keyName, String scope, SigningPublicKey key)
|
||||||
|
throws GeneralSecurityException, IOException;
|
||||||
|
}
|
@ -26,6 +26,11 @@ public class SHA1Hash extends SimpleDataStructure {
|
|||||||
|
|
||||||
public final static int HASH_LENGTH = SHA1.HASH_LENGTH;
|
public final static int HASH_LENGTH = SHA1.HASH_LENGTH;
|
||||||
|
|
||||||
|
/** @since 0.9.9 */
|
||||||
|
public SHA1Hash() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
/** @throws IllegalArgumentException if data is not 20 bytes (null is ok) */
|
/** @throws IllegalArgumentException if data is not 20 bytes (null is ok) */
|
||||||
public SHA1Hash(byte data[]) {
|
public SHA1Hash(byte data[]) {
|
||||||
super(data);
|
super(data);
|
||||||
|
@ -11,8 +11,13 @@ import java.io.IOException;
|
|||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.security.DigestInputStream;
|
import java.security.DigestInputStream;
|
||||||
import java.security.DigestOutputStream;
|
import java.security.DigestOutputStream;
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.util.EnumSet;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import net.i2p.I2PAppContext;
|
import net.i2p.I2PAppContext;
|
||||||
@ -21,6 +26,9 @@ import net.i2p.data.DataHelper;
|
|||||||
import net.i2p.data.Signature;
|
import net.i2p.data.Signature;
|
||||||
import net.i2p.data.SigningPrivateKey;
|
import net.i2p.data.SigningPrivateKey;
|
||||||
import net.i2p.data.SigningPublicKey;
|
import net.i2p.data.SigningPublicKey;
|
||||||
|
import net.i2p.data.SimpleDataStructure;
|
||||||
|
import net.i2p.util.HexDump;
|
||||||
|
import net.i2p.util.SecureFileOutputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Succesor to the ".sud" format used in TrustedUpdate.
|
* Succesor to the ".sud" format used in TrustedUpdate.
|
||||||
@ -42,6 +50,7 @@ public class SU3File {
|
|||||||
private long _contentLength;
|
private long _contentLength;
|
||||||
private SigningPublicKey _signerPubkey;
|
private SigningPublicKey _signerPubkey;
|
||||||
private boolean _headerVerified;
|
private boolean _headerVerified;
|
||||||
|
private SigType _sigType;
|
||||||
|
|
||||||
private static final byte[] MAGIC = DataHelper.getUTF8("I2Psu3");
|
private static final byte[] MAGIC = DataHelper.getUTF8("I2Psu3");
|
||||||
private static final int FILE_VERSION = 0;
|
private static final int FILE_VERSION = 0;
|
||||||
@ -55,7 +64,7 @@ public class SU3File {
|
|||||||
private static final int CONTENT_PLUGIN = 2;
|
private static final int CONTENT_PLUGIN = 2;
|
||||||
private static final int CONTENT_RESEED = 3;
|
private static final int CONTENT_RESEED = 3;
|
||||||
|
|
||||||
private static final int SIG_DSA_160 = SigType.DSA_SHA1.getCode();
|
private static final SigType DEFAULT_TYPE = SigType.DSA_SHA1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Uses TrustedUpdate's default keys for verification.
|
* Uses TrustedUpdate's default keys for verification.
|
||||||
@ -68,7 +77,8 @@ public class SU3File {
|
|||||||
* Uses TrustedUpdate's default keys for verification.
|
* Uses TrustedUpdate's default keys for verification.
|
||||||
*/
|
*/
|
||||||
public SU3File(File file) {
|
public SU3File(File file) {
|
||||||
this(file, (new TrustedUpdate()).getKeys());
|
//this(file, (new TrustedUpdate()).getKeys());
|
||||||
|
this(file, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -129,13 +139,14 @@ public class SU3File {
|
|||||||
if (foo != FILE_VERSION)
|
if (foo != FILE_VERSION)
|
||||||
throw new IOException("bad file version");
|
throw new IOException("bad file version");
|
||||||
skip(in, 1);
|
skip(in, 1);
|
||||||
int sigType = in.read();
|
int sigTypeCode = in.read();
|
||||||
|
_sigType = SigType.getByCode(sigTypeCode);
|
||||||
// TODO, for other known algos we must start over with a new MessageDigest
|
// TODO, for other known algos we must start over with a new MessageDigest
|
||||||
// (rewind 10 bytes)
|
// (rewind 10 bytes)
|
||||||
if (sigType != SIG_DSA_160)
|
if (_sigType == null)
|
||||||
throw new IOException("bad sig type");
|
throw new IOException("unknown sig type: " + sigTypeCode);
|
||||||
_signerLength = (int) DataHelper.readLong(in, 2);
|
_signerLength = (int) DataHelper.readLong(in, 2);
|
||||||
if (_signerLength != Signature.SIGNATURE_BYTES)
|
if (_signerLength != _sigType.getSigLen())
|
||||||
throw new IOException("bad sig length");
|
throw new IOException("bad sig length");
|
||||||
skip(in, 1);
|
skip(in, 1);
|
||||||
int _versionLength = in.read();
|
int _versionLength = in.read();
|
||||||
@ -181,9 +192,19 @@ public class SU3File {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// testing
|
||||||
|
KeyRing ring = new DirKeyRing(new File("su3keyring"));
|
||||||
|
try {
|
||||||
|
_signerPubkey = ring.getKey(_signer, "default", _sigType);
|
||||||
|
} catch (GeneralSecurityException gse) {
|
||||||
|
IOException ioe = new IOException("keystore error");
|
||||||
|
ioe.initCause(gse);
|
||||||
|
throw ioe;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (_signerPubkey == null)
|
if (_signerPubkey == null)
|
||||||
throw new IOException("unknown signer: " + _signer);
|
throw new IOException("unknown signer: " + _signer);
|
||||||
}
|
|
||||||
_headerVerified = true;
|
_headerVerified = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,12 +231,33 @@ public class SU3File {
|
|||||||
* @return true if signature is good
|
* @return true if signature is good
|
||||||
*/
|
*/
|
||||||
public boolean verifyAndMigrate(File migrateTo) throws IOException {
|
public boolean verifyAndMigrate(File migrateTo) throws IOException {
|
||||||
DigestInputStream in = null;
|
InputStream in = null;
|
||||||
OutputStream out = null;
|
OutputStream out = null;
|
||||||
boolean rv = false;
|
boolean rv = false;
|
||||||
try {
|
try {
|
||||||
MessageDigest md = SHA1.getInstance();
|
in = new BufferedInputStream(new FileInputStream(_file));
|
||||||
in = new DigestInputStream(new BufferedInputStream(new FileInputStream(_file)), md);
|
// read 10 bytes to get the sig type
|
||||||
|
in.mark(10);
|
||||||
|
// following is a dup of that in verifyHeader()
|
||||||
|
byte[] magic = new byte[MAGIC.length];
|
||||||
|
DataHelper.read(in, magic);
|
||||||
|
if (!DataHelper.eq(magic, MAGIC))
|
||||||
|
throw new IOException("Not an su3 file");
|
||||||
|
skip(in, 1);
|
||||||
|
int foo = in.read();
|
||||||
|
if (foo != FILE_VERSION)
|
||||||
|
throw new IOException("bad file version");
|
||||||
|
skip(in, 1);
|
||||||
|
int sigTypeCode = in.read();
|
||||||
|
_sigType = SigType.getByCode(sigTypeCode);
|
||||||
|
if (_sigType == null)
|
||||||
|
throw new IOException("unknown sig type: " + sigTypeCode);
|
||||||
|
// end duplicate code
|
||||||
|
// rewind
|
||||||
|
in.reset();
|
||||||
|
MessageDigest md = _sigType.getDigestInstance();
|
||||||
|
DigestInputStream din = new DigestInputStream(in, md);
|
||||||
|
in = din;
|
||||||
if (!_headerVerified)
|
if (!_headerVerified)
|
||||||
verifyHeader(in);
|
verifyHeader(in);
|
||||||
else
|
else
|
||||||
@ -233,10 +275,13 @@ public class SU3File {
|
|||||||
tot += read;
|
tot += read;
|
||||||
}
|
}
|
||||||
byte[] sha = md.digest();
|
byte[] sha = md.digest();
|
||||||
in.on(false);
|
din.on(false);
|
||||||
Signature signature = new Signature();
|
Signature signature = new Signature(_sigType);
|
||||||
signature.readBytes(in);
|
signature.readBytes(in);
|
||||||
SHA1Hash hash = new SHA1Hash(sha);
|
SimpleDataStructure hash = _sigType.getHashInstance();
|
||||||
|
hash.setData(sha);
|
||||||
|
//System.out.println("hash\n" + HexDump.dump(sha));
|
||||||
|
//System.out.println("sig\n" + HexDump.dump(signature.getData()));
|
||||||
rv = _context.dsa().verifySignature(signature, hash, _signerPubkey);
|
rv = _context.dsa().verifySignature(signature, hash, _signerPubkey);
|
||||||
} catch (DataFormatException dfe) {
|
} catch (DataFormatException dfe) {
|
||||||
IOException ioe = new IOException("foo");
|
IOException ioe = new IOException("foo");
|
||||||
@ -268,14 +313,15 @@ public class SU3File {
|
|||||||
boolean ok = false;
|
boolean ok = false;
|
||||||
try {
|
try {
|
||||||
in = new BufferedInputStream(new FileInputStream(content));
|
in = new BufferedInputStream(new FileInputStream(content));
|
||||||
MessageDigest md = SHA1.getInstance();
|
SigType sigType = privkey.getType();
|
||||||
|
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);
|
||||||
out.write((byte) 0);
|
out.write((byte) 0);
|
||||||
out.write((byte) FILE_VERSION);
|
out.write((byte) FILE_VERSION);
|
||||||
out.write((byte) 0);
|
out.write((byte) 0);
|
||||||
out.write((byte) SIG_DSA_160);
|
out.write((byte) sigType.getCode());
|
||||||
DataHelper.writeLong(out, 2, Signature.SIGNATURE_BYTES);
|
DataHelper.writeLong(out, 2, sigType.getSigLen());
|
||||||
out.write((byte) 0);
|
out.write((byte) 0);
|
||||||
byte[] verBytes = DataHelper.getUTF8(version);
|
byte[] verBytes = DataHelper.getUTF8(version);
|
||||||
if (verBytes.length == 0 || verBytes.length > 255)
|
if (verBytes.length == 0 || verBytes.length > 255)
|
||||||
@ -315,8 +361,11 @@ public class SU3File {
|
|||||||
|
|
||||||
byte[] sha = md.digest();
|
byte[] sha = md.digest();
|
||||||
out.on(false);
|
out.on(false);
|
||||||
SHA1Hash hash = new SHA1Hash(sha);
|
SimpleDataStructure hash = sigType.getHashInstance();
|
||||||
|
hash.setData(sha);
|
||||||
Signature signature = _context.dsa().sign(hash, privkey);
|
Signature signature = _context.dsa().sign(hash, privkey);
|
||||||
|
//System.out.println("hash\n" + HexDump.dump(sha));
|
||||||
|
//System.out.println("sig\n" + HexDump.dump(signature.getData()));
|
||||||
signature.writeBytes(out);
|
signature.writeBytes(out);
|
||||||
ok = true;
|
ok = true;
|
||||||
} catch (DataFormatException dfe) {
|
} catch (DataFormatException dfe) {
|
||||||
@ -344,9 +393,17 @@ public class SU3File {
|
|||||||
if ("showversion".equals(args[0])) {
|
if ("showversion".equals(args[0])) {
|
||||||
ok = showVersionCLI(args[1]);
|
ok = showVersionCLI(args[1]);
|
||||||
} else if ("sign".equals(args[0])) {
|
} else if ("sign".equals(args[0])) {
|
||||||
|
if (args[1].equals("-t"))
|
||||||
|
ok = signCLI(args[2], args[3], args[4], args[5], args[6], args[7]);
|
||||||
|
else
|
||||||
ok = signCLI(args[1], args[2], args[3], args[4], args[5]);
|
ok = signCLI(args[1], args[2], args[3], args[4], args[5]);
|
||||||
} else if ("verifysig".equals(args[0])) {
|
} else if ("verifysig".equals(args[0])) {
|
||||||
ok = verifySigCLI(args[1]);
|
ok = verifySigCLI(args[1]);
|
||||||
|
} else if ("keygen".equals(args[0])) {
|
||||||
|
if (args[1].equals("-t"))
|
||||||
|
ok = genKeysCLI(args[2], args[3], args[4]);
|
||||||
|
else
|
||||||
|
ok = genKeysCLI(args[1], args[2]);
|
||||||
} else {
|
} else {
|
||||||
showUsageCLI();
|
showUsageCLI();
|
||||||
}
|
}
|
||||||
@ -358,9 +415,42 @@ public class SU3File {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static final void showUsageCLI() {
|
private static final void showUsageCLI() {
|
||||||
System.err.println("Usage: SU3File showversion signedFile.su3");
|
System.err.println("Usage: SU3File keygen [-t type|code] publicKeyFile privateKeyFile");
|
||||||
System.err.println(" SU3File sign inputFile.zip signedFile.su3 privateKeyFile version signerName@mail.i2p");
|
System.err.println(" SU3File showversion signedFile.su3");
|
||||||
|
System.err.println(" SU3File sign [-t type|code] inputFile.zip signedFile.su3 privateKeyFile version signerName@mail.i2p");
|
||||||
System.err.println(" SU3File verifysig signedFile.su3");
|
System.err.println(" SU3File verifysig signedFile.su3");
|
||||||
|
System.err.println(dumpSigTypes());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @since 0.9.9 */
|
||||||
|
private static String dumpSigTypes() {
|
||||||
|
StringBuilder buf = new StringBuilder(256);
|
||||||
|
buf.append("Available signature types:\n");
|
||||||
|
for (SigType t : EnumSet.allOf(SigType.class)) {
|
||||||
|
buf.append(" ").append(t).append("\t(code: ").append(t.getCode()).append(')');
|
||||||
|
if (t == SigType.DSA_SHA1)
|
||||||
|
buf.append(" DEFAULT");
|
||||||
|
buf.append('\n');
|
||||||
|
}
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param stype number or name
|
||||||
|
* @return null if not found
|
||||||
|
* @since 0.9.9
|
||||||
|
*/
|
||||||
|
private static SigType parseSigType(String stype) {
|
||||||
|
try {
|
||||||
|
return SigType.valueOf(stype.toUpperCase(Locale.US));
|
||||||
|
} catch (IllegalArgumentException iae) {
|
||||||
|
try {
|
||||||
|
int code = Integer.parseInt(stype);
|
||||||
|
return SigType.getByCode(code);
|
||||||
|
} catch (NumberFormatException nfe) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return success */
|
/** @return success */
|
||||||
@ -372,6 +462,13 @@ public class SU3File {
|
|||||||
System.out.println("No version string found in file '" + signedFile + "'");
|
System.out.println("No version string found in file '" + signedFile + "'");
|
||||||
else
|
else
|
||||||
System.out.println("Version: " + versionString);
|
System.out.println("Version: " + versionString);
|
||||||
|
String signerString = file.getSignerString();
|
||||||
|
if (signerString.equals(""))
|
||||||
|
System.out.println("No signer string found in file '" + signedFile + "'");
|
||||||
|
else
|
||||||
|
System.out.println("Signer: " + signerString);
|
||||||
|
if (file._sigType != null)
|
||||||
|
System.out.println("SigType: " + file._sigType);
|
||||||
return !versionString.equals("");
|
return !versionString.equals("");
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
ioe.printStackTrace();
|
ioe.printStackTrace();
|
||||||
@ -380,28 +477,47 @@ public class SU3File {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @return success */
|
/** @return success */
|
||||||
private static final boolean signCLI(String inputFile, String signedFile, String privateKeyFile,
|
private static final boolean signCLI(String inputFile, String signedFile,
|
||||||
String version, String signerName) {
|
String privateKeyFile, String version, String signerName) {
|
||||||
InputStream in = null;
|
return signCLI(DEFAULT_TYPE, inputFile, signedFile, privateKeyFile, version, signerName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return success
|
||||||
|
* @since 0.9.9
|
||||||
|
*/
|
||||||
|
private static final boolean signCLI(String stype, String inputFile, String signedFile,
|
||||||
|
String privateKeyFile, String version, String signerName) {
|
||||||
|
SigType type = parseSigType(stype);
|
||||||
|
if (type == null) {
|
||||||
|
System.out.println("Signature type " + stype + " is not supported");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return signCLI(type, inputFile, signedFile, privateKeyFile, version, signerName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return success
|
||||||
|
* @since 0.9.9
|
||||||
|
*/
|
||||||
|
private static final boolean signCLI(SigType type, String inputFile, String signedFile,
|
||||||
|
String privateKeyFile, String version, String signerName) {
|
||||||
try {
|
try {
|
||||||
in = new FileInputStream(privateKeyFile);
|
File pkfile = new File(privateKeyFile);
|
||||||
SigningPrivateKey spk = new SigningPrivateKey();
|
PrivateKey pk = SigUtil.importJavaPrivateKey(pkfile, type);
|
||||||
spk.readBytes(in);
|
SigningPrivateKey spk = SigUtil.fromJavaKey(pk, type);
|
||||||
in.close();
|
|
||||||
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, spk);
|
||||||
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 (DataFormatException dfe) {
|
} catch (GeneralSecurityException gse) {
|
||||||
System.out.println("Error signing input file '" + inputFile + "'");
|
System.out.println("Error signing input file '" + inputFile + "'");
|
||||||
dfe.printStackTrace();
|
gse.printStackTrace();
|
||||||
return false;
|
return false;
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
System.out.println("Error signing input file '" + inputFile + "'");
|
System.out.println("Error signing input file '" + inputFile + "'");
|
||||||
ioe.printStackTrace();
|
ioe.printStackTrace();
|
||||||
return false;
|
return false;
|
||||||
} finally {
|
|
||||||
if (in != null) try { in.close(); } catch (IOException ioe) {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -410,11 +526,12 @@ public class SU3File {
|
|||||||
InputStream in = null;
|
InputStream in = null;
|
||||||
try {
|
try {
|
||||||
SU3File file = new SU3File(signedFile);
|
SU3File file = new SU3File(signedFile);
|
||||||
|
//// fixme
|
||||||
boolean isValidSignature = file.verifyAndMigrate(new File("/dev/null"));
|
boolean isValidSignature = file.verifyAndMigrate(new File("/dev/null"));
|
||||||
if (isValidSignature)
|
if (isValidSignature)
|
||||||
System.out.println("Signature VALID (signed by " + file.getSignerString() + ')');
|
System.out.println("Signature VALID (signed by " + file.getSignerString() + ' ' + file._sigType + ')');
|
||||||
else
|
else
|
||||||
System.out.println("Signature INVALID (signed by " + file.getSignerString() + ')');
|
System.out.println("Signature INVALID (signed by " + file.getSignerString() + ' ' + file._sigType +')');
|
||||||
return isValidSignature;
|
return isValidSignature;
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
System.out.println("Error verifying input file '" + signedFile + "'");
|
System.out.println("Error verifying input file '" + signedFile + "'");
|
||||||
@ -422,4 +539,75 @@ public class SU3File {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return success
|
||||||
|
* @since 0.9.9
|
||||||
|
*/
|
||||||
|
private static final boolean genKeysCLI(String publicKeyFile, String privateKeyFile) {
|
||||||
|
return genKeysCLI(DEFAULT_TYPE, publicKeyFile, privateKeyFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return success
|
||||||
|
* @since 0.9.9
|
||||||
|
*/
|
||||||
|
private static final boolean genKeysCLI(String stype, String publicKeyFile, String privateKeyFile) {
|
||||||
|
SigType type = parseSigType(stype);
|
||||||
|
if (type == null) {
|
||||||
|
System.out.println("Signature type " + stype + " is not supported");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return genKeysCLI(type, publicKeyFile, privateKeyFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes Java-encoded keys (X.509 for public and PKCS#8 for private)
|
||||||
|
* @return success
|
||||||
|
* @since 0.9.9
|
||||||
|
*/
|
||||||
|
private static final boolean genKeysCLI(SigType type, String publicKeyFile, String privateKeyFile) {
|
||||||
|
File pubFile = new File(publicKeyFile);
|
||||||
|
File privFile = new File(privateKeyFile);
|
||||||
|
if (pubFile.exists()) {
|
||||||
|
System.out.println("Error: Not overwriting file " + publicKeyFile);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (privFile.exists()) {
|
||||||
|
System.out.println("Error: Not overwriting file " + privateKeyFile);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
FileOutputStream fileOutputStream = null;
|
||||||
|
I2PAppContext context = I2PAppContext.getGlobalContext();
|
||||||
|
try {
|
||||||
|
// inefficiently go from Java to I2P to Java formats
|
||||||
|
SimpleDataStructure signingKeypair[] = context.keyGenerator().generateSigningKeys(type);
|
||||||
|
SigningPublicKey signingPublicKey = (SigningPublicKey) signingKeypair[0];
|
||||||
|
SigningPrivateKey signingPrivateKey = (SigningPrivateKey) signingKeypair[1];
|
||||||
|
PublicKey pubkey = SigUtil.toJavaKey(signingPublicKey);
|
||||||
|
PrivateKey privkey = SigUtil.toJavaKey(signingPrivateKey);
|
||||||
|
|
||||||
|
fileOutputStream = new SecureFileOutputStream(pubFile);
|
||||||
|
fileOutputStream.write(pubkey.getEncoded());
|
||||||
|
fileOutputStream.close();
|
||||||
|
fileOutputStream = null;
|
||||||
|
|
||||||
|
fileOutputStream = new SecureFileOutputStream(privFile);
|
||||||
|
fileOutputStream.write(privkey.getEncoded());
|
||||||
|
|
||||||
|
System.out.println("\r\n" + type + " Private key written to: " + privateKeyFile);
|
||||||
|
System.out.println(type + " Public key written to: " + publicKeyFile);
|
||||||
|
System.out.println("\r\nPublic key: " + signingPublicKey.toBase64() + "\r\n");
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("Error writing keys:");
|
||||||
|
e.printStackTrace();
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
if (fileOutputStream != null)
|
||||||
|
try {
|
||||||
|
fileOutputStream.close();
|
||||||
|
} catch (IOException ioe) {}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,9 @@ import java.security.spec.InvalidParameterSpecException;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import net.i2p.data.Hash;
|
||||||
|
import net.i2p.data.SimpleDataStructure;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the properties for various signature types
|
* Defines the properties for various signature types
|
||||||
* that I2P supports or may someday support.
|
* that I2P supports or may someday support.
|
||||||
@ -23,15 +26,27 @@ public enum SigType {
|
|||||||
* Pubkey 128 bytes; privkey 20 bytes; hash 20 bytes; sig 40 bytes
|
* Pubkey 128 bytes; privkey 20 bytes; hash 20 bytes; sig 40 bytes
|
||||||
* @since 0.9.8
|
* @since 0.9.8
|
||||||
*/
|
*/
|
||||||
DSA_SHA1(0, 128, 20, 20, 40, "SHA-1", "SHA1withDSA", null),
|
DSA_SHA1(0, 128, 20, 20, 40, "SHA-1", "SHA1withDSA", CryptoConstants.DSA_SHA1_SPEC),
|
||||||
/** Pubkey 48 bytes; privkey 24 bytes; hash 20 bytes; sig 48 bytes */
|
/** Pubkey 48 bytes; privkey 24 bytes; hash 20 bytes; sig 48 bytes */
|
||||||
ECDSA_SHA1_P192(1, 48, 24, 20, 48, "SHA-1", "SHA1withECDSA", null),
|
ECDSA_SHA1_P192(1, 48, 24, 20, 48, "SHA-1", "SHA1withECDSA", ECConstants.P192_SPEC),
|
||||||
/** Pubkey 64 bytes; privkey 32 bytes; hash 32 bytes; sig 64 bytes */
|
/** Pubkey 64 bytes; privkey 32 bytes; hash 32 bytes; sig 64 bytes */
|
||||||
ECDSA_SHA256_P256(2, 64, 32, 32, 64, "SHA-256", "SHA256withECDSA", null),
|
ECDSA_SHA256_P256(2, 64, 32, 32, 64, "SHA-256", "SHA256withECDSA", ECConstants.P256_SPEC),
|
||||||
/** Pubkey 96 bytes; privkey 48 bytes; hash 48 bytes; sig 96 bytes */
|
/** Pubkey 96 bytes; privkey 48 bytes; hash 48 bytes; sig 96 bytes */
|
||||||
ECDSA_SHA384_P384(3, 96, 48, 48, 96, "SHA-384", "SHA384withECDSA", null),
|
ECDSA_SHA384_P384(3, 96, 48, 48, 96, "SHA-384", "SHA384withECDSA", ECConstants.P384_SPEC),
|
||||||
/** Pubkey 132 bytes; privkey 66 bytes; hash 64 bytes; sig 132 bytes */
|
/** Pubkey 132 bytes; privkey 66 bytes; hash 64 bytes; sig 132 bytes */
|
||||||
ECDSA_SHA512_P521(4, 132, 66, 64, 132, "SHA-512", "SHA512withECDSA", null),
|
ECDSA_SHA512_P521(4, 132, 66, 64, 132, "SHA-512", "SHA512withECDSA", ECConstants.P521_SPEC),
|
||||||
|
|
||||||
|
// TESTING....................
|
||||||
|
|
||||||
|
ECDSA_SHA256_P192(5, 48, 24, 32, 48, "SHA-256", "SHA256withECDSA", ECConstants.P192_SPEC),
|
||||||
|
ECDSA_SHA256_P384(6, 96, 48, 32, 96, "SHA-256", "SHA256withECDSA", ECConstants.P384_SPEC),
|
||||||
|
ECDSA_SHA256_P521(7, 132, 66, 32, 132, "SHA-256", "SHA256withECDSA", ECConstants.P521_SPEC),
|
||||||
|
|
||||||
|
ECDSA_SHA384_P256(8, 64, 32, 48, 64, "SHA-384", "SHA384withECDSA", ECConstants.P256_SPEC),
|
||||||
|
ECDSA_SHA384_P521(9, 132, 66, 48, 132, "SHA-384", "SHA384withECDSA", ECConstants.P521_SPEC),
|
||||||
|
|
||||||
|
ECDSA_SHA512_P256(10, 64, 32, 64, 64, "SHA-512", "SHA512withECDSA", ECConstants.P256_SPEC),
|
||||||
|
ECDSA_SHA512_P384(11, 96, 48, 64, 96, "SHA-512", "SHA512withECDSA", ECConstants.P384_SPEC),
|
||||||
|
|
||||||
//MD5
|
//MD5
|
||||||
//ELGAMAL_SHA256
|
//ELGAMAL_SHA256
|
||||||
@ -100,6 +115,25 @@ public enum SigType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 0.9.9
|
||||||
|
* @throws UnsupportedOperationException if not supported
|
||||||
|
*/
|
||||||
|
public SimpleDataStructure getHashInstance() {
|
||||||
|
switch (getHashLen()) {
|
||||||
|
case 20:
|
||||||
|
return new SHA1Hash();
|
||||||
|
case 32:
|
||||||
|
return new Hash();
|
||||||
|
case 48:
|
||||||
|
return new Hash384();
|
||||||
|
case 64:
|
||||||
|
return new Hash512();
|
||||||
|
default:
|
||||||
|
throw new UnsupportedOperationException("Unsupported hash length: " + getHashLen());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static final Map<Integer, SigType> BY_CODE = new HashMap<Integer, SigType>();
|
private static final Map<Integer, SigType> BY_CODE = new HashMap<Integer, SigType>();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
|
444
core/java/src/net/i2p/crypto/SigUtil.java
Normal file
444
core/java/src/net/i2p/crypto/SigUtil.java
Normal file
@ -0,0 +1,444 @@
|
|||||||
|
package net.i2p.crypto;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.KeyFactory;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.security.SignatureException;
|
||||||
|
import java.security.interfaces.DSAPrivateKey;
|
||||||
|
import java.security.interfaces.DSAPublicKey;
|
||||||
|
import java.security.interfaces.ECPrivateKey;
|
||||||
|
import java.security.interfaces.ECPublicKey;
|
||||||
|
import java.security.spec.DSAPrivateKeySpec;
|
||||||
|
import java.security.spec.DSAPublicKeySpec;
|
||||||
|
import java.security.spec.ECField;
|
||||||
|
import java.security.spec.ECFieldFp;
|
||||||
|
import java.security.spec.ECGenParameterSpec;
|
||||||
|
import java.security.spec.ECParameterSpec;
|
||||||
|
import java.security.spec.ECPrivateKeySpec;
|
||||||
|
import java.security.spec.ECPublicKeySpec;
|
||||||
|
import java.security.spec.ECPoint;
|
||||||
|
import java.security.spec.EllipticCurve;
|
||||||
|
import java.security.spec.KeySpec;
|
||||||
|
import java.security.spec.PKCS8EncodedKeySpec;
|
||||||
|
import java.security.spec.X509EncodedKeySpec;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import net.i2p.data.Signature;
|
||||||
|
import net.i2p.data.SigningPrivateKey;
|
||||||
|
import net.i2p.data.SigningPublicKey;
|
||||||
|
import net.i2p.util.LHMCache;
|
||||||
|
import net.i2p.util.NativeBigInteger;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utilities for Signing keys and Signatures
|
||||||
|
*
|
||||||
|
* @since 0.9.9
|
||||||
|
*/
|
||||||
|
class SigUtil {
|
||||||
|
|
||||||
|
private static final Map<SigningPublicKey, ECPublicKey> _pubkeyCache = new LHMCache(64);
|
||||||
|
private static final Map<SigningPrivateKey, ECPrivateKey> _privkeyCache = new LHMCache(16);
|
||||||
|
|
||||||
|
private SigUtil() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return JAVA key!
|
||||||
|
*/
|
||||||
|
public static PublicKey toJavaKey(SigningPublicKey pk)
|
||||||
|
throws GeneralSecurityException {
|
||||||
|
if (pk.getType() == SigType.DSA_SHA1)
|
||||||
|
return toJavaDSAKey(pk);
|
||||||
|
else
|
||||||
|
return toJavaECKey(pk);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return JAVA key!
|
||||||
|
*/
|
||||||
|
public static PrivateKey toJavaKey(SigningPrivateKey pk)
|
||||||
|
throws GeneralSecurityException {
|
||||||
|
if (pk.getType() == SigType.DSA_SHA1)
|
||||||
|
return toJavaDSAKey(pk);
|
||||||
|
else
|
||||||
|
return toJavaECKey(pk);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param pk JAVA key!
|
||||||
|
*/
|
||||||
|
public static SigningPublicKey fromJavaKey(PublicKey pk, SigType type)
|
||||||
|
throws GeneralSecurityException {
|
||||||
|
if (type == SigType.DSA_SHA1)
|
||||||
|
return fromJavaKey((DSAPublicKey) pk);
|
||||||
|
else
|
||||||
|
return fromJavaKey((ECPublicKey) pk, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param pk JAVA key!
|
||||||
|
*/
|
||||||
|
public static SigningPrivateKey fromJavaKey(PrivateKey pk, SigType type)
|
||||||
|
throws GeneralSecurityException {
|
||||||
|
if (type == SigType.DSA_SHA1)
|
||||||
|
return fromJavaKey((DSAPrivateKey) pk);
|
||||||
|
else
|
||||||
|
return fromJavaKey((ECPrivateKey) pk, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return JAVA key!
|
||||||
|
*/
|
||||||
|
public static ECPublicKey toJavaECKey(SigningPublicKey pk)
|
||||||
|
throws GeneralSecurityException {
|
||||||
|
ECPublicKey rv;
|
||||||
|
synchronized (_pubkeyCache) {
|
||||||
|
rv = _pubkeyCache.get(pk);
|
||||||
|
}
|
||||||
|
if (rv != null)
|
||||||
|
return rv;
|
||||||
|
rv = cvtToJavaECKey(pk);
|
||||||
|
synchronized (_pubkeyCache) {
|
||||||
|
_pubkeyCache.put(pk, rv);
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return JAVA key!
|
||||||
|
*/
|
||||||
|
public static ECPrivateKey toJavaECKey(SigningPrivateKey pk)
|
||||||
|
throws GeneralSecurityException {
|
||||||
|
ECPrivateKey rv;
|
||||||
|
synchronized (_privkeyCache) {
|
||||||
|
rv = _privkeyCache.get(pk);
|
||||||
|
}
|
||||||
|
if (rv != null)
|
||||||
|
return rv;
|
||||||
|
rv = cvtToJavaECKey(pk);
|
||||||
|
synchronized (_privkeyCache) {
|
||||||
|
_privkeyCache.put(pk, rv);
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ECPublicKey cvtToJavaECKey(SigningPublicKey pk)
|
||||||
|
throws GeneralSecurityException {
|
||||||
|
SigType type = pk.getType();
|
||||||
|
int len = type.getPubkeyLen();
|
||||||
|
int sublen = len / 2;
|
||||||
|
byte[] b = pk.getData();
|
||||||
|
byte[] bx = new byte[sublen];
|
||||||
|
byte[] by = new byte[sublen];
|
||||||
|
System.arraycopy(b, 0, bx, 0, sublen);
|
||||||
|
System.arraycopy(b, sublen, by, 0, sublen);
|
||||||
|
BigInteger x = new BigInteger(1, bx);
|
||||||
|
BigInteger y = new BigInteger(1, by);
|
||||||
|
ECPoint w = new ECPoint(x, y);
|
||||||
|
// see ECConstants re: casting
|
||||||
|
ECPublicKeySpec ks = new ECPublicKeySpec(w, (ECParameterSpec) type.getParams());
|
||||||
|
KeyFactory kf = KeyFactory.getInstance("EC");
|
||||||
|
return (ECPublicKey) kf.generatePublic(ks);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ECPrivateKey cvtToJavaECKey(SigningPrivateKey pk)
|
||||||
|
throws GeneralSecurityException {
|
||||||
|
SigType type = pk.getType();
|
||||||
|
int len = type.getPubkeyLen();
|
||||||
|
int sublen = len / 2;
|
||||||
|
byte[] b = pk.getData();
|
||||||
|
BigInteger s = new BigInteger(1, b);
|
||||||
|
// see ECConstants re: casting
|
||||||
|
ECPrivateKeySpec ks = new ECPrivateKeySpec(s, (ECParameterSpec) type.getParams());
|
||||||
|
KeyFactory kf = KeyFactory.getInstance("EC");
|
||||||
|
return (ECPrivateKey) kf.generatePrivate(ks);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SigningPublicKey fromJavaKey(ECPublicKey pk, SigType type)
|
||||||
|
throws GeneralSecurityException {
|
||||||
|
ECPoint w = pk.getW();
|
||||||
|
BigInteger x = w.getAffineX();
|
||||||
|
BigInteger y = w.getAffineY();
|
||||||
|
int len = type.getPubkeyLen();
|
||||||
|
int sublen = len / 2;
|
||||||
|
byte[] b = new byte[len];
|
||||||
|
byte[] bx = rectify(x, sublen);
|
||||||
|
byte[] by = rectify(y, sublen);
|
||||||
|
System.arraycopy(bx, 0, b, 0, sublen);
|
||||||
|
System.arraycopy(by, 0, b, sublen, sublen);
|
||||||
|
return new SigningPublicKey(type, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SigningPrivateKey fromJavaKey(ECPrivateKey pk, SigType type)
|
||||||
|
throws GeneralSecurityException {
|
||||||
|
BigInteger s = pk.getS();
|
||||||
|
int len = type.getPrivkeyLen();
|
||||||
|
byte[] bs = rectify(s, len);
|
||||||
|
return new SigningPrivateKey(type, bs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DSAPublicKey toJavaDSAKey(SigningPublicKey pk)
|
||||||
|
throws GeneralSecurityException {
|
||||||
|
KeyFactory kf = KeyFactory.getInstance("DSA");
|
||||||
|
// y p q g
|
||||||
|
KeySpec ks = new DSAPublicKeySpec(new NativeBigInteger(1, pk.getData()),
|
||||||
|
CryptoConstants.dsap,
|
||||||
|
CryptoConstants.dsaq,
|
||||||
|
CryptoConstants.dsag);
|
||||||
|
return (DSAPublicKey) kf.generatePublic(ks);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DSAPrivateKey toJavaDSAKey(SigningPrivateKey pk)
|
||||||
|
throws GeneralSecurityException {
|
||||||
|
KeyFactory kf = KeyFactory.getInstance("DSA");
|
||||||
|
// x p q g
|
||||||
|
KeySpec ks = new DSAPrivateKeySpec(new NativeBigInteger(1, pk.getData()),
|
||||||
|
CryptoConstants.dsap,
|
||||||
|
CryptoConstants.dsaq,
|
||||||
|
CryptoConstants.dsag);
|
||||||
|
return (DSAPrivateKey) kf.generatePrivate(ks);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SigningPublicKey fromJavaKey(DSAPublicKey pk)
|
||||||
|
throws GeneralSecurityException {
|
||||||
|
BigInteger y = pk.getY();
|
||||||
|
SigType type = SigType.DSA_SHA1;
|
||||||
|
int len = type.getPubkeyLen();
|
||||||
|
byte[] by = rectify(y, len);
|
||||||
|
return new SigningPublicKey(type, by);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SigningPrivateKey fromJavaKey(DSAPrivateKey pk)
|
||||||
|
throws GeneralSecurityException {
|
||||||
|
BigInteger x = pk.getX();
|
||||||
|
SigType type = SigType.DSA_SHA1;
|
||||||
|
int len = type.getPrivkeyLen();
|
||||||
|
byte[] bx = rectify(x, len);
|
||||||
|
return new SigningPrivateKey(type, bx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return ASN.1 representation
|
||||||
|
*/
|
||||||
|
public static byte[] toJavaSig(Signature sig) {
|
||||||
|
return sigBytesToASN1(sig.getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param asn ASN.1 representation
|
||||||
|
* @return a Signature with SigType type
|
||||||
|
*/
|
||||||
|
public static Signature fromJavaSig(byte[] asn, SigType type)
|
||||||
|
throws SignatureException {
|
||||||
|
return new Signature(type, aSN1ToSigBytes(asn, type.getSigLen()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return JAVA key!
|
||||||
|
*/
|
||||||
|
public static PublicKey importJavaPublicKey(File file, SigType type)
|
||||||
|
throws GeneralSecurityException, IOException {
|
||||||
|
byte[] data = getData(file);
|
||||||
|
KeySpec ks = new X509EncodedKeySpec(data);
|
||||||
|
String algo = type == SigType.DSA_SHA1 ? "DSA" : "EC";
|
||||||
|
KeyFactory kf = KeyFactory.getInstance(algo);
|
||||||
|
return kf.generatePublic(ks);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return JAVA key!
|
||||||
|
*/
|
||||||
|
public static PrivateKey importJavaPrivateKey(File file, SigType type)
|
||||||
|
throws GeneralSecurityException, IOException {
|
||||||
|
byte[] data = getData(file);
|
||||||
|
KeySpec ks = new PKCS8EncodedKeySpec(data);
|
||||||
|
String algo = type == SigType.DSA_SHA1 ? "DSA" : "EC";
|
||||||
|
KeyFactory kf = KeyFactory.getInstance(algo);
|
||||||
|
return kf.generatePrivate(ks);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 16 KB max */
|
||||||
|
private static byte[] getData(File file) throws IOException {
|
||||||
|
byte buf[] = new byte[1024];
|
||||||
|
InputStream in = null;
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
|
||||||
|
try {
|
||||||
|
in = new FileInputStream(file);
|
||||||
|
int read = 0;
|
||||||
|
int tot = 0;
|
||||||
|
while ( (read = in.read(buf)) != -1) {
|
||||||
|
out.write(buf, 0, read);
|
||||||
|
tot += read;
|
||||||
|
if (tot > 16*1024)
|
||||||
|
throw new IOException("too big");
|
||||||
|
}
|
||||||
|
return out.toByteArray();
|
||||||
|
} finally {
|
||||||
|
if (in != null)
|
||||||
|
try { in.close(); } catch (IOException ioe) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bi non-negative
|
||||||
|
* @return array of exactly len bytes
|
||||||
|
*/
|
||||||
|
public static byte[] rectify(BigInteger bi, int len)
|
||||||
|
throws InvalidKeyException {
|
||||||
|
byte[] b = bi.toByteArray();
|
||||||
|
if (b.length == len) {
|
||||||
|
// just right
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
if (b.length > len + 1)
|
||||||
|
throw new InvalidKeyException("key too big (" + b.length + ") max is " + (len + 1));
|
||||||
|
byte[] rv = new byte[len];
|
||||||
|
if (b.length == 0)
|
||||||
|
return rv;
|
||||||
|
if ((b[0] & 0x80) != 0)
|
||||||
|
throw new InvalidKeyException("negative");
|
||||||
|
if (b.length > len) {
|
||||||
|
// leading 0 byte
|
||||||
|
if (b[0] != 0)
|
||||||
|
throw new InvalidKeyException("key too big (" + b.length + ") max is " + len);
|
||||||
|
System.arraycopy(b, 1, rv, 0, len);
|
||||||
|
} else {
|
||||||
|
// smaller
|
||||||
|
System.arraycopy(b, 0, rv, len - b.length, b.length);
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* http://download.oracle.com/javase/1.5.0/docs/guide/security/CryptoSpec.html
|
||||||
|
* Signature Format ASN.1 sequence of two INTEGER values: r and s, in that order:
|
||||||
|
* SEQUENCE ::= { r INTEGER, s INTEGER }
|
||||||
|
*
|
||||||
|
* http://en.wikipedia.org/wiki/Abstract_Syntax_Notation_One
|
||||||
|
* 30 -- tag indicating SEQUENCE
|
||||||
|
* xx - length in octets
|
||||||
|
*
|
||||||
|
* 02 -- tag indicating INTEGER
|
||||||
|
* xx - length in octets
|
||||||
|
* xxxxxx - value
|
||||||
|
*
|
||||||
|
* Convert to BigInteger and back so we have the minimum length representation, as required.
|
||||||
|
* r and s are always non-negative.
|
||||||
|
*
|
||||||
|
* Only supports sigs up to about 252 bytes. See code to fix BER encoding for this before you
|
||||||
|
* add a SigType with bigger signatures.
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if too big
|
||||||
|
* @since 0.8.7, moved to SigUtil in 0.9.9
|
||||||
|
*/
|
||||||
|
private static byte[] sigBytesToASN1(byte[] sig) {
|
||||||
|
//System.out.println("pre TO asn1\n" + net.i2p.util.HexDump.dump(sig));
|
||||||
|
int len = sig.length;
|
||||||
|
int sublen = len / 2;
|
||||||
|
byte[] tmp = new byte[sublen];
|
||||||
|
|
||||||
|
System.arraycopy(sig, 0, tmp, 0, sublen);
|
||||||
|
BigInteger r = new BigInteger(1, tmp);
|
||||||
|
byte[] rb = r.toByteArray();
|
||||||
|
if (rb.length > 127)
|
||||||
|
throw new IllegalArgumentException("FIXME R length > 127");
|
||||||
|
System.arraycopy(sig, sublen, tmp, 0, sublen);
|
||||||
|
BigInteger s = new BigInteger(1, tmp);
|
||||||
|
byte[] sb = s.toByteArray();
|
||||||
|
if (sb.length > 127)
|
||||||
|
throw new IllegalArgumentException("FIXME S length > 127");
|
||||||
|
int seqlen = rb.length + sb.length + 4;
|
||||||
|
if (seqlen > 255)
|
||||||
|
throw new IllegalArgumentException("FIXME seq length > 255");
|
||||||
|
int totlen = seqlen + 2;
|
||||||
|
if (seqlen > 127)
|
||||||
|
totlen++;
|
||||||
|
byte[] rv = new byte[totlen];
|
||||||
|
int idx = 0;
|
||||||
|
|
||||||
|
rv[idx++] = 0x30;
|
||||||
|
if (seqlen > 127)
|
||||||
|
rv[idx++] =(byte) 0x81;
|
||||||
|
rv[idx++] = (byte) seqlen;
|
||||||
|
|
||||||
|
rv[idx++] = 0x02;
|
||||||
|
rv[idx++] = (byte) rb.length;
|
||||||
|
System.arraycopy(rb, 0, rv, idx, rb.length);
|
||||||
|
idx += rb.length;
|
||||||
|
|
||||||
|
rv[idx++] = 0x02;
|
||||||
|
rv[idx++] = (byte) sb.length;
|
||||||
|
System.arraycopy(sb, 0, rv, idx, sb.length);
|
||||||
|
|
||||||
|
//System.out.println("post TO asn1\n" + net.i2p.util.HexDump.dump(rv));
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See above.
|
||||||
|
* Only supports sigs up to about 252 bytes. See code to fix BER encoding for bigger than that.
|
||||||
|
*
|
||||||
|
* @return len bytes
|
||||||
|
* @since 0.8.7, moved to SigUtil in 0.9.9
|
||||||
|
*/
|
||||||
|
private static byte[] aSN1ToSigBytes(byte[] asn, int len)
|
||||||
|
throws SignatureException {
|
||||||
|
//System.out.println("pre from asn1 len=" + len + "\n" + net.i2p.util.HexDump.dump(asn));
|
||||||
|
if (asn[0] != 0x30)
|
||||||
|
throw new SignatureException("asn[0] = " + (asn[0] & 0xff));
|
||||||
|
// handles total len > 127
|
||||||
|
int idx = 2;
|
||||||
|
if ((asn[1] & 0x80) != 0)
|
||||||
|
idx += asn[1] & 0x7f;
|
||||||
|
if (asn[idx] != 0x02)
|
||||||
|
throw new SignatureException("asn[2] = " + (asn[idx] & 0xff));
|
||||||
|
byte[] rv = new byte[len];
|
||||||
|
int sublen = len / 2;
|
||||||
|
int rlen = asn[++idx];
|
||||||
|
if ((rlen & 0x80) != 0)
|
||||||
|
throw new SignatureException("FIXME R length > 127");
|
||||||
|
if ((asn[++idx] & 0x80) != 0)
|
||||||
|
throw new SignatureException("R is negative");
|
||||||
|
if (rlen > sublen + 1)
|
||||||
|
throw new SignatureException("R too big " + rlen);
|
||||||
|
if (rlen == sublen + 1)
|
||||||
|
System.arraycopy(asn, idx + 1, rv, 0, sublen);
|
||||||
|
else
|
||||||
|
System.arraycopy(asn, idx, rv, sublen - rlen, rlen);
|
||||||
|
idx += rlen;
|
||||||
|
int slenloc = idx + 1;
|
||||||
|
if (asn[idx] != 0x02)
|
||||||
|
throw new SignatureException("asn[s] = " + (asn[idx] & 0xff));
|
||||||
|
int slen = asn[slenloc];
|
||||||
|
if ((slen & 0x80) != 0)
|
||||||
|
throw new SignatureException("FIXME S length > 127");
|
||||||
|
if ((asn[slenloc + 1] & 0x80) != 0)
|
||||||
|
throw new SignatureException("S is negative");
|
||||||
|
if (slen > sublen + 1)
|
||||||
|
throw new SignatureException("S too big " + slen);
|
||||||
|
if (slen == sublen + 1)
|
||||||
|
System.arraycopy(asn, slenloc + 2, rv, sublen, sublen);
|
||||||
|
else
|
||||||
|
System.arraycopy(asn, slenloc + 1, rv, len - slen, slen);
|
||||||
|
//System.out.println("post from asn1\n" + net.i2p.util.HexDump.dump(rv));
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void clearCaches() {
|
||||||
|
synchronized(_pubkeyCache) {
|
||||||
|
_pubkeyCache.clear();
|
||||||
|
}
|
||||||
|
synchronized(_privkeyCache) {
|
||||||
|
_privkeyCache.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -21,6 +21,7 @@ import net.i2p.data.Signature;
|
|||||||
import net.i2p.data.SigningPrivateKey;
|
import net.i2p.data.SigningPrivateKey;
|
||||||
import net.i2p.data.SigningPublicKey;
|
import net.i2p.data.SigningPublicKey;
|
||||||
import net.i2p.util.Log;
|
import net.i2p.util.Log;
|
||||||
|
import net.i2p.util.SecureFileOutputStream;
|
||||||
import net.i2p.util.VersionComparator;
|
import net.i2p.util.VersionComparator;
|
||||||
import net.i2p.util.ZipFileComment;
|
import net.i2p.util.ZipFileComment;
|
||||||
|
|
||||||
@ -315,20 +316,29 @@ riCe6OlAEiNpcc6mMyIYYWFICbrDFTrDR3wXqwc/Jkcx6L5VVWoagpSzbo3yGhc=
|
|||||||
|
|
||||||
/** @return success */
|
/** @return success */
|
||||||
private static final boolean genKeysCLI(String publicKeyFile, String privateKeyFile) {
|
private static final boolean genKeysCLI(String publicKeyFile, String privateKeyFile) {
|
||||||
|
File pubFile = new File(publicKeyFile);
|
||||||
|
File privFile = new File(privateKeyFile);
|
||||||
|
if (pubFile.exists()) {
|
||||||
|
System.out.println("Error: Not overwriting file " + publicKeyFile);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (privFile.exists()) {
|
||||||
|
System.out.println("Error: Not overwriting file " + privateKeyFile);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
FileOutputStream fileOutputStream = null;
|
FileOutputStream fileOutputStream = null;
|
||||||
|
|
||||||
I2PAppContext context = I2PAppContext.getGlobalContext();
|
I2PAppContext context = I2PAppContext.getGlobalContext();
|
||||||
try {
|
try {
|
||||||
Object signingKeypair[] = context.keyGenerator().generateSigningKeypair();
|
Object signingKeypair[] = context.keyGenerator().generateSigningKeypair();
|
||||||
SigningPublicKey signingPublicKey = (SigningPublicKey) signingKeypair[0];
|
SigningPublicKey signingPublicKey = (SigningPublicKey) signingKeypair[0];
|
||||||
SigningPrivateKey signingPrivateKey = (SigningPrivateKey) signingKeypair[1];
|
SigningPrivateKey signingPrivateKey = (SigningPrivateKey) signingKeypair[1];
|
||||||
|
|
||||||
fileOutputStream = new FileOutputStream(publicKeyFile);
|
fileOutputStream = new SecureFileOutputStream(pubFile);
|
||||||
signingPublicKey.writeBytes(fileOutputStream);
|
signingPublicKey.writeBytes(fileOutputStream);
|
||||||
fileOutputStream.close();
|
fileOutputStream.close();
|
||||||
fileOutputStream = null;
|
fileOutputStream = null;
|
||||||
|
|
||||||
fileOutputStream = new FileOutputStream(privateKeyFile);
|
fileOutputStream = new SecureFileOutputStream(privFile);
|
||||||
signingPrivateKey.writeBytes(fileOutputStream);
|
signingPrivateKey.writeBytes(fileOutputStream);
|
||||||
|
|
||||||
System.out.println("\r\nPrivate key written to: " + privateKeyFile);
|
System.out.println("\r\nPrivate key written to: " + privateKeyFile);
|
||||||
|
Reference in New Issue
Block a user