Last active
March 4, 2016 09:50
-
-
Save icchan/b44663433f560867e326 to your computer and use it in GitHub Desktop.
Some example functions on how to do digital signatures, both asymmetric and symmetric.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package net.bubblemix.signature; | |
import java.io.UnsupportedEncodingException; | |
import java.nio.charset.Charset; | |
import java.nio.charset.StandardCharsets; | |
import java.security.InvalidKeyException; | |
import java.security.KeyFactory; | |
import java.security.KeyPair; | |
import java.security.KeyPairGenerator; | |
import java.security.NoSuchAlgorithmException; | |
import java.security.PrivateKey; | |
import java.security.PublicKey; | |
import java.security.Signature; | |
import java.security.SignatureException; | |
import java.security.spec.InvalidKeySpecException; | |
import java.security.spec.PKCS8EncodedKeySpec; | |
import java.security.spec.X509EncodedKeySpec; | |
import java.util.Base64; | |
import org.slf4j.Logger; | |
import org.slf4j.LoggerFactory; | |
/** | |
* @author ian.chen | |
* | |
*/ | |
public class AsymmetricKeySignature { | |
private static final Logger LOG = LoggerFactory.getLogger(AsymmetricKeySignature.class); | |
private static final String KEY_ALGORITHM = "RSA"; | |
private static final String SIG_ALGORITHM = "SHA1withRSA"; | |
private static final Charset MESSAGE_ENCODING = StandardCharsets.UTF_8; | |
private static final Charset SIG_ENCODING = StandardCharsets.UTF_8; | |
/** | |
* Generate a public/private key pair for asymmetric key | |
* | |
* @return | |
* @throws NoSuchAlgorithmException | |
*/ | |
public static KeyPair generateKeyPair() throws NoSuchAlgorithmException { | |
KeyPairGenerator kpg = KeyPairGenerator.getInstance(KEY_ALGORITHM); | |
kpg.initialize(1024); | |
return kpg.genKeyPair(); | |
} | |
/** | |
* Generate a public/private key pair for asymmetric key signing | |
* | |
* @return Keys as Strings | |
* @throws NoSuchAlgorithmException | |
*/ | |
public static StringKeyPair generateStringKeyPair() throws NoSuchAlgorithmException { | |
KeyPair keyPair = generateKeyPair(); | |
// String used by the signer | |
PrivateKey privateKey = keyPair.getPrivate(); | |
PKCS8EncodedKeySpec pemcontents = new PKCS8EncodedKeySpec(privateKey.getEncoded()); | |
String privateKeyString = new String(Base64.getEncoder().encode(pemcontents.getEncoded())); | |
LOG.info("privateKeyString: " + privateKeyString); | |
// String used by the verifier | |
PublicKey publicKey = keyPair.getPublic(); | |
String publicKeyString = new String(Base64.getEncoder().encode(publicKey.getEncoded())); | |
LOG.info("publicKeyString: " + publicKeyString); | |
StringKeyPair skp = new StringKeyPair(); | |
skp.setPrivateKey(privateKeyString); | |
skp.setPublicKey(publicKeyString); | |
return skp; | |
} | |
/** | |
* Sign a string message with a string key | |
* | |
* @param message | |
* @param privateKeyString | |
* @return | |
* @throws UnsupportedEncodingException | |
* @throws NoSuchAlgorithmException | |
* @throws InvalidKeyException | |
* @throws SignatureException | |
* @throws InvalidKeySpecException | |
*/ | |
public static String sign(String message, String privateKeyString) | |
throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, InvalidKeySpecException { | |
KeyFactory kf = KeyFactory.getInstance(KEY_ALGORITHM); | |
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKeyString.getBytes(SIG_ENCODING))); | |
PrivateKey privateKey = kf.generatePrivate(spec); | |
return sign(message.getBytes(MESSAGE_ENCODING), privateKey); | |
} | |
/** | |
* Type safe signing | |
* | |
* @param messageBytes | |
* @param privateKey | |
* @return | |
* @throws NoSuchAlgorithmException | |
* @throws InvalidKeyException | |
* @throws SignatureException | |
*/ | |
public static String sign(byte[] messageBytes, PrivateKey privateKey) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { | |
Signature sig = Signature.getInstance(SIG_ALGORITHM); | |
sig.initSign(privateKey); | |
sig.update(messageBytes); | |
byte[] signatureBytes = sig.sign(); | |
return new String(Base64.getEncoder().encode(signatureBytes)); | |
} | |
/** | |
* | |
* @param message raw message | |
* @param signature base64 encoded signature | |
* @param publicKeyString base64 encoded public key | |
* @return | |
* @throws NoSuchAlgorithmException | |
* @throws InvalidKeySpecException | |
* @throws UnsupportedEncodingException | |
* @throws InvalidKeyException | |
* @throws SignatureException | |
* @throws Base64DecodingException | |
*/ | |
public static boolean verify(String message, String signature, String publicKeyString) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, SignatureException { | |
X509EncodedKeySpec spec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKeyString.getBytes(SIG_ENCODING))); | |
KeyFactory kf = KeyFactory.getInstance(KEY_ALGORITHM); | |
PublicKey publicKey = kf.generatePublic(spec); | |
return verify(message.getBytes(MESSAGE_ENCODING), Base64.getDecoder().decode(signature), publicKey); | |
} | |
/** | |
* | |
* @param data raw data | |
* @param signature raw bytes of the signature | |
* @param publicKey the public key | |
* @return | |
* @throws NoSuchAlgorithmException | |
* @throws InvalidKeySpecException | |
* @throws UnsupportedEncodingException | |
* @throws InvalidKeyException | |
* @throws SignatureException | |
*/ | |
public static boolean verify(byte[] data, byte[] signature, PublicKey publicKey) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { | |
Signature sig = Signature.getInstance(SIG_ALGORITHM); | |
sig.initVerify(publicKey); | |
sig.update(data); | |
return sig.verify(signature); | |
} | |
} | |
class StringKeyPair { | |
private String publicKey; | |
private String privateKey; | |
public StringKeyPair() {} | |
/** | |
* @return the publicKey | |
*/ | |
public String getPublicKey() { | |
return publicKey; | |
} | |
/** | |
* @param publicKey the publicKey to set | |
*/ | |
public void setPublicKey(String publicKey) { | |
this.publicKey = publicKey; | |
} | |
/** | |
* @return the privateKey | |
*/ | |
public String getPrivateKey() { | |
return privateKey; | |
} | |
/** | |
* @param privateKey the privateKey to set | |
*/ | |
public void setPrivateKey(String privateKey) { | |
this.privateKey = privateKey; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package net.bubblemix.signature; | |
/** | |
* @author ian.chen | |
* | |
*/ | |
public class DigitalSignaturesExample { | |
/** | |
* @param args | |
*/ | |
public static void main(String[] args) { | |
String message = "Hello, I am a message"; | |
try { | |
// Asymmetric style | |
// generate asymetric key pair | |
StringKeyPair kp = AsymmetricKeySignature.generateStringKeyPair(); | |
// sign with the private key | |
String asymSignature = AsymmetricKeySignature.sign(message, kp.getPrivateKey()); | |
// verify with the public key | |
boolean asymmetricOK = AsymmetricKeySignature.verify(message, asymSignature, kp.getPublicKey()); | |
System.out.println("Asym OK? " + asymmetricOK); | |
// Symmetric style | |
// generate a shared key, actually any string is ok for this | |
// but its generating a hard to guess one is probly better | |
String sharedSecretKey = SymmetricKeySignature.generateHmacKey(); | |
// generate a signature | |
String symSignature = SymmetricKeySignature.generateDigest(message, sharedSecretKey); | |
// verification of a symmetric key signature is just done by | |
// recreating the signature and checking they match. | |
System.out.println("symSignature? " + symSignature); | |
} catch (Exception e) { | |
// TODO This is just a test, don't do this | |
e.printStackTrace(); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package net.bubblemix.signature; | |
import java.nio.charset.Charset; | |
import java.nio.charset.StandardCharsets; | |
import java.security.GeneralSecurityException; | |
import java.security.InvalidKeyException; | |
import java.security.NoSuchAlgorithmException; | |
import java.util.Base64; | |
import javax.crypto.KeyGenerator; | |
import javax.crypto.Mac; | |
import javax.crypto.SecretKey; | |
import javax.crypto.spec.SecretKeySpec; | |
import javax.xml.bind.DatatypeConverter; | |
public class SymmetricKeySignature { | |
private static final String DIGEST_ALGORITHM = "HmacSHA256"; | |
private static final Charset MESSAGE_ENCODING = StandardCharsets.UTF_8; | |
private static final Charset SALT_ENCODING = StandardCharsets.UTF_8; | |
private static final String SYMMETRIC_KEY_ALGORITHM = "HmacSHA256"; | |
/** | |
* Generate a key for symmetric key signing | |
* | |
* @return | |
* @throws NoSuchAlgorithmException | |
*/ | |
public static String generateHmacKey() throws NoSuchAlgorithmException { | |
KeyGenerator generator = KeyGenerator.getInstance(SYMMETRIC_KEY_ALGORITHM); | |
generator.init(256); | |
SecretKey key = generator.generateKey(); | |
return DatatypeConverter.printHexBinary(key.getEncoded()); | |
} | |
/** | |
* Generates a HMAC digest | |
* | |
* @param data | |
* @param salt | |
* @return | |
* @throws GeneralSecurityException | |
*/ | |
public static String generateDigest(String data, String salt) throws GeneralSecurityException { | |
byte[] hmacData = null; | |
try { | |
SecretKeySpec secretKey = new SecretKeySpec(salt.getBytes(SALT_ENCODING), DIGEST_ALGORITHM); | |
Mac mac = Mac.getInstance(DIGEST_ALGORITHM); | |
mac.init(secretKey); | |
hmacData = mac.doFinal(data.getBytes(MESSAGE_ENCODING)); | |
return new String(Base64.getEncoder().encode(hmacData)); | |
} catch (NoSuchAlgorithmException | InvalidKeyException e) { | |
// TODO: handle exception | |
e.printStackTrace(); | |
throw new GeneralSecurityException(e); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment