Skip to content

Commit

Permalink
Merge branch 'oss'
Browse files Browse the repository at this point in the history
  • Loading branch information
kohsuke committed Mar 19, 2011
2 parents dfead9c + 04584b8 commit 2066157
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 19 deletions.
47 changes: 35 additions & 12 deletions core/src/main/java/hudson/model/UsageStatistics.java
Expand Up @@ -38,6 +38,7 @@
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.CipherInputStream;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.io.OutputStreamWriter;
Expand All @@ -47,8 +48,11 @@
import java.io.InputStream;
import java.io.DataInputStream;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.interfaces.RSAKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.List;
Expand All @@ -64,7 +68,7 @@ public class UsageStatistics extends PageDecorator {
/**
* Lazily computed {@link PublicKey} representation of {@link #keyImage}.
*/
private volatile transient PublicKey key;
private volatile transient RSAPublicKey key;

/**
* When was the last time we asked a browser to send the usage stats for us?
Expand Down Expand Up @@ -99,16 +103,13 @@ public boolean isDue() {
return false;
}

private Cipher getCipher() {
private RSAPublicKey getKey() {
try {
if (key == null) {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
key = keyFactory.generatePublic(new X509EncodedKeySpec(Util.fromHexString(keyImage)));
key = (RSAPublicKey)keyFactory.generatePublic(new X509EncodedKeySpec(Util.fromHexString(keyImage)));
}

Cipher cipher = Secret.getCipher("RSA");
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher;
return key;
} catch (GeneralSecurityException e) {
throw new Error(e); // impossible
}
Expand Down Expand Up @@ -166,7 +167,7 @@ public String getStatData() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();

// json -> UTF-8 encode -> gzip -> encrypt -> base64 -> string
OutputStreamWriter w = new OutputStreamWriter(new GZIPOutputStream(new CombinedCipherOutputStream(baos,getCipher(),"AES")), "UTF-8");
OutputStreamWriter w = new OutputStreamWriter(new GZIPOutputStream(new CombinedCipherOutputStream(baos,getKey(),"AES")), "UTF-8");
o.write(w);
w.close();

Expand All @@ -187,16 +188,21 @@ public CombinedCipherOutputStream(OutputStream out, Cipher asym, String algorith
super(out);

// create a new symmetric cipher key used for this stream
SecretKey symKey = KeyGenerator.getInstance(algorithm).generateKey();
String keyAlgorithm = getKeyAlgorithm(algorithm);
SecretKey symKey = KeyGenerator.getInstance(keyAlgorithm).generateKey();

// place the symmetric key by encrypting it with asymmetric cipher
out.write(asym.doFinal(symKey.getEncoded()));

// the rest of the data will be encrypted by this symmetric cipher
Cipher sym = Secret.getCipher(algorithm);
sym.init(Cipher.ENCRYPT_MODE,symKey);
sym.init(Cipher.ENCRYPT_MODE,symKey, keyAlgorithm.equals(algorithm) ? null : new IvParameterSpec(symKey.getEncoded()));
super.out = new CipherOutputStream(out,sym);
}

public CombinedCipherOutputStream(OutputStream out, RSAKey key, String algorithm) throws IOException, GeneralSecurityException {
this(out,toCipher(key,Cipher.ENCRYPT_MODE),algorithm);
}
}

/**
Expand All @@ -211,16 +217,33 @@ public static final class CombinedCipherInputStream extends FilterInputStream {
public CombinedCipherInputStream(InputStream in, Cipher asym, String algorithm, int keyLength) throws IOException, GeneralSecurityException {
super(in);

String keyAlgorithm = getKeyAlgorithm(algorithm);

// first read the symmetric key cipher
byte[] symKeyBytes = new byte[keyLength/8];
new DataInputStream(in).readFully(symKeyBytes);
SecretKey symKey = new SecretKeySpec(asym.doFinal(symKeyBytes),algorithm);
SecretKey symKey = new SecretKeySpec(asym.doFinal(symKeyBytes),keyAlgorithm);

// the rest of the data will be decrypted by this symmetric cipher
Cipher sym = Secret.getCipher(algorithm);
sym.init(Cipher.DECRYPT_MODE,symKey);
sym.init(Cipher.DECRYPT_MODE,symKey, keyAlgorithm.equals(algorithm) ? null : new IvParameterSpec(symKey.getEncoded()));
super.in = new CipherInputStream(in,sym);
}

public CombinedCipherInputStream(InputStream in, RSAKey key, String algorithm) throws IOException, GeneralSecurityException {
this(in,toCipher(key,Cipher.DECRYPT_MODE),algorithm,key.getModulus().bitLength());
}
}

private static String getKeyAlgorithm(String algorithm) {
int index = algorithm.indexOf('/');
return (index>0)?algorithm.substring(0,index):algorithm;
}

private static Cipher toCipher(RSAKey key, int mode) throws GeneralSecurityException {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(mode, (Key)key);
return cipher;
}

/**
Expand Down
10 changes: 3 additions & 7 deletions test/src/test/java/hudson/model/UsageStatisticsTest.java
Expand Up @@ -30,11 +30,10 @@
import org.apache.commons.io.IOUtils;
import org.jvnet.hudson.test.HudsonTestCase;

import javax.crypto.Cipher;
import java.io.ByteArrayInputStream;
import java.io.InputStreamReader;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.zip.GZIPInputStream;

Expand All @@ -54,14 +53,11 @@ public void testRoundtrip() throws Exception {
System.out.println(data);

KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey priv = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(Util.fromHexString(privateKey)));

Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, priv);
RSAPrivateKey priv = (RSAPrivateKey)keyFactory.generatePrivate(new PKCS8EncodedKeySpec(Util.fromHexString(privateKey)));

byte[] cipherText = Base64.decode(data.toCharArray());
InputStreamReader r = new InputStreamReader(new GZIPInputStream(
new CombinedCipherInputStream(new ByteArrayInputStream(cipherText),cipher,"AES",1024)), "UTF-8");
new CombinedCipherInputStream(new ByteArrayInputStream(cipherText),priv,"AES")), "UTF-8");
JSONObject o = JSONObject.fromObject(IOUtils.toString(r));
System.out.println(o);
assertEquals(1,o.getInt("stat"));
Expand Down

0 comments on commit 2066157

Please sign in to comment.