1 /* 2 * Copyright 2007 Google, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package net.oauth.signature; 18 19 import java.io.ByteArrayInputStream; 20 import java.io.UnsupportedEncodingException; 21 import java.security.GeneralSecurityException; 22 import java.security.KeyFactory; 23 import java.security.PrivateKey; 24 import java.security.PublicKey; 25 import java.security.Signature; 26 import java.security.cert.CertificateFactory; 27 import java.security.cert.X509Certificate; 28 import java.security.spec.EncodedKeySpec; 29 import java.security.spec.PKCS8EncodedKeySpec; 30 import java.security.spec.X509EncodedKeySpec; 31 32 import net.oauth.OAuth; 33 import net.oauth.OAuthAccessor; 34 import net.oauth.OAuthException; 35 36 /** 37 * Class to handle RSA-SHA1 signatures on OAuth requests. A consumer 38 * that wishes to use public-key signatures on messages does not need 39 * a shared secret with the service provider, but it needs a private 40 * RSA signing key. You create it like this: 41 * 42 * OAuthConsumer c = new OAuthConsumer(callback_url, consumer_key, 43 * null, provider); 44 * c.setProperty(RSA_SHA1.PRIVATE_KEY, consumer_privateRSAKey); 45 * 46 * consumer_privateRSAKey must be an RSA signing key and 47 * of type java.security.PrivateKey, String, or byte[]. In the latter two 48 * cases, the key must be PKCS#8-encoded (byte[]) or PKCS#8-encoded and 49 * then Base64-encoded (String). 50 * 51 * A service provider that wishes to verify signatures made by such a 52 * consumer does not need a shared secret with the consumer, but it needs 53 * to know the consumer's public key. You create the necessary 54 * OAuthConsumer object (on the service provider's side) like this: 55 * 56 * OAuthConsumer c = new OAuthConsumer(callback_url, consumer_key, 57 * null, provider); 58 * c.setProperty(RSA_SHA1.PUBLIC_KEY, consumer_publicRSAKey); 59 * 60 * consumer_publicRSAKey must be the consumer's public RSAkey and 61 * of type java.security.PublicKey, String, or byte[]. In the latter two 62 * cases, the key must be X509-encoded (byte[]) or X509-encoded and 63 * then Base64-encoded (String). 64 * 65 * Alternatively, a service provider that wishes to verify signatures made 66 * by such a consumer can use a X509 certificate containing the consumer's 67 * public key. You create the necessary OAuthConsumer object (on the service 68 * provider's side) like this: 69 * 70 * OAuthConsumer c = new OAuthConsumer(callback_url, consumer_key, 71 * null, provider); 72 * c.setProperty(RSA_SHA1.X509_CERTIFICATE, consumer_cert); 73 * 74 * consumer_cert must be a X509 Certificate containing the consumer's public 75 * key and be of type java.security.cert.X509Certificate, String, 76 * or byte[]. In the latter two cases, the certificate must be DER-encoded 77 * (byte[]) or PEM-encoded (String). 78 * 79 * @author Dirk Balfanz 80 * @hide 81 * 82 */ 83 public class RSA_SHA1 extends OAuthSignatureMethod { 84 85 final static public String PRIVATE_KEY = "RSA-SHA1.PrivateKey"; 86 final static public String PUBLIC_KEY = "RSA-SHA1.PublicKey"; 87 final static public String X509_CERTIFICATE = "RSA-SHA1.X509Certificate"; 88 89 private PrivateKey privateKey = null; 90 private PublicKey publicKey = null; 91 92 @Override 93 protected void initialize(String name, OAuthAccessor accessor) 94 throws OAuthException { 95 super.initialize(name, accessor); 96 97 Object privateKeyObject = accessor.consumer.getProperty(PRIVATE_KEY); 98 try { 99 if (privateKeyObject != null) { 100 if (privateKeyObject instanceof PrivateKey) { 101 privateKey = (PrivateKey)privateKeyObject; 102 } else if (privateKeyObject instanceof String) { 103 privateKey = getPrivateKeyFromPem((String)privateKeyObject); 104 } else if (privateKeyObject instanceof byte[]) { 105 privateKey = getPrivateKeyFromDer((byte[])privateKeyObject); 106 } else { 107 throw new IllegalArgumentException( 108 "Private key set through RSA_SHA1.PRIVATE_KEY must be of " + 109 "type PrivateKey, String, or byte[], and not " + 110 privateKeyObject.getClass().getName()); 111 } 112 } 113 114 Object publicKeyObject = accessor.consumer.getProperty(PUBLIC_KEY); 115 if (publicKeyObject != null) { 116 if (publicKeyObject instanceof PublicKey) { 117 publicKey = (PublicKey)publicKeyObject; 118 } else if (publicKeyObject instanceof String) { 119 publicKey = getPublicKeyFromPem((String)publicKeyObject); 120 } else if (publicKeyObject instanceof byte[]) { 121 publicKey = getPublicKeyFromDer((byte[])publicKeyObject); 122 } else { 123 throw new IllegalArgumentException( 124 "Public key set through RSA_SHA1.PRIVATE_KEY must be of " + 125 "type PublicKey, String, or byte[], and not " + 126 publicKeyObject.getClass().getName()); 127 } 128 } else { // public key was null. perhaps they gave us a X509 cert. 129 Object certObject = accessor.consumer.getProperty(X509_CERTIFICATE); 130 if (certObject != null) { 131 if (certObject instanceof X509Certificate) { 132 publicKey = ((X509Certificate) certObject).getPublicKey(); 133 } else if (certObject instanceof String) { 134 publicKey = getPublicKeyFromPemCert((String)certObject); 135 } else if (certObject instanceof byte[]) { 136 publicKey = getPublicKeyFromDerCert((byte[])certObject); 137 } else { 138 throw new IllegalArgumentException( 139 "X509Certificate set through RSA_SHA1.X509_CERTIFICATE" + 140 " must be of type X509Certificate, String, or byte[]," + 141 " and not " + certObject.getClass().getName()); 142 } 143 } 144 } 145 } catch (GeneralSecurityException e) { 146 throw new OAuthException(e); 147 } 148 } 149 150 private PublicKey getPublicKeyFromPemCert(String certObject) 151 throws GeneralSecurityException { 152 CertificateFactory fac = CertificateFactory.getInstance("X509"); 153 ByteArrayInputStream in = new ByteArrayInputStream(certObject.getBytes()); 154 X509Certificate cert = (X509Certificate)fac.generateCertificate(in); 155 return cert.getPublicKey(); 156 } 157 158 private PublicKey getPublicKeyFromDerCert(byte[] certObject) 159 throws GeneralSecurityException { 160 CertificateFactory fac = CertificateFactory.getInstance("X509"); 161 ByteArrayInputStream in = new ByteArrayInputStream(certObject); 162 X509Certificate cert = (X509Certificate)fac.generateCertificate(in); 163 return cert.getPublicKey(); 164 } 165 166 private PublicKey getPublicKeyFromDer(byte[] publicKeyObject) 167 throws GeneralSecurityException { 168 KeyFactory fac = KeyFactory.getInstance("RSA"); 169 EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(publicKeyObject); 170 return fac.generatePublic(pubKeySpec); 171 } 172 173 private PublicKey getPublicKeyFromPem(String publicKeyObject) 174 throws GeneralSecurityException { 175 return getPublicKeyFromDer(decodeBase64(publicKeyObject)); 176 } 177 178 private PrivateKey getPrivateKeyFromDer(byte[] privateKeyObject) 179 throws GeneralSecurityException { 180 KeyFactory fac = KeyFactory.getInstance("RSA"); 181 EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(privateKeyObject); 182 return fac.generatePrivate(privKeySpec); 183 } 184 185 private PrivateKey getPrivateKeyFromPem(String privateKeyObject) 186 throws GeneralSecurityException { 187 return getPrivateKeyFromDer(decodeBase64(privateKeyObject)); 188 } 189 190 @Override 191 protected String getSignature(String baseString) throws OAuthException { 192 try { 193 byte[] signature = sign(baseString.getBytes(OAuth.ENCODING)); 194 return base64Encode(signature); 195 } catch (UnsupportedEncodingException e) { 196 throw new OAuthException(e); 197 } catch (GeneralSecurityException e) { 198 throw new OAuthException(e); 199 } 200 } 201 202 @Override 203 protected boolean isValid(String signature, String baseString) 204 throws OAuthException { 205 try { 206 return verify(decodeBase64(signature), 207 baseString.getBytes(OAuth.ENCODING)); 208 } catch (UnsupportedEncodingException e) { 209 throw new OAuthException(e); 210 } catch (GeneralSecurityException e) { 211 throw new OAuthException(e); 212 } 213 } 214 215 private byte[] sign(byte[] message) throws GeneralSecurityException { 216 if (privateKey == null) { 217 throw new IllegalStateException("need to set private key with " + 218 "OAuthConsumer.setProperty when " + 219 "generating RSA-SHA1 signatures."); 220 } 221 Signature signer = Signature.getInstance("SHA1withRSA"); 222 signer.initSign(privateKey); 223 signer.update(message); 224 return signer.sign(); 225 } 226 227 private boolean verify(byte[] signature, byte[] message) 228 throws GeneralSecurityException { 229 if (publicKey == null) { 230 throw new IllegalStateException("need to set public key with " + 231 " OAuthConsumer.setProperty when " + 232 "verifying RSA-SHA1 signatures."); 233 } 234 Signature verifier = Signature.getInstance("SHA1withRSA"); 235 verifier.initVerify(publicKey); 236 verifier.update(message); 237 return verifier.verify(signature); 238 } 239 } 240