1 // Copyright 2023 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 //////////////////////////////////////////////////////////////////////////////// 16 17 package com.google.crypto.tink.signature; 18 19 import static com.google.common.truth.Truth.assertThat; 20 import static java.nio.charset.StandardCharsets.UTF_8; 21 22 import com.google.crypto.tink.InsecureSecretKeyAccess; 23 import com.google.crypto.tink.KeyTemplates; 24 import com.google.crypto.tink.KeysetHandle; 25 import com.google.crypto.tink.PublicKeySign; 26 import com.google.crypto.tink.PublicKeyVerify; 27 import com.google.crypto.tink.RegistryConfiguration; 28 import com.google.crypto.tink.util.SecretBigInteger; 29 import java.security.KeyFactory; 30 import java.security.KeyPair; 31 import java.security.KeyPairGenerator; 32 import java.security.PrivateKey; 33 import java.security.PublicKey; 34 import java.security.Signature; 35 import java.security.interfaces.ECPrivateKey; 36 import java.security.interfaces.ECPublicKey; 37 import java.security.interfaces.RSAPrivateCrtKey; 38 import java.security.interfaces.RSAPublicKey; 39 import java.security.spec.RSAPrivateCrtKeySpec; 40 import java.security.spec.RSAPublicKeySpec; 41 import org.junit.BeforeClass; 42 import org.junit.Test; 43 import org.junit.runner.RunWith; 44 import org.junit.runners.JUnit4; 45 46 @RunWith(JUnit4.class) 47 public final class KeyConversionTest { 48 49 @BeforeClass setUp()50 public static void setUp() throws Exception { 51 SignatureConfig.register(); 52 } 53 54 @Test signAndVerifyWithEcdsaUsingJavaECKeys()55 public void signAndVerifyWithEcdsaUsingJavaECKeys() throws Exception { 56 // Generate a EC key pair 57 KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC"); 58 keyGen.initialize(EcdsaParameters.CurveType.NIST_P384.toParameterSpec()); 59 KeyPair keyPair = keyGen.generateKeyPair(); 60 PrivateKey privateKey = keyPair.getPrivate(); 61 PublicKey publicKey = keyPair.getPublic(); 62 63 // Convert publicKey into a Tink EcdsaPublicKey. 64 ECPublicKey ecPublicKey = (ECPublicKey) publicKey; 65 EcdsaPublicKey ecdsaPublicKey = 66 EcdsaPublicKey.builder() 67 .setParameters( 68 EcdsaParameters.builder() 69 .setSignatureEncoding(EcdsaParameters.SignatureEncoding.IEEE_P1363) 70 .setCurveType(EcdsaParameters.CurveType.NIST_P384) 71 .setHashType(EcdsaParameters.HashType.SHA384) 72 .setVariant(EcdsaParameters.Variant.NO_PREFIX) 73 .build()) 74 .setPublicPoint(ecPublicKey.getW()) 75 .build(); 76 77 // Convert privateKey and publicKey into a Tink EcdsaPrivateKey. 78 ECPrivateKey ecPrivateKey = (ECPrivateKey) privateKey; 79 EcdsaPrivateKey ecdsaPrivateKey = 80 EcdsaPrivateKey.builder() 81 .setPublicKey( 82 EcdsaPublicKey.builder() 83 .setParameters( 84 EcdsaParameters.builder() 85 .setSignatureEncoding(EcdsaParameters.SignatureEncoding.IEEE_P1363) 86 .setCurveType(EcdsaParameters.CurveType.NIST_P384) 87 .setHashType(EcdsaParameters.HashType.SHA384) 88 .setVariant(EcdsaParameters.Variant.NO_PREFIX) 89 .build()) 90 .setPublicPoint(ecPublicKey.getW()) 91 .build()) 92 .setPrivateValue( 93 SecretBigInteger.fromBigInteger(ecPrivateKey.getS(), InsecureSecretKeyAccess.get())) 94 .build(); 95 96 // Generate a PublicKeySign primitive from ecdsaPrivateKey and sign a message. 97 KeysetHandle privateHandle = 98 KeysetHandle.newBuilder() 99 .addEntry(KeysetHandle.importKey(ecdsaPrivateKey).withRandomId().makePrimary()) 100 .build(); 101 PublicKeySign signer = 102 privateHandle.getPrimitive(RegistryConfiguration.get(), PublicKeySign.class); 103 byte[] data = "data".getBytes(UTF_8); 104 byte[] sig = signer.sign(data); 105 106 // Generate a PublicKeyVerify primitive from ecdsaPublicKey, and verify the signature. 107 KeysetHandle publicHandle = 108 KeysetHandle.newBuilder() 109 .addEntry(KeysetHandle.importKey(ecdsaPublicKey).withRandomId().makePrimary()) 110 .build(); 111 PublicKeyVerify verifier = 112 publicHandle.getPrimitive(RegistryConfiguration.get(), PublicKeyVerify.class); 113 verifier.verify(sig, data); 114 } 115 116 /** 117 * This test show how Tink can be used with Java RSA keys, by importing them as RSA SSA PKCS1 118 * keys. 119 */ 120 @Test signAndVerifyUsingJavaRSAKeys()121 public void signAndVerifyUsingJavaRSAKeys() throws Exception { 122 // Generate a RSA key pair 123 KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); 124 keyGen.initialize(/* keysize= */ 2048); 125 KeyPair keyPair = keyGen.generateKeyPair(); 126 PrivateKey privateKey = keyPair.getPrivate(); 127 PublicKey publicKey = keyPair.getPublic(); 128 129 // Convert publicKey into a Tink RsaSsaPkcs1PublicKey. 130 RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey; 131 RsaSsaPkcs1PublicKey rsaSsaPkcs1PublicKey = 132 RsaSsaPkcs1PublicKey.builder() 133 .setParameters( 134 RsaSsaPkcs1Parameters.builder() 135 .setModulusSizeBits(rsaPublicKey.getModulus().bitLength()) 136 .setPublicExponent(rsaPublicKey.getPublicExponent()) 137 .setHashType(RsaSsaPkcs1Parameters.HashType.SHA256) 138 .setVariant(RsaSsaPkcs1Parameters.Variant.NO_PREFIX) 139 .build()) 140 .setModulus(rsaPublicKey.getModulus()) 141 .build(); 142 143 // Convert privateKey and publicKey into a Tink RsaSsaPkcs1PrivateKey. 144 RSAPrivateCrtKey rsaPrivateKey = (RSAPrivateCrtKey) privateKey; 145 RsaSsaPkcs1PrivateKey rsaSsaPkcs1PrivateKey = 146 RsaSsaPkcs1PrivateKey.builder() 147 .setPublicKey(rsaSsaPkcs1PublicKey) 148 .setPrimes( 149 SecretBigInteger.fromBigInteger( 150 rsaPrivateKey.getPrimeP(), InsecureSecretKeyAccess.get()), 151 SecretBigInteger.fromBigInteger( 152 rsaPrivateKey.getPrimeQ(), InsecureSecretKeyAccess.get())) 153 .setPrivateExponent( 154 SecretBigInteger.fromBigInteger( 155 rsaPrivateKey.getPrivateExponent(), InsecureSecretKeyAccess.get())) 156 .setPrimeExponents( 157 SecretBigInteger.fromBigInteger( 158 rsaPrivateKey.getPrimeExponentP(), InsecureSecretKeyAccess.get()), 159 SecretBigInteger.fromBigInteger( 160 rsaPrivateKey.getPrimeExponentQ(), InsecureSecretKeyAccess.get())) 161 .setCrtCoefficient( 162 SecretBigInteger.fromBigInteger( 163 rsaPrivateKey.getCrtCoefficient(), InsecureSecretKeyAccess.get())) 164 .build(); 165 166 // Generate a PublicKeySign primitive from rsaSsaPkcs1PrivateKey and sign a message. 167 KeysetHandle privateHandle = 168 KeysetHandle.newBuilder() 169 .addEntry(KeysetHandle.importKey(rsaSsaPkcs1PrivateKey).withRandomId().makePrimary()) 170 .build(); 171 PublicKeySign signer = 172 privateHandle.getPrimitive(RegistryConfiguration.get(), PublicKeySign.class); 173 byte[] data = "data".getBytes(UTF_8); 174 byte[] sig = signer.sign(data); 175 176 // Generate a PublicKeyVerify primitive from rsaSsaPkcs1PublicKey, and verify the signature. 177 KeysetHandle publicHandle = 178 KeysetHandle.newBuilder() 179 .addEntry(KeysetHandle.importKey(rsaSsaPkcs1PublicKey).withRandomId().makePrimary()) 180 .build(); 181 PublicKeyVerify verifier = 182 publicHandle.getPrimitive(RegistryConfiguration.get(), PublicKeyVerify.class); 183 verifier.verify(sig, data); 184 185 // Verify using java.security.Signature. 186 Signature signatureVerify = Signature.getInstance("SHA256withRSA"); 187 signatureVerify.initVerify(publicKey); 188 signatureVerify.update(data); 189 assertThat(signatureVerify.verify(sig)).isTrue(); 190 } 191 192 /** This test shows how Tink keys can be exported to Java RSA keys. */ 193 @Test exportToJavaRSAKey()194 public void exportToJavaRSAKey() throws Exception { 195 KeysetHandle privateHandle = 196 KeysetHandle.generateNew(KeyTemplates.get("RSA_SSA_PKCS1_3072_SHA256_F4_RAW")); 197 KeysetHandle publicHandle = privateHandle.getPublicKeysetHandle(); 198 199 PublicKeySign signer = 200 privateHandle.getPrimitive(RegistryConfiguration.get(), PublicKeySign.class); 201 byte[] data = "data".getBytes(UTF_8); 202 byte[] sig = signer.sign(data); 203 204 // Export private key and sign using java.security.Signature. 205 RsaSsaPkcs1PrivateKey rsaSsaPkcs1PrivateKey = 206 (RsaSsaPkcs1PrivateKey) privateHandle.getAt(0).getKey(); 207 PrivateKey privateKey = 208 KeyFactory.getInstance("RSA") 209 .generatePrivate( 210 new RSAPrivateCrtKeySpec( 211 rsaSsaPkcs1PrivateKey.getPublicKey().getModulus(), 212 rsaSsaPkcs1PrivateKey.getParameters().getPublicExponent(), 213 rsaSsaPkcs1PrivateKey 214 .getPrivateExponent() 215 .getBigInteger(InsecureSecretKeyAccess.get()), 216 rsaSsaPkcs1PrivateKey.getPrimeP().getBigInteger(InsecureSecretKeyAccess.get()), 217 rsaSsaPkcs1PrivateKey.getPrimeQ().getBigInteger(InsecureSecretKeyAccess.get()), 218 rsaSsaPkcs1PrivateKey 219 .getPrimeExponentP() 220 .getBigInteger(InsecureSecretKeyAccess.get()), 221 rsaSsaPkcs1PrivateKey 222 .getPrimeExponentQ() 223 .getBigInteger(InsecureSecretKeyAccess.get()), 224 rsaSsaPkcs1PrivateKey 225 .getCrtCoefficient() 226 .getBigInteger(InsecureSecretKeyAccess.get()))); 227 Signature signatureSigner = Signature.getInstance("SHA256withRSA"); 228 signatureSigner.initSign(privateKey); 229 signatureSigner.update(data); 230 // These signatures are deterministic, so they must be equal. 231 assertThat(signatureSigner.sign()).isEqualTo(sig); 232 233 // Export public key and verify using java.security.Signature. 234 RsaSsaPkcs1PublicKey rsaSsaPkcs1PublicKey = 235 (RsaSsaPkcs1PublicKey) publicHandle.getAt(0).getKey(); 236 PublicKey publicKey = 237 KeyFactory.getInstance("RSA") 238 .generatePublic( 239 new RSAPublicKeySpec( 240 rsaSsaPkcs1PublicKey.getModulus(), 241 rsaSsaPkcs1PublicKey.getParameters().getPublicExponent())); 242 Signature signatureVerify = Signature.getInstance("SHA256withRSA"); 243 signatureVerify.initVerify(publicKey); 244 signatureVerify.update(data); 245 assertThat(signatureVerify.verify(sig)).isTrue(); 246 } 247 } 248