* Added basic trusted update creation/verification
This commit is contained in:
12
apps/routerconsole/jsp/verifyupdate.jsp
Normal file
12
apps/routerconsole/jsp/verifyupdate.jsp
Normal file
@ -0,0 +1,12 @@
|
||||
<%@page contentType="text/html" %>
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
|
||||
<html><head>
|
||||
<title>I2P Router Console - verify update file signature</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- net.i2p.crypto.TrustedUpdate.verify(request.getParameter("filename")) -->
|
||||
|
||||
</body>
|
||||
</html>
|
154
core/java/src/net/i2p/crypto/TrustedUpdate.java
Normal file
154
core/java/src/net/i2p/crypto/TrustedUpdate.java
Normal file
@ -0,0 +1,154 @@
|
||||
package net.i2p.crypto;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.Signature;
|
||||
import net.i2p.data.SigningPrivateKey;
|
||||
import net.i2p.data.SigningPublicKey;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Handles DSA signing and verification of I2P update archives.
|
||||
*
|
||||
* @author smeghead
|
||||
*/
|
||||
public class TrustedUpdate {
|
||||
|
||||
private static byte[] I2P_PUBLICKEY = { 'p', 'k' };
|
||||
|
||||
private I2PAppContext _context;
|
||||
private Log _log;
|
||||
|
||||
public TrustedUpdate() {
|
||||
_context = I2PAppContext.getGlobalContext();
|
||||
_log = _context.logManager().getLog(TrustedUpdate.class);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// If no context is defined, don't define one.
|
||||
// Expose verify(inputFile, publicKeyFile) via cli param
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the version string from a signed I2P update file.
|
||||
*
|
||||
* @param inputFile A signed I2P update file.
|
||||
*
|
||||
* @return The update version string read, or an empty string if no version
|
||||
* string is present.
|
||||
*/
|
||||
public String getUpdateVersion(String inputFile) {
|
||||
String updateVersion = null;
|
||||
byte[] data = readFileBytes(inputFile, 0, 16);
|
||||
try {
|
||||
updateVersion = new String(data, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
// If this ever gets called, you need a new JVM.
|
||||
}
|
||||
return updateVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the given private key to sign the given input file with DSA. The
|
||||
* output will be a binary file where the first 16 bytes are the I2P
|
||||
* update's version string encoded in UTF-8 (padded with trailing
|
||||
* <code>0h</code> characters if necessary), the next 40 bytes are the
|
||||
* resulting DSA signature, and the remaining bytes are the input file.
|
||||
*
|
||||
* @param inputFile The file to be signed.
|
||||
* @param outputFile The signed file to write.
|
||||
* @param privateKeyFile The name of the file containing the private key to
|
||||
* sign <code>inputFile</code> with.
|
||||
* @param updateVersion The version number of the I2P update. If this
|
||||
* string is longer than 16 characters it will be
|
||||
* truncated.
|
||||
*
|
||||
* @return An instance of {@link net.i2p.data.Signature}.
|
||||
*/
|
||||
public Signature sign(String inputFile, String outputFile, String privateKeyFile, String updateVersion) {
|
||||
byte[] headerUpdateVersion = {
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00 };
|
||||
byte[] updateVersionBytes = {};
|
||||
if (updateVersion.length() > 16)
|
||||
updateVersion = updateVersion.substring(0, 16);
|
||||
try {
|
||||
updateVersionBytes = updateVersion.getBytes("UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
// If this ever gets called, you need a new JVM.
|
||||
}
|
||||
for (int i = 0; i < updateVersionBytes.length; i++)
|
||||
headerUpdateVersion[i] = updateVersionBytes[i];
|
||||
byte[] data = readFileBytes(inputFile, 0, (int) new File(inputFile).length());
|
||||
Signature signature = DSAEngine.getInstance().sign(data, new SigningPrivateKey(readFileBytes(privateKeyFile, 0, (int) new File(privateKeyFile).length()-1)));
|
||||
FileOutputStream fileOutputStream = null;
|
||||
try {
|
||||
fileOutputStream = new FileOutputStream(outputFile);
|
||||
fileOutputStream.write(headerUpdateVersion);
|
||||
fileOutputStream.write(signature.getData());
|
||||
fileOutputStream.write(data);
|
||||
fileOutputStream.close();
|
||||
} catch (IOException ioe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.log(Log.WARN, "Error writing signed I2P update file " + outputFile, ioe);
|
||||
}
|
||||
|
||||
return signature;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the DSA signature of a signed I2P update.
|
||||
*
|
||||
* @param inputFile The signed update file to check.
|
||||
*
|
||||
* @return <code>true</code> if the file has a valid signature.
|
||||
*/
|
||||
public boolean verify(String inputFile) {
|
||||
DSAEngine.getInstance().verifySignature(new Signature(readFileBytes(inputFile, 16, 55)),
|
||||
readFileBytes(inputFile, 56, (int) new File(inputFile).length()-57),
|
||||
new SigningPublicKey(I2P_PUBLICKEY));
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the DSA signature of a signed I2P update.
|
||||
*
|
||||
* @param inputFile The signed update file to check.
|
||||
* @param publicKeyFile The public key to use for verification.
|
||||
*
|
||||
* @return <code>true</code> if the file has a valid signature.
|
||||
*/
|
||||
public boolean verify(String inputFile, String publicKeyFile) {
|
||||
DSAEngine.getInstance().verifySignature(new Signature(readFileBytes(inputFile, 16, 55)),
|
||||
readFileBytes(inputFile, 56, (int) new File(inputFile).length()-57),
|
||||
new SigningPublicKey(readFileBytes(publicKeyFile, 0, (int) new File(publicKeyFile).length()-1)));
|
||||
return false;
|
||||
}
|
||||
|
||||
private byte[] readFileBytes(String inputFile, int offset, int length) {
|
||||
byte[] bytes = new byte[length];
|
||||
FileInputStream fileInputStream = null;
|
||||
|
||||
try {
|
||||
fileInputStream = new FileInputStream(inputFile);
|
||||
fileInputStream.read(bytes, offset, length);
|
||||
fileInputStream.close();
|
||||
} catch (FileNotFoundException fnfe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.log(Log.WARN, "File " + inputFile + " not found", fnfe);
|
||||
} catch (IOException ioe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.log(Log.WARN, "Error reading file " + inputFile, ioe);
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user