Java tutorial
/* * MODIFICATIONS to the original source have been made by * Scott Douglass <scott@swdouglass.com> * * Copyright 2009 Scott Douglass <scott@swdouglass.com> * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * The original copyright notice and license terms are below. */ // // (C) Copyright 2007 VeriSign, Inc. All Rights Reserved. // // VeriSign, Inc. shall have no responsibility, financial or // otherwise, for any consequences arising out of the use of // this material. The program material is provided on an "AS IS" // BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. // // Distributed under an Apache License // http://www.apache.org/licenses/LICENSE-2.0 // package com.swdouglass.joid; import java.math.BigInteger; import java.security.InvalidKeyException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import javax.crypto.Mac; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import org.apache.commons.codec.binary.Base64; /** * Implements the cryptography needed for OpenID. */ public class Crypto { /** * Creates an instance of the crypto library. */ public Crypto() { } private DiffieHellman dh; /** * Digests a message using SHA-1. * @param text the bytes to digest. * @return the digested bytes. * @throws NoSuchAlgorithmException if SHA-1 is not available. */ public static byte[] sha1(byte[] text) throws NoSuchAlgorithmException { MessageDigest d = MessageDigest.getInstance("SHA-1"); return d.digest(text); } /** * Digests a message using SHA-256. * @param text the bytes to digest. * @return the digested bytes. * @throws NoSuchAlgorithmException if SHA-256 is not available. */ public static byte[] sha256(byte[] text) throws NoSuchAlgorithmException { MessageDigest d = MessageDigest.getInstance("SHA-256"); return d.digest(text); } /** * Signs a message using HMAC SHA1. * * @param key the key to sign with. * @param text the bytes to sign. * @return the signed bytes. * @throws InvalidKeyException if <code>key</code> is not a good HMAC key. * @throws NoSuchAlgorithmException if HMACSHA1 is not available. */ public static byte[] hmacSha1(byte[] key, byte[] text) throws InvalidKeyException, NoSuchAlgorithmException { return hmacShaX("HMACSHA1", key, text); } /** * Signs a message using HMAC SHA256. * * @param key the key to sign with. * @param text the bytes to sign. * @return the signed bytes. * @throws InvalidKeyException if <code>key</code> is not a good HMAC key. * @throws NoSuchAlgorithmException if HMACSHA1 is not available. */ public static byte[] hmacSha256(byte[] key, byte[] text) throws InvalidKeyException, NoSuchAlgorithmException { return hmacShaX("HMACSHA256", key, text); } private static byte[] hmacShaX(String keySpec, byte[] key, byte[] text) throws InvalidKeyException, NoSuchAlgorithmException { SecretKey sk = new SecretKeySpec(key, keySpec); Mac m = Mac.getInstance(sk.getAlgorithm()); m.init(sk); return m.doFinal(text); } private static SecureRandom random; static { try { random = SecureRandom.getInstance("SHA1PRNG"); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("No secure random available."); } } /** * Generates a random handle, in UUID format. * * @return a random handle. */ public static String generateHandle() { return java.util.UUID.randomUUID().toString(); } /** * Generates a random 'crumb' value. * * @return a four-byte random string. */ public static String generateCrumb() { byte[] b = new byte[4]; random.nextBytes(b); return convertToString(b); } /** * Generates random bytes. * * @param s the session or association type * @return random bytes, length fitting to the given session or * association type (length 0 to 32 bytes). */ public byte[] generateRandom(String s) { int len = 0; if (AssociationRequest.DH_SHA1.equals(s)) { len = 20; } else if (AssociationRequest.DH_SHA256.equals(s)) { len = 32; } else if (AssociationRequest.NO_ENCRYPTION.equals(s)) { len = 0; } else if (AssociationRequest.HMAC_SHA1.equals(s)) { len = 20; } else if (AssociationRequest.HMAC_SHA256.equals(s)) { len = 32; } byte[] result = new byte[len]; random.nextBytes(result); return result; } /** * Sets the Diffie-Hellman key values. * * @param mod the Diffie-Hellman modulus. * @param gen the Diffie-Hellman generator. */ public void setDiffieHellman(BigInteger mod, BigInteger gen) { this.dh = new DiffieHellman(mod, gen); } /** * Sets the Diffie-Hellman key values. * * @param dh the Diffie-Hellman value. */ public void setDiffieHellman(DiffieHellman dh) { this.dh = dh; } /** * Returns the Diffie-Hellman public key set * by {@link #setDiffieHellman(BigInteger, BigInteger)}. * * @return the Diffie-Hellman public key. * @throws IllegalArgumentException if this crypto instance has not * yet been initialized. */ public BigInteger getPublicKey() { if (dh == null) { throw new IllegalArgumentException("DH not yet initialized"); } return dh.getPublicKey(); } /** * Decrypts a secret using Diffie-Hellman. * * @param consumerPublic the public key used to decrypt. * @param secret the value to decrypt. * @return the decrypted value. */ public byte[] decryptSecret(BigInteger consumerPublic, byte[] secret) throws OpenIdException { return encryptSecret(consumerPublic, secret); } /** * Encrypts a secret using Diffie-Hellman. * * @param consumerPublic the public key used to encrypt. * @param secret the value to encrypt. * @return the encrypted secret value. */ public byte[] encryptSecret(BigInteger consumerPublic, byte[] secret) throws OpenIdException { if (dh == null) { throw new IllegalArgumentException("No DH implementation set"); } byte[] xoredSecret = null; try { xoredSecret = dh.xorSecret(consumerPublic, secret); return xoredSecret; } catch (NoSuchAlgorithmException e) { throw new OpenIdException(e); } } /** * Converts a string to bytes. * * @param s the string to convert. * @return the base 64 decoded bytes. */ public static byte[] convertToBytes(String s) { return Base64.decodeBase64(s.getBytes()); } /** * Converts bytes to string. * * @param b the bytes to encode. * @return the base 64 encoded string. */ public static String convertToString(byte[] b) { String s = new String(Base64.encodeBase64(b)); s = s.replaceAll("\n", ""); return s; } /** * Converts a big integer to string. * * @param b the big integer to convert. * @return the base 64 encoded string. */ public static String convertToString(BigInteger b) { return convertToString(b.toByteArray()); } /** * Converts a base64-encoded string to big int. * * @param s the string to convert, by way of base64 decoding. * @return the converted big int. */ public static BigInteger convertToBigIntegerFromString(String s) { return new BigInteger(Base64.decodeBase64(s.getBytes())); } }