crypto.createCredentials PKCS12 not works #4050

Closed
tugrul opened this Issue Sep 23, 2012 · 22 comments

Comments

Projects
None yet

tugrul commented Sep 23, 2012

crypto.createCredentials() don't resolve PKCS12 to PEM pkey and cert.

I'm trying to use Google's service API with node.js. But I should use oauth to authorize with p12. Following code don't resolve p12 to der format.

var crypto = require('crypto');
var fs = require("fs");

var p12 = fs.readFileSync("googleservice.p12");
var credentials = crypto.createCredentials({pfx: p12, passphrase: "notasecret"});

console.log(credentials);

output:

{ context: {} }

I could resolve p12 with openssl toolkit or php to der format. I'm trying to use der format for signer.

Owner

bnoordhuis commented Sep 23, 2012

{ context: {} }

That's because the context doesn't have any enumerable properties, it's glue for a C++ object. What exactly is the problem?

tugrul commented Sep 23, 2012

@bnoordhuis

PKCS12 is password protected container format for other certificates. This operation should resolve from PKCS12 to DER format.

Following code writen in PHP and has same operation.

<?php
$credentials = array();
$p12 = file_get_contents("privatekey.p12");
$password = 'notasecret';

openssl_pkcs12_read($p12, $credentials, $password);

print_r($credentials);

Result:

Array
(
    [cert] => -----BEGIN CERTIFICATE-----
there was a cert deleted by me
-----END CERTIFICATE-----

    [pkey] => -----BEGIN RSA PRIVATE KEY-----
there was a private key deleted by me
-----END RSA PRIVATE KEY-----

    [extracerts] => Array
        (
        )

)
Owner

bnoordhuis commented Sep 23, 2012

This operation should resolve from PKCS12 to DER format.

Are you saying that you expect crypto.createCredentials() to create a textual representation of the PKCS12 input? What for?

tugrul commented Sep 23, 2012

@bnoordhuis

I don't know. It is assumed that the operation. There isn't any use case about PKCS12 input on the documentation. There are definitions only on the documentations.

http://nodejs.org/api/crypto.html#crypto_crypto_createcredentials_details

*pfx : A string or buffer holding the PFX or PKCS12 encoded private key, certificate and CA certificates
*passphrase : A string of passphrase for the private key or pfx

Also I found this:
joyent#2847

Owner

bnoordhuis commented Sep 23, 2012

Sorry, I'm going to close this issue. We're down five messages and I'm still not sure what the question is.

bnoordhuis closed this Sep 23, 2012

tugrul commented Sep 24, 2012

@bnoordhuis

There is a bug or documentation miss. Documentation says crypto.createCredentials() has PKCS12 support with weak information. I'm trying this function but I didn't any result with this function. It returns empty context object.

There are definitions about PKCS12 on documentation but there aren't any use case about this functionality. Actually I guess it because PKCS12 is password protected container format for private keys and certificates.

I want to documentation improvement if I use wrong this function.

Can you to check it? Is it bug? Is it documentation miss?

Owner

bnoordhuis commented Sep 24, 2012

There are definitions about PKCS12 on documentation but there aren't any use case about this functionality

Okay, I think I'm following this time.

crypto.createCredentials() creates a credentials object that you can pass to tls.createSecurePair() (which most people will never need to do but that's another story). The credentials object itself is opaque and uninteresting.

PKCS12 support in node.js is mostly restricted to the ability to load PFX files which can then be used to set up a TLS server or connection. It's possible to add functionality that lets you extract the key and certificates in PEM format but you'd have to convince me of the why first.

If you tell me what parts of the documentation are unclear and why, I'll update them.

tugrul commented Sep 25, 2012

After I have looked tls.createSecurePair(), I understood crypto.createCredentials()'s credentials object.

But I think this detail may be on this page: http://nodejs.org/api/crypto.html#crypto_crypto_createcredentials_details

It's possible to add functionality that lets you extract the key and certificates in PEM format but you'd have to convince me of the why first.

It's required for me when i want to make Google Service API client. Google API console provides me PKCS12 certificate for service-service authorization. I should extract PEM private key inside from PKCS12 to signing JWT request in runtime. You can see about information this pages:
https://developers.google.com/console/help/#service_accounts
https://developers.google.com/accounts/docs/OAuth2ServiceAccount

Also I added part of Official Google PHP API Client's jwt signer:

<?php
/*
 * Copyright 2011 Google Inc.
 *
 * 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.
 */

/**
 * Signs data.
 *
 * @author Brian Eaton <beaton@google.com>
 */
abstract class Google_Signer {
  /**
   * Signs data, returns the signature as binary data.
   */
  abstract public function sign($data);
}

/**
 * Signs data.
 *
 * Only used for testing.
 *
 * @author Brian Eaton <beaton@google.com>
 */
class Google_P12Signer extends Google_Signer {
  // OpenSSL private key resource
  private $privateKey;

  // Creates a new signer from a .p12 file.
  function __construct($p12, $password) {
    if (!function_exists('openssl_x509_read')) {
      throw new Exception(
          'The Google PHP API library needs the openssl PHP extension');
    }

    // This throws on error
    $certs = array();
    if (!openssl_pkcs12_read($p12, $certs, $password)) {
      throw new Google_AuthException("Unable to parse the p12 file.  " .
          "Is this a .p12 file?  Is the password correct?  OpenSSL error: " .
          openssl_error_string());
    }
    // TODO(beaton): is this part of the contract for the openssl_pkcs12_read
    // method?  What happens if there are multiple private keys?  Do we care?
    if (!array_key_exists("pkey", $certs) || !$certs["pkey"]) {
      throw new Google_AuthException("No private key found in p12 file.");
    }
    $this->privateKey = openssl_pkey_get_private($certs["pkey"]);
    if (!$this->privateKey) {
      throw new Google_AuthException("Unable to load private key in ");
    }
  }

  function __destruct() {
    if ($this->privateKey) {
      openssl_pkey_free($this->privateKey);
    }
  }

  function sign($data) {
    if (!openssl_sign($data, $signature, $this->privateKey, "sha256")) {
      throw new Google_AuthException("Unable to sign data");
    }
    return $signature;
  }
}
Owner

bnoordhuis commented Sep 25, 2012

Okay, reopening the issue.

bnoordhuis reopened this Sep 25, 2012

@threedot did you get this to work towards the Google API? I am constantly getting an { "error" : "invalid_grant" } it would be great if you could share your code.

vitorbal commented Feb 3, 2013

bump, would also like to know how this ended

Owner

bnoordhuis commented Feb 3, 2013

bump, would also like to know how this ended

See #4070. The short answer is 'not fixed.'

bendiy commented May 9, 2013

@MarkNijhof I wrote a simple JWT Encode/Decode App that's compatible with the Google API. It doesn't use the PKCS12 from Google, but instead I've extracted the Public/Private PEM files from the keystore. You can take a look at the code here:

https://github.com/xtuple/jwt-encode-decode-app

All of Google's API Client libraries just extract the private key from the PKCS12 keystore to sign the JWT

http://code.google.com/p/google-api-php-client/source/browse/trunk/src/auth/Google_P12Signer.php#65

jaybhunt commented Jun 4, 2013

Anyone aware of plans to resolve this? Would be really useful when signing a piece of data using the PEM key within a p12 cert.

dlongley commented Sep 7, 2013

Until node provides what's needed here, forge could be used as an alternative: https://github.com/digitalbazaar/forge#pkcs12

fabiob commented Apr 11, 2014

@dlongley, how can forge be used to extract a certificate from a pkcs12 archive?

@fabiob, I updated the README to show how to get access to a certificate from a safe bag in a pkcs12 archive. But I'll just copy and paste the example here quickly:

// decode p12 from base64
var p12Der = forge.util.decode64(p12b64);
// get p12 as ASN.1 object
var p12Asn1 = forge.asn1.fromDer(p12Der);
// decrypt p12
var p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1, 'password');
// p12.safeContents is an array of safe contents, each of
// which contains an array of safeBags, you can iterate over these
// to find what you want or use the getBags() API below

// get bags by type
var bags = pkcs12.getBags({bagType: forge.pki.oids.certBag});
// each bag as a 'cert' property with the certificate object
var cert = bags[0].cert;
// if you had fetched key bags instead, you can use the 'key' property
// to get the key

// get bags by friendlyName and filter on bag type
var bags = pkcs12.getBags({
  friendlyName: 'test',
  bagType: forge.pki.oids.certBag
});

Once retrieved from the pkcs12 archive, the cert object can be used as usual with the other forge API calls, such as forge.pki.certificateToPem(cert).

@indutny do you know if is this resolved in v0.12?

rayshan referenced this issue in google/google-api-nodejs-client Nov 19, 2014

Open

Support PKCS12 to .pem conversion for JWT auth #326

Owner

jasnell commented May 20, 2015

Does not appear to have ever landed. Marking as a feature request.

Owner

jasnell commented Jun 24, 2015

@indutny @bnoordhuis ... any updates on this one?

Owner

indutny commented Jul 6, 2015

What is the profit of doing it in core? PKCS12 is pretty fully described in https://tools.ietf.org/html/rfc7292, and can be parsed using pure javascript ASN.1 parser (moment of self-promotion: https://github.com/indutny/asn1.js ).

Do I miss something?

Owner

jasnell commented Aug 26, 2015

@indutny ... I definitely don't disagree. I'm inclined to close this one. If someone wishes to pursue this further, http://github.com/nodejs/node would be the right place to discuss it.

jasnell closed this Aug 26, 2015

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment