我正在尝试在我的应用中使用网络推送通知和网络推送协议。为了使用带有VAPID的Push API,我需要一个applicationServerKey
。
PushManager subscribe
方法将VAPID密钥(仅限公共密钥)作为参数,并提供订阅终点和推送消息的密钥。
要生成VAPID密钥,我一直在使用node.js(google web-push
包)和openssl
。但在我的用例中,VAPID密钥应该在Java中生成并传递给JavaScript以从浏览器订阅。
我正在尝试使用Java中的代码来生成VAPID密钥。我能够成功创建密钥但是当我传递生成的公钥(base64编码的字符串)时,subscribe
方法返回错误说:
无法注册服务人员。 DOMException:无法执行 '订阅' on' PushManager':提供的applicationServerKey不是 有效..
请帮我解决这个问题。下面是我的Java代码:
ECNamedCurveParameterSpec parameterSpec =
ECNamedCurveTable.getParameterSpec("prime256v1");
KeyPairGenerator keyPairGenerator =
KeyPairGenerator.getInstance("ECDH", "BC");
keyPairGenerator.initialize(parameterSpec);
KeyPair serverKey = keyPairGenerator.generateKeyPair();
PrivateKey priv = serverKey.getPrivate();
PublicKey pub = serverKey.getPublic();`
System.out.println(Base64.toBase64String(pub.getEncoded()));
答案 0 :(得分:1)
请参阅以下链接以获取MartijnDwars的答案。 https://github.com/web-push-libs/webpush-java/issues/30
您可以使用Utils.savePublicKey来转换您生成的Java PublicKey为byte []。然后将此byte []传递给 PushManager.subscribe方法。
在Java中对base64编码byte []可能更方便 base64解码JavaScript中的字符串。例如,生成之后 Java中的密钥对:
KeyPair keyPair = generateKeyPair(); byte[] publicKey = Utils.savePublicKey((ECPublicKey) keyPair.getPublic()); String publicKeyBase64 = BaseEncoding.base64Url().encode(publicKey); System.out.println("PublicKey = " + publicKeyBase64); // PublicKey = BPf36QAqZNNvvnl9kkpTDerXUOt6Nm6P4x9GEvmFVFKgVyCVWy24KUTs6wLQtbV2Ug81utbNnx86_vZzXDyrl88=
然后在JavaScript中:
function subscribe() { const publicKey = base64UrlToUint8Array('BPf36QAqZNNvvnl9kkpTDerXUOt6Nm6P4x9GEvmFVFKgVyCVWy24KUTs6wLQtbV2Ug81utbNnx86_vZzXDyrl88='); navigator.serviceWorker.ready.then(function (serviceWorkerRegistration) { serviceWorkerRegistration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: publicKey }) .then(function (subscription) { return sendSubscriptionToServer(subscription); }) .catch(function (e) { if (Notification.permission === 'denied') { console.warn('Permission for Notifications was denied'); } else { console.error('Unable to subscribe to push.', e); } }); }); } function base64UrlToUint8Array(base64UrlData) { const padding = '='.repeat((4 - base64UrlData.length % 4) % 4); const base64 = (base64UrlData + padding) .replace(/\-/g, '+') .replace(/_/g, '/'); const rawData = atob(base64); const buffer = new Uint8Array(rawData.length); for (let i = 0; i < rawData.length; ++i) { buffer[i] = rawData.charCodeAt(i); } return buffer; }
答案 1 :(得分:1)
花了数小时的时间,我想我将分享从收集几个网站中得出的解决方案,避免使用Bouncy Castle,它会产生很多其他问题。请尝试以下操作:
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC");
ECGenParameterSpec spec = new ECGenParameterSpec("secp256r1");
keyPairGenerator.initialize(spec, new SecureRandom());
KeyPair keyPair = keyPairGenerator.generateKeyPair();
ECPublicKey publicKey = (ECPublicKey) keyPair.getPublic();
ECPoint ecp = publicKey.getW();
byte[] x = ecp.getAffineX().toByteArray();
byte[] y = ecp.getAffineY().toByteArray();
// Convert 04 to bytes
String s= "04";
int len = s.length();
byte[] firstBit = new byte[len / 2];
for (int i = 0; i < len; i += 2)
{
firstBit[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) +
Character.digit(s.charAt(i+1), 16));
}
ByteArrayOutputStream outputStream = new ByteArrayOutputStream( );
outputStream.write(firstBit);
outputStream.write(x);
outputStream.write(y);
publicKeyBytes = outputStream.toByteArray( );
Base64 encoder = new Base64(-1,null,true);
byte[] encodedBytes = encoder.encode(publicKeyBytes);
String publicKeyBase64 = new String(encodedBytes, StandardCharsets.UTF_8);
答案 2 :(得分:0)
使用bountycastle,您可以使用以下代码生成有效的密钥:
ECNamedCurveParameterSpec parameterSpec = ECNamedCurveTable.getParameterSpec("prime256v1");
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("ECDH", "BC");
keyPairGenerator.initialize(parameterSpec);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
ECPublicKey publicKey = (ECPublicKey) keyPair.getPublic();
ECPrivateKey privateKey = (ECPrivateKey) keyPair.getPrivate();
String publicKeyString = Base64.getUrlEncoder().withoutPadding().encodeToString(publicKey.getQ().getEncoded(false));
System.out.println(publicKeyString);
String privateKeyString = Base64.getUrlEncoder().withoutPadding().encodeToString(privateKey.getD().toByteArray());
System.out.println(privateKeyString);