<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html xmlns:fn="http://www.w3.org/2005/02/xpath-functions"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <link rel="stylesheet" href="../../../../doc/otp_doc.css" type="text/css"> <title>Erlang -- Using the public_key API</title> </head> <body bgcolor="white" text="#000000" link="#0000ff" vlink="#ff00ff" alink="#ff0000"><div id="container"> <script id="js" type="text/javascript" language="JavaScript" src="../../../../doc/js/flipmenu/flipmenu.js"></script><script id="js2" type="text/javascript" src="../../../../doc/js/erlresolvelinks.js"></script><script language="JavaScript" type="text/javascript"> <!-- function getWinHeight() { var myHeight = 0; if( typeof( window.innerHeight ) == 'number' ) { //Non-IE myHeight = window.innerHeight; } else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) { //IE 6+ in 'standards compliant mode' myHeight = document.documentElement.clientHeight; } else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) { //IE 4 compatible myHeight = document.body.clientHeight; } return myHeight; } function setscrollpos() { var objf=document.getElementById('loadscrollpos'); document.getElementById("leftnav").scrollTop = objf.offsetTop - getWinHeight()/2; } function addEvent(obj, evType, fn){ if (obj.addEventListener){ obj.addEventListener(evType, fn, true); return true; } else if (obj.attachEvent){ var r = obj.attachEvent("on"+evType, fn); return r; } else { return false; } } addEvent(window, 'load', setscrollpos); //--></script><div id="leftnav"><div class="innertube"> <img alt="Erlang logo" src="../../../../doc/erlang-logo.png"><br><small><a href="users_guide.html">User's Guide</a><br><a href="index.html">Reference Manual</a><br><a href="release_notes.html">Release Notes</a><br><a href="../pdf/public_key-0.17.pdf">PDF</a><br><a href="../../../../doc/index.html">Top</a></small><p><strong>public_key</strong><br><strong>User's Guide</strong><br><small>Version 0.17</small></p> <br><a href="javascript:openAllFlips()">Expand All</a><br><a href="javascript:closeAllFlips()">Contract All</a><p><small><strong>Chapters</strong></small></p> <ul class="flipMenu" imagepath="../../../../doc/js/flipmenu"> <li id="no" title="Introduction" expanded="false">Introduction<ul> <li><a href="introduction.html"> Top of chapter </a></li> <li title="Purpose"><a href="introduction.html#id56658">Purpose</a></li> <li title="Prerequisites"><a href="introduction.html#id62235">Prerequisites</a></li> <li title="Performance tips"><a href="introduction.html#id56630">Performance tips</a></li> </ul> </li> <li id="no" title="Public key records" expanded="false">Public key records<ul> <li><a href="public_key_records.html"> Top of chapter </a></li> <li title="RSA as defined by the PKCS-1 standard and RFC 3447."><a href="public_key_records.html#id60524">RSA as defined by the PKCS-1 standard and RFC 3447.</a></li> <li title="DSA as defined by Digital Signature Standard (NIST FIPS PUB 186-2) "><a href="public_key_records.html#id60554">DSA as defined by Digital Signature Standard (NIST FIPS PUB 186-2) </a></li> </ul> </li> <li id="no" title="Certificate records" expanded="false">Certificate records<ul> <li><a href="cert_records.html"> Top of chapter </a></li> <li title="Common Data Types"><a href="cert_records.html#id61390">Common Data Types</a></li> <li title=" PKIX Certificates"><a href="cert_records.html#id61050"> PKIX Certificates</a></li> <li title="Standard certificate extensions"><a href="cert_records.html#id62358">Standard certificate extensions</a></li> <li title="Private Internet Extensions"><a href="cert_records.html#id62027">Private Internet Extensions</a></li> <li title=" CRL and CRL Extensions Profile"><a href="cert_records.html#id62112"> CRL and CRL Extensions Profile</a></li> </ul> </li> <li id="loadscrollpos" title="Using the public_key API" expanded="true">Using the public_key API<ul> <li><a href="using_public_key.html"> Top of chapter </a></li> <li title="General information"><a href="using_public_key.html#id60775">General information</a></li> <li title="PEM files"><a href="using_public_key.html#id60793">PEM files</a></li> <li title="RSA public key cryptography "><a href="using_public_key.html#id63884">RSA public key cryptography </a></li> <li title="Digital signatures"><a href="using_public_key.html#id63919">Digital signatures</a></li> <li title="SSH files"><a href="using_public_key.html#id63952">SSH files</a></li> </ul> </li> </ul> </div></div> <div id="content"> <div class="innertube"> <h1>4 Using the public_key API</h1> <h3><a name="id60775">4.1 General information</a></h3> <p> This chapter is dedicated to showing some examples of how to use the public_key API. Keys and certificates used in the following sections are generated only for the purpose of testing the public key application.</p> <p>Note that some shell printouts, in the following examples, have been abbreviated for increased readability.</p> <h3><a name="id60793">4.2 PEM files</a></h3> <p> Pulic key data (keys, certificates etc) may be stored in PEM format. PEM files comes from the Private Enhanced Mail Internet standard and has a structure that looks like this:</p> <div class="example"><pre><text> -----BEGIN <SOMETHING>----- <Attribute> : <Value> <Base64 encoded DER data> -----END <SOMETHING>----- <text></pre></div> <p>A file can contain several BEGIN/END blocks. Text lines between blocks are ignored. Attributes, if present, are currently ignored except for <span class="code">Proc-Type</span> and <span class="code">DEK-Info</span> that are used when the DER data is encrypted.</p> <h4>DSA private key</h4> <p>Note file handling is not done by the public_key application. </p> <div class="example"><pre>1> {ok, PemBin} = file:read_file("dsa.pem"). {ok,<<"-----BEGIN DSA PRIVATE KEY-----\nMIIBuw"...>>}</pre></div> <p>This PEM file only has one entry a private DSA key.</p> <div class="example"><pre>2> [DSAEntry] = public_key:pem_decode(PemBin). [{'DSAPrivateKey',<<48,130,1,187,2,1,0,2,129,129,0,183, 179,230,217,37,99,144,157,21,228,204, 162,207,61,246,...>>, not_encrypted}]</pre></div> <div class="example"><pre>3> Key = public_key:pem_entry_decode(DSAEntry). #'DSAPrivateKey'{version = 0, p = 12900045185019966618...6593, q = 1216700114794736143432235288305776850295620488937, g = 10442040227452349332...47213, y = 87256807980030509074...403143, x = 510968529856012146351317363807366575075645839654}</pre></div> <h4>RSA private key encrypted with a password.</h4> <div class="example"><pre>1> {ok, PemBin} = file:read_file("rsa.pem"). {ok,<<"Bag Attribut"...>>}</pre></div> <p>This PEM file only has one entry a private RSA key.</p> <div class="example"><pre>2>[RSAEntry] = public_key:pem_decode(PemBin). [{'RSAPrivateKey',<<224,108,117,203,152,40,15,77,128,126, 221,195,154,249,85,208,202,251,109, 119,120,57,29,89,19,9,...>>, {"DES-EDE3-CBC",<<"kÙeø¼pµL">>}}] </pre></div> <p>In this example the password is "abcd1234".</p> <div class="example"><pre>3> Key = public_key:pem_entry_decode(RSAEntry, "abcd1234"). #'RSAPrivateKey'{version = 'two-prime', modulus = 1112355156729921663373...2737107, publicExponent = 65537, privateExponent = 58064406231183...2239766033, prime1 = 11034766614656598484098...7326883017, prime2 = 10080459293561036618240...77738643771, exponent1 = 77928819327425934607...22152984217, exponent2 = 36287623121853605733...20588523793, coefficient = 924840412626098444...41820968343, otherPrimeInfos = asn1_NOVALUE}</pre></div> <h4>X509 Certificates</h4> <div class="example"><pre>1> {ok, PemBin} = file:read_file("cacerts.pem"). {ok,<<"-----BEGIN CERTIFICATE-----\nMIIC7jCCAl"...>>}</pre></div> <p>This file includes two certificates</p> <div class="example"><pre>2> [CertEntry1, CertEntry2] = public_key:pem_decode(PemBin). [{'Certificate',<<48,130,2,238,48,130,2,87,160,3,2,1,2,2, 9,0,230,145,97,214,191,2,120,150,48,13, ...>>, not_encrypted}, {'Certificate',<<48,130,3,200,48,130,3,49,160,3,2,1,2,2,1, 1,48,13,6,9,42,134,72,134,247,...>>>, not_encrypted}]</pre></div> <p>Certificates may of course be decoded as usual ... </p> <div class="example"><pre>2> Cert = public_key:pem_entry_decode(CertEntry1). #'Certificate'{ tbsCertificate = #'TBSCertificate'{ version = v3,serialNumber = 16614168075301976214, signature = #'AlgorithmIdentifier'{ algorithm = {1,2,840,113549,1,1,5}, parameters = <<5,0>>}, issuer = {rdnSequence, [[#'AttributeTypeAndValue'{ type = {2,5,4,3}, value = <<19,8,101,114,108,97,110,103,67,65>>}], [#'AttributeTypeAndValue'{ type = {2,5,4,11}, value = <<19,10,69,114,108,97,110,103,32,79,84,80>>}], [#'AttributeTypeAndValue'{ type = {2,5,4,10}, value = <<19,11,69,114,105,99,115,115,111,110,32,65,66>>}], [#'AttributeTypeAndValue'{ type = {2,5,4,7}, value = <<19,9,83,116,111,99,107,104,111,108,109>>}], [#'AttributeTypeAndValue'{ type = {2,5,4,6}, value = <<19,2,83,69>>}], [#'AttributeTypeAndValue'{ type = {1,2,840,113549,1,9,1}, value = <<22,22,112,101,116,101,114,64,101,114,...>>}]]}, validity = #'Validity'{ notBefore = {utcTime,"080109082929Z"}, notAfter = {utcTime,"080208082929Z"}}, subject = {rdnSequence, [[#'AttributeTypeAndValue'{ type = {2,5,4,3}, value = <<19,8,101,114,108,97,110,103,67,65>>}], [#'AttributeTypeAndValue'{ type = {2,5,4,11}, value = <<19,10,69,114,108,97,110,103,32,79,84,80>>}], [#'AttributeTypeAndValue'{ type = {2,5,4,10}, value = <<19,11,69,114,105,99,115,115,111,110,32,...>>}], [#'AttributeTypeAndValue'{ type = {2,5,4,7}, value = <<19,9,83,116,111,99,107,104,111,108,...>>}], [#'AttributeTypeAndValue'{ type = {2,5,4,6}, value = <<19,2,83,69>>}], [#'AttributeTypeAndValue'{ type = {1,2,840,113549,1,9,1}, value = <<22,22,112,101,116,101,114,64,...>>}]]}, subjectPublicKeyInfo = #'SubjectPublicKeyInfo'{ algorithm = #'AlgorithmIdentifier'{ algorithm = {1,2,840,113549,1,1,1}, parameters = <<5,0>>}, subjectPublicKey = {0,<<48,129,137,2,129,129,0,203,209,187,77,73,231,90,...>>}}, issuerUniqueID = asn1_NOVALUE, subjectUniqueID = asn1_NOVALUE, extensions = [#'Extension'{ extnID = {2,5,29,19}, critical = true, extnValue = [48,3,1,1,255]}, #'Extension'{ extnID = {2,5,29,15}, critical = false, extnValue = [3,2,1,6]}, #'Extension'{ extnID = {2,5,29,14}, critical = false, extnValue = [4,20,27,217,65,152,6,30,142|...]}, #'Extension'{ extnID = {2,5,29,17}, critical = false, extnValue = [48,24,129,22,112,101,116,101|...]}]}, signatureAlgorithm = #'AlgorithmIdentifier'{ algorithm = {1,2,840,113549,1,1,5}, parameters = <<5,0>>}, signature = {0, <<163,186,7,163,216,152,63,47,154,234,139,73,154,96,120, 165,2,52,196,195,109,167,192,...>>}} </pre></div> <p> Parts of certificates can be decoded with public_key:der_decode/2 using that parts ASN.1 type. Although application specific certificate extension requires application specific ASN.1 decode/encode-functions. Example, the first value of the rdnSequence above is of ASN.1 type 'X520CommonName'. ({2,5,4,3} = ?id-at-commonName)</p> <div class="example"><pre>public_key:der_decode('X520CommonName', <<19,8,101,114,108,97,110,103,67,65>>). {printableString,"erlangCA"}</pre></div> <p>... but certificates can also be decode using the pkix_decode_cert/2 that can customize and recursively decode standard parts of a certificate.</p> <div class="example"><pre>3>{_, DerCert, _} = CertEntry1.</pre></div> <div class="example"><pre>4> public_key:pkix_decode_cert(DerCert, otp). #'OTPCertificate'{ tbsCertificate = #'OTPTBSCertificate'{ version = v3,serialNumber = 16614168075301976214, signature = #'SignatureAlgorithm'{ algorithm = {1,2,840,113549,1,1,5}, parameters = 'NULL'}, issuer = {rdnSequence, [[#'AttributeTypeAndValue'{ type = {2,5,4,3}, value = {printableString,"erlangCA"}}], [#'AttributeTypeAndValue'{ type = {2,5,4,11}, value = {printableString,"Erlang OTP"}}], [#'AttributeTypeAndValue'{ type = {2,5,4,10}, value = {printableString,"Ericsson AB"}}], [#'AttributeTypeAndValue'{ type = {2,5,4,7}, value = {printableString,"Stockholm"}}], [#'AttributeTypeAndValue'{type = {2,5,4,6},value = "SE"}], [#'AttributeTypeAndValue'{ type = {1,2,840,113549,1,9,1}, value = "peter@erix.ericsson.se"}]]}, validity = #'Validity'{ notBefore = {utcTime,"080109082929Z"}, notAfter = {utcTime,"080208082929Z"}}, subject = {rdnSequence, [[#'AttributeTypeAndValue'{ type = {2,5,4,3}, value = {printableString,"erlangCA"}}], [#'AttributeTypeAndValue'{ type = {2,5,4,11}, value = {printableString,"Erlang OTP"}}], [#'AttributeTypeAndValue'{ type = {2,5,4,10}, value = {printableString,"Ericsson AB"}}], [#'AttributeTypeAndValue'{ type = {2,5,4,7}, value = {printableString,"Stockholm"}}], [#'AttributeTypeAndValue'{type = {2,5,4,6},value = "SE"}], [#'AttributeTypeAndValue'{ type = {1,2,840,113549,1,9,1}, value = "peter@erix.ericsson.se"}]]}, subjectPublicKeyInfo = #'OTPSubjectPublicKeyInfo'{ algorithm = #'PublicKeyAlgorithm'{ algorithm = {1,2,840,113549,1,1,1}, parameters = 'NULL'}, subjectPublicKey = #'RSAPublicKey'{ modulus = 1431267547247997...37419, publicExponent = 65537}}, issuerUniqueID = asn1_NOVALUE, subjectUniqueID = asn1_NOVALUE, extensions = [#'Extension'{ extnID = {2,5,29,19}, critical = true, extnValue = #'BasicConstraints'{ cA = true,pathLenConstraint = asn1_NOVALUE}}, #'Extension'{ extnID = {2,5,29,15}, critical = false, extnValue = [keyCertSign,cRLSign]}, #'Extension'{ extnID = {2,5,29,14}, critical = false, extnValue = [27,217,65,152,6,30,142,132,245|...]}, #'Extension'{ extnID = {2,5,29,17}, critical = false, extnValue = [{rfc822Name,"peter@erix.ericsson.se"}]}]}, signatureAlgorithm = #'SignatureAlgorithm'{ algorithm = {1,2,840,113549,1,1,5}, parameters = 'NULL'}, signature = {0, <<163,186,7,163,216,152,63,47,154,234,139,73,154,96,120, 165,2,52,196,195,109,167,192,...>>}} </pre></div> <p>This call is equivalent to public_key:pem_entry_decode(CertEntry1)</p> <div class="example"><pre>5> public_key:pkix_decode_cert(DerCert, plain). #'Certificate'{ ...} </pre></div> <h4>Encoding public key data to PEM format</h4> <p>If you have public key data and and want to create a PEM file you can do that by calling the functions public_key:pem_entry_encode/2 and pem_encode/1 and then saving the result to a file. For example assume you have PubKey = 'RSAPublicKey'{} then you can create a PEM-"RSA PUBLIC KEY" file (ASN.1 type 'RSAPublicKey') or a PEM-"PUBLIC KEY" file ('SubjectPublicKeyInfo' ASN.1 type).</p> <p> The second element of the PEM-entry will be the ASN.1 DER encoded key data.</p> <div class="example"><pre>1> PemEntry = public_key:pem_entry_encode('RSAPublicKey', RSAPubKey). {'RSAPublicKey', <<48,72,...>>, not_encrypted} 2> PemBin = public_key:pem_encode([PemEntry]). <<"-----BEGIN RSA PUBLIC KEY-----\nMEgC...>> 3> file:write_file("rsa_pub_key.pem", PemBin). ok</pre></div> <p> or </p> <div class="example"><pre>1> PemBin = public_key:pem_entry_encode('SubjectPublicKeyInfo', RSAPubKey). {'SubjectPublicKeyInfo', <<48,92...>>, not_encrypted} 2> PemBin = public_key:pem_encode([PemEntry]). <<"-----BEGIN PUBLIC KEY-----\nMFw...>> 3> file:write_file("pub_key.pem", PemBin). ok</pre></div> <h3><a name="id63884">4.3 RSA public key cryptography </a></h3> <p> Suppose you have PrivateKey = #'RSAPrivateKey{}' and the plaintext Msg = binary() and the corresponding public key PublicKey = #'RSAPublicKey'{} then you can do the following. Note that you normally will only do one of the encrypt or decrypt operations and the peer will do the other. </p> <p>Encrypt with the private key </p> <div class="example"><pre>RsaEncrypted = public_key:encrypt_private(Msg, PrivateKey), Msg = public_key:decrypt_public(RsaEncrypted, PublicKey),</pre></div> <p>Encrypt with the public key </p> <div class="example"><pre>RsaEncrypted = public_key:encrypt_public(Msg, PublicKey), Msg = public_key:decrypt_private(RsaEncrypted, PrivateKey),</pre></div> <h3><a name="id63919">4.4 Digital signatures</a></h3> <p> Suppose you have PrivateKey = #'RSAPrivateKey{}'or #'DSAPrivateKey'{} and the plaintext Msg = binary() and the corresponding public key PublicKey = #'RSAPublicKey'{} or {integer(), #'DssParams'{}} then you can do the following. Note that you normally will only do one of the sign or verify operations and the peer will do the other. </p> <div class="example"><pre>Signature = public_key:sign(Msg, sha, PrivateKey), true = public_key:verify(Msg, sha, Signature, PublicKey),</pre></div> <p>It might be appropriate to calculate the message digest before calling sign or verify and then you can use the none as second argument.</p> <div class="example"><pre>Digest = crypto:sha(Msg), Signature = public_key:sign(Digest, none, PrivateKey), true = public_key:verify(Digest, none, Signature, PublicKey), </pre></div> <h3><a name="id63952">4.5 SSH files</a></h3> <p>SSH typically uses PEM files for private keys but has its own file format for storing public keys. The erlang public_key application can be used to parse the content of SSH public key files.</p> <h4> RFC 4716 SSH public key files </h4> <p>RFC 4716 SSH files looks confusingly like PEM files, but there are some differences.</p> <div class="example"><pre>1> {ok, SshBin} = file:read_file("ssh2_rsa_pub"). {ok, <<"---- BEGIN SSH2 PUBLIC KEY ----\nAAAA"...>>}</pre></div> <p>This is equivalent to calling public_key:ssh_decode(SshBin, rfc4716_public_key). </p> <div class="example"><pre>2> public_key:ssh_decode(SshBin, public_key). [{#'RSAPublicKey'{modulus = 794430685...91663, publicExponent = 35}, []}] </pre></div> <h4> Openssh public key format </h4> <div class="example"><pre>1> {ok, SshBin} = file:read_file("openssh_dsa_pub"). {ok,<<"ssh-dss AAAAB3Nza"...>>}</pre></div> <p>This is equivalent to calling public_key:ssh_decode(SshBin, openssh_public_key). </p> <div class="example"><pre>2> public_key:ssh_decode(SshBin, public_key). [{{15642692...694280725, #'Dss-Parms'{p = 17291273936...696123221, q = 1255626590179665817295475654204371833735706001853, g = 10454211196...480338645}}, [{comment,"dhopson@VMUbuntu-DSH"}]}] </pre></div> <h4> Known hosts - openssh format</h4> <div class="example"><pre>1> {ok, SshBin} = file:read_file("known_hosts"). {ok,<<"hostname.domain.com,192.168.0.1 ssh-rsa AAAAB...>>}</pre></div> <p>Returns a list of public keys and their related attributes each pair of key and attributes corresponds to one entry in the known hosts file.</p> <div class="example"><pre>2> public_key:ssh_decode(SshBin, known_hosts). [{#'RSAPublicKey'{modulus = 1498979460408...72721699, publicExponent = 35}, [{hostnames,["hostname.domain.com","192.168.0.1"]}]}, {#'RSAPublicKey'{modulus = 14989794604088...2721699, publicExponent = 35}, [{comment,"foo@bar.com"}, {hostnames,["|1|BWO5qDxk/cFH0wa05JLdHn+j6xQ=|rXQvIxh5cDD3C43k5DPDamawVNA="]}]}] </pre></div> <h4> Authorized keys - openssh format</h4> <div class="example"><pre>1> {ok, SshBin} = file:read_file("auth_keys"). {ok, <<"command=\"dump /home\",no-pty,no-port-forwarding ssh-rsa AAA...>>}</pre></div> <p>Returns a list of public keys and their related attributes each pair of key and attributes corresponds to one entry in the authorized key file.</p> <div class="example"><pre>2> public_key:ssh_decode(SshBin, auth_keys). [{#'RSAPublicKey'{modulus = 794430685...691663, publicExponent = 35}, [{comment,"dhopson@VMUbuntu-DSH"}, {options,["command=\"dump/home\"","no-pty", "no-port-forwarding"]}]}, {{1564269258491...607694280725, #'Dss-Parms'{p = 17291273936185...763696123221, q = 1255626590179665817295475654204371833735706001853, g = 10454211195705...60511039590076780999046480338645}}, [{comment,"dhopson@VMUbuntu-DSH"}]}] </pre></div> <h4> Creating an SSH file from public key data </h4> <p>If you got a public key <span class="code">PubKey</span> and a related list of attributes <span class="code">Attributes</span> as returned by ssh_decode/2 you can create a new ssh file for example</p> <div class="example"><pre>N> SshBin = public_key:ssh_encode([{PubKey, Attributes}], openssh_public_key), <<"ssh-rsa "...>> N+1> file:write_file("id_rsa.pub", SshBin). ok</pre></div> </div> <div class="footer"> <hr> <p>Copyright © 2008-2012 Ericsson AB, All Rights Reserved</p> </div> </div> </div></body> </html>